Module:Number list

local export = {}

local m_links = require("Module:links")

local form_types = { {key = "cardinal", display = "Cardinal"}, {key = "ordinal", display = "Ordinal"}, {key = "adverbial", display = "Adverbial"}, {key = "multiplier", display = "Multiplier"}, {key = "distributive", display = "Distributive"}, {key = "collective", display = "Collective"}, {key = "fractional", display = "Fractional"}, }

local function index_of_number_type(t, type) for i, subtable in ipairs(t) do		if subtable.key == type then return i		end end end

-- additional_types is an array of tables like form_types, -- but each table can contain the keys "before" or "after", which specify -- the numeral type that the form should appear before or after. -- The transformations are applied in order. local function add_form_types(additional_types) local types = require "Module:table".deepcopy(form_types) for _, type in ipairs(additional_types) do		type = require "Module:table".shallowcopy(type) local i		if type.before or type.after then i = index_of_number_type(types, type.before or type.after) end -- For now, simply log an error message -- if the "before" or "after" number type was not found, -- and insert the number type at the end. if i then if type.before then table.insert(types, i - 1, type) else table.insert(types, i + 1, type) end else table.insert(types, type) if type.before or type.after then mw.log("Number type "					.. (type.before or type.after)					.. " was not found.") end end type.before, type.after = nil, nil end return types end

function export.get_number_types(language_code) local m_data = require("Module:number list/data/" .. language_code) local final_form_types = form_types if m_data.additional_number_types then final_form_types = add_form_types(m_data.additional_number_types) end return final_form_types end

function export.display_number_type(number_type) if number_type.display then return number_type.display else return (number_type.key:gsub("^.", string.upper):gsub("_", " ")) end end

local a = ("a"):byte local function multiple_num_links(terms, lang) local links = {} for i, term in ipairs(terms) do links[i] = m_links.language_link{lang = lang, term = term, alt = "[" .. string.char(a + i - 1) .. "]", tr = "-"} end return "" .. table.concat(links, ", ") .. "" end

function map(func, array) local new_array = {} for i,v in ipairs(array) do		new_array[i] = func(v) end return new_array end

local function unsuffix(term) if type(term) == "table" then return map(unsuffix, term) end if term:find("^-a ") ~= nil then return term:sub(4) end if term:sub(1, 1) == "-" then return term:sub(2) end return term end

-- See Digit grouping. local function add_separator(number, separator, group, start) number = tostring(number) start = start or group if start >= #number then return number end local parts = { number:sub(-start) } for i = start + 1, #number, group do		table.insert(parts, 1, number:sub(-(i + group - 1), -i)) end return table.concat(parts, separator) end

local function add_thousands_separator(number, separator) return add_separator(number, separator, 3) end

local function add_Indic_separator(number, separator) return add_separator(number, separator, 2, 3) end

function export.generate_decimal_numeral(numeral_config, number) if type(number) ~= "number" then return nil end if numeral_config.module and numeral_config.func then return require("Module:" .. numeral_config.module)[numeral_config.func](number) end local thousands_separator, Indic_separator, zero_codepoint = numeral_config.thousands_separator, numeral_config.Indic_separator, numeral_config.zero_codepoint if not zero_codepoint then return nil end local number_string = tostring(number) if thousands_separator then number_string = add_thousands_separator(number_string, thousands_separator) elseif Indic_separator then number_string = add_Indic_separator(number_string, Indic_separator) end return number_string:gsub("[0-9]", function (digit)		return mw.ustring.char(zero_codepoint + tonumber(digit))	end) end

local function remove_duplicate_entry_names(lang, terms) local entries = require "Module:fun".map(		function(term) return lang:makeEntryName(term) end,		terms) local entry_set = {} return require "Module:fun".filter(		function(entry)			local already_seen = entry_set[entry]			entry_set[entry] = true			return not already_seen		end,		entries) end

