Module:Wordify

local mf = require('Module:Formatnum')

local yesno = require('Module:Yesno')

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(' Formatting error: %s ', 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 getCurrentLanguage local result = mw.title.getCurrentTitle.pageLanguage if not result then result = mw.language.getContentLanguage:getCode end return result end local function _round(value, precision) local rescale = math.pow(10, precision or 0); return math.floor(value * rescale + 0.5) / rescale; end

--[[

-- tableLength -- -- This function returns the number of keys in a table.

--]] local function tableLength(t) local count = 0 for _ in pairs(t) do count = count + 1 end return count end

--[[

-- singleEntry -- -- If a table contains a single entry, it returns the key and value, otherwise nil.

--]] local function singleEntry(t) local count = 0 local resultk, resultv for k, v in pairs(t) do    if count > 0 then return nil else count = count + 1 resultk = k       resultv = v    end end if count == 1 then return resultk, resultv else return nil end 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.

--]] local 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 = {} local ispi = isPositiveInteger for k, _ in pairs(t) do       if ispi(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 reversed 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

--[[

-- addMultipleKeys -- -- This takes a table and adds all the values in an array as keys to a single object

--]] local function addMultipleKeys(arr, keys, val) if keys then for _, v in ipairs(keys) do           arr[v] = val end end end

--[[

-- deepReverseTable -- -- This takes a table and returns a new table where the values are keys and vice versa

--]] local function deepReverseTable(t) local rev = {} for k, v in pairs(t) do       if type(v) == 'table' then addMultipleKeys(rev, v, k)       else rev[v] = k       end end return rev end

--[[

-- makeLengthTable -- -- This takes a table and returns a new table where keys are the lengths of the string in each subtable

--]] local function makeLengthTable(t) local result = {} local ulen = mw.ustring.len local ins = table.insert for _, v in pairs(t) do       local len = ulen(v) local subr = result[len] if subr then ins(subr, v)       else result[len] = { v } end end return result end

--[[

-- Object -- -- Root object

--]] local Object = {}

function Object:new (o) o = o or {}  -- create object if user does not provide one o.super = self setmetatable(o, self) self.__index = self return o end

function Object:tableInherit(name, k)   repeat local field = rawget(self, name) local result = field and field[k] if result then return result else self = rawget(self, 'super') end until not self return nil end

local parameters = {}

local parameter_lookup = {}

local Parameter = Object:new{ defaults = {} }

function Parameter:new (o) local result = self.super.new(self, o)   result.lookup = deepReverseTable(result.values) local code = rawget(result, 'code') if code and code ~= '' then parameters[code] = result end parameter_lookup[code] = code addMultipleKeys(parameter_lookup, result.names, code) return result end

function Parameter:default(lang, arg, arg2) if arg and arg ~= '' then return arg:lower else if lang and lang ~= '' then local d = rawget(self, 'defaults') if d then local l = d[lang] if type(l) == 'table' then l = l[arg2] end if l then return l               end end end local fallback = rawget(self, 'fallback') if type(fallback) == 'function' then return fallback else return fallback end end end

function Parameter:get(arg) local result = rawget(self, 'lookup')[arg] if result then return result else return nil end end

function Parameter:getArgument(args) local code = self.code if code and code ~= '' then local result = args[code] if result then return result else local names = rawget(self, 'names') if names then for _, v in pairs(names) do                   result = args[v] if result then return result end end end end end return nil end

local function setupParameters for k, v in pairs(parameters) do       parameter_lookup[k] = k        local names = rawget(v, 'names') addMultipleKeys(parameter_lookup, names, k)   end end

local function translateParameters(args) local result = {} for k, v in pairs(args) do       local c        if tonumber(k) then c = k       else c = parameter_lookup[k] end --       if not c then            return err('Illegal parameter: " .. k)        end                if c then            result[c] = v        end    end    return result end --[[ wordify

Usage:

--]]

function p.main(frame) local args = getArgs(frame) local translated = translateParameters(args) if type(translated) ~= 'table' then return err('Illegal argument: ' .. translated) end local x = args[1] local numsys = parameters.numsys:getArgument(translated) local prec = parameters.prec:getArgument(translated) local lk = parameters.lk:getArgument(translated) local lang = parameters.lang:getArgument(translated) local script = parameters.script:getArgument(translated) local state = parameters.state:getArgument(translated) local case = parameters.case:getArgument(translated) local class = parameters.class:getArgument(translated) local possessed = parameters.possessed:getArgument(translated) local person = parameters.person:getArgument(translated) local plural = parameters.plural:getArgument(translated) local exclude = parameters.exclude:getArgument(translated) local simplify = parameters.simplify:getArgument(translated) return p._wordify(x, prec, yesno(lk), numsys, lang, script, state, case, class, yesno(possessed), tonumber(person), yesno(plural), tonumber(exclude), yesno(simplify)) end

local language = Parameter:new{ code = 'lang', names = { 'language' }, values = {}, fallback = getCurrentLanguage }

local precision = Parameter:new{ code = 'prec', names = { 'precision' }, values = {}, fallback = 0 }

local link = Parameter:new{ code = 'lk', names = { 'link' }, values = {}, fallback = false }

local number_systems = Parameter:new{ code = 'numsys', names = { 'scale' }, values = { indian = { 'indian', 'ind' }, myriad = { 'myriad' }, long = { 'long', 'fra' }, short = {'short', 'usa' } },   defaults = {}, fallback = 'long' }

local possessed = Parameter:new{ code = 'possessed', values = {}, fallback = false }

local person = Parameter:new{ code = 'person', values = {}, fallback = false }

local plural = Parameter:new{ code = 'plural', values = {}, fallback = false }

local exclude = Parameter:new{ code = 'exclude', values = {}, fallback = false }

local simplify = Parameter:new{ code = 'simplify', values = {}, fallback = false }

local scripts = Parameter:new{ code = 'script', values = { arab = { 'arabic' }, aran = { 'nastaliq' }, armn = { 'armenian' }, bali = { 'balinese' }, beng = { 'bengali' }, bugi = { 'buginese', 'lontara' }, cans = { 'cans' }, cher = { 'cherokee' }, cyrl = { 'cyrilic' }, deva = { 'devanagari' }, ethi = { 'ethiopic', "ge'ez" }, geor = { 'georgian' }, geok = { 'khutsuri' }, grek = { 'greek' }, gujr = { 'gujarati' }, guru = { 'gurmunkhi' }, hang = { 'hangul' }, hani = { 'hanzi', 'kanji', 'hanja' }, hans = { 'han simplified', 'simplified han', 'simplified chinese', 'chinese simplified' }, hant = { 'han traditional', 'traditional han', 'traditional chinese', 'chinese traditional' }, hebr = { 'hebrew' }, java = { 'javanese' }, khmr = { 'khmer' }, knda = { 'kannada' }, laoo = { 'lao'}, latn = { 'latin' }, mong = { 'mongolian' }, mlym = { 'malayalam' }, mymr = { 'myanmar', 'burmese' }, orya = { 'oriya', 'odia' }, rohg = { 'hanifi rohingya' }, sinh = { 'sinhala' }, sund = { 'sundanese' }, syrc = { 'syriac' }, syre = { 'ʾesṭrangēlā' }, syrj = { 'western syriac' }, syrn = { 'eastern syriac' }, taml = { 'tamil'}, tavt = { 'tai viet' }, telu = { 'telugu' }, tfng = { 'tifinagh' }, tglg = { 'tagalog' }, thaa = { 'thaana' }, thai = { 'thai' }, tibt = { 'tibetan' }, yiii = { 'yi' } },   defaults = {}, fallback = 'latin' }

