|
|
| Ligne 1 : |
Ligne 1 : |
| --[[
| |
|
| |
|
| This module provides a number of basic mathematical operations.
| |
|
| |
| ]]
| |
| local z = {}
| |
|
| |
| -- Generate random number
| |
| function z.random( frame )
| |
| first = tonumber(frame.args[1]) -- if it doesn't exist it's NaN, if not a number it's nil
| |
| second = tonumber(frame.args[2])
| |
|
| |
| if first then -- if NaN or nil, will skip down to final return
| |
| if first <= second then -- could match if both nil, but already checked that first is a number in last line
| |
| return math.random(first, second)
| |
| end
| |
| return math.random(first)
| |
| end
| |
| return math.random()
| |
| end
| |
|
| |
| --[[
| |
| order
| |
|
| |
| Determine order of magnitude of a number
| |
|
| |
| Usage:
| |
| {{#invoke: Math | order | value }}
| |
| ]]
| |
| function z.order(frame)
| |
| local input_string = (frame.args[1] or frame.args.x or '0');
| |
| local input_number;
| |
|
| |
| input_number = z._cleanNumber( frame, input_string );
| |
| if input_number == nil then
| |
| return '<strong class="error">Formatting error: Order of magnitude input appears non-numeric</strong>'
| |
| else
| |
| return z._order( input_number )
| |
| end
| |
| end
| |
| function z._order(x)
| |
| if x == 0 then return 0 end
| |
| return math.floor(math.log10(math.abs(x)))
| |
| end
| |
|
| |
| --[[
| |
| precision
| |
|
| |
| Detemines the precision of a number using the string representation
| |
|
| |
| Usage:
| |
| {{ #invoke: Math | precision | value }}
| |
| ]]
| |
| function z.precision( frame )
| |
| local input_string = (frame.args[1] or frame.args.x or '0');
| |
| local trap_fraction = frame.args.check_fraction or false;
| |
| local input_number;
| |
|
| |
| if type( trap_fraction ) == 'string' then
| |
| trap_fraction = trap_fraction:lower();
| |
| if trap_fraction == 'false' or trap_fraction == '0' or
| |
| trap_fraction == 'no' or trap_fraction == '' then
| |
| trap_fraction = false;
| |
| else
| |
| trap_fraction = true;
| |
| end
| |
| end
| |
|
| |
| if trap_fraction then
| |
| local pos = string.find( input_string, '/', 1, true );
| |
| if pos ~= nil then
| |
| if string.find( input_string, '/', pos + 1, true ) == nil then
| |
| local denominator = string.sub( input_string, pos+1, -1 );
| |
| local denom_value = tonumber( denominator );
| |
| if denom_value ~= nil then
| |
| return math.log10(denom_value);
| |
| end
| |
| end
| |
| end
| |
| end
| |
|
| |
| input_number, input_string = z._cleanNumber( frame, input_string );
| |
| if input_string == nil then
| |
| return '<strong class="error">Formatting error: Precision input appears non-numeric</strong>'
| |
| else
| |
| return z._precision( input_string )
| |
| end
| |
| end
| |
| function z._precision( x )
| |
| x = string.upper( x )
| |
|
| |
| local decimal = string.find( x, '.', 1, true )
| |
| local exponent_pos = string.find( x, 'E', 1, true )
| |
| local result = 0;
| |
|
| |
| if exponent_pos ~= nil then
| |
| local exponent = string.sub( x, exponent_pos + 1 )
| |
| x = string.sub( x, 1, exponent_pos - 1 )
| |
| result = result - tonumber( exponent )
| |
| end
| |
|
| |
| if decimal ~= nil then
| |
| result = result + string.len( x ) - decimal
| |
| return result
| |
| end
| |
|
| |
| local pos = string.len( x );
| |
| while x:byte(pos) == string.byte('0') do
| |
| pos = pos - 1
| |
| result = result - 1
| |
| if pos <= 0 then
| |
| return 0
| |
| end
| |
| end
| |
|
| |
| return result
| |
| end
| |
|
| |
| --[[
| |
| max
| |
|
| |
| Finds the maximum argument
| |
|
| |
| Usage:
| |
| {{#invoke:Math| max | value1 | value2 | ... }}
| |
| OR
| |
| {{#invoke:Math| max }}
| |
|
| |
| When used with no arguments, it takes its input from the parent
| |
| frame. Note, any values that do not evaluate to numbers are ignored.
| |
| ]]
| |
| function z.max( frame )
| |
| local args = frame.args;
| |
|
| |
| if args[1] == nil then
| |
| local parent = frame:getParent();
| |
| args = parent.args;
| |
| end
| |
| local max_value = nil;
| |
|
| |
| local i = 1;
| |
| while args[i] ~= nil do
| |
| local val = z._cleanNumber( frame, args[i] );
| |
| if val ~= nil then
| |
| if max_value == nil or val > max_value then
| |
| max_value = val;
| |
| end
| |
| end
| |
| i = i + 1;
| |
| end
| |
|
| |
| return max_value
| |
| end
| |
|
| |
| --[[
| |
| min
| |
|
| |
| Finds the minimum argument
| |
|
| |
| Usage:
| |
| {{#invoke:Math| min | value1 | value2 | ... }}
| |
| OR
| |
| {{#invoke:Math| min }}
| |
|
| |
| When used with no arguments, it takes its input from the parent
| |
| frame. Note, any values that do not evaluate to numbers are ignored.
| |
| ]]
| |
| function z.min( frame )
| |
| local args = frame.args;
| |
|
| |
| if args[1] == nil then
| |
| local parent = frame:getParent();
| |
| args = parent.args;
| |
| end
| |
| local min_value = nil;
| |
|
| |
| local i = 1;
| |
| while args[i] ~= nil do
| |
| local val = z._cleanNumber( frame, args[i] );
| |
| if val ~= nil then
| |
| if min_value == nil or val < min_value then
| |
| min_value = val;
| |
| end
| |
| end
| |
| i = i + 1;
| |
| end
| |
|
| |
| return min_value
| |
| end
| |
|
| |
| --[[
| |
| round
| |
|
| |
| Rounds a number to specified precision
| |
|
| |
| Usage:
| |
| {{#invoke:Math | round | value | precision }}
| |
|
| |
| --]]
| |
| function z.round(frame)
| |
| local value, precision;
| |
|
| |
| value = z._cleanNumber( frame, frame.args[1] or frame.args.value or 0 );
| |
| precision = z._cleanNumber( frame, frame.args[2] or frame.args.precision or 0 );
| |
|
| |
| if value == nil or precision == nil then
| |
| return '<strong class="error">Formatting error: Round input appears non-numeric</strong>'
| |
| else
| |
| return z._round( value, precision );
| |
| end
| |
| end
| |
| function z._round( value, precision )
| |
| local rescale = math.pow( 10, precision );
| |
| return math.floor( value * rescale + 0.5 ) / rescale;
| |
| end
| |
|
| |
| --[[
| |
| precision_format
| |
|
| |
| Rounds a number to the specified precision and formats according to rules
| |
| originally used for {{template:Rnd}}. Output is a string.
| |
|
| |
| Usage:
| |
| {{#invoke: Math | precision_format | number | precision }}
| |
| ]]
| |
| function z.precision_format( frame )
| |
| -- For access to Mediawiki built-in formatter.
| |
| local lang = mw.getContentLanguage();
| |
|
| |
| local value_string, value, precision;
| |
| value, value_string = z._cleanNumber( frame, frame.args[1] or 0 );
| |
| precision = z._cleanNumber( frame, frame.args[2] or 0 );
| |
|
| |
| -- Check for non-numeric input
| |
| if value == nil or precision == nil then
| |
| return '<strong class="error">Formatting error: invalid input when rounding</strong>'
| |
| end
| |
|
| |
| local current_precision = z._precision( value );
| |
|
| |
| local order = z._order( value );
| |
|
| |
| -- Due to round-off effects it is neccesary to limit the returned precision under
| |
| -- some circumstances because the terminal digits will be inaccurately reported.
| |
| if order + precision >= 14 then
| |
| orig_precision = z._precision( value_string );
| |
| if order + orig_precision >= 14 then
| |
| precision = 13 - order;
| |
| end
| |
| end
| |
|
| |
| -- If rounding off, truncate extra digits
| |
| if precision < current_precision then
| |
| value = z._round( value, precision );
| |
| current_precision = z._precision( value );
| |
| end
| |
|
| |
| local formatted_num = lang:formatNum( math.abs(value) );
| |
| local sign;
| |
|
| |
| -- Use proper unary minus sign rather than ASCII default
| |
| if value < 0 then
| |
| sign = '−';
| |
| else
| |
| sign = '';
| |
| end
| |
|
| |
| -- Handle cases requiring scientific notation
| |
| if string.find( formatted_num, 'E', 1, true ) ~= nil or math.abs(order) >= 9 then
| |
| value = value * math.pow( 10, -order );
| |
| current_precision = current_precision + order;
| |
| precision = precision + order;
| |
| formatted_num = lang:formatNum( math.abs(value) );
| |
| else
| |
| order = 0;
| |
| end
| |
| formatted_num = sign .. formatted_num;
| |
|
| |
| -- Pad with zeros, if needed
| |
| if current_precision < precision then
| |
| local padding;
| |
| if current_precision <= 0 then
| |
| if precision > 0 then
| |
| local zero_sep = lang:formatNum( 1.1 );
| |
| formatted_num = formatted_num .. zero_sep:sub(2,2);
| |
|
| |
| padding = precision;
| |
| if padding > 20 then
| |
| padding = 20;
| |
| end
| |
|
| |
| formatted_num = formatted_num .. string.rep( '0', padding );
| |
| end
| |
| else
| |
| padding = precision - current_precision
| |
| if padding > 20 then
| |
| padding = 20;
| |
| end
| |
| formatted_num = formatted_num .. string.rep( '0', padding );
| |
| end
| |
| end
| |
|
| |
| -- Add exponential notation, if necessary.
| |
| if order ~= 0 then
| |
| -- Use proper unary minus sign rather than ASCII default
| |
| if order < 0 then
| |
| order = '−' .. lang:formatNum( math.abs(order) );
| |
| else
| |
| order = lang:formatNum( order );
| |
| end
| |
|
| |
| formatted_num = formatted_num .. '<span style="margin:0 .15em 0 .25em">×</span>10<sup>' .. order .. '</sup>'
| |
| end
| |
|
| |
| return formatted_num;
| |
| 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 z._cleanNumber( frame, number_string )
| |
| if number_string == nil or number_string:len() == 0 then
| |
| 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 attempt = frame:preprocess( '{{#expr: ' .. number_string .. '}}' );
| |
| attempt = tonumber( attempt );
| |
| if attempt ~= nil then
| |
| number = attempt;
| |
| number_string = tostring( number );
| |
| else
| |
| number = nil;
| |
| number_string = nil;
| |
| end
| |
| else
| |
| -- String is valid but may contain padding, clean it.
| |
| number_string = number_string:match( "^%s*(.-)%s*$" );
| |
| end
| |
|
| |
| return number, number_string;
| |
| end
| |
|
| |
| return z
| |