-- Input info.clean uses en digits (it has been translated, if necessary).
-- Output show uses en or non-en digits as appropriate, or can be spelled.
if out_current.builtin == 'hand' then
return cvt_to_hand(parms, info, in_current, out_current)
end
local invalue = in_current.builtin == 'hand' and info.altvalue or info.value
local outvalue, extra = convert(parms, invalue, info, in_current, out_current)
if parms.need_table_or_sort then
parms.need_table_or_sort = nil -- process using first input value only
make_table_or_sort(parms, invalue, info, in_current)
end
if extra then
if not outvalue then return false, extra end
invalue = extra.invalue or invalue
outvalue = extra.outvalue
end
if not valid_number(outvalue) then
return false, { 'cvt_invalid_num' }
end
local isnegative
if outvalue < 0 then
isnegative = true
outvalue = -outvalue
end
local precision, show, exponent
local denominator = out_current.frac
if denominator then
show = fraction_table(outvalue, denominator)
else
precision = parms.precision
if not precision then
if parms.sigfig then
show, exponent = make_sigfig(outvalue, parms.sigfig)
elseif parms.opt_round then
local n = parms.opt_round
if n == 0.5 then
local integer, fracpart = math.modf(floor(2 * outvalue + 0.5) / 2)
if fracpart == 0 then
show = format('%.0f', integer)
else
show = format('%.1f', integer + fracpart)
end
else
show = format('%.0f', floor((outvalue / n) + 0.5) * n)
end
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
end
end
if precision then
if precision >= 0 then
local fudge
if precision <= 8 then
-- Add a fudge to handle common cases of bad rounding due to inability
-- to precisely represent some values. This makes the following work:
-- and .
-- Old template uses #expr round, which invokes PHP round.
-- LATER: Investigate how PHP round works.
fudge = 2e-14
else
fudge = 0
end
local fmt = '%.' .. format('%d', precision) .. 'f'
local success
success, show = pcall(format, fmt, outvalue + fudge)
if not success then
return false, { 'cvt_big_prec', tostring(precision) }
end
else
precision = -precision -- #digits to zero (in addition to any digits after dot)
local shift = 10 ^ precision
show = format('%.0f', outvalue/shift)
if show ~= '0' then
exponent = #show + precision
end
end
end
local t = format_number(parms, show, exponent, isnegative)
if type(show) == 'string' then
-- Set singular using match because on some systems 0.99999999999999999 is 1.0.
if exponent then
t.singular = (exponent == 1 and show:match('^10*$'))
else
t.singular = (show == '1' or show:match('^1%.0*$'))
end
else
t.fraction_table = show
t.singular = (outvalue <= 1) -- cannot have 'fraction == 1', but if it were possible it would be singular
end
t.raw_absvalue = outvalue -- absolute value before rounding
return true, setmetatable(t, {
__index = function (self, key)
if key == 'absvalue' then
-- Calculate absolute value after rounding, if needed.
local clean, exponent = rawget(self, 'clean'), rawget(self, 'exponent')
local value = tonumber(clean) -- absolute value (any negative sign has been ignored)
if exponent then
value = value * 10^exponent
end
rawset(self, key, value)
return value
end
end })
end  
function 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
end  
local function evaluate_condition(value, condition)
-- Return true or false from applying a conditional expression to value,
-- or throw an error if invalid.
-- A very limited set of expressions is supported:
-- v < 9
-- v * 9 < 9
-- where
-- 'v' is replaced with value
-- 9 is any number (as defined by Lua tonumber)
-- only en digits are accepted
-- '<' can also be '<=' or '>' or '>='
-- In addition, the following form is supported:
-- LHS and RHS
-- where
-- LHS, RHS = any of above expressions.
local function compare(value, text)
local arithop, factor, compop, limit = text:match('^%s*v%s*([*]?)(.-)([<>]=?)(.*)$')
if arithop == nil then
error('Invalid default expression', 0)
elseif arithop == '*' then
factor = tonumber(factor)
if factor == nil then
error('Invalid default expression', 0)
end
value = value * factor
end
limit = tonumber(limit)
if limit == nil then
error('Invalid default expression', 0)
end
if compop == '<' then
return value < limit
elseif compop == '<=' then
return value <= limit
elseif compop == '>' then
return value > limit
elseif compop == '>=' then
return value >= limit
end
error('Invalid default expression', 0) -- should not occur
end
local lhs, rhs = condition:match('^(.-%W)and(%W.*)')
if lhs == nil then
return compare(value, condition)
end
return compare(value, lhs) and compare(value, rhs)
end  
local function get_default(value, unit_table)
-- Return true, s where s = name of unit's default output unit,
-- or return false, t where t is an error message table.
-- Some units have a default that depends on the input value
-- (the first value if a range of values is used).
-- If '!' is in the default, the first bang-delimited field is an
-- expression that uses 'v' to represent the input value.
-- Example: 'v < 120 ! small ! big ! suffix' (suffix is optional)
-- evaluates 'v < 120' as a boolean with result
-- 'smallsuffix' if (value < 120), or 'bigsuffix' otherwise.
-- Input must use en digits and '.' decimal mark.
local default = data_code.default_exceptions[unit_table.defkey or unit_table.symbol] or unit_table.default
if not default then
local per = unit_table.per
if per then
local function a_default(v, u)
local success, ucode = get_default(v, u)
if not success then
return '?' -- an unlikely error has occurred; will cause lookup of default to fail
end
-- Attempt to use only the first unit if a combination or output multiple.
-- This is not bulletproof but should work for most cases.