local states = Parameter:new{ code = 'state', values = { c = { 'construct' }, d = { 'definite' }, i = { 'indefinite' } },   fallback = 'indefinite' }

local classes = Parameter:new{ code = 'class', values = { an = { 'animate' }, ['cl-1'] = { 'class 1', 'class i' }, ['cl-1a'] = { 'class 1a' }, ['cl-2'] = { 'class 2', 'class ii' }, ['cl-2a'] = { 'class 2a' }, ['cl-3'] = { 'class 3', 'class iii' }, ['cl-4'] = { 'class 4', 'class iv' }, ['cl-5'] = { 'class 5', 'class v' }, ['cl-6'] = { 'class 6', 'class vi' }, ['cl-7'] = { 'class 7', 'class vii' }, ['cl-8'] = { 'class 8', 'class viii' }, ['cl-9'] = { 'class 9', 'class ix' }, ['cl-10'] = { 'class 10', 'class x' }, co = { 'common' }, f = { 'feminine' }, ['in'] = { 'inanimate' }, m = { 'masculine' }, ['m-an'] = { 'masculine animate' }, ['m-i'] = { 'masculine inanimate' }, n = { 'neuter' }, ve = { 'vegetable' }, x = { 'x' }, y = { 'y' } },   fallback = 'neuter', class_strings = { 'kategória', 'κατηγορία', 'osztály', 'flokkur', 'classe', 'clâsse', 'klasse', 'luokka', 'darasa', 'razred', 'разред', 'разряд', 'ประเภท', 'clase', 'klase', 'klaso', 'klasa', 'clasă', 'class', 'klass', 'kelas', 'aicme', 'sinif', 'třída', 'είδος', 'класс', 'класа', 'קלאַס', 'พรรค์', 'မျိုး', 'صِنْف', 'صَنْف', 'clas', 'klas', 'klad', 'rühm', 'τάξη', 'клас', 'しゅるい', 'ชนิด', 'ຊະນິດ', 'کلاس', 'դաս', 'صنف', 'رده', 'cl', '種類', '种类', '類', '类' } }

classes.class_lengths = makeLengthTable(classes.class_strings)

function classes:get(class) local function matchSimilarClasses(class, classlen, pos, t, len, lr) local function makeNumberedClass(class, affixlen) local rest if lr then rest = mw.ustring.sub(class, affixlen+1) else rest = mw.ustring.sub(class, 1, -(affixlen+1)) if mw.ustring.sub(rest, -1, -1) == "." then rest = mw.ustring.sub(rest, 1, -2) -- 1. Klasse end end local num = tonumber(rest) if num then return 'cl-' .. tostring(num) else return nil end end

local len1 = len + 1 if classlen > len1 then local cand if lr then cand = mw.ustring.sub(class, 1, len) else cand = mw.ustring.sub(class, -len) end for _, v in pairs(t) do               if cand == v then return makeNumberedClass(class, len1) end end end return nil end

local result = self.super.get(self, class) if result then return result else local l = mw.ustring.len(class) local bstart, bend = string.find(class, ' ') local dstart, dend = string.find(class, '-') if bstart or dstart then local pos if bstart and dstart then if bstart > dstart then pos = dstart else pos = bstart end elseif bstart then pos = bstart else pos = dstart end if pos > 1 then local len = pos-1 local v = self.class_lengths[len] if v then local cls = matchSimilarClasses(class, l, pos, v, len, true) if cls then return cls end end end if bstart then bstart, bend = string.find(class, " [^ ]*$") end if dstart then dstart, dend = string.find(class, "-[^-]*$") end if bstart and dstart then if bstart > dstart then pos = bstart else pos = dstart end elseif bstart then pos = bstart else pos = dstart end if pos < l then local len = l-pos local v = self.class_lengths[len] if v then local cls = matchSimilarClasses(class, l, pos, v, len, true) if cls then return cls end end end end local plain = mw.ustring.gsub(class, '-', '') return self.lookup[plain] end end local cases = Parameter:new{ code = 'case', names = {}, values = { abe = { 'abessiv' }, abl = { 'ablativ' }, abs = { 'absolutiv' }, accus = { 'accusativ', 'akkusativ', 'wenfall', 'acusativo', 'akuzativ', 'tožilnik', 'biernik', 'galininkas', 'þolfall', 'вини́тельный' }, ['accus-n'] = { 'accusative-nominative', 'akkusativ-nominativ', 'acusativo-nominativo', 'nominatiiviakkusatiivi' }, ['accus-g'] = { 'accusative-genitive', 'akkusativ-genitiv', 'acusativo-genitivo', 'genetiiviakkusatiivi' }, accud = { 'accudativ', 'akkudativ' }, adel = { 'adelativ' }, ades = { 'adessiv', 'adesyvas' }, adv = { 'adverbial' }, al = { 'allativ', 'adlativ', 'direktiv', 'aliatyvas' }, av = { 'aversiv' }, ag = { 'agentiv' }, an = { 'antessiv' }, ap = { 'apudessiv' }, b = { 'benefactiv', 'benefaktiv', 'destinativ' }, ca = { 'causal' }, ['ca-fi'] = { 'causal-final' }, comi = { 'comitativ', 'komitativ', 'assoziativ' }, comp = { 'comparativ' }, da = { 'dativ', 'wemfall', 'dajalnik', 'celownik', 'naudininkas', 'þágufall', 'да́тельный' }, de = { 'delativ' }, di = { 'distributiv' }, ['di-tem'] = { 'distributive-temporal' }, eg = { 'egressiv' }, el = { 'elativ' }, eq = { 'equativ', 'äquativ' }, er = { 'ergativ' }, ['er-g'] = { 'ergative-genitive' }, es = { 'essiv' }, ['es-fo'] = { 'essive-formal' }, ['es-mod'] = { 'essive-modal' }, ex = { 'exessiv' }, fo = { 'formal' }, g = { 'genitiv', 'wesfall', 'wessenfall', 'genetiv', 'rodilnik', 'dopełniacz', 'kilmininkas', 'eignarfall', 'роди́тельный' }, id = { 'identical' }, il = { 'illativ', 'kryptininkas' }, ine = { 'inessiv' }, ini = { 'initiativ' }, intran = { 'intransitiv' }, intrat = { 'intrativ' }, instruc = { 'instructiv', 'instruktiv' }, instrum = { 'instrumental', 'instrumentál', 'orodnik', 'narzędnik', 'įnagininkas', 'твори́тельный' }, ['instrum-comi'] = { 'instrumental-comitativ' }, la = { 'lativ' }, li = { 'limitativ' }, lo = { 'locativ', 'lokativ', 'lokál', 'mestnik', 'miejscownik', 'vietininkas' }, mod = { 'modal' }, n = { 'nominativ', 'werfall', 'rektus', 'rectus', 'imenovalnik', 'mianownik', 'vardininkas', 'nefnifall', 'имени́тельный' }, obl = { 'oblique', 'obliquus', 'oblik' }, obj = { 'objectiv' }, ['obj/obl'] = { 'objective/oblique' }, ori = { 'orientativ' }, orn = { 'ornativ' }, pa = { 'partitiv' }, peg = { 'pegativ' }, perl = { 'perlativ' }, pert = { 'pertingent' }, possesse = { 'possessed' }, possessi = { 'possessiv' }, postel = { 'postelativ' }, postes = { 'postessiv' }, pre = { 'prepositional', 'präpositiv', 'предло́жный' }, pri = { 'privativ' }, pro = { 'prolativ' }, r = { 'revertiv' }, se = { 'semblativ' }, so = { 'sociativ' }, sube = { 'subessiv' }, subl = { 'sublativ' }, supere = { 'superessiv' }, superl = { 'superlativ' }, tem = { 'temporal' }, ter = { 'terminativ' }, tr = { 'translativ' }, v = { 'vocativ', 'vokativ', 'wołacz', 'šauksmininkas' } },   fallback = 'nominative' }