function export.show_box(frame) local full_link = m_links.full_link local params = { [1] = {required = true}, [2] = {required = true}, [3] = {},		[4] = {},		["type"] = {}, }	local args = require("Module:parameters").process(frame:getParent.args, params) local lang = args[1] or "und" local cur_num = args[2] or 2 local cur_type = args.type if not (type(cur_num) == "number" or cur_num:find "^%d+$") then error("Extraneous characters in parameter 2: should be decimal number (integer).") end local alt_pagename = args[3] or false local remove_suffix = args[4] or false lang = require("Module:languages").getByCode(lang) or error("The language code \"" .. lang .. "\" is not valid.") -- Get the data from the data module. -- Module:number list/data/en has to be loaded with require because its -- exported numbers table has a metatable. local module_name = "Module:number list/data/" .. lang:getCode local m_data = require(module_name) -- Numbers that can't be represented exactly as a Lua number -- must be stored as a string. The first power of 10 that cannot be	-- represented exactly is 10^22 (ten sextillion in short scale,	-- ten thousand trillion in long scale), but the first power of ten whose -- neighboring numbers cannot be represented exactly -- is 10^16 (ten quadrillion or ten thousand billion). -- The number data must be looked up using the string, but after that -- the number string must be converted to a number, because some code -- after this point requires a number. This might cause bugs! -- Ideally we would use a big integer library of some kind. local cur_data = m_data.numbers[cur_num] or m_data.numbers[tonumber(cur_num)] if not cur_data then error('The number "' .. cur_num .. '" is not found in the "numbers" table in ' .. module_name .. '.') end -- Save original cur_num if it is a string, for use below. orig_cur_num = cur_num cur_num = tonumber(cur_num) -- Go over each number and make links local forms = {} local full_pagename = (mw.title.getCurrentTitle.nsText=="Reconstruction" and "*" or "") .. mw.title.getCurrentTitle.subpageText if alt_pagename then full_pagename = alt_pagename end if cur_type and not cur_data[cur_type] then error("The numeral type " .. cur_type .. " for " .. orig_cur_num			.. " is not found in " .. module_name .. ".") end for _, form_type in ipairs(export.get_number_types(lang:getCode)) do		local numeral = cur_data[form_type.key] if numeral then local form = {} local numerals if type(numeral) == "string" then numerals = {numeral} elseif type(numeral) == "table" then numerals = numeral end for _, numeral in ipairs(numerals) do				-- If this number is the current page, then store the key for later use if not cur_type and lang:makeEntryName(numeral) == full_pagename then cur_type = form_type.key end table.insert(form, full_link({lang = lang, term = remove_suffix and unsuffix(numeral) or numeral, alt = numeral })) end local displayed_number_type = export.display_number_type(form_type) if form_type.key == cur_type then displayed_number_type = "'''" .. displayed_number_type .. "'''"			end table.insert(forms, "    " .. displayed_number_type .. ": " .. table.concat(form, ", ")) end end if not cur_type and mw.title.getCurrentTitle.nsText ~= "Template" then error("The current page name does not match any of the numbers listed in " .. module_name .. " for " .. cur_num .. ". Check the data module or the spelling of the page.") end -- Current number in header -- Prevent large numbers, such as 100 trillion (རབ་བཀྲམ་ཆེན་པོ) from being -- displayed in scientific notation (1+e14). local cur_display = ("%i"):format(cur_num) ---	if cur_num >= 1000 then		cur_display = add_thousands_separator(cur_display, ",")	end	-- local numeral if m_data.numeral_config then numeral = export.generate_decimal_numeral(m_data.numeral_config, cur_num) elseif cur_data["numeral"] then numeral = tostring(cur_data["numeral"]) end if numeral then cur_display = full_link({lang = lang, alt = numeral, tr = "-"}) .. " " .. cur_display .. " "	end -- Link to previous number local prev_data = m_data.numbers[cur_num - 1] local prev_display = "" --	Current format: --		if multiple entries: --			[a], [b], ... ← 	--		else --			← 	local prev_num = prev_data and prev_data[cur_type] if prev_num then local entries if type(prev_num) == "table" then entries = remove_duplicate_entry_names(lang, prev_num) else entries = { prev_num } end mw.logObject(prev_num, 'prev_num') mw.logObject(entries, 'entries') if #entries > 1 then prev_display = multiple_num_links(remove_suffix and unsuffix(entries) or entries, lang) .. " ← " .. (cur_num - 1) else prev_display = m_links.language_link { lang = lang, term = remove_suffix and unsuffix(entries[1]) or entries[1], alt = " ← " .. (cur_num - 1), tr = "-", }		end end -- Link to next number local next_data = m_data.numbers[cur_num + 1] local next_display = "" --	Current format: --		if multiple entries: --			 → [a], [b], ... --		else --			 →	local next_num = next_data and next_data[cur_type] if next_num then local entries if type(next_num) == "table" then entries = remove_duplicate_entry_names(lang, next_num) else entries = { next_num } end if #entries > 1 then next_display = (cur_num + 1) .. " → "				.. multiple_num_links(remove_suffix and unsuffix(entries) or entries, lang) else next_display = m_links.language_link { lang = lang, term = remove_suffix and unsuffix(entries[1]) or entries[1], alt = (cur_num + 1) .. " → ",				tr = "-", }		end end -- Link to number times ten and divided by ten -- Show this only if the number is a power of ten times a number 1-9 (that is, of the form x000...) local up_display local down_display -- This test *could* be done numerically, but this is nice and simple and it works. if tostring(cur_num):find("^[1-9]0*$") then up_num = cur_num * 10 local up_data = m_data.numbers[up_num] if up_data and up_data[cur_type] then if type(up_data[cur_type]) == "table" then up_display = up_num .. multiple_num_links(remove_suffix and unsuffix(up_data[cur_type]) or up_data[cur_type], lang) else up_display = full_link({lang = lang, term = remove_suffix and unsuffix(up_data[cur_type]) or up_data[cur_type], alt = up_num, tr = "-"}) end end -- Only divide by 10 if the number is at least 10 if cur_num >= 10 then local down_num = cur_num / 10 local down_data = m_data.numbers[down_num] if down_data and down_data[cur_type] then if type(down_data[cur_type]) == "table" then down_display = down_num .. multiple_num_links(remove_suffix and unsuffix(down_data[cur_type]) or down_data[cur_type], lang) else down_display = full_link({lang = lang, term = remove_suffix and unsuffix(down_data[cur_type]) or down_data[cur_type], alt = down_num, tr = "-"}) end end end end local canonical_name = lang:getCanonicalName local appendix1 = canonical_name .. ' numerals' local appendix2 = canonical_name .. ' numbers' local appendix local title if mw.title.new(appendix1, "Appendix").exists then appendix = appendix1 elseif mw.title.new(appendix2, "Appendix").exists then appendix = appendix2 end if appendix then title =  .. appendix2 ..  else title = appendix2 end local edit_link = ' ( [' ..		tostring(mw.uri.fullUrl(module_name, { action = "edit" })) ..		" edit] ) " return [=[{| class="floatright" cellpadding="5" cellspacing="0" style="background: #ffffff; border: 1px #aaa solid; border-collapse: collapse; margin-top: .5em;" rules="all" (up_display and [=[
 * + ]=] .. title .. edit_link .. "" ..


 * - style="text-align: center; background:#dddddd;"
 * style="font-size:smaller;" | ]=] .. up_display .. [=[
 * style="font-size:smaller;" | ]=] .. up_display .. [=[

]=] or "\n") .. [=[|- style="text-align: center;"
 * style="min-width: 6em; font-size:smaller; background:#dddddd;" | ]=] .. prev_display .. [=[

! style="min-width: 6em; font-size:larger;" | ]=] .. cur_display .. [=[


 * style="min-width: 6em; font-size:smaller; background:#dddddd;" | ]=] .. next_display .. [=[

]=] .. (down_display and [=[|- style="text-align: center; background:#dddddd;"
 * style="font-size:smaller;" | ]=] .. down_display .. [=[
 * style="font-size:smaller;" | ]=] .. down_display .. [=[

]=] or "") .. [=[|-
 * colspan="3" | ]=] .. table.concat(forms, " ") .. [=[

end
 * }]=]

local trim = mw.text.trim

-- Assumes string or nil (or false), the types that can be found in an args table. local function if_not_empty(val) if val and trim(val) == "" then return nil else return val end end

function export.show_box_manual(frame) local m_links = require("Module:links") local num_type = frame.args["type"] local args = {} --cloning parent's args while also assigning nil to empty strings for pname, param in pairs(frame:getParent.args) do		args[pname] = if_not_empty(param) end local lang = args[1] or (mw.title.getCurrentTitle.nsText == "Template" and "und") or error("Language code has not been specified. Please pass parameter 1 to the template.") local sc = args["sc"]; local headlink = args["headlink"] local wplink = args["wplink"] local alt = args["alt"] local tr = args["tr"] local prev_symbol = if_not_empty(args[2]) local cur_symbol = if_not_empty(args[3]); local next_symbol = if_not_empty(args[4]) local prev_term = if_not_empty(args[5]) local next_term = if_not_empty(args[6])

local cardinal_term = args["card"]; local cardinal_alt = args["cardalt"]; local cardinal_tr = args["cardtr"] local ordinal_term = args["ord"]; local ordinal_alt = args["ordalt"]; local ordinal_tr = args["ordtr"] local adverbial_term = args["adv"]; local adverbial_alt = args["advalt"]; local adverbial_tr = args["advtr"] local multiplier_term = args["mult"]; local multiplier_alt = args["multalt"]; local multiplier_tr = args["multtr"] local distributive_term = args["dis"]; local distributive_alt = args["disalt"]; local distributive_tr = args["distr"] local collective_term = args["coll"]; local collective_alt = args["collalt"]; local collective_tr = args["colltr"] local fractional_term = args["frac"]; local fractional_alt = args["fracalt"]; local fractional_tr = args["fractr"] local optional1_title = args["opt"] local optional1_term = args["optx"]; local optional1_alt = args["optxalt"]; local optional1_tr = args["optxtr"] local optional2_title = args["opt2"] local optional2_term = args["opt2x"]; local optional2_alt = args["opt2xalt"]; local optional2_tr = args["opt2xtr"]

lang = require("Module:languages").getByCode(lang) or error("The language code \"" .. lang .. "\" is not valid.") sc = (sc and (require("Module:scripts").getByCode(sc) or error("The script code \"" .. sc .. "\" is not valid.")) or nil) require("Module:debug").track("number list/" .. lang:getCode) if sc then require("Module:debug").track("number list/sc") end if headlink then require("Module:debug").track("number list/headlink") end if wplink then require("Module:debug").track("number list/wplink") end if alt then require("Module:debug").track("number list/alt") end if cardinal_alt or ordinal_alt or adverbial_alt or multiplier_alt or distributive_alt or collective_alt or fractional_alt or optional1_alt or optional2_alt then require("Module:debug").track("number list/xalt") end local lang_type = lang:getType local subpage = mw.title.getCurrentTitle.subpageText local is_reconstructed = lang_type == "reconstructed" or mw.title.getCurrentTitle.nsText == "Reconstruction" alt = alt or (is_reconstructed and "*" or "") .. subpage if num_type == "cardinal" then cardinal_term = (is_reconstructed and "*" or "") .. subpage cardinal_alt = alt cardinal_tr = tr	elseif num_type == "ordinal" then ordinal_term = (is_reconstructed and "*" or "") .. subpage ordinal_alt = alt ordinal_tr = tr	end local header = lang:getCanonicalName .. " " .. num_type .. " numbers" if headlink then header = "" .. header .. "" end local previous = "" if prev_term or prev_symbol then previous = m_links.full_link({lang = lang, sc = sc, term = prev_term, alt = " &lt; " .. prev_symbol, tr = "-"}) end local current = m_links.full_link({lang = lang, sc = sc, alt = cur_symbol, tr = "-"}) local next = "" if next_term or next_symbol then next = m_links.full_link({lang = lang, sc = sc, term = next_term, alt = next_symbol .. " &gt; ", tr = "-"}) end local forms = {} if cardinal_term then table.insert(forms, "    Cardinal : " .. m_links.full_link({lang = lang, sc = sc, term = cardinal_term, alt = cardinal_alt, tr = cardinal_tr})) end if ordinal_term then table.insert(forms, "    Ordinal : " .. m_links.full_link({lang = lang, sc = sc, term = ordinal_term, alt = ordinal_alt, tr = ordinal_tr})) end if adverbial_term then table.insert(forms, "    Adverbial : " .. m_links.full_link({lang = lang, sc = sc, term = adverbial_term, alt = adverbial_alt, tr = adverbial_tr})) end if multiplier_term then table.insert(forms, "    Multiplier : " .. m_links.full_link({lang = lang, sc = sc, term = multiplier_term, alt = multiplier_alt, tr = multiplier_tr})) end if distributive_term then table.insert(forms, "    Distributive : " .. m_links.full_link({lang = lang, sc = sc, term = distributive_term, alt = distributive_alt, tr = distributive_tr})) end if collective_term then table.insert(forms, "    Collective : " .. m_links.full_link({lang = lang, sc = sc, term = collective_term, alt = collective_alt, tr = collective_tr})) end if fractional_term then table.insert(forms, "    Fractional : " .. m_links.full_link({lang = lang, sc = sc, term = fractional_term, alt = fractional_alt, tr = fractional_tr})) end if optional1_title then table.insert(forms, "    " .. optional1_title .. " : " .. m_links.full_link({lang = lang, sc = sc, term = optional1_term, alt = optional1_alt, tr = optional1_tr})) end if optional2_title then table.insert(forms, "    " .. optional2_title .. " : " .. m_links.full_link({lang = lang, sc = sc, term = optional2_term, alt = optional2_alt, tr = optional2_tr})) end local footer = "" if wplink then footer = "" .. lang:getCanonicalName .. " Wikipedia article on " .. m_links.full_link({lang = lang, sc = sc, term = "w:" .. lang:getCode .. ":" .. wplink, alt = alt, tr = tr}) end return [=[{| class="floatright" cellpadding="5" cellspacing="0" style="background: #ffffff; border: 1px #aaa solid; border-collapse: collapse; margin-top: .5em;" rules="all"
 * + ]=] .. header .. [=[
 * style="width: 64px; background:#dddddd; text-align: center; font-size:smaller;" | ]=] .. previous .. [=[
 * style="width: 64px; background:#dddddd; text-align: center; font-size:smaller;" | ]=] .. previous .. [=[

! style="width: 98px; text-align: center; font-size:larger;" | ]=] .. current .. [=[


 * style="width: 64px; text-align: center; background:#dddddd; font-size:smaller;" | ]=] .. next .. [=[


 * colspan="3" | ]=] .. table.concat(forms, " ") .. [=[
 * colspan="3" | ]=] .. table.concat(forms, " ") .. [=[


 * colspan="3" style="text-align: center; background: #dddddd;" | ]=] .. footer .. [=[
 * colspan="3" style="text-align: center; background: #dddddd;" | ]=] .. footer .. [=[

end
 * }]=]

return export