Jump to content

Module:Formatnum/sandbox

From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by Trigenibinion (talk | contribs) at 01:41, 1 March 2021 (Simplify). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.
-- This module is intended to replace the functionality of Template:Formatnum and related templates. 
local p = {} -- Holds functions to be returned from #invoke, and functions to make available to other Lua modules.

--[[
Helper functions used to avoid redundant code.
]]

local function err(msg)
    -- Generates wikitext error messages.
    return mw.ustring.format('<strong class="error">Formatting error: %s</strong>', msg)
end

local function getArgs(frame)
    local args = {}
    for key, value in pairs(frame:getParent().args) do
        args[key] = value
    end
    for key, value in pairs(frame.args) do
        args[key] = value
    end
    return args
end

local function _round(value, precision)
    local rescale = math.pow(10, precision or 0);
    return math.floor(value * rescale + 0.5) / rescale;
end

--[[
------------------------------------------------------------------------------------
-- isPositiveInteger
--
-- This function returns true if the given value is a positive integer, and false
-- if not. Although it doesn't operate on tables, it is included here as it is
-- useful for determining whether a given table key is in the array part or the
-- hash part of a table.
------------------------------------------------------------------------------------
--]]
function isPositiveInteger(v)
    return type(v) == 'number' and v >= 1 and math.floor(v) == v and v < math.huge
end