function cases:get(case) local result = self.super.get(self, case) if result then return result else local l = mw.ustring.len(case) local matched if l > 4 then local last = mw.ustring.sub(case, -4, -1) if last == 'ivus' then matched = true case = mw.ustring.sub(case, 1, -3) result = self.lookup[case] if result then return result end elseif last == 'iivi' then matched = true case = mw.ustring.sub(case, 1, -4) .. 'v'               result = self.lookup[case] if result then return result end end end if l > 3 and not matched then local last = mw.ustring.sub(case, -3, -1) if last == 'ive' or last == 'ivo' then matched = true case = mw.ustring.sub(case, 1, -2) result = self.lookup[case] if result then return result end end end if l > 2 and not matched then local last = mw.ustring.sub(case, -2, -1) if last == 'if' then matched = true case = mw.ustring.sub(case, 1, -2) .. 'v'               result = self.lookup[case] if result then return result end end end local plain = mw.ustring.gsub(case, '-', '') return self.lookup[plain] end end

local function linkBegin(lang) if not lang or lang == '' then return "[["   else        return "[[:" .. lang .. ":"    end end

local function linkEnd(u) if not u or u == '' then return "]]" else return "|" .. u .. "]]"   end end

local function link_en(i, stem, u)   return (i > 39 and "Names of large numbers" or ("Orders of magnitude (numbers)#10" .. i)) .. linkEnd(u) end

local Adder = Object:new{}

function Adder:getAdditional(lang, i)   local one, simplify local top = self:tableInherit('additional', i)   if top then local entry = (top and lang and top[lang]) or top if entry then one, simplify = entry[1], entry.simplify end end

return one, simplify end

--[[ Rules grammars

 ::= "{}" | | "{"  "}" |   ::=  |  ","   ::= "{"  "}"  :=  |  ","   := "{ {"  ","  "} }" | <possessor-function> <singular-possessed> ::= <plural-possessed> ::= ::= | "{" <possessed-cases> "}" | <possessed-function> <plural-posessors> ::= "{" <plural-possessor-cases> "}" <plural-possessor-cases> := <plural-posessor-case> | <plural-possessor-case> "," <plural-possessor-cases> <plural-possessor-case> ::= "{" <clusivity-cases> "}" <clusivity-cases> ::= <simple-possessor-case> | <simple-possessor-case> "," <clusivity-cases>

<inflection-rules> ::= "{}" | "{" <stem-statement> "}" | "{" <number-cases> "}" | "{" <stem-statement> "," <number-cases> "}" <stem-statement> ::= "stem" "=" <stem-function> <number-cases> ::= <number-case> | <number-case> "," <number-cases> <number-case> ::= <number-index> <number-rule> <number-index> ::= "" | "[" "]" "=" | | "[" "fraction" "]"  "=" <number-rule> ::= | <ending-cases-list> | <state-cases> | <number-function> <ending-cases-list> ::= "{" <ending-cases> "}" <ending-cases> ::= <ending-case> | <ending-case> "," <ending-cases> <ending-case> ::= <string-index> "=" <ending-rule> <ending-rule> ::= | <class-cases-list> | <state-cases> | <ending-function> <class-cases-list> ::= "{" <class-cases> "}" <class-cases> ::= <class-case> | <class-case> "," <class-cases> <class-case> ::= <string-index> "=" <class-rule> <class-rule> ::= | <grammatical-cases-list> | <state-cases> | <class-function> <state-cases> ::= "{" <indefinite-rule> "}" | "{" <indefinite-rule> "," <definite-rule> "}" | "{" <indefinite-rule> "," <definite-rule> "," <construct-rule> "}" <indefinite-rule> ::= <state-rule> <definite-rule> ::= <state-rule> <construct-rule> ::= <state-rule> <state-rule> ::= | <grammatical-cases-list> | <state-function> <grammatical-cases-list> ::= "{" <grammatical-cases> "}" <grammatical-cases> ::= <grammatical-case> | <grammatical-case> "," <grammatical-cases> <grammatical-case> ::= <string-index> "=" <grammatical-rule> <grammatical-rule> ::= | <simple-class-cases-list> | "{" <possessed-cases> "}" | <grammatical-function> <simple-class-cases-list> ::= "{" <simple-class-cases> "}" <simple-class-cases> ::= <simple-class-case> | <simple-class-case> "," <simple-class-cases> <simple-class-case> ::= <string-index> "=" <simple-class-rule> <simple-class-rule> ::= | <simple-ending-cases-list> | <class-function> <simple-ending-cases-list> ::= "{" <simple-ending-cases> "}" <simple-ending-cases> ::= <simple-ending-case> | <simple-ending-case> "," <simple-ending-cases> <simple-ending-case> ::= <string-index> "=" <simple-ending-rule> <simple-ending-rule> ::= | <ending-function>

<possessed-cases> ::= <possessed-case> | <possessed-case> "," <possessed-cases> <possessed-case> ::= <string-index> "=" <possessed-rule> <string-index> ::= | "[" "]" <possessed-rule> ::= | <possessed-function> ]]

local families = { indian = {}, myriad = {}, short = {}, long = {} }

local Family = Object:new{}

function Family:setDefaults(defscale) local name = self.code if name and name ~= '' then local langs = number_systems.defaults if not langs then langs = {} number_systems.defaults = langs end if defscale then langs[name] = defscale end local scale = self.scale if scale then for k, v in pairs(scale) do               families[k][name] = self if not defscale and v.default then langs[name] = k               end local s = v.default_script if not s then local script = v.script if script then s = singleEntry(script) end end if s then local scales = scripts.defaults if not scales then scales = {} scripts.defaults = scales end local ls = scales[name] if not ls then ls = {} scales[name] = ls                   end ls[k] = s               end end end end return nil end

function Family:new (o) local result = Object.new(self, o)   local name = result.code if name and name ~= '' then local k = result.default_scale if not k then local scale = result.scale if scale then k = singleEntry(scale) end end result:setDefaults(k) end local pars = rawget(result, 'parameters') if pars then for k, v in pairs(pars) do           local p = parameters[k] local names = v.names if names then for _, n in names do                   local ns = p.names if not ns then ns = {} p.names = ns                   end table.insert(ns, n)               end addMultipleKeys(parameter_lookup, names, name) end local vales = v.values if values then for i, n in values do                   local ns = p.lookup if not ns then ns = {} p.lookup = ns                   end ns[n] = i               end end end end return result end

