local d={}d.default={precision=3;removeTrailingZeros=true;delimiter=" ",scale="\083\073";unit=""}d.scales={SI={"\075";"\077","\071","\084";"\080";"\069";"\090";"\089";[0]="";[-1]="\109";[-2]="\206\188",[-3]="\110",[-4]="\112",[-5]="\102",[-6]="\097",[-7]="\122";[-8]="\121"},shortScale={"\116\104\111\117\115\097\110\100";"\109\105\108\108\105\111\110","\098\105\108\108\105\111\110","\116\114\105\108\108\105\111\110","\113\117\097\100\114\105\108\108\105\111\110","\113\117\105\110\116\105\108\108\105\111\110","\115\101\120\116\105\108\108\105\111\110";"\115\101\112\116\105\108\108\105\111\110";[0]="",[-1]="\116\104\111\117\115\097\110\100\116\104",[-2]="\109\105\108\108\105\111\110\116\104",[-3]="\098\105\108\108\105\111\110\116\104";[-4]="\116\114\105\108\108\105\111\110\116\104";[-5]="\113\117\097\100\114\105\108\108\105\111\110\116\104",[-6]="\113\117\105\110\116\105\108\108\105\111\110\116\104";[-7]="\115\101\120\116\105\108\108\105\111\110\116\104";[-8]="\115\101\112\116\105\108\108\105\111\110\116\104"},longScale={"\116\104\111\117\115\097\110\100";"\109\105\108\108\105\111\110";"\109\105\108\108\105\097\114\100";"\098\105\108\108\105\111\110","\098\105\108\108\105\097\114\100";"\116\114\105\108\108\105\111\110";"\116\114\105\108\108\105\097\114\100";"\113\117\097\100\114\105\108\108\105\111\110";[0]="",[-1]="\116\104\111\117\115\097\110\100\116\104";[-2]="\109\105\108\108\105\111\110\116\104";[-3]="\109\105\108\108\105\097\114\100\116\104",[-4]="\098\105\108\108\105\111\110\116\104";[-5]="\098\105\108\108\105\097\114\100\116\104";[-6]="\116\114\105\108\108\105\111\110\116\104";[-7]="\116\114\105\108\108\105\097\114\100\116\104",[-8]="\113\117\097\100\114\105\108\108\105\111\110\116\104"}}local h={}h.__index=d local e={__index=h}local function O(d)return d~=d end local function t(d)return d==-math.huge or d==math.huge end local function x(d)return tonumber(d)end local function Q(d)return tonumber(d)and tonumber(d)==math.floor(d)end local function M(d)return type(d)=="\115\116\114\105\110\103"or type(d)=="\110\117\109\098\101\114"end local function a(...)local d={...}local h=select("\035",...)for h=1,h,1 do if not M(d[h])then return false end end return true end local function y(d)if t(d)then return"\226\136\158"elseif O(d)then return"\078\097\078"else return string.format("\037\103",d)end end local function u(h,e,O,t,Q,M)assert(x(h),"\087\114\111\110\103 \097\114\103\117\109\101\110\116 \103\105\118\101\110 \102\111\114 \110\117\109\098\101\114\044 \101\120\112\101\099\116\101\100 \110\117\109\098\101\114 \098\117\116 \105\110\115\116\101\097\100 \114\101\099\101\105\118\101\100 "..typeof(h))local a=math.floor(math.log(math.abs(h),1000))local u=d.scales[Q][a]local v if u then v=string.format("\037"..((O and"\035"or"")..("\046"..(e.."\102"))),h/10^(a*3))if O then v=string.gsub(v,"\037\046\063\048\042\036","")end else u=""v=y(h)end return v..(t..(u..M))end function h.format(d,...)local h={...}local e=select("\035",...)local O=table.create(e)for e=1,e,1 do O[e]=u(h[e],d.precision,d.removeTrailingZeros,d.delimiter,d.scale,d.unit)end return unpack(O)end function d.new(h,O,t,x,M)h=h or d.default.precision O=O==nil and d.default.removeTrailingZeros or O t=t or d.default.delimiter x=x or d.default.scale M=M or d.default.unit assert(Q(h),"\087\114\111\110\103 \097\114\103\117\109\101\110\116 \103\105\118\101\110 \102\111\114 \112\114\101\099\105\115\105\111\110\044 \101\120\112\101\099\116\101\100 \105\110\116\101\103\101\114 \098\117\116 \114\101\099\101\105\118\101\100 "..typeof(h))assert(d.scales[x],"\119\114\111\110\103 \097\114\103\117\109\101\110\116 \103\105\118\101\110 \102\111\114 \115\099\097\108\101\044 \101\120\112\101\099\116\101\100 \097 \115\116\114\105\110\103 \040\083\073\044 \115\104\111\114\116\083\099\097\108\101\044 \108\111\110\103\083\099\097\108\101 \111\114 \097 \099\117\115\116\111\109 \115\099\097\108\101\041\033")assert(a(t,M),"\087\114\111\110\103 \097\114\103\117\109\101\110\116 \103\105\118\101 \102\111\114 \115\099\097\108\101\047\117\110\105\116\044 \101\120\112\101\099\116\101\100 \115\116\114\105\110\103 \098\117\116 \114\101\099\101\105\118\101\100 "..(typeof(t)..(" \097\110\100 "..typeof(M))))return setmetatable({precision=h;removeTrailingZeros=O;delimiter=t,scale=x,unit=M},e)end function d.format(h,...)return(d.new(...)):format(h)end return d
-- @Author: VerdommeMan, see https://github.com/VerdommeMan/convert-to-human-readable-numbers for more information 
-- @Version: 1.0.3