--[[
------------------------------------------------------------------------------------
-- reverseNumKeys
--
-- This takes a table and returns an array containing the numbers of any numerical
-- keys that have non-nil values, sorted in reverse numerical order.
------------------------------------------------------------------------------------
--]]
local function reverseNumKeys(t)
    local nums = {}
    for k, v in pairs(t) do
        if isPositiveInteger(k) then
            nums[#nums + 1] = k
        end
    end
    table.sort(nums, function(a, b) return a > b end)
    return nums
end

--[[
------------------------------------------------------------------------------------
-- reverseSparseIpairs
--
-- This is a reverse iterator for sparse arrays. It can be used like a resersed ipairs, but can
-- handle nil values.
------------------------------------------------------------------------------------
--]]
local function reverseSparseIpairs(t)
    local nums = reverseNumKeys(t)
    local i = 0
    local lim = #nums
    return function ()
        i = i + 1
        if i <= lim then
            local key = nums[i]
            return key, t[key]
        else
            return nil, nil
        end
    end
end


function p.main(frame)
    local args = getArgs(frame)
    local prec    = args.prec or ''
    local sep     = args.sep or ''
    local number  = args[1] or args.number or ''
    local lang    = args[2] or args.lang or ''
    -- validate the language parameter within MediaWiki's caller frame
    if lang:lower() == "none" then
        -- no language, so do nothing
    elseif lang == "arabic-indic" then -- only for back-compatibility ("arabic-indic" is not a SupportedLanguage)
        lang = "fa" -- better support than "ks"
    elseif lang == '' or not mw.language.isSupportedLanguage(lang) then
        -- Note that 'SupportedLanguages' are not necessarily 'BuiltinValidCodes', and so they are not necessarily
        -- 'KnownLanguages' (with a language name defined at least in the default localisation of the local wiki).
        -- But they all are ValidLanguageCodes (suitable as Wiki subpages or identifiers: no slash, colon, HTML tags, or entities)
        -- In addition, they do not contain any capital letter in order to be unique in page titles (restriction inexistant in BCP47),
        -- but they may violate the standard format of BCP47 language tags for specific needs in MediaWiki.
        -- Empty/unspecified and unsupported languages are treated here in Commons using the user's language,
        -- instead of the local 'ContentLanguage' of the Wiki.
        lang = frame:callParserFunction( "int", "lang" ) -- get user's chosen language
        if not mw.language.isSupportedLanguage(lang) then
            lang = mw.language.getContentLanguage().code
        end
    end
    return p.formatNum(number, lang, prec, sep ~= '')
end

local digit = { -- substitution of decimal digits for languages not supported by mw.language:formatNum() in core Lua libraries for MediaWiki
    ["ml-old"] = { '൦', '൧', '൨', '൩', '൪', '൫', '൬', '൭', '൮', '൯' },
    ["mn"]     = { '᠐', '᠑', '᠒', '᠓', '᠔', '᠕', '᠖', '᠗', '᠘', '᠙'},
    ["ta"]     = { '௦', '௧', '௨', '௩', '௪', '௫', '௬', '௭', '௮', '௯'},
    ["te"]     = { '౦', '౧', '౨', '౩', '౪', '౫', '౬', '౭', '౮', '౯'},
    ["th"]     = { '๐', '๑', '๒', '๓', '๔', '๕', '๖', '๗', '๘', '๙'}
}

function p.formatNum(number, lang, prec, compact)
    -- Do not alter the specified value when it is not a valid number, return it as is
    local value = tonumber(number)
    if value == nil then
        return number
    end
    -- Basic ASCII-only formatting (without paddings)
    number = tostring(value)

    -- Check the presence of an exponent (incorrectly managed in mw.language:FormatNum() and even forgotten due to an internal bug, e.g. in Hindi)
    local exponent
    local pos = string.find(number, '[Ee]')
    if pos ~= nil then
        exponent = string.sub(number, pos + 1, string.len(number))
        number = string.sub(number, 1, pos - 1)
    else
        exponent = ''
    end

    -- Check the minimum precision requested
    prec = tonumber(prec) -- nil if not specified as a true number
    if prec ~= nil then
        prec = math.floor(prec)
        if prec < 0 then
            prec = nil -- discard an incorrect precision (not a positive integer)
        elseif prec > 14 then
            prec = 14 -- maximum precision supported by tostring(number)
        end
    end

    -- Preprocess the minimum precision in the ASCII string
    local dot
    if (prec or 0) > 0 then
        pos = string.find(number, '.', 1, true) -- plain search, no regexp
        if pos ~= nil then
            prec = pos + prec - string.len(number) -- effective number of trailing decimals to add or remove
            dot = '' -- already present
        else
            dot = '.' -- must be added
        end
    else
        dot = '' -- don't add dot
        prec = 0 -- don't alter the precision
    end
    
    if lang ~= nil and mw.language.isKnownLanguageTag(lang) == true then
        -- Convert number to localized digits, decimal separator, and group separators
        local language = mw.getLanguage(lang)
        if compact then
            number = language:formatNum(tonumber(number), { noCommafy = 'y' }) -- caveat: can load localized resources for up to 20 languages
        else
            number = language:formatNum(tonumber(number)) -- caveat: can load localized resources for up to 20 languages
        end
        -- Postprocessing the precision
        if prec > 0 then
            local zero = language:formatNum(0)
            number = number .. dot .. mw.ustring.rep(zero, prec)
        elseif prec < 0 then
            -- TODO: rounding of last decimal; here only truncate decimals in excess
            number = mw.ustring.sub(number, 1, mw.ustring.len(number) + prec)
        end
        -- Append the localized base-10 exponent without grouping separators (there's no reliable way to detect a localized leading symbol 'E')
        if exponent ~= '' then
            number = number .. 'E' .. language:formatNum(tonumber(exponent),{noCommafy=true})
        end
    else -- not localized, ASCII only
        -- Postprocessing the precision
        if prec > 0 then
            number = number .. dot .. mw.string.rep('0', prec)
        elseif prec < 0 then
            -- TODO: rounding of last decimal; here only truncate decimals in excess
            number = mw.string.sub(number, 1, mw.string.len(number) + prec)
        end
        -- Append the base-10 exponent
        if exponent ~= '' then
            number = number .. 'E' .. exponent
        end
    end

    -- Special cases for substitution of ASCII digits (missing support in Lua core libraries for some languages)
    if digit[lang] then
        for i, v in ipairs(digit[lang]) do
            number = mw.ustring.gsub(number, tostring(i - 1), v)
        end
    end

    return number
end

--[[
wordify

Usage:
{{#invoke:Formatnum | wordify | x | y | round= | precision= }}

--]]

local usa = {
    [6] = 'million',
    [9] = 'billion',
    [12] = 'trillion',
    [15] = 'quadrillion',
    [18] = 'quintillion',
    [21] = 'sextillion',
    [24] = 'septillion',
    [27] = 'octillion',
    [30] = 'nonillion',
    [33] = 'decillion',
    [36] = 'undecillion',
    [39] = 'duodecillion',
    [42] = 'tredecillion',
    [45] = 'quattuordecillion',
    [48] = 'quindecillion',
    [51] = 'sexdecillion',
    [54] = 'septendecillion',
    [57] = 'octodecillion',
    [60] = 'novemdecillion',
    [63] = 'vigintillion',
    [100] = 'googol',
    [303] = 'centillion'
}

local ind = {
    [5] = 'lakh',
    [7] = 'crore',
    [12] = 'lakh crore',
    [14] = 'crore crore'
}

local buildOne

local unit

local declension = {
    ['ca'] = { '', function (u) 
                        local n = mw.ustring.len(u) 
                        if mw.ustring.sub(u, n, n) == "d" then return u .. "s" else return mw.ustring.sub(u, 1, -2) .. "ons" end 
                   end },
    ['da'] = { '', 'er' },
    ['de'] = { '', function (u) 
                        local n = mw.ustring.len(u) 
                        if mw.ustring.sub(u, n, n) == "e" then return u .. "n" else return u .. "en" end 
                   end },
    ['eo'] = { '', 'j' },
    ['es'] = { function (arr, i, lang)
					if i < 15 then
						return buildOne(arr, i, lang)
					end
					local j = math.floor(i / 6)
					if (j*6)+3 == i then
						return "mil " .. unit(arr, i-3, lang, 1000), true
					else
						return buildOne(arr, i, lang)
					end
			   end,
			   function (u) 
					if mw.ustring.sub(u, 1, 4) == "mil " then
						return u
					else
						local n = mw.ustring.len(u) 
						if mw.ustring.sub(u, n, n) == "o" then return u .. "s" else return mw.ustring.sub(u, 1, -3) .. "ones" end 
					end
			   end },
    ['fr'] = { '', 's' },
    ['it'] = { '', function (u) 
                        return mw.ustring.sub(u, 1, -2) .. "i"
                   end },
    ['la'] = { '', function (u) 
                        local n = mw.ustring.len(u) 
                        if mw.ustring.sub(u, n, n) == "o" then return u .. "nibus" else return mw.ustring.sub(u, 1, -3) .. "a" end 
                   end },
    ['no'] = { '', 'er' },
    ['pl'] = { '', 'y', [5] = 'ów', ["fraction"] = 'a' },
	['pt'] = { function (arr, i, lang)
					if i < 9 then
						return buildOne(arr, i, lang)
					end
					local j = math.floor(i / 6)
					if (j*6)+3 == i then
						return "mil " .. unit(arr, i-3, lang, 1000), true
					else
						return buildOne(arr, i, lang)
					end
			   end,
			   function (u) 
					if mw.ustring.sub(u, 1, 4) == "mil " then
						return u
					else
						return mw.ustring.sub(u, 1, -3) .. "ões" 
					end
               end },
    ['sk'] = { '', function (u) 
                        local n = mw.ustring.len(u) 
                        if mw.ustring.sub(u, n, n) == "n" then return u .. 'y' else return mw.ustring.sub(u, 1, -2) .. "y" end
                   end,
                   [5] = function (u) 
                            local n = mw.ustring.len(u) 
                            if mw.ustring.sub(u, n, n) == "n" then return u .. 'ov' else return mw.ustring.sub(u, 1, -5) .. "árd" end 
                         end,
                   ["fraction"] = function (u) 
                                    local n = mw.ustring.len(u) 
                                    if mw.ustring.sub(u, n, n) == "n" then return u .. 'a' else return mw.ustring.sub(u, 1, -5) .. "árd" end 
                                  end 
             },
    ['sl'] = { '', function (u) 
                        local n = mw.ustring.len(u) 
                        if mw.ustring.sub(u, n, n) == "a" then return mw.ustring.sub(u, 1, -2) .. 'i' else return u .. "a" end
                   end,
                   function (u) 
                        local n = mw.ustring.len(u) 
                        if mw.ustring.sub(u, n, n) == "a" then return mw.ustring.sub(u, 1, -2) .. 'e' else return u .. "e" end
                   end,
                   [5] = function (u) 
                            local n = mw.ustring.len(u) 
                            if mw.ustring.sub(u, n, n) == "a" then return mw.ustring.sub(u, 1, -2) else return u .. "ov" end 
                         end,
                   ["fraction"] = function (u) 
                                    local n = mw.ustring.len(u) 
                                    if mw.ustring.sub(u, n, n) == "a" then return mw.ustring.sub(u, 1, -2) .. 'e' else return u .. "a" end 
                                  end 
             },
    ['sv'] = { '', 'er' }
}

local latin = {
    ['language'] = {
			['']  = true,
			['ca'] = true,
			['da'] = true,
			['de'] = true,
			['en'] = true,
			['eo'] = true,
			['es'] = true,
			['fr'] = true,
			['it'] = true,
			['la'] = true,
			['nl'] = true,
			['no'] = true,
			['pl'] = true,
			['pt'] = true,
			['sk'] = true,
			['sl'] = true,
			['sv'] = true
		},
		
	['language_top'] = {
			['']  = 306,
			['ca'] = 60,
			['da'] = 60,
			['de'] = 60,
			['en'] = 306,
			['eo'] = 117,
			['es'] = 120,
			['fr'] = 123,
			['it'] = 60,
			['la'] = 306,
			['nl'] = 60,
			['no'] = 60,
			['pl'] = 60,
			['pt'] = 60,
			['sk'] = 60,
			['sl'] = 60,
			['sv'] = 60
		},
		
	['exception'] = {
		[6] = {
			['pt'] = { 'milhão' }
		},
		[36] = {
			['it'] = 'none'
		},
		[39] = {
			['it'] = 'none'
		},
		[42] = {
			['it'] = 'none'
		},
		[45] = {
			['it'] = 'none'
		},
		[48] = {
			['it'] = 'none'
		},
		[51] = {
			['it'] = 'none',
		},
		[54] = {
			['it'] = 'none'
		},
		[57] = {
			['it'] = 'none'
		}
	},

	['base'] = {
			['']  = { 'lli' },
			['ca'] = { 'li' },
			['da'] = { 'lli' },
			['de'] = { 'lli' },
			['en'] = { 'lli' },
			['eo'] = { 'li' },
			['es'] = { 'll' },
			['fr'] = { 'lli' },
			['it'] = { 'li' },
			['la'] = { 'lli' },
			['nl'] = { 'lj' },
			['no'] = { 'lli' },
			['pl'] = { 'li' },
			['pt'] = { 'li' },
			['sk'] = { 'li' },
			['sl'] = { 'lij' },
			['sv'] = { 'lj' }
	},
		
	['suffix'] = {
			['']  = { 'on', 'ard' },
			['ca'] = { 'ó', 'ard' },
			['da'] = { 'on', 'ard' },
			['de'] = { 'on', 'arde' },
			['en'] = { 'on', 'ard' },
			['eo'] = { 'ono', 'ardo' },
			['es'] = { 'ón', 'ardo' },
			['fr'] = { 'on', 'ard' },
			['it'] = { 'one', 'ardo' },
			['la'] = { 'o', 'ardum' },
			['nl'] = { 'oen', 'ard' },
			['no'] = { 'on', 'ard' },
			['pl'] = { 'on', 'ard' },
			['pt'] = { 'ão' },
			['sk'] = { 'ón', 'arda' },
			['sl'] = { 'on', 'arda' },
			['sv'] = { 'on', 'ard' }
	},

	['capitalize'] = {
		['de'] = true
	},
	
	['prefix'] = { 'mi', 'bi', 'tri', 'quadri', 'quinti', 'sexti', 'septi', 'octi', 'noni', 'deci',
		[16] = 'sedeci',
		[20] = 'viginti',
		[30] = 'triginti',
		[40] = 'quadraginti',
		[50] = 'quinquaginti',
		[60] = 'sexaginti',
		[70] = 'septuaginti',
		[80] = 'octoginti',
		[90] = 'nonaginti',
		[100] = 'centi'
	},
	
	['unit_prefix'] = { 'un', 'duo', 'tre', 'quattuor', 'quin', 'sex', 'septen', 'octo', 'novem' },
	
	['prefix_exception'] = {
		[1] = {},
		[2] = {
			['eo'] = { 'dui' }
		},
		[3] = {
			['pl'] = { 'try' }
		},
		[4] = {
			['da'] = { 'kvadri' },
			['eo'] = { 'kvari' },
			['es'] = { 'cuatri' },
			['no'] = { 'kvadri' },
			['pl'] = { 'kwadry' },
			['sk'] = { 'kvadri' },
			['sl'] = { 'kvadri' },
			['sv'] = { 'kvadri' }
		},
		[5] = {
			['da'] = { 'kvinti' },
			['eo'] = { 'kvini' },
			['no'] = { 'kvinti' },
			['pl'] = { 'kwinty' },
			['sk'] = { 'kvinti' },
			['sl'] = { 'kvinti' },
			['sv'] = { 'kvinti' }
		},
		[6] = {
			['da'] = { 'seksti' },
			['eo'] = { 'sesi' },
			['no'] = { 'seksti' },
			['pl'] = { 'seksty' },
			['sl'] = { 'seksti' }
		},
		[7] = {
			['eo'] = { 'sepi' },
			['pl'] = { 'septy' },
		},
		[8] = {
			['da'] = { 'okti' },
			['de'] = { 'Okti' },
			['eo'] = { 'oki' },
			['no'] = { 'okti' },
			['pl'] = { 'okty' },
			['sk'] = { 'okti' },
			['sl'] = { 'okti' },
			['sv'] = { 'okti' }
		},
		[9] = {
			['eo'] = { 'naŭi' }
		},
		[10] = {
			['de'] = { 'Dezi' },
			['eo'] = { 'deki' },
			['fr'] = { 'déci' },
			['no'] = { 'desi' },
			['pl'] = { 'decy' }
		},
		[11] = {
		},
		[12] = {
		},
		[13] = {
		},
		[14] = {
		},
		[15] = {
		},
		[16] = {
		},
		[17] = {
		},
		[18] = {
		},
		[19] = {
		},
		[20] = {
		}
	},
	
	['unit_prefix_exception'] = {
		[1] = {},
		[2] = {
		},
		[3] = {
			['fr'] = { 'tré' }
		},
		[4] = {
			['eo'] = { 'kvatuor' },
			['es'] = { 'cuatro' }
		},
		[5] = {
			['eo'] = { 'kvin' }
		},
		[6] = {
			['eo'] = { 'seks' }
		},
		[7] = {
		},
		[8] = {
			['eo'] = { 'okto' }
		},
		[9] = {
			['es'] = { 'noven' },
			['fr'] = { 'noni' }
		}
	}
}

local function makeprefix(arr, j, lang)
	local entry = arr['prefix_exception'][j][(lang or "")]
	local p
	if entry then
		p = entry[1]
	else
		p = arr['prefix'][j]
		if not p then
			if j < 11 then
				return nil
			elseif p < 100 then
				local k = math.floor(j / 10)
				local d = k * 10
				local l = j - d
				local unitentry = arr['unit_prefix_exception'][l][(lang or "")]
				local u
				if unitentry then
					u = unitentry[1]
				else
					u = arr['unit_prefix'][l]
				end
				local decentry = arr['prefix_exception'][d][(lang or "")]
				local x
				if decentry then
					x = deceentry[1]
				else
					x = arr['prefix'][d]
				end
				p = u .. x
			else
				return nil
			end
		end
	end
	if arr['capitalize'][(lang or "")] then
		return mw.ustring.upper(mw.ustring.sub(p, 1, 1)) .. mw.ustring.sub(p, 2)
	else
		return p
	end
end

buildOne = function(arr, i, lang)
	local j = math.floor(i / 6)
	if j < 1 then
		return nil
	else
		local pref = makeprefix(arr, j, lang)
		local suf = (j * 6) == i and arr['suffix'][(lang or "")][1] or arr['suffix'][(lang or "")][2]
		return pref ..  arr['base'][(lang or "")][1] .. suf
	end
end

local function makeOne(arr, i, lang)
    local top = arr['exception'][i]
	local entry = top and top[(lang or "")]
	local one = nil
	local simplify = nil
	if entry then 
		one, simplify = entry[1], entry['simplify']
	end
	if one then
		return one, simplify
	else
		if declension[lang] and declension[lang][1] then
            local d = declension[lang][1]
            if type(d) == 'function' then
				return d(arr, i, lang)
			end
		end
		return buildOne(arr, i, lang)
	end
end

unit = function(arr, i, lang, r)
	local one, simplify = makeOne(arr, i, lang)
	local top = arr['exception'][i]
	local entry = top and top[(lang or "")]
    if r == 1 then
        return one, simplify
    elseif r == 2 then
        if entry and entry[2] then
            return entry and entry[2]
        elseif declension[lang] and declension[lang][2] then
            local d = declension[lang][2]
            if type(d) == 'function' then
                return d(one)
            else
                return one .. d
            end
        else
            return one
        end
    elseif r == 3 or r == 4 then
        if entry and entry[3] then
            return entry and entry[3]
        elseif declension[lang] and declension[lang][3] then
            local d = declension[lang][3]
            if type(d) == 'function' then
                return d(one)
            else
                return one .. d
            end
        elseif entry and entry[2] then
            return entry and entry[2]
        elseif declension[lang] and declension[lang][2] then
            local d = declension[lang][2]
            if type(d) == 'function' then
                return d(one)
            else
                return one .. d
            end
        else
            return one
        end
    elseif r == _round(r, 0) then
        if entry and entry[5] then
            return entry and entry[5]
        elseif declension[lang] and declension[lang][5] then
            local d = declension[lang][5]
            if type(d) == 'function' then
                return d(one)
            else
                return one .. d
            end
        elseif entry and entry[3] then
            return entry and entry[3]
        elseif declension[lang] and declension[lang][3] then
            local d = declension[lang][3]
            if type(d) == 'function' then
                return d(one)
            else
                return one .. d
            end
        elseif entry and entry[2] then
            return entry and entry[2]
        elseif declension[lang] and declension[lang][2] then
            local d = declension[lang][2]
            if type(d) == 'function' then
                return d(one)
            else
                return one .. d
            end
        else
            return one
        end
    else
        if entry and entry["fraction"] then
            return entry and entry["fraction"]
        elseif declension[lang] and declension[lang]["fraction"] then
            local d = declension[lang]["fraction"]
            if type(d) == 'function' then
                return d(one)
            else
                return one .. d
            end
        elseif entry and entry[5] then
            return entry and entry[5]
        elseif declension[lang] and declension[lang][5] then
            local d = declension[lang][5]
            if type(d) == 'function' then
                return d(one)
            else
                return one .. d
            end
        elseif entry and entry[3] then
            return entry and entry[3]
        elseif declension[lang] and declension[lang][3] then
            local d = declension[lang][3]
            if type(d) == 'function' then
                return d(one)
            else
                return one .. d
            end
        elseif entry and entry[2] then
            return (r < 2 and one or entry and entry[2])
        elseif declension[lang] and declension[lang][2] then
            local d = declension[lang][2]
            if type(d) == 'function' then
                return (r < 2 and one or d(one))
            else
                return (r < 2 and one or (one .. d))
            end
        else
            return one
        end
    end
end

local function linkend(u)
    return "|" .. u .. "]]"
end

local function unitlink(lk, i, u, lang)
    if lk then
        if lang == "ca" then
            return "[[ca:" .. "Escales curta i llarga" .. linkend(u)
        elseif lang == "da" then
            return "[[da:" .. "Store tal" .. linkend(u)
        elseif lang == "de" then
            return "[[de:" .. "Zahlennamen" .. linkend(u)
        elseif lang == "eo" then
            return "[[eo:" .. "Vortoj por grandegaj nombroj" .. linkend(u)
        elseif lang == "fr" then
            return "[[fr:" .. (i > 36 and "Noms des grands nombres" or ("Ordres de grandeur de nombres#10" .. i)) .. linkend(u)
        elseif lang == "it" then
            return "[[it:" .. ("Ordini di grandezza (numeri)#10" .. i) .. linkend(u)
        elseif lang == "la" then
            return "[[la:" .. "Nomina permagnorum numerorum" .. linkend(u)
        elseif lang == "nl" then
            return "[[nl:" .. "Lijst van machten van tien" .. linkend(u)
        elseif lang == "no" then
            return "[[no:" .. "Navn på store tall" .. linkend(u)
        elseif lang == "pl" then
            return "[[pl:" .. "Liczebniki główne potęg tysiąca" .. linkend(u)
        elseif lang == "sk" then
            return "[[sk:" .. "Veľké čísla" .. linkend(u)
        elseif lang == "sl" then
            return "[[sl:" .. "Imena velikih števil" .. linkend(u)
        elseif lang == "sv" then
            return "[[sv:" .. "Namn på stora tal" .. linkend(u)
        else
            return "[[en:" .. (i > 39 and "Names of large numbers" or ("Orders of magnitude (numbers)#10" .. i)) .. linkend(u)
        end
    else
        return u
    end
end

function p.wordify(frame)
    local args = getArgs(frame)
    local x = args[1]
    local numsys = args.numsys
    local prec =  args.prec
    local lk = args.lk
    local lang = args.lang
    local simplify = args.simplify
    return p._wordify(x, numsys, prec, (lk == "on" and true), lang, (simplify == "yes" and true))
end

function p._wordify(x, numsys, prec, lk, lang, simplify)
    if tonumber(x) then
        if numsys == nil or numsys == "" or numsys:lower() == "usa" then
            for i, v in reverseSparseIpairs(usa) do
                local y = x / math.pow(10,i)
                local r = _round(y, prec)
                if r >= 1 then
                    if r == 1 and simplify then
                        return unitlink(lk, i, v, (lang or "en"))
                    else
                        return p.formatNum(r, (lang or "en"), prec) .. " " .. unitlink(lk, i, v, (lang or "en"))
                    end
                end
            end
                    
            return p.formatNum(_round(x, prec), (lang or "en"), prec) 
        elseif numsys:lower() == "fra" then
			if latin['language'][(lang or "")] then
				for	i = latin['language_top'], 6, -3 do  
					local entry = latin['exception'][i]
					if not entry or entry[(lang or "")] ~= "none" then
						local y = x / math.pow(10,i)
						local r = _round(y, prec)
						if r >= 1 then
							local u, simp = unit(latin, i, lang, r)
							if r == 1 and (simplify or simp) then
								return unitlink(lk, i, u, (lang or "")) 
							else
								return p.formatNum(r, (lang or "en"), prec) .. " " .. unitlink(lk, i, u, (lang or "en"))
							end
						end
					end
				end
			end
			
            return p.formatNum(_round(x, prec), (lang or "en"), prec) 
        elseif numsys:lower() == "ind" then
            local y = x / 1E14
            local r = _round(y, prec)
            if r >= 1 then
                if r == 1 and simplify then
                    return (lk and "[[crore]] crore" or ind[14])
                else
                    return p.formatNum(r, (lang or "en"), prec) .. " " .. (lk and "[[crore]] crore" or ind[14])
                end
            else
                local y = x / 1E12
                local r = _round(y, prec)
                if r >= 1 then
                    if r == 1 and simplify then
                        return (lk and "[[lakh]] [[crore]]" or ind[12])
                    else
                        return p.formatNum(r, (lang or "en"), prec) .. " " .. (lk and "[[lakh]] [[crore]]" or ind[12])
                    end
                else
                    local y = x / 1E7
                    local r = _round(y, prec)
                    if r >= 1 then
                        local v = ind[7]
                        if r == 1 and simplify then
                            return (lk and "[[" .. v .. "]]" or v)
                        else
                            return p.formatNum(r, (lang or "en"), prec) .. " " .. (lk and "[[" .. v .. "]]" or v)
                        end
                    else
                        local y = x / 1E5
                        local r = _round(y, prec)
                        if r >= 1 then
                            local v = ind[5]
                            if r == 1 and simplify then
                                return (lk and "[[" .. v .. "]]" or v)
                            else
                                return p.formatNum(r, (lang or "en"), prec) .. " " .. (lk and "[[" .. v .. "]]" or v)
                            end
                        else
                            return p.formatNum(_round(x, prec), (lang or "en"), prec) 
                        end
                    end
                end
            end
        else
            return err("number system not supported")
        end 
    else
      return err("Not a number: " .. x)
    end
end

--[[
Helper function that interprets the input numerically.  If the
input does not appear to be a number, attempts evaluating it as
a parser functions expression.
]]

function p._cleanNumber(number_string)
    if type(number_string) == 'number' then
        -- We were passed a number, so we don't need to do any processing.
        return number_string, tostring(number_string)
    elseif type(number_string) ~= 'string' or not number_string:find('%S') then
        -- We were passed a non-string or a blank string, so exit.
        return nil, nil;
    end

    -- Attempt basic conversion
    local number = tonumber(number_string)

    -- If failed, attempt to evaluate input as an expression
    if number == nil then
        local success, result = pcall(mw.ext.ParserFunctions.expr, number_string)
        if success then
            number = tonumber(result)
            number_string = tostring(number)
        else
            number = nil
            number_string = nil
        end
    else
        number_string = number_string:match("^%s*(.-)%s*$") -- String is valid but may contain padding, clean it.
        number_string = number_string:match("^%+(.*)$") or number_string -- Trim any leading + signs.
        if number_string:find('^%-?0[xX]') then
            -- Number is using 0xnnn notation to indicate base 16; use the number that Lua detected instead.
            number_string = tostring(number)
        end
    end

    return number, number_string
end

return p