local Script = Adder:new{}

local hant_script = Script:new{ code = 'hani', additional = { [4] = { '萬' }, [8] = { '億' }, [12] = { '兆' }, [16] = { '京' }, [20] = { '垓' }, [24] = { '秭' }, [28] = { '穣' }, [32] = { '溝' }, [36] = { '澗' }, [40] = { '正' }, [44] = { '載' }, [48] = { '極' } } }

local hanja_script = hant_script:new{}

local kanji_script = hant_script:new{ additional = { [4] = { '万' } } }

local hans_script = kanji_script:new{ code = 'hans', additional = { [8] = { '亿' }, [32] = { '沟' }, [36] = { '涧' }, [44] = { '载' }, [48] = { '极' } } }

local hangul_script = Script:new{ code = 'hang', additional = { [4] = { '만' }, [8] = { '억' }, [12] = { '조' }, [16] = { '경' }, [20] = { '해' }, [24] = { '자' }, [28] = { '양' }, [32] = { '구' }, [36] = { '간' }, [40] = { '정' }, [44] = { '재' }, [48] = { '극' } } }

local language_ko = Family:new{ code = 'ko', scale = { myriad = Adder:new{ top = 48, link = '큰_수의_이름', script = { hang = hangul_script, hani = hanja_script }, default_script = 'hang' } } }

local language_ja = Family:new{ code = 'ja', scale = { myriad = Adder:new{ top = 48, link = '10の冪', script = { hani = kanji_script } } } }

local language_zh = Family:new{ code = 'zh', scale = { myriad = Adder:new{ top = 48, link = '中文数字', script = { hans = hans_script, hant = hant_script }, default_script = 'hans' } } }

