return function(NumberStringFormatter)

function NumberStringFormatter:coefficient()
    local decimalFormatter = NumberStringFormatter:decimal()
    local positiveOneString = decimalFormatter:format(1, 0)
    local negativeOneString = decimalFormatter:format(-1, 0)
    return {
        format = function(self, number, unitSymbol, unitValue, ...)
            number = tonumber(number)
            if not number then
                return
            end
            unitValue = tonumber(unitValue)
            if not unitValue or unitValue <= 0 then
                unitValue = 1
            end
            local decimalString = decimalFormatter:format(number / unitValue, ...)
            if unitSymbol and decimalString == positiveOneString then
                return unitSymbol
            elseif unitSymbol and decimalString == negativeOneString then
                return '-' .. unitSymbol
            elseif number == 0 or not unitSymbol then
                return decimalString
            end
            return decimalString .. unitSymbol
        end
    }
end

local function _scale(number, roundingFactor, scale)
    local symbol
    local roundedMagnitude = math.abs(number) / roundingFactor
    for index = 1, #scale do
        local magnitude = scale[index][1]
        if roundedMagnitude >= magnitude then
            if magnitude ~= 0 then
                number = number / magnitude
            end
            symbol = scale[index][2]
            break
        end
    end
    return number, symbol
end

function NumberStringFormatter:metric()
    local decimalFormatter = NumberStringFormatter:decimal()
    local metricPrefixes = {{1e12, 'T'}, {1e9, 'G'}, {1e6, 'M'}, {1e3, 'k'}, {1, ''}, {1e-3, 'm'}, {1e-6, 'µ'}, {1e-9, 'n'}, {1e-12, 'p'}, {0, ''}}
    return {
        format = function(self, number, unitSymbol, fractionDigits)
            number = tonumber(number)
            if not number then
                return
            end
            if type(unitSymbol) ~= 'string' then
                unitSymbol = ''
            end
            fractionDigits = math.floor(math.max(tonumber(fractionDigits) or 0, 0))
            local roundingFactor = 1 - 0.0005 * 10 ^ -fractionDigits
            local scaleNumber, scaleSymbol = _scale(number, roundingFactor, metricPrefixes)
            local decimalString = decimalFormatter:format(scaleNumber, fractionDigits)
            unitSymbol = scaleSymbol .. unitSymbol
            if unitSymbol ~= '' then
                return decimalString .. ' ' .. unitSymbol
            end
            return decimalString
        end
    }
end

function NumberStringFormatter:scale()
    local decimalFormatter = NumberStringFormatter:decimal()
    local commonPrefixes = {{1e12, 'T'}, {1e9, 'B'}, {1e6, 'M'}, {1e3, 'K'}, {0, ''}}
    return {
        format = function(self, number, fractionDigits)
            number = tonumber(number)
            if not number then
                return
            end
            fractionDigits = math.floor(math.max(tonumber(fractionDigits) or 0, 0))
            local roundingFactor = 1 - 0.0005 * 10 ^ -fractionDigits
            local scaleNumber, scaleSymbol = _scale(number, roundingFactor, commonPrefixes)
            local decimalString = decimalFormatter:format(scaleNumber, fractionDigits)
            return decimalString .. scaleSymbol
        end
    }
end

end