local Formatter = {}
Formatter.default = {precision = 3, removeTrailingZeros = true, delimiter = " ",  scale = "SI", unit = ""}
Formatter.scales = {
    SI = {"K", "M", "G", "T", "P", "E", "Z", "Y", [0] = "", [-1] = "m", [-2] = "μ", [-3] = "n", [-4] = "p", [-5] = "f", [-6] = "a", [-7] = "z", [-8] = "y"},
    shortScale = {"thousand", "million", "billion", "trillion", "quadrillion", "quintillion", "sextillion", "septillion", [0] = "", [-1] = "thousandth", [-2] = "millionth", [-3] = "billionth", [-4] = "trillionth", [-5] = "quadrillionth", [-6] = "quintillionth", [-7] = "sextillionth", [-8] = "septillionth"},
    longScale = {"thousand", "million", "milliard", "billion", "billiard", "trillion", "trilliard", "quadrillion", [0] = "", [-1] = "thousandth", [-2] = "millionth", [-3] = "milliardth", [-4] = "billionth", [-5] = "billiardth", [-6] = "trillionth", [-7] = "trilliardth", [-8] = "quadrillionth"}
}

local instance = {}
instance.__index = Formatter
local mt = {__index = instance}

local function isNaN(arg)
    return arg ~= arg
end

local function isInf(arg)
   return arg == -math.huge or arg == math.huge
end

-- these are modified to allow string-number coersion
local function isNumber(arg)
    return tonumber(arg)
end

local function isInt(arg)
    return tonumber(arg) and tonumber(arg) == math.floor(arg)
end

local function isString(arg)
    return type(arg) == "string" or type(arg) == "number"
end

local function areStrings(...)
    local args = {...}
    local n = select("#", ...)

    for i = 1, n do
        if not isString(args[i]) then
            return false
        end
    end
    return true
end


local function formatExceptions(number)
    if isInf(number) then
        return "∞"
    elseif isNaN(number) then
        return "NaN"
    else
        return string.format("%g", number)
    end
end

local function format(number, precision, removeTrailingZeros, delimiter, scale, unit) -- no type checking and defaulting
    assert(isNumber(number), "Wrong argument given for number, expected number but instead received " .. typeof(number))

    local index = math.floor(math.log(math.abs(number), 1000))
    local prefix = Formatter.scales[scale][index] 
    local formattedNumber 

    if prefix then
        formattedNumber = string.format("%".. (removeTrailingZeros and "#" or "") .. "." .. precision .. "f",  number / 10 ^ (index * 3))
        if removeTrailingZeros then
            formattedNumber = string.gsub(formattedNumber, "%.?0*$", "")
        end
    else -- defaults to standard behaviour, incase when number is bigger or smaller than 1e-+24 or is zero
        prefix = ""
        formattedNumber = formatExceptions(number)
    end

    return formattedNumber .. delimiter .. prefix .. unit
end

function instance:format(...)
    local args = {...}
    local n = select("#", ...)
    local returns = table.create(n)
    
    for i = 1, n do
        returns[i] = format(args[i], self.precision, self.removeTrailingZeros, self.delimiter, self.scale, self.unit)
    end

    return unpack(returns)
end

function Formatter.new(precision, removeTrailingZeros, delimiter, scale, unit)
    -- set defaults
    precision = precision or Formatter.default.precision
    removeTrailingZeros = removeTrailingZeros == nil and Formatter.default.removeTrailingZeros or removeTrailingZeros
    delimiter = delimiter or Formatter.default.delimiter
    scale = scale or Formatter.default.scale
    unit = unit or Formatter.default.unit
    -- type checking
    assert(isInt(precision), "Wrong argument given for precision, expected integer but received " .. typeof(precision))
    assert(Formatter.scales[scale], "wrong argument given for scale, expected a string (SI, shortScale, longScale or a custom scale)!")
    assert(areStrings(delimiter, unit), "Wrong argument give for scale/unit, expected string but received " .. typeof(delimiter) .. " and " .. typeof(unit))

    return setmetatable({
        precision = precision,
        removeTrailingZeros = removeTrailingZeros,
        delimiter = delimiter,
        scale = scale,
        unit = unit
    }, mt)
end

function Formatter.format(number, ...)
    return Formatter.new(...):format(number)
end

return Formatter