local latin = Family:new{ code = 'la', space = true, root = 'lli', suffix = { 'o', 'ard' },

prefix = { 'mi', 'bi', 'tri', 'quadri', 'quinti', 'sexti', 'septi', 'octi', 'noni', 'deci', [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 = { [16] = { la = 'sedeci' } }, unit_prefix_exception = {}, inflection = { { o = { { [] = , g = 'nis', da = 'ni', accus = { [] = 'nem', n = }, abl = 'ni' } }, [] = { { [] = 'um', g = 'i', da = 'o', abl = 'o' } } }, { o = { { [] = { [] = 'nes', n = 'nia' }, g = 'nium', da = 'nibus', abl = 'nibus' } }, [] = { { [] = 'a', g = 'orum', da = 'is', abl = 'is' } } } }, scale = { long = Adder:new{ top = 306, link = 'Nomina permagnorum numerorum' } } }

local function makeArdMaker(minval, thousand) return function (scale, i, step, args, r, simplify) if i < minval then return nil end local j = math.floor(i / step) if (j*step)+3 == i then local u, simp, stem = scale:unit(i-3, args, 1000, false) return thousand .. " " .. u, (r == 1), stem else return nil end end end

local language_ia = latin:new{ code = 'ia', suffix = { 'on', 'ardo' }, inflection = { stem = makeArdMaker(15, "mille"), '',                  function (u, args, r, simplify) if mw.ustring.sub(u, 1, 6) == "mille " then return u                       else local n = mw.ustring.len(u) if mw.ustring.sub(u, n, n) == "o" then return u .. "s" else return u .. "es" end end end }, scale = { long = Adder:new{ top = 18, link = function(i, stem, u)                                   local start, finish = string.find(u, ' ') if not start then return stem .. linkEnd(u) else local first = mw.ustring.sub(u, 1, start - 1) local second = mw.ustring.sub(u, finish + 1) local secondlink = linkBegin('ia') .. stem .. linkEnd(second) return first .. " " .. secondlink, true end end } } }

local language_is = latin:new{ code = 'is', root = 'llj', suffix = { 'ón', 'arð' }, prefix = { [4] = 'kvaðri', [5] = 'kvinti' }, inflection = { { n = { { [] = , g = 'ar' }, { n = 'in', accus = 'ina', da = 'inni', g = 'arinnar' } }, [] = { { n = 'ur', accus = , da = 'i', g = 's' }, { n = 'urinn', accus = 'inn', da = 'inum', g = 'sins' } } }, { n = { { [''] = 'ir', da = 'um', g = 'a' }, { [''] = 'irnar', da = 'unum', g = 'anna' } }, [''] = { { n = 'ar', accus = 'a', da = 'um', g = 'a' } , { n = 'arnir', accus = 'ana', da = 'unum', g = 'anna' } } } }, scale = { long = Adder:new{ top = 30, link = 'Stórar tölur' } } }

local language_nl = latin:new{ code = 'nl', root = 'lj', suffix = { 'oen', 'ard' }, prefix_exception = { [16] = { nl = 'sedeci' } }, inflection = {}, scale = { long = Adder:new{ top = 141, link = 'Lijst van machten van tien' } } }

local language_af = language_nl:new{ code = 'af', prefix = { [4] = 'kwadri', [5] = 'kwinti', [6] = 'seksti', [8] = 'okti', [10] = 'desi' }, scale = { long = Adder:new{ top = 63, link = 'Kort en lang skaalverdeling' } } }

local c_family = latin:new{ code = '', capitalize = true }

local language_de = c_family:new{ code = 'de', suffix = { 'on', 'arde' }, prefix = { [8] = 'Okti', [10] = 'dezi' }, unit_prefix = { [2] = 'Do', [6] = 'Se', [8] = 'Okto' }, inflection = { '', { e = 'n', [''] = 'en' } }, scale = { long = Adder:new{ top = 306, link = 'Zahlennamen' } } }

local language_lb = c_family:new{ code = 'lb', suffix = { 'oun', 'ard' }, inflection = { '', 'en' }, scale = { long = Adder:new{ top = 27, link = 'Lëscht vun de Prefixe fir Moosseenheeten' } } }

local li_family = latin:new{ code = '', root = 'li' }

local language_ca = li_family:new{ code = 'ca', suffix = { 'ó', 'ard' }, inflection = { '', { d = 's', [''] = function (u, args, r, simplify) return mw.ustring.sub(u, 1, -2) .. "ons" end } }, scale = { long = Adder:new{ top = 306, link = 'Escales curta i llarga' } } }

local language_eo = li_family:new{ code = 'eo', suffix = { 'ono', 'ardo' }, prefix = { [2] = 'dui', [4] = 'kvari', [5] = 'kvini', [6] = 'sesi', [7] = 'sepi', [8] = 'oki', [9] = 'naŭi', [10] = 'deki' }, unit_prefix = { [4] = 'kvatuor', [5] = 'kvin', [6] = 'seks', [8] = 'okto' }, inflection = { '', 'j' }, scale = { long = Adder:new{ top = 177, link = 'Vortoj por grandegaj nombroj' } } }

local language_it = li_family:new{ code = 'it', suffix = { 'one', 'ardo' }, inflection = { '', function (u, args, r, simplify) return mw.ustring.sub(u, 1, -2) .. "i" end }, scale = { long = Adder:new{ top = 63, link = function(i, stem, u) return ("Ordini di grandezza (numeri)#10" .. i) .. linkEnd(u) end, exception = { [36] = 'none', [39] = 'none', [42] = 'none', [45] = 'none', [48] = 'none', [51] = 'none', [54] = 'none', [57] = 'none' } } } }

local language_scn = li_family:new{ code = 'scn', suffix = { 'uni', 'ardu' }, inflection = { '', function (u, args, r, simplify) local n = mw.ustring.len(u) if mw.ustring.sub(u, n, n) == "i" then return u else return mw.ustring.sub(u, 1, -2) .. "i" end end}, scale = { long = Adder:new{ top = 63, exception = { [30] = 'none', [33] = 'none', [36] = 'none', [39] = 'none', [42] = 'none', [45] = 'none', [48] = 'none', [51] = 'none', [54] = 'none', [57] = 'none' } } } }

local language_pl = li_family:new{ code = 'pl', suffix = { 'on', 'ard' }, prefix = { [3] = 'try', [4] = 'kwadry', [5] = 'kwinty', [6] = 'seksty', [7] = 'septy', [8] = 'okty', [10] = 'decy' }, inflection = { { { n = , g = 'a', da = 'owi', accus = , instrum = 'em', [] = { n = 'ie', [] = 'zie' } } }, { { [''] = 'y', g = 'ów', da = 'om', instrum = 'ami', lo = 'ach' } }, [5] = { { [''] = function (u, args, r, simplify) local floor = math.floor local n = floor(r/100) local m = r - n * 100 if m < 2 or (m > 4 and m < 22) then return u .. 'ów' elseif m < 5 then return u .. 'y'                                       else local o = floor(m/10) local p = m - o * 10 if p > 1 and p < 5 then return u .. 'y'                                           else return u .. 'ów' end end end, g = 'ów', da = 'om', instrum = 'ami', lo = 'ach', v = 'y' } }, fraction = 'a' }, scale = { long = Adder:new{ top = 75, link = 'Liczebniki główne potęg tysiąca' } } }

local language_pt = li_family:new{ code = 'pt', suffix = { 'ão' }, inflection = { stem = makeArdMaker(9, "mil"), '',                 function (u, args, r, simplify) if mw.ustring.sub(u, 1, 4) == "mil " then return u                       else return mw.ustring.sub(u, 1, -3) .. "ões" end end }, scale = { long = Adder:new{ top = 60, exception = { [6] = { 'milhão' } } } } }

local ib_family = latin:new{ code = '', root = 'll', suffix = { 'ón', 'ardo' } }

local milArdMaker = makeArdMaker(9, "mil")

local language_an = ib_family:new{ code = 'an', prefix = { [4] = 'quatri' }, inflection = { stem = milArdMaker, '',                  function (u, args, r, simplify) 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) .. "ons" end end end }, scale = { long = Adder:new{ top = 24, link = 'Potencia de diez' } } }

local language_gl = ib_family:new{ code = 'gl', inflection = { stem = milArdMaker, '',                  function (u, args, r, simplify) if mw.ustring.sub(u, 1, 4) == "mil " then return u                       else return u .. "s" end end }, scale = { long = Adder:new{ top = 18 } } }

local es_family = ib_family:new{ code = '', prefix = { [4] = 'cuatri' }, plural = function (u, args, r, simplify) 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 } local language_ast = es_family:new{ code = 'ast', inflection = { stem = milArdMaker, '',                  es_family.plural }, scale = { long = Adder:new{ top = 30, link = 'Escales numbériques llarga y curtia' } } }

local language_es = es_family:new{ code = 'es', unit_prefix = { [4] = 'cuatro', [9] = 'noven' }, inflection = { stem = makeArdMaker(15, "mil"), '',                  es_family.plural }, scale = { long = Adder:new{ top = 120, link = function(i, stem, u) return ("Orden de magnitud (números)#10" .. i) .. linkEnd(u) end } } }

local onard_family = latin:new{ code = '', suffix = { 'on', 'ard' }, }

local language_en = onard_family:new{ code = 'en', inflection = { { { n = '', possessi = "'s" } }, { { n = function (u, args, r, simplify) if simplify then return u .. 's'                               else return u                               end end, possessi = function (u, args, r, simplify) if simplify then return u .. "s'" else return u .. "'s"                                   end end } } }, default_scale = 'short', scale = { long  = Adder:new{ top = 306, link = link_en, additional = { [100] = { 'googol' } } }, short = Adder:new{ top = 303, link = link_en, additional = { [100] = { 'googol' } } }, indian = Adder:new{ top = 14, link = function(i, stem, u)                               local start, finish = string.find(u, ' ') if not start then return u .. linkEnd(u) else local first = mw.ustring.sub(u, 1, start - 1) local second = mw.ustring.sub(u, finish + 1) local firstlink = linkBegin('en') .. first .. linkEnd(first) if first == second then return firstlink .. " " .. second, true else return firstlink .. " " .. linkBegin('en') .. second .. linkEnd(second), true end end end, additional = { [5] = { 'lakh' }, [7] = { 'crore' }, [12] = { 'lakh crore' }, [14] = { 'crore crore' } } }   } }

local language_fr = onard_family:new{ code = 'fr', prefix = { [10] = 'déci' }, unit_prefix = { [3] = 'tré', [9] = 'noni' }, inflection = { '', 's' }, scale = { long = Adder:new{ top = 57, link = function(i, stem, u) return (i > 36 and "Noms des grands nombres" or ("Ordres de grandeur de nombres#10" .. i)) .. linkEnd(u) end } } }

local k_family = onard_family:new{ code = '', prefix = { [4] = 'kvadri', [5] = 'kvinti', [8] = 'okti' } }

local language_hu = k_family:new{ code = 'hu', suffix = { 'ó', 'árd' }, prefix = { [6] = 'szexti', [7] = 'szepti' }, unit_prefix = { [3] = 'tri', [4] = 'kva', [5] = 'kvint', [6] = 'szex' }, inflection = { { d = { { n = '', accus = 'ot', da = 'nak', instrum = 'dal', ['ca-fi'] = 'ért', tr = 'dá', ter = 'ig', ['es-fo'] = 'ként', ine = 'ban', supere = 'on', ades = 'nál', il = 'ba', subl = 'ra', al = 'hoz', el = 'ból', de = 'ról', abl = 'tól', possessi = { 'é', 'éi' } } }, [] = { { n = , accus = 't', da = 'nak', instrum = 'val', ['ca-fi'] = 'ért', tr = 'vá', ter = 'ig', ['es-fo'] = 'ként', ine = 'ban', supere = 'n', ades = 'nál', il = 'ba', subl = 'ra', al = 'hoz', el = 'ból', de = 'ról', abl = 'tól', possessi = { 'é', 'éi' } } } }, { d = { { n = 'ok', accus = 'okat', da = 'oknak', instrum = 'okkal', ['ca-fi'] = 'okért', tr = 'okká', ter = 'okig', ['es-fo'] = 'okként', ine = 'okban', supere = 'okon', ades = 'oknál', il = 'okba', subl = 'okra', al = 'okhoz', el = 'okból', de = 'okról', abl = 'októl', possessi = { 'oké', 'okéi' } } }, [''] = { { n = 'k', accus = 'kat', da = 'knak', instrum = 'kkal', ['ca-fi'] = 'kért', tr = 'kká', ter = 'kig', ['es-fo'] = 'kként', ine = 'kban', supere = 'kon', ades = 'knál', il = 'kba', subl = 'kra', al = 'khoz', el = 'kból', de = 'król', abl = 'któl', possessi = { 'ké', 'kéi' } } } } },   possessive = { d = { { { { 'om', 'aim' } }, { { 'od', 'jaid' } }, { { 'ja', 'jai'} } }, { { { 'unk', 'jaink' } }, { { 'otok', 'jaitok' } }, { { 'juk', 'jaik'} } } }, [''] = { { { { 'm', 'im' } }, { { 'd', 'id' } }, { { 'ja', 'i'} } }, { { { 'nk', 'ink' } }, { { 'tok', 'itok' } }, { { 'juk', 'ik'} } } } }, scale = { long = Adder:new{ top = 96, link = 'Tíz hatványai' } } }

local language_sv = k_family:new{ code = 'sv', root = 'lj', inflection = { { { n = '', g = 's' }, { n = 'en', g = 'ens' } }, { { n = 'er', g = 'ers' }, { n = 'erna', g = 'ernas' } } }, scale = { long = Adder:new{ top = 177, link = 'Namn på stora tal' } } }

local s_family = k_family:new{ code = '', prefix = { [6] = 'seksti' } }

local language_da = s_family:new{ code = 'da', inflection = { { { n = '', possessi = "s" }, { n = 'en', possessi = "ens" } }, { { n = 'er', possessi = "ers" }, { n = 'erne', possessi = "ernes" } } }, scale = { long = Adder:new{ top = 63, link = 'Store tal' } } }

local language_fi = s_family:new{ code = 'fi', root = 'lj', suffix = { 'oon' }, prefix = { [9] = 'novi', [10] = 'deki' }, inflection = { stem = makeArdMaker(9, "tuhat"), function (u, args, r, simplify) local case = args.case if mw.ustring.sub(u, 1, 6) == "tuhat " then local base = 'tuha' local cases = { n = 't', ['accus-n'] = 't', ['accus-g'] = 'nnen', g = 'nnen', pa = 'tta', ine = 'nnessa', el = 'nnesta', il = 'nteen', ades = 'nnella', abl = 'nnelta', al = 'nnelle', es = 'netena', tr = 'nneksi', abe = 'nnetta' } return base .. cases[case] .. mw.ustring.sub(u, 6) else local cases = { n = 'a', ['accus-n'] = 'a', ['accus-g'] = 'an', g = 'an', pa = 'aa', ine = 'assa', el = 'asta', il = 'aan', ades = 'alla', abl = 'alta', al = 'alle', es = 'ana', tr = 'aksi', abe = 'atta', } return u .. cases[case] end end, function (u, args, r, simplify) local case = args.case if mw.ustring.sub(u, 1, 6) == "tuhat " then local base = 'tuha' local cases = { n = 'tta', ['accus-n'] = 'nnet', g = 'nsien', pa = 'nsia', ine = 'nsissa', el = 'nsista', il = 'nsiin', ades = 'nsilla', abl = 'nsilta', al = 'nsille', es = 'nsina', tr = 'nsiksi', instruc = 'nsin', abe = 'nsitta', comi = 'nsine' } if simplify and (case == '' or case == 'n') then return u                           else return base .. cases[case] .. mw.ustring.sub(u, 6) end else local cases = { n = 'aa', ['accus-n'] = 'at', g = 'ien', pa = 'ia', ine = 'issa', el = 'ista', il = 'iin', ades = 'illa', abl = 'ilta', al = 'ille', es = 'ina', tr = 'iksi', instruc = 'in', abe = 'itta', comi = 'ine' } if simplify and (case == '' or case == 'n') then return u .. 'at' else return u .. cases[case] end end end }, scale = { long = Adder:new{ top = 63, link = 'Suurten lukujen nimet' } } }

local language_no = s_family:new{ code = 'no', prefix = { [10] = 'desi' }, unit_prefix = { [2] = 'do', [5] = 'kvin', [6] = 'seks', [8] = 'okto' }, inflection = { { { n = '', g = 's' }, { n = 'en', g = 'ens' } }, { { n = 'er', g = 'ers' }, { n = 'ene', g = 'enes' } } }, scale = { long = Adder:new{ top = 81, link = 'Navn på store tall' } } }

local language_sl = s_family:new{ code = 'sl', root = 'lij', inflection = { { n = '', [''] = 'a' }, { d = 'i', [''] = 'a' }, 'e', [5] = { d = '', [''] = 'ov' }, fraction = { d = 'e', [''] = 'a' } }, scale = { long = Adder:new{ top = 63, link = 'Imena velikih števil' } } }

local u_family = k_family:new{ code = '', root = 'li', unit_prefix = { [4] = 'kvadro', [5] = 'kvin' } }

local language_cs = u_family:new{ code = 'cs', prefix = { [50] = 'kvinkvaginti' }, inflection = { { n = { { n = , [] = 'u', accus = '', instrum = 'em', v = 'e'} }, [] = { { n = 'a', g = 'y', [] = 'ě', accus = 'u', instrum = 'ou', v = 'o'} } }, { n = { { [''] = 'y', g = 'ů', da = 'ům', lo = 'ech'} }, [] = { { [] = 'y', g = '', da = 'ám', instrum = 'ami', lo = 'ách'} } }, [5] = { n = { { [''] = 'ů', da = 'ům', instrum = 'y', lo = 'ech', v = 'y' } }, [] = { { [] = '', da = 'ám', instrum = 'ami', lo = 'ách', v = 'y' } } }, fraction = { n = 'u', [''] = 'y' } }, scale = { long = Adder:new{ top = 99, link = 'Desítková soustava#Názvy velkých čísel' } } }

local function ardInflectGenitivePlural_sk(u, args, r, simplify) return mw.ustring.sub(u, 1, -4) .. "árd" end

local language_sk = u_family:new{ code = 'sk', suffix = { 'ón', 'ard' }, prefix = { [40] = 'kvadraginti', [50] = 'kvinginti', [60] = 'seksaaginti', [80] = 'oktoginti', [90] = 'nonaginti' }, unit_prefix = { [8] = 'okto' }, inflection = { { n = { { [] = , g = 'a', da = 'u', instrum = 'om', lo = 'e' } }, [] = { { n = 'a', g = 'y', accus = 'u', instrum = 'ou', [] = 'e' } } }, { n = { { [''] = 'y', g = 'ov', da = 'om', instrum = 'mi', lo = 'och' } }, [''] = { { n = 'y', da = 'ám', accus = 'y', instrum = 'ami', lo = 'ách', g = ardInflectGenitivePlural_sk } } }, [5] = { n = { { [''] = 'ov', da = 'om', instrum = 'mi', lo = 'och' } }, [] = { { [] = ardInflectGenitivePlural_sk, da = 'ám', instrum = 'ami', lo = 'ách' } } }, fraction = { n = 'a', [''] = ardInflectGenitivePlural_sk } }, scale = { long = Adder:new{ top = 306, link = 'Veľké čísla' } } }

function Family:getPrefix(i) return self:tableInherit('prefix', i) end

function Family:getUnitPrefix(i) return self:tableInherit('unit_prefix', i) end

function Family:makePrefix(j) local entry = self.prefix_exception[j] and self.prefix_exception[j][self.code] local p   if entry then p = entry else p = self:getPrefix(j) if not p then if j < 11 then return nil elseif j < 100 then local k = math.floor(j / 10) local d = k * 10 local l = j - d               local unitentry = self.unit_prefix_exception[l] and self.unit_prefix_exception[l][self.code] local u               if unitentry then u = unitentry else u = self:getUnitPrefix(l) end local decentry = self.prefix_exception[d] and self.prefix_exception[d][self.code] local x               if decentry then x = decentry else x = self:getPrefix(d) end p = u .. x           else return nil end end end if self.capitalize then return mw.ustring.upper(mw.ustring.sub(p, 1, 1)) .. mw.ustring.sub(p, 2) else return p   end end

function Family:buildOne(i, step) local k = i / 3 if k ~= _round(k, 0) then return nil end local j   if step == 3 then j = math.floor((i - 3)/ step) else j = math.floor(i / step) end if j < 1 then return nil else local pref = self:makePrefix(j) local suf if step == 3 then suf = self.suffix[1] else suf = (j * step) == i and self.suffix[1] or self.suffix[2] end return pref .. self.root .. suf end end

local Scale = Adder:new{}

local indian_scale = Scale:new{ name = 'indian', step = 1, family = families['indian'] }

local myriad_scale = Scale:new{ name = 'myriad', step = 4, family = families['myriad'] }

local scale3 = Scale:new{ build = true }

local short_scale = scale3:new{ name = 'short', step = 3, family = families['short'] }

local long_scale = scale3:new{ name = 'long', step = 6, family = families['long'] }

local scales = { indian = indian_scale, long = long_scale, myriad = myriad_scale, short = short_scale, ind = indian_scale, fra = long_scale, usa = short_scale }

function Scale:getLanguage(lang) local family = self.family return family and family[lang] end

function Scale:getField(name, lang, top) local family = self.family local language = family and family[lang] local scales = language and language.scale local special = scales and scales[self.name] local result = special and special[name] if result then return result, true -- overriden else local v = self[name] if top then return v, false else return v and v[lang], false end end end

function Scale:getOne(i, args, r)   local lang = args.lang local one, simplify local function getAdditional(o, lang) local one, simplify local script = o.script if script then local top = (lang and script[lang]) or script local scr = top and top[args.script] if scr then one, simplify = scr:getAdditional(nil, i)           end end if not one then one, simplify = o:getAdditional(lang, i)       end return one, simplify end do       local exception, over = self:getField('exception', lang, true) if exception then local top = exception[i] local entry = top and ((over or type(top) == 'string') and top or top[lang]) if entry then one, simplify = entry[1], entry.simplify end end end local language = args.language if not one then local scales = language.scale local special = scales and scales[self.name] if special then one, simplify = getAdditional(special, nil) end end if not one then one, simplify = getAdditional(self, lang) end if one then return one, simplify elseif language.inflection and language.inflection.stem then local d = language.inflection.stem if type(d) == 'function' then return d(self, i, self.step, args, r, simplify) end else return nil end end

function Scale:unit(i, args, r, simplify) local language, one, stem, entry, inflectState, inflectEndings, inflectClass local function makeApplier(simplify, f)       return function (d, arg) if not d then return nil elseif type(d) == 'function' then return d(one, args, r, simplify), simplify, stem elseif type(d) == 'table' then return f(d, arg) else return one .. d, simplify, stem end end end

local function makeDoer(topn, ensure, arr, simplify, f)       return function (n, arg) local d = arr[n] if d then return f(d, arg) else if topn > 1 and n > 2 then d = arr[2] if d then return f(d, arg) end end if topn > 0 and n > 1 then d = arr[1] if d then return f(d, arg) end end if ensure then return one, simplify, stem else return nil end end end end local function doMain(arr, simplify, ensure, applier, doer, f)       if arr then return f(applier, doer) elseif ensure then return one, simplify, stem else return nil end end local function doFun(arr, simplify, topn, ensure, application, main) local applier = makeApplier(simplify, application) local doer if topn then doer = makeDoer(topn, ensure, arr, simplify, applier) end return doMain(arr, implify, ensure, applier, doer, main) end local function inflectPossessed(arr, other, simplify) return doFun(arr, simplify, 1, true,                     function(d, arg) return inflectEndings(d, simplify, false, false, false) end,                     function (applier, doer)                        if (other and args.possessed) or ((not other) and r > 1) then                            return doer(2)                        else                            return doer(1)                        end                     end) end local function declineCase(d, simplify, docla) local applyCase = makeApplier(simplify,                                      function(d, arg)                                         if docla then                                            local result, simp, s = inflectClass(d, simplify, false, true, false)                                            if result then                                                return result, simp, s                                            end                                        end                                        return inflectPossessed(d, true, simplify)                                      end) local c = d[args.case] if not c then c = d[''] -- default inflection end return applyCase(c) end

inflectClass = function(t, simplify, dosta, dosub, docas) local class = args.class return doFun(t, simplify, 0, false,                                     function(v, arg)                                         local result, simp, s                                        if dosub then                                            result, simp, s = inflectEndings(v, simplify, false, false, false)                                        end                                        if result then                                            return result, simp, s                                        elseif docas then                                            result, simp, s = declineCase(v, simplify, false)                                        end                                        if result then                                            return result, simp, s                                        elseif dosta then                                            return inflectState(v, simplify, false) else return nil end end, function(applier, doer) local done, elsevalue for k, v in pairs(t) do                                           if k == '' then elsevalue = v                                               if done then break end elseif not done and type(k) == 'string' then local result, simp, s = doer(class) if result then return result, simp, s                                               else if elsevalue then break else done = true end end end end return applier(elsevalue) end)                  end

inflectState = function(arr, simplify, docla) return doFun(arr, simplify, 1, true,                                     function(d, arg) return declineCase(d, simplify, docla) end,                                     function(applier, doer)                                        local state = args.state                                        if state == 'c' then                                            return doer(3)                                        elseif state == 'd' then                                            return doer(2)                                        else                                            return doer(1)                                        end                                     end) end local function possessiveClusivity(arr, n, plural, simplify) return doFun(arr, simplify, 1, true,                     function(d, arg) return inflectPossessed(d, false, simplify) end,                     function(applier, doer)                        if plural then                            local exclude = args.exclude                            if not exclude or n ~= 2 then                                return doer(1)                            elseif exclude == 3 then                                return doer(2)                            elseif exclude == 2 then                                return doer(3)                            else                                return doer(1)                            end                        else                            return doer(1)                        end                     end) end local function possessivePerson(arr, plural, arg, simplify) return doFun(arr, simplify, 2, true,                     function(d, arg) return possessiveClusivity(d, arg, plural, simplify) end,                     function(applier, doer) return doer(args.person, args.person) end) end local function possessivePlural(arr, simplify) return doFun(arr, simplify, 1, true,                     function(d, arg) return possessivePerson(d, arg, nil, simplify) end,                     function(applier, doer)                        if args.plural then                            return doer(2, true)                        else                            return doer(1, false)                        end                     end) end inflectEndings = function(t, simplify, docla, dosta, dopos) return doFun(t, simplify, nil, true,                                     function(v, arg)                                         local result, simp, s                                        if docla then                                            result, simp, s = inflectClass(v, simplify, true, false, true)                                        end                                        if result then                                            return result, simp, s                                        elseif dosta then                                            return inflectState(v, simplify, docla)                                        elseif dopos then                                            return possessivePlural(v, simplify)                                        else                                            return nil                                        end end, function(applier, doer) local olen, elsevalue local ulen = mw.ustring.len local usub = mw.ustring.sub for k, v in pairs(t) do                                           if k == '' then elsevalue = v                                           elseif type(k) == 'string' then local l = ulen(k) if not olen then olen = ulen(one) end if olen >= l then if k == usub(one, -l, -1) then return applier(v) end end end end return applier(elsevalue) end)                    end

local function inflectNumberCase(number_case, simplify) local applyNumberCase = makeApplier(simplify,                                            function(d, arg)                                                 local result, simp, s = inflectEndings(d, simplify, true, true, false)                                                if result then                                                    return result, simp, s                                                else                                                    return inflectState(d, simplify, true)                                                end                                            end) if entry and entry[number_case] then return entry[number_case], simplify, entry[1] or stem elseif not one then return nil elseif language.inflection then return applyNumberCase(language.inflection[number_case]) end end

local function inflectNumber(number_case, simplify) local result, simp, st       if number_case == 5 then result, simp, st = inflectNumberCase(5, simplify) end if result then return result, simp, st       end if number_case == 5 or number_case == 3 then result, simp, st = inflectNumberCase(3, simplify) end if result then return result, simp, st       end if number_case == 5 or number_case == 3 or number_case == 2 then result, simp, st = inflectNumberCase(2, simplify) end if result then return result, simp, st       else result, simp, st = inflectNumberCase(1, simplify) if result then return result, simp, st           else return one, simplify, stem end end end local function possessive(simplify) local applyPossessive = makeApplier(simplify,                                            function(d, arg)                                                 local result, simp, s = inflectEndings(d, simplify, false, false, true)                                                if result then                                                    return result, simp, s                                                else                                                     return possessivePlural(d, simplify)                                                end                                            end) if entry and entry.possessive then return entry.possessive, simplify, entry.possessive elseif not one then return nil else return applyPossessive(language.possessive) end end language = args.language local s   one, s, stem = self:getOne(i, args, r)    local simp = simplify or s    if not one then one = self.build and language and language:buildOne(i, self.step) stem = one end local exception, over = self:getField('exception', lang, true) local top = exception and exception[i] entry = top and ((over or type(top) == 'string') and top or top[lang]) if args.person then return possessive(simplify) else if r == 1 then return inflectNumber(1, simp) elseif r == 2 then return inflectNumber(2, simplify) elseif r == 3 or r == 4 then return inflectNumber(3, simplify) elseif r == _round(r, 0) then return inflectNumber(5, simplify) else local result, rs, st = inflectNumberCase('fraction', simp) if result then return result, rs, st           else return inflectNumber(5, simplify) end end end end

function Scale:unitLink(lk, i, stem, u, lang) if lk then local f = self:getField('link', lang) if f then if type(f) == 'function' then local l, full = f(i, stem, u)               if full then return l               else return linkBegin(lang) .. l               end else return linkBegin(lang) .. f .. linkEnd(u) end else return linkBegin('en') .. link_en(i, stem, u)       end else return u   end end

function Scale:found(args, i, r)   local lk = args.lk    local u, simp, stem = self:unit(i, args, r, args.simplify) if not u then return nil elseif args.simplify or simp then return self:unitLink(lk, i, stem, u, args.lang) else local lang = args.lang return mf.formatNum(r, lang, args.prec) .. ((args.language.space and " ") or "") .. self:unitLink(lk, i, stem, u, lang) end end function Scale:binary(x, args, top, bottom) local target, target_r local lang = args.lang local prec = args.prec local floor = math.floor local pow = math.pow local rou = _round local block, iblock if self.step == 4 then block = 10000 iblock = 4 else block = 1000 iblock = 3 end while top >= bottom do         local i        if top == bottom then i = top else local diff = top - bottom local d = floor(diff / 2) local m = bottom + d           if m/iblock == floor(m/iblock) then i = m           else i = m + 2 end end local y = x / pow(10,i) local r = rou(y, prec) local exception, over = self:getField('exception', lang, true) local entry = exception and exception[i] if not entry or (entry ~= 'none' and (over or entry[lang] ~= "none")) then if r >= block and top > i then bottom = i + iblock target = i               target_r = r            elseif r >= 1 then local result = self:found(args, i, r)               if result then return result else top = i - iblock end else top = i - iblock end elseif r < block or top == i then top = i - iblock else local result if top > i and r >= block then result = self:binary(x, args, top, i + iblock) end if result then return result elseif i > bottom then result = self:binary(x, args, i - iblock, bottom) if result then return result end end break end end if target then local result = self:found(args, target, target_r) if result then return result else return nil end end return nil end

function Scale:wordify(x, args) local prec = args.prec return mf.formatNum(_round(x, prec), args.lang, prec) end

function scale3:wordify(x, args) local toptop = self:getField('top', args.lang) if toptop then do           local top = toptop local bottom = 102 local result = self:binary(x, args, top, bottom) if result then return result end end if 100 <= toptop then -- allow googol local top = 100 local bottom = 100 local result = self:binary(x, args, top, bottom) if result then return result end end do           local top = (99 <= toptop and 99) or toptop local bottom = 6 local result = self:binary(x, args, top, bottom) if result then return result end end end return Scale.wordify(self, x, args) end

function myriad_scale:wordify(x, args) local toptop = self:getField('top', args.lang) if toptop then do           local top = toptop local bottom = 4 local result = self:binary(x, args, top, bottom) if result then return result end end end return Scale.wordify(self, x, args) end

function indian_scale:wordify(x, args) local pow = math.pow local rou = _round local prec = args.prec local scales = args.language.scale local special = scales and scales[self.name] local scripts = special and special.script local scr = (scripts and scripts[lang]) or special for i, _ in reverseSparseIpairs((scr and scr.additional) or self.additional) do       local y = x / pow(10,i) local r = rou(y, prec) if r >= 1 then return self:found(args, i, r)        end end return Scale.wordify(self, x, args) end function p._wordify(x, prec, lk, numsys, lang, script, state, case, class, possessed, person, plural, exclude, simplify) local deflang = language:default(nil, lang) local defsys = number_systems:default(deflang, numsys) local canonsys = number_systems:get(defsys) or defsys if tonumber(x) then local scale = scales[canonsys] if scale then local language = scale:getLanguage(deflang) if language then local defscript = scripts:default(deflang, script, canonsys) local canonscript = scripts:get(defscript) or defscript local defstate = states:default(deflang, state) local canonstate = states:get(defstate) or defstate local defcase = cases:default(deflang, case) local canoncase = cases:get(defcase) or defcase local defclass = classes:default(deflang, class) local canonclass = classes:get(defclass) or defclass local args = { prec = prec, lk = lk, lang = deflang, language = language, script = canonscript, state = canonstate, case = canoncase, class = canonclass, possessed = possessed, person = person, plural = plural, exclude = exclude, simplify = simplify }               return scale:wordify(x, args) else return err("Language '" .. deflang .. "' not supported by number system '" .. canonsys .. "'") end else return err("number system '" .. canonsys .. "' 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