The following lines were added (+) and removed (-):
local SIprefixes, all_categories, all_messages, customary_units, disp_joinslocal text_codelocal en_option_name, en_option_value, eng_scales, range_aliases, range_typeslocal varname -- can be a code to use variable names that depend on valuelocal from_en_table -- to translate an output string of en digits to local languagelocal to_en_table -- to translate an input string of digits in local language to en-- Use translation_table in convert/text to change the following.local from_en_table -- to translate an output string of en digits to local languagelocal to_en_table -- to translate an input string of digits in local language to en local data_module, text_module, data_code, text_code local data_module, text_module, data_code SIprefixes = text_code.SIprefixes all_categories = text_code.all_categories all_messages = text_code.all_messages customary_units = text_code.customary_units disp_joins = text_code.disp_joins en_option_name = text_code.en_option_name en_option_value = text_code.en_option_value eng_scales = text_code.eng_scales range_aliases = text_code.range_aliases range_types = text_code.range_types varname = translation.varnameendlocal function divide(numerator, denominator) -- Return integers quotient, remainder resulting from dividing the two -- given numbers, which should be unsigned integers. local quotient, remainder = floor(numerator / denominator), numerator % denominator if not (0 <= remainder and remainder < denominator) then -- Floating point limits may need this, as in {{convert|160.02|Ym|ydftin}}. remainder = 0 end return quotient, remainder -- Default wanted namespaces are 0 (article) and 10 (template). local nsdefault = '0' -- default namespace: '0' = article; '0,10' = article and template for _, v in ipairs(split(config.nscat or '0,10', ',')) do for _, v in ipairs(split(config.nscat or nsdefault, ',')) do local msg = all_messages[mcode[1]] local msg = text_code.all_messages[mcode[1]] local nowiki = mw.text.nowiki local title = format(msg[1] or 'Missing message', local parts = {} mcode[2] or '?', mcode[3] or '?', mcode[4] or '?') local text = msg[2] or 'Missing message' local cat = wanted_category(all_categories[msg[3]]) or '' local anchor = msg[4] or '' local fmt = all_messages['cvt_format'] or 'convert: bug' if regex and replace then for i = 1, 3 do title = title:gsub(regex, replace) local limit = 40 local s = mcode[i + 1] if s then if regex and replace then s = s:gsub(regex, replace) limit = nil -- allow long "should be" messages end -- Escape user input so it does not break the message. -- To avoid reference tags (like {{convert|1<ref>xyz</ref>|m}}) or other tags -- breaking the mouseover title, any strip marker starting with char(127) is -- replaced with escaped '<ref>...</ref>' or '...' (text not needing i18n). local append = '' local pos = s:find(string.char(127), 1, true) if pos then if s:find('-ref-', 1, true) then append = '<ref>...</ref>' else append = '...' end s = s:sub(1, pos - 1) end if limit and ulen(s) > limit then s = usub(s, 1, limit) if append == '' then append = '...' end end s = nowiki(s) .. append else s = '?' end parts[i] = s local title = format(msg[1] or 'Missing message', parts[1], parts[2], parts[3]) local text = msg[2] or 'Missing message' local cat = wanted_category(text_code.all_categories[msg[3]]) or '' local anchor = msg[4] or '' local fmt = text_code.all_messages['cvt_format'] or 'convert: bug'local function spell_number(parms, number, numerator, denominator)local function spell_number(parms, inout, number, numerator, denominator) local case = parms.opt_spell_upper local case parms.opt_spell_upper = nil -- only uppercase first number in a multiple unit if parms.spell_upper == inout then case = true parms.spell_upper = nil -- only uppercase first word in a multiple unit end -- Warning: The boolean value 'false' is returned for any missing field -- so __index is not called twice for the same field in a given unit. elseif key == 'builtin' then value = false return nil value = false elseif key == 'builtin' then else else return nil elseif key == 'builtin' then else else return nil if i == 1 and (v == '$' or v == '£') then if i == 1 and text_code.currency[v] then if what == 'no_combination' or (what == 'only_multiple' and multiple == nil) then if what == 'no_combination' or (what == 'only_multiple' and not multiple) then local SIprefixes = text_code.SIprefixes local exponent, baseunit = unitcode:match('^e(%d+)(.*)') local has_plus = unitcode:find('+', 1, true) if exponent then if not has_plus then local engscale = eng_scales[exponent] local exponent, baseunit = unitcode:match('^e(%d+)(.*)') if engscale then if exponent then local success, result = lookup(baseunit, opt_sp_us, 'no_combination', utable, fails, depth) local engscale = text_code.eng_scales[exponent] if not success then return false, result end if engscale then if not (result.offset or result.builtin or result.engscale) then local success, result = lookup(baseunit, opt_sp_us, 'no_combination', utable, fails, depth) result.defkey = unitcode -- key to lookup default exception if not success then return false, result end result.engscale = engscale if not (result.offset or result.builtin or result.engscale) then result.scale = result.scale * 10 ^ tonumber(exponent) result.defkey = unitcode -- key to lookup default exception return true, result result.engscale = engscale result.scale = result.scale * 10 ^ tonumber(exponent) return true, result end if unitcode:find('+', 1, true) then if has_plus then local success, t = lookup(v, opt_sp_us, 'no_combination', utable, fails, depth) local success, t = lookup(v, opt_sp_us, 'only_multiple', utable, fails, depth) -- Input text is a number in en digits and with '.' decimal mark. -- Input text is a number in en digits and optional '.' decimal mark. local exp, frac = math.modf(log10(value)) local exp, fracpart = math.modf(log10(value)) if frac >= 0 then if fracpart >= 0 then frac = frac - 1 fracpart = fracpart - 1 local digits = format('%.0f', 10^(frac + sigfig)) local digits = format('%.0f', 10^(fracpart + sigfig))end-- Fraction output format.local fracfmt = { { -- Like {{frac}} (fraction slash). -- 1/2 : sign, numerator, denominator -- 1+2/3 : signed_wholenumber, numerator, denominator '<span class="frac nowrap">%s<sup>%s</sup>⁄<sub>%s</sub></span>', '<span class="frac nowrap">%s<span class="visualhide"> </span><sup>%s</sup>⁄<sub>%s</sub></span>', }, { -- Like {{sfrac}} (fraction horizontal bar). -- 1//2 : sign, numerator, denominator (sign should probably be before the fraction, but then it can wrap, and html is already too long) -- 1+2//3 : signed_wholenumber, numerator, denominator '<span class="sfrac nowrap" style="display:inline-block; vertical-align:-0.5em; font-size:85%%; text-align:center;"><span style="display:block; line-height:1em; padding:0 0.1em;">%s%s</span><span class="visualhide">/</span><span style="display:block; line-height:1em; padding:0 0.1em; border-top:1px solid;">%s</span></span>', '<span class="sfrac nowrap">%s<span class="visualhide"> </span><span style="display:inline-block; vertical-align:-0.5em; font-size:85%%; text-align:center;"><span style="display:block; line-height:1em; padding:0 0.1em;">%s</span><span class="visualhide">/</span><span style="display:block; line-height:1em; padding:0 0.1em; border-top:1px solid;">%s</span></span></span>', },}local function format_fraction(parms, inout, negative, wholestr, numstr, denstr, do_spell, style) -- Return wikitext for a fraction, possibly spelled. -- Inputs use en digits and have no sign; output uses digits in local language. local wikitext if not style then style = parms.opt_fraction_horizontal and 2 or 1 end if wholestr == '' then wholestr = nil end if wholestr then local decorated = with_separator(parms, wholestr) if negative then decorated = MINUS .. decorated end local fmt = fracfmt[style][2] wikitext = format(fmt, decorated, from_en(numstr), from_en(denstr)) else local sign = negative and MINUS or '' wikitext = format(fracfmt[style][1], sign, from_en(numstr), from_en(denstr)) end if do_spell then if negative then if wholestr then wholestr = '-' .. wholestr else numstr = '-' .. numstr end end wikitext = spell_number(parms, inout, wholestr, numstr, denstr) or wikitext end return wikitext -- Parameter show is a number in en digits and with '.' decimal mark. -- Parameter show is a string or a table containing strings. -- Each string is a formatted number in en digits and optional '.' decimal mark. -- A table represents a fraction: integer, numerator, denominator; -- if a table is given, exponent must be nil. -- * Includes a Unicode minus if isnegative. -- * Includes a Unicode minus if isnegative and not spelled. if exponent == nil then local tfrac local integer, dot, fraction = show:match('^(%d*)(%.?)(.*)') if type(show) == 'table' then tfrac = show show = tfrac.wholestr assert(exponent == nil, 'Bug: exponent given with fraction') end if not tfrac and not exponent then local integer, dot, decimals = show:match('^(%d*)(%.?)(.*)') show = integer .. fraction show = integer .. decimals local zeros, figs = fraction:match('^(0*)([^0]?.*)') local zeros, figs = decimals:match('^(0*)([^0]?.*)') if isnegative and show:match('^0.?0*$') then local formatted_show sign = '' -- don't show minus if result is negative but rounds to zero if tfrac then end show = tostring(tfrac.value) -- to set clean in returned table local formatted_show = sign .. with_separator(parms, show) formatted_show = format_fraction(parms, 'out', isnegative, tfrac.wholestr, tfrac.numstr, tfrac.denstr, parms.opt_spell_out) if parms.opt_spell_out then else formatted_show = spell_number(parms, sign .. show) or formatted_show if isnegative and show:match('^0.?0*$') then sign = '' -- don't show minus if result is negative but rounds to zero end formatted_show = sign .. with_separator(parms, show) if parms.opt_spell_out then formatted_show = spell_number(parms, 'out', sign .. show) or formatted_show end is_scientific = false, -- to avoid calling __index-- Fraction output format.-- 2013-07-20 Trying new styles proposed at [[Template talk:Convert]].local fracfmt = { { -- Like {{frac}} (fraction slash). -- 1/2 : sign, numerator, denominator -- 1+2/3 : signed_wholenumber, numerator, denominator '<span class="frac nowrap">%s<sup>%s</sup>⁄<sub>%s</sub></span>', '<span class="frac nowrap">%s<sup> %s</sup>⁄<sub>%s</sub></span>', }, { -- Like {{sfrac}} (fraction horizontal bar). -- 1//2 : sign, numerator, denominator (sign should probably be before the fraction, but then it can wrap, and html is already too long) -- 1+2//3 : signed_wholenumber, numerator, denominator '<span class="sfrac nowrap" style="display:inline-block; vertical-align:-0.5em; font-size:85%%; text-align:center;"><span style="display:block; line-height:1em; padding:0 0.1em;">%s%s</span><span style="display:none;">/</span><span style="display:block; line-height:1em; padding:0 0.1em; border-top:1px solid;">%s</span></span>', '<span class="sfrac nowrap">%s<span style="display:none;"> </span><span style="display:inline-block; vertical-align:-0.5em; font-size:85%%; text-align:center;"><span style="display:block; line-height:1em; padding:0 0.1em;">%s</span><span style="display:none;">/</span><span style="display:block; line-height:1em; padding:0 0.1em; border-top:1px solid;">%s</span></span></span>', }, { -- Like old {{convert}} template. -- 1///2 : sign, numerator, denominator -- 1+2///3: signed_wholenumber, sign, numerator, denominator '<span style="white-space:nowrap">%s<sup>%s</sup>⁄<sub>%s</sub></span>', '<span class="frac nowrap">%s<s style="display:none">%s</s><sup>%s</sup>⁄<sub>%s</sub></span>', },} -- Template interprets '1.23e+2+12/24' as '123(12/24)' = 123.5! -- Old template interprets '1.23e+2+12/24' as '123(12/24)' = 123.5! local style = #slash -- kludge: 1, 2, or 3 slashes can be used to select style local style = #slash -- kludge: 1 or 2 slashes can be used to select style if style > 3 then style = 3 end if style > 2 then style = 2 end local wikitext local wikitext = format_fraction(parms, 'in', negative, wholestr, numstr, denstr, do_spell, style) if wholestr then if negative then wholestr = change_sign(wholestr) end local fmt = fracfmt[style][2] if style < 3 then wikitext = format(fmt, use_minus(from_en(wholestr)), from_en(numstr), from_en(denstr)) else local sign = negative and MINUS or '+' wikitext = format(fmt, use_minus(from_en(wholestr)), sign, from_en(numstr), from_en(denstr)) end else local sign = negative and MINUS or '' wikitext = format(fracfmt[style][1], sign, from_en(numstr), from_en(denstr)) end if do_spell then local numsign = (wholestr or not negative) and '' or '-' wikitext = spell_number(parms, wholestr, numsign .. numstr, denstr) or wikitext end singular = true -- for example, "¹/₂ mile" or "one half mile" (singular unit) singular = true -- for example, "½ mile" or "one half mile" (singular unit) value = value + 2e-14 -- fudge for some common cases of bad rounding show = fmt:format(value) show = fmt:format(value + 2e-14) -- fudge for some common cases of bad rounding show = spell_number(parms, propersign .. clean) or show show = spell_number(parms, 'in', propersign .. clean) or show local altvalue = altvalue or value altvalue = -altvalue altvalue = altvalue or value, altvalue = altvalue, local integer, fraction = math.modf(number) local integer, fracpart = math.modf(number) return number, (fraction == 0) return number, (fracpart == 0) end endend local function gcd(a, b) -- Return the greatest common denominator for the given values, -- which are known to be positive integers. if a > b then a, b = b, a end if a <= 0 then return b end local r = b % a if r <= 0 then return a end if r == 1 then return 1 end return gcd(r, a)end local function fraction_table(value, denominator) -- Return value as a string or a table: -- * If result is a string, there is no fraction, and the result -- is value formatted as a string of en digits. -- * If result is a table, it represents a fraction with named fields: -- wholestr, numstr, denstr (strings of en digits for integer, numerator, denominator). -- The result is rounded to the nearest multiple of (1/denominator). -- If the multiple is zero, no fraction is included. -- No fraction is included if value is very large as the fraction would -- be unhelpful, particularly if scientific notation is required. -- Input value is a non-negative number. -- Input denominator is a positive integer for the desired fraction. if value <= 0 then return '0' end if denominator <= 0 or value > 1e8 then return format('%.2f', value) end local integer, decimals = math.modf(value) local numerator = floor((decimals * denominator) + 0.5 + 2e-14) -- add fudge for some common cases of bad rounding if numerator >= denominator then integer = integer + 1 numerator = 0 end local wholestr = tostring(integer) if numerator > 0 then local div = gcd(numerator, denominator) if div > 1 then numerator = numerator / div denominator = denominator / div return { wholestr = (integer > 0) and wholestr or '', numstr = tostring(numerator), denstr = tostring(denominator), value = value, } return wholestr local en_name = en_option_name[loc_name] local en_name = text_code.en_option_name[loc_name] if en_name == 'sigfig' then if en_name == 'frac' or en_name == 'sigfig' then local minimum if number and is_integer and number > 0 then if en_name == 'frac' then minimum = 2 if number and number < 0 then parms.opt_fraction_horizontal = true number = -number end else minimum = 1 end if number and is_integer and number >= minimum then add_warning(parms, 1, 'cvt_bad_sigfig', loc_value) add_warning(parms, 1, (en_name == 'frac' and 'cvt_bad_frac' or 'cvt_bad_sigfig'), loc_value) en_value = en_option_value[en_name][loc_value] en_value = text_code.en_option_value[en_name][loc_value] -- It is known that adj is 'ri1' or 'ri2' or 'ri3', so precision is valid. -- It is known that adj is 'riN' where N is a single digit, so precision is valid. -- Only en digits are accepted. -- Only a single en digit is accepted. end end local cfg_abbr = config.abbr if cfg_abbr then if cfg_abbr == 'on always' then parms.abbr = 'on' elseif cfg_abbr == 'on default' then if parms.abbr == nil then parms.abbr = 'on' end elseif parms.opt_hand_hh then parms.abbr_org = 'on' parms.abbr = 'on' if parms.opt_spell_in then if parms.opt_spell_in and not parms.opt_spell_out then -- user cannot set an option to spell the output. -- user cannot set an option to spell the output only. end if parms.opt_spell_upper then parms.spell_upper = parms.opt_flip and 'out' or 'in' local ranges = text_code.ranges end local function extractor(i) -- If the parameter is not a value, try unpacking it as a range ("1-23" for "1 to 23"). -- However, "-1-2/3" is a negative fraction (-1⅔), so it must be extracted first. -- Unpacked items are inserted into the parms table. local valstr = strip(parms[i]) -- trim so any '-' as a negative sign will be at start local success, result = extract_number(parms, valstr, i > 1) if not success and valstr and i < 20 then -- check i to limit abuse for _, sep in ipairs(ranges.words) do local start, stop = valstr:find(sep, 2, true) -- start at 2 to skip any negative sign for range '-' if start then parms[i] = valstr:sub(stop + 1) table.insert(parms, i, sep) table.insert(parms, i, valstr:sub(1, start - 1)) return extractor(i) -- this allows combinations like "1 x 2 to 3 x 4" end end end return success, result local is_change local success, info = extract_number(parms, parms[i], i > 1) -- need to set parms.opt_nocomma before calling this local success, info = extractor(i) -- need to set parms.opt_nocomma before calling this if is_change then info.is_change = true -- value is after "±" and so is a change (significant for range like {{convert|5|±|5|°C}}) is_change = nil end local range_item = range_types[next] or range_types[range_aliases[next]] local range_item = ranges.types[next] or ranges.types[ranges.aliases[next]] parms.is_range_x = (type(range_item) == 'table') and range_item.is_range_x or nil if type(range_item) == 'table' then parms.is_range_x = range_item.is_range_x is_change = range_item.is_range_change endendlocal function simple_get_values(parms) -- If input is like "{{convert|valid_value|valid_unit|...}}", -- return true, v, 3, in_unit, in_unit_table -- (as for get_values(), but with a unit name and table for a valid unit; -- 3 = index in parms of whatever follows valid_unit, if anything). -- The valid_value is not negative and does not use a fraction, and -- no options requiring further processing of the input are used. -- Otherwise, return nothing and caller will reparse the input. -- Testing shows this function is successful for 96% of converts in articles, -- and that on average it speeds up converts by 8%. if parms.input_precision or parms.opt_spell_in then return end local clean = to_en(strip(parms[1] or '')) if #clean > 10 or not clean:match('^[0-9.]+$') then return end local value = tonumber(clean) if not value then return end local info = { value = value, altvalue = value, singular = (value == 1), clean = clean, show = with_separator(parms, clean), } local in_unit = strip(parms[2]) local success, in_unit_table = lookup(in_unit, parms.opt_sp_us, 'no_combination') if not success then return end return true, { info }, 3, in_unit, in_unit_table local success, valinfo, i = get_values(parms) local success, valinfo, i, in_unit, in_unit_table = simple_get_values(parms) if not success then return false, valinfo end local in_unit = strip(parms[i]) i = i + 1 local success, in_unit_table = lookup(in_unit, parms.opt_sp_us, 'no_combination') if in_unit == nil then success, valinfo, i = get_values(parms) in_unit = '' if not success then return false, valinfo end in_unit = strip(parms[i]) i = i + 1 success, in_unit_table = lookup(in_unit, parms.opt_sp_us, 'no_combination') if not success then if in_unit == nil then in_unit = '' end if parms.opt_ignore_error then -- display given unit code with no error (for use with {{val}}) in_unit_table = nil end in_unit_table = setmetatable({ symbol = in_unit, name2 = in_unit, utype = "length", scale = 1, bad_mcode = in_unit_table, default = "m" }, unit_mt) in_unit_table = setmetatable({ symbol = in_unit, name2 = in_unit, utype = "length", scale = 1, bad_mcode = in_unit_table }, unit_mt)local function default_precision(invalue, inclean, denominator, outvalue, in_current, out_current, extra)local function record_default_precision(parms, out_current, precision) -- If necessary, adjust parameters and return a possibly adjusted precision. -- When converting a range of values where a default precision is required, -- that default is calculated for each value because the result sometimes -- depends on the precise input and output values. This function may cause -- the entire convert process to be repeated in order to ensure that the -- same default precision is used for each individual convert. -- If that were not done, a range like 1000 to 1000.4 may give poor results -- because the first output could be heavily rounded, while the second is not. -- For range 1000.4 to 1000, this function can give the second convert the -- same default precision that was used for the first. if not parms.opt_round_each then local maxdef = out_current.max_default_precision if maxdef then if maxdef < precision then parms.do_convert_again = true out_current.max_default_precision = precision else precision = out_current.max_default_precision end else out_current.max_default_precision = precision end end return precisionend local function default_precision(parms, invalue, inclean, denominator, outvalue, in_current, out_current, extra) local integer, dot, fraction, expstr = inclean:match('^(%d*)(%.?)(%d*)(.*)') local integer, dot, decimals, expstr = inclean:match('^(%d*)(%.?)(%d*)(.*)') prec = #fraction prec = #decimals return 0 return record_default_precision(parms, out_current, 0) return math.max(floor(prec + adjust), minprec) return record_default_precision(parms, out_current, math.max(floor(prec + adjust), minprec))local function convert(invalue, inclean, in_current, out_current)local function convert(parms, invalue, info, in_current, out_current) if in_current.invert then if in_current.invert or out_current.invert then -- Fuel efficiency (there are no built-ins for this type of unit). -- Inverted units, such as inverse length, inverse time, or if in_current.invert * out_current.invert < 0 then -- fuel efficiency. Built-in units do not have invert set. if (in_current.invert or 1) * (out_current.invert or 1) < 0 then if info.is_change then return invalue * (inscale / outscale) end -- Fractions of a hand are only defined for the first digit, and -- Decimals of a hand are only defined for the first digit, and -- However, this code interprets the entire fraction as the number -- However, this code interprets the entire fractional part as the number local integer, fraction = math.modf(invalue) local integer, fracpart = math.modf(invalue) local outvalue = (integer + 2.5 * fraction) * (inscale / outscale) local inch_value = 4 * integer + 10 * fracpart -- equivalent number of inches local inch_value = 4 * integer + 10 * fraction -- equivalent number of inches local factor = inscale / outscale local fracstr = inclean:match('%.(.*)') or '' if factor == 4 then -- Am converting to inches: show exact result, and use "inches" not "in" by default. if parms.abbr_org == nil then out_current.usename = true end local show = format('%g', abs(inch_value)) -- show and clean are unsigned if not show:find('e', 1, true) then return true, { invalue = inch_value, outvalue = inch_value, clean = show, show = show, } end end local outvalue = (integer + 2.5 * fracpart) * factor local fracstr = info.clean:match('%.(.*)') or '' inclean = format(fmt, inch_value), clean = format(fmt, inch_value),local cvt_to_hand local invalue, inclean local invalue invalue, inclean = info.value, info.clean invalue = info.value -- Convert to hands, then convert the fractional part to inches. return cvt_to_hand(parms, info, in_current, out_current) -- Code is not correct when output is spelled, and it ignores any requested -- precision if the output uses scientific notation (very large, or very -- small). Not worth more complexity as these cases should be very rare. if parms.abbr_org == nil then out_current.usename = true -- default is to show name not symbol end local dummy_unit_table = { scale = out_current.scale } local success, outinfo = cvtround(parms, info, in_current, dummy_unit_table) if not success then return false, outinfo end local fmt if outinfo.is_scientific then fmt = '%.1f' else local fraction = (outinfo.show):match('[' .. numdot .. '](.*)') or '' -- outinfo.show is in local language if fraction == '' then if not outinfo.use_default_precision then return true, outinfo end fmt = '%.0f' else fmt = '%.' .. format('%d', ulen(fraction) - 1) .. 'f' end end local hands, inches = math.modf(outinfo.raw_absvalue) inches = format(fmt, inches * 4) if inches:sub(1, 1) == '4' then hands = hands + 1 inches = '0' .. inches:sub(2) if tonumber(inches) == 0 then inches = '0' end end if inches:sub(2, 2) == '.' then inches = inches:sub(1, 1) .. inches:sub(3) end outinfo.show = outinfo.sign .. with_separator(parms, format('%d', hands)) .. numdot .. from_en(inches) return true, outinfo local outvalue, extra = convert(invalue, inclean, in_current, out_current) local outvalue, extra = convert(parms, invalue, info, in_current, out_current) inclean = extra.inclean or inclean local success, use_default_precision, show, exponent local numerator, precision, success, show, exponent local precision = parms.precision local denominator = out_current.frac if not precision then if denominator then local sigfig = parms.sigfig show = fraction_table(outvalue, denominator) if sigfig then else show, exponent = make_sigfig(outvalue, sigfig) precision = parms.precision elseif parms.opt_round5 then if not precision then show = format('%.0f', floor((outvalue / 5) + 0.5) * 5) local sigfig = parms.sigfig else if sigfig then use_default_precision = true show, exponent = make_sigfig(outvalue, sigfig) precision = default_precision(invalue, inclean, info.denominator, outvalue, in_current, out_current, extra) elseif parms.opt_round5 or parms.opt_round25 then local n = parms.opt_round5 and 5 or 25 show = format('%.0f', floor((outvalue / n) + 0.5) * n) else local inclean = info.clean if extra then inclean = extra.clean or inclean show = extra.show end if not show then precision = default_precision(parms, invalue, inclean, info.denominator, outvalue, in_current, out_current, extra) end end local fudge outvalue = outvalue + 2e-14 fudge = 2e-14 else fudge = 0 success, show = pcall(format, fmt, outvalue) success, show = pcall(format, fmt, outvalue + fudge) t.singular = ((show == '1' or show:match('^1%.0*$') ~= nil) and not isnegative) t.singular = (type(show) == 'string' and (show == '1' or show:match('^1%.0*$') ~= nil) and not isnegative) t.fraction_table = (type(show) == 'table') and show or nil t.use_default_precision = use_default_precisionendfunction cvt_to_hand(parms, info, in_current, out_current) -- Convert input to hands, inches. -- Return true, t where t is a table with the conversion results; -- or return false, t where t is an error message table. if parms.abbr_org == nil then out_current.usename = true -- default is to show name not symbol end local precision = parms.precision local frac = out_current.frac if not frac and precision and precision > 1 then frac = (precision == 2) and 2 or 4 end local out_next = out_current.out_next if out_next then -- Use magic knowledge to determine whether the next unit is inches without requiring i18n. -- The following ensures that when the output combination "hand in" is used, the inches -- value is rounded to match the hands value. Also, displaying say "61½" instead of 61.5 -- is better as 61.5 implies the value is not 61.4. if out_next.exception == 'subunit_more_precision' then out_next.frac = frac end end -- Convert to inches; calculate hands from that. local dummy_unit_table = { scale = out_current.scale / 4, frac = frac } local success, outinfo = cvtround(parms, info, in_current, dummy_unit_table) if not success then return false, outinfo end local tfrac = outinfo.fraction_table local inches = outinfo.raw_absvalue if tfrac then inches = floor(inches) -- integer part only; fraction added later else inches = floor(inches + 0.5) -- a hands measurement never shows decimals of an inch end local hands, inches = divide(inches, 4) outinfo.absvalue = hands + inches/4 -- supposed to be the absolute rounded value, but this is close enough local inchstr = tostring(inches) -- '0', '1', '2' or '3' if precision and precision <= 0 then -- using negative or 0 for precision rounds to nearest hand hands = floor(outinfo.raw_absvalue/4 + 0.5) inchstr = '' elseif tfrac then -- Always show an integer before fraction (like "15.0½") because "15½" means 15-and-a-half hands. inchstr = numdot .. format_fraction(parms, 'out', false, inchstr, tfrac.numstr, tfrac.denstr) else inchstr = numdot .. from_en(inchstr) end outinfo.show = outinfo.sign .. with_separator(parms, format('%.0f', hands)) .. inchstr return true, outinfo if default == nil then if not default then -- overlinking for conversions like (each links "mile" twice): -- overlinking for conversions like the following (each links "mile" twice): if link == nil or link == '' or linked_pages[link_key] then if not link or link == '' or linked_pages[link_key] thenlocal function linked_id(unit_table, key_id, want_link)local function variable_name(clean, unit_table) -- For sl.wiki (Slovenian Wikipedia), a unit name depends on the value. -- Parameter clean is the unsigned rounded value in en digits, as a string. -- Value Source Example for "m" -- integer 1: name1 meter (also is the name of the unit) -- integer 2: var{1} metra -- integer 3 and 4: var{2} metri -- integer else: var{3} metrov (0 and 5 or more) -- real/fraction: var{4} metra -- var{i} means the i'th field in unit_table.varname if it exists and has -- an i'th field, otherwise name2. -- Fields are separated with "!" and are not empty. -- A field for a unit using an SI prefix has the prefix name inserted, -- replacing '#' if found, or before the field otherwise. local vname if clean == '1' then vname = unit_table.name1 elseif unit_table.varname then local i if clean == '2' then i = 1 elseif clean == '3' or clean == '4' then i = 2 elseif clean:find('.', 1, true) then i = 4 else i = 3 end vname = split(unit_table.varname, '!')[i] end if vname then local si_name = rawget(unit_table, 'si_name') or '' local pos = vname:find('#', 1, true) if pos then vname = vname:sub(1, pos - 1) .. si_name .. vname:sub(pos + 1) else vname = si_name .. vname end return vname end return unit_table.name2end local function linked_id(unit_table, key_id, want_link, clean) result = (unit1 and unit1[key_id] or '') .. result .. unit2[key_id2] if abbr_on or not varname then result = (unit1 and unit1[key_id] or '') .. result .. unit2[key_id2] else result = (unit1 and variable_name(clean, unit1) or '') .. result .. variable_name('1', unit2) end result = linked_id(unit1, key_id, want_link) .. result result = linked_id(unit1, key_id, want_link, clean) .. result return result .. linked_id(unit2, key_id2, want_link) return result .. linked_id(unit2, key_id2, want_link, '1') local id = unit_table.fixed_name or unit_table[key_id] local id = unit_table.fixed_name or ((varname and not abbr_on) and variable_name(clean, unit_table) or unit_table[key_id]) local link = link_exceptions[unit_table.symbol] or unit_table.link local link = link_exceptions[unit_table.linkey or unit_table.symbol] or unit_table.link local customary = customary_units[i] local customary = text_code.customary_units[i] local valinfo = unit_table.valinfo local info = unit_table.valinfo[which] local want_link = (lk == 'on' or lk == inout) local singular = valinfo[which].singular local singular = info.singular if lk == 'on' or lk == inout then if want_link then local value = valinfo[which].value local value = info.value singular = (valinfo[which].absvalue < 1.0001 and singular = (info.absvalue < 1.0001 and not valinfo[which].is_scientific) not info.is_scientific) if lk == nil and unit_table.builtin == 'hand' then want_link = true end value = valinfo[which].value value = info.value value = valinfo[which].absvalue value = info.absvalue if unit_table.builtin == 'hand' then if parms.opt_hand_hh then unit_table.symbol = 'hh' -- LATER: might want i18n applied to this end end return linked_id(unit_table, key, lk == 'on' or lk == inout), want_name return linked_id(unit_table, key, want_link, info.clean), want_name local info local prefix = unit_table.vprefix if engscale or prefix then info = unit_table.valinfo[which] if info.decorated then return -- do not redecorate if repeating convert end info.decorated = true end local info = unit_table.valinfo[which] local prefix = unit_table.vprefix local info = unit_table.valinfo[which] local disp_joins = text_code.disp_joins local function make_result(info) local do_spell = parms.opt_spell_out parms.opt_spell_out = nil -- so the call to cvtround does not spell the value local function make_result(info, isfirst) local thisvalue, strforce local tfrac, thisvalue, strforce local fraction local decimals out_current.frac = out_unit_table.frac if isfirst then out_unit_table.valinfo = { outinfo } -- in case output value of first least significant unit is needed end tfrac = outinfo.fraction_table fraction = '' decimals = '' elseif tfrac then decimals = '' fraction = (outinfo.show):match('[' .. numdot .. '](.*)') or '' -- outinfo.show is in local language decimals = (outinfo.show):match('[' .. numdot .. '](.*)') or '' -- outinfo.show is in local language fmt = '%.' .. ulen(fraction) .. 'f' -- to reproduce precision fmt = '%.' .. ulen(decimals) .. 'f' -- to reproduce precision if fraction == '' then if decimals == '' then outvalue = floor(outinfo.raw_absvalue + 0.5) -- keep all integer digits of least significant unit if tfrac then outvalue = floor(outinfo.raw_absvalue) -- integer part only; fraction added later else outvalue = floor(outinfo.raw_absvalue + 0.5) -- keep all integer digits of least significant unit end outvalue, thisvalue = floor(outvalue / scale), outvalue % scale outvalue, thisvalue = divide(outvalue, scale) id = out_current[(thisvalue == 1) and 'name1' or 'name2'] if varname then local clean if strforce or tfrac then clean = '.1' -- dummy value to force name for floating point else clean = format(fmt, thisvalue) end id = variable_name(clean, out_current) else id = out_current[(thisvalue == 1) and 'name1' or 'name2'] end local inout = (i == #combos or outvalue == 0) and 'out' or '' -- trick so the last value processed (first displayed) has uppercase, if requested elseif tfrac then local wholestr = (thisvalue > 0) and tostring(thisvalue) or nil strval = format_fraction(parms, inout, false, wholestr, tfrac.numstr, tfrac.denstr, do_spell) if do_spell then strval = spell_number(parms, inout, strval) or strval end fmt = '%.0f' -- only least significant unit can have a fraction fmt = '%.0f' -- only least significant unit can have a non-integral value local success, result = make_result(valinfo[1]) local success, result = make_result(valinfo[1], true)local function process(parms, in_unit_table)local function process(parms, in_unit_table, out_unit_table) local success, bad_output, out_unit_table local success, bad_output, out_first local bad_input_mcode = in_unit_table.bad_mcode -- nil if input unit is valid local bad_input_mcode = in_unit_table.bad_mcode -- false if input unit is valid parms.out_unit = out_unit if not bad_output then if not bad_output and not out_unit_table then if out_unit_table.multiple == nil then -- nil ('ft' or 'm ft'), or table of factors ('ftin') if not out_unit_table.multiple then -- nil/false ('ft' or 'm ft'), or table of factors ('ftin') end local frac = parms.frac -- nil or denominator of fraction for output values if frac then -- Apply fraction to the unit (if only one), or to non-SI units (if a combination), -- except that if a precision is also specified, the fraction only applies to -- the hand unit; that allows the following result: -- {{convert|156|cm|in hand|1|frac=2}} → 156 centimetres (61.4 in; 15.1½ hands) -- However, the following is handled elsewhere as a special case: -- {{convert|156|cm|hand in|1|frac=2}} → 156 centimetres (15.1½ hands; 61½ in) if combos then local precision = parms.precision for _, unit in ipairs(combos) do if unit.builtin == 'hand' or (not precision and not unit.prefixes) then unit.frac = frac end end else out_unit_table.frac = frac end if out_current.multiple == nil then if i == 1 then success, item = make_output_single(parms, in_unit_table, out_current) out_first = out_current else if imax > 1 and out_current.builtin == 'hand' then out_current.out_next = combos[2] -- built-in hand can influence next unit in a combination end end if out_current.multiple then else success, item = make_output_single(parms, in_unit_table, out_current) parts[part] = parms.opt_input_unit_only and '' or table.concat(outputs, '; ') local sep = parms.table_joins and parms.table_joins[2] or '; ' parts[part] = parms.opt_input_unit_only and '' or table.concat(outputs, sep) if parms.opt_sortable then if parms.opt_sortable_in or parms.opt_sortable_out then parts[1] = ntsh(invalue1, parms.opt_sortable_debug) .. parts[1] local value if parms.opt_sortable_in then value = invalue1 else local info = out_first and out_first.valinfo if info then info = info[1] value = info.raw_absvalue if value and info.sign == MINUS then value = -value end end end parts[1] = ntsh((value or 0), parms.opt_sortable_debug) .. parts[1] return true, wikitext return true, wikitext, out_unit_table -- Do convert, and if needed, do it again with higher default precision. local result local result, out_unit_table success, result = process(parms, in_unit_table) for i = 1, 2 do -- use counter so cannot get stuck repeating convert success, result, out_unit_table = process(parms, in_unit_table, out_unit_table) if success and parms.do_convert_again then parms.do_convert_again = false else break end end