163 lines
5.3 KiB
Lua
163 lines
5.3 KiB
Lua
-- ------------------------------------------------------------------------------ --
|
|
-- TradeSkillMaster --
|
|
-- https://tradeskillmaster.com --
|
|
-- All Rights Reserved - Detailed license information included with addon. --
|
|
-- ------------------------------------------------------------------------------ --
|
|
|
|
--- Math Functions
|
|
-- @module Math
|
|
|
|
local _, TSM = ...
|
|
local Math = TSM.Init("Util.Math")
|
|
local TempTable = TSM.Include("Util.TempTable")
|
|
local NAN = math.huge * 0
|
|
local IS_NAN_GT_INF = NAN > math.huge
|
|
local NAN_STR = tostring(NAN)
|
|
local private = {
|
|
keysTemp = {},
|
|
}
|
|
|
|
|
|
|
|
-- ============================================================================
|
|
-- Module Functions
|
|
-- ============================================================================
|
|
|
|
--- Returns NAN.
|
|
-- @treturn number NAN
|
|
function Math.GetNan()
|
|
return NAN
|
|
end
|
|
|
|
--- Checks if a value is NAN.
|
|
-- @tparam number value The number to check
|
|
-- @treturn boolean Whether or not the value is NAN
|
|
function Math.IsNan(value)
|
|
if IS_NAN_GT_INF then
|
|
-- optimization if NAN > math.huge (which it is in Wow's version of lua)
|
|
return value > math.huge and tostring(value) == NAN_STR
|
|
else
|
|
return tostring(value) == NAN_STR
|
|
end
|
|
end
|
|
|
|
--- Rounds a value to a specified significant value.
|
|
-- @tparam number value The number to be rounded
|
|
-- @tparam number sig The value to round to the nearest multiple of
|
|
-- @treturn number The rounded value
|
|
function Math.Round(value, sig)
|
|
sig = sig or 1
|
|
return floor((value / sig) + 0.5) * sig
|
|
end
|
|
|
|
--- Rounds a value down to a specified significant value.
|
|
-- @tparam number value The number to be rounded
|
|
-- @tparam number sig The value to round down to the nearest multiple of
|
|
-- @treturn number The rounded value
|
|
function Math.Floor(value, sig)
|
|
sig = sig or 1
|
|
return floor(value / sig) * sig
|
|
end
|
|
|
|
--- Rounds a value up to a specified significant value.
|
|
-- @tparam number value The number to be rounded
|
|
-- @tparam number sig The value to round up to the nearest multiple of
|
|
-- @treturn number The rounded value
|
|
function Math.Ceil(value, sig)
|
|
sig = sig or 1
|
|
return ceil(value / sig) * sig
|
|
end
|
|
|
|
--- Scales a value from one range to another.
|
|
-- @tparam number value The number to be scaled
|
|
-- @tparam number fromStart The start value of the range to scale from
|
|
-- @tparam number fromEnd The end value of the range to scale from (can be less than fromStart)
|
|
-- @tparam number toStart The start value of the range to scale to
|
|
-- @tparam number toEnd The end value of the range to scale to (can be less than toStart)
|
|
-- @treturn number The scaled value
|
|
function Math.Scale(value, fromStart, fromEnd, toStart, toEnd)
|
|
assert(value >= min(fromStart, fromEnd) and value <= max(fromStart, fromEnd))
|
|
return toStart + ((value - fromStart) / (fromEnd - fromStart)) * (toEnd - toStart)
|
|
end
|
|
|
|
--- Bounds a number between a min and max value.
|
|
-- @tparam number value The number to be bounded
|
|
-- @tparam number minValue The min value
|
|
-- @tparam number maxValue The max value
|
|
-- @treturn number The bounded value
|
|
function Math.Bound(value, minValue, maxValue)
|
|
return min(max(value, minValue), maxValue)
|
|
end
|
|
|
|
--- Calculates the has of the specified data
|
|
-- This data can handle data of type string or number. It can also handle a table being passed as the data assuming
|
|
-- all keys and values of the table are also hashable (strings, numbers, or tables with the same restriction). This
|
|
-- function uses the [djb2 algorithm](http://www.cse.yorku.ca/~oz/hash.html).
|
|
-- @param data The data to be hased
|
|
-- @tparam[opt] number hash The initial value of the hash
|
|
-- @treturn number The hash value
|
|
function Math.CalculateHash(data, hash)
|
|
hash = hash or 5381
|
|
local maxValue = 2 ^ 24
|
|
local dataType = type(data)
|
|
if dataType == "string" then
|
|
-- iterate through 8 bytes at a time
|
|
for i = 1, ceil(#data / 8) do
|
|
local b1, b2, b3, b4, b5, b6, b7, b8 = strbyte(data, (i - 1) * 8 + 1, i * 8)
|
|
hash = (hash * 33 + b1) % maxValue
|
|
if not b2 then break end
|
|
hash = (hash * 33 + b2) % maxValue
|
|
if not b3 then break end
|
|
hash = (hash * 33 + b3) % maxValue
|
|
if not b4 then break end
|
|
hash = (hash * 33 + b4) % maxValue
|
|
if not b5 then break end
|
|
hash = (hash * 33 + b5) % maxValue
|
|
if not b6 then break end
|
|
hash = (hash * 33 + b6) % maxValue
|
|
if not b7 then break end
|
|
hash = (hash * 33 + b7) % maxValue
|
|
if not b8 then break end
|
|
hash = (hash * 33 + b8) % maxValue
|
|
end
|
|
elseif dataType == "number" then
|
|
assert(data == floor(data), "Invalid number")
|
|
if data < 0 then
|
|
data = data * -1
|
|
hash = (hash * 33 + 59) % maxValue
|
|
end
|
|
while data > 0 do
|
|
hash = (hash * 33 + data % 256) % maxValue
|
|
data = floor(data / 256)
|
|
end
|
|
elseif dataType == "table" then
|
|
local keys = nil
|
|
if private.keysTemp.inUse then
|
|
keys = TempTable.Acquire()
|
|
else
|
|
keys = private.keysTemp
|
|
private.keysTemp.inUse = true
|
|
end
|
|
for k in pairs(data) do
|
|
tinsert(keys, k)
|
|
end
|
|
sort(keys)
|
|
for _, key in ipairs(keys) do
|
|
hash = Math.CalculateHash(key, hash)
|
|
hash = Math.CalculateHash(data[key], hash)
|
|
end
|
|
if keys == private.keysTemp then
|
|
wipe(private.keysTemp)
|
|
else
|
|
TempTable.Release(keys)
|
|
end
|
|
elseif dataType == "boolean" then
|
|
hash = (hash * 33 + (data and 1 or 0)) % maxValue
|
|
elseif dataType == "nil" then
|
|
hash = (hash * 33 + 17) % maxValue
|
|
else
|
|
error("Invalid data")
|
|
end
|
|
return hash
|
|
end
|