Module:Math
Appearance
![Documentation icon](https://upload.wikimedia.org/wikipedia/commons/thumb/4/43/Test_Template_Info-Icon_-_Version_%282%29.svg/50px-Test_Template_Info-Icon_-_Version_%282%29.svg.png)
You might want togibuhona documentation page for thisScribunto module. Editors can experiment in this module's sandbox(gibuhon|salmíng)and testcases(gibuhon)pages. Pakiulay na magdagdag nin mga kategorya sa subpahinang/doc.Subpages of this modyul. |
--[[
This module provides a number of basic mathematical operations.
]]
localyesno,getArgs-- lazily initialized
localp={}-- Holds functions to be returned from #invoke, and functions to make available to other Lua modules.
localwrap={}-- Holds wrapper functions that process arguments from #invoke. These act as intemediary between functions meant for #invoke and functions meant for Lua.
--[[
Helper functions used to avoid redundant code.
]]
localfunctionerr(msg)
-- Generates wikitext error messages.
returnmw.ustring.format('<strong class= "error" >Formatting error: %s</strong>',msg)
end
localfunctionunpackNumberArgs(args)
-- Returns an unpacked list of arguments specified with numerical keys.
localret={}
fork,vinpairs(args)do
iftype(k)=='number'then
table.insert(ret,v)
end
end
returnunpack(ret)
end
localfunctionmakeArgArray(...)
-- Makes an array of arguments from a list of arguments that might include nils.
localargs={...}-- Table of arguments. It might contain nils or non-number values, so we can't use ipairs.
localnums={}-- Stores the numbers of valid numerical arguments.
localret={}
fork,vinpairs(args)do
v=p._cleanNumber(v)
ifvthen
nums[#nums+1]=k
args[k]=v
end
end
table.sort(nums)
fori,numinipairs(nums)do
ret[#ret+1]=args[num]
end
returnret
end
localfunctionapplyFuncToArgs(func,...)
-- Use a function on all supplied arguments, and return the result. The function must accept two numbers as parameters,
-- and must return a number as an output. This number is then supplied as input to the next function call.
localvals=makeArgArray(...)
localcount=#vals-- The number of valid arguments
ifcount==0thenreturn
-- Exit if we have no valid args, otherwise removing the first arg would cause an error.
nil,0
end
localret=table.remove(vals,1)
for_,valinipairs(vals)do
ret=func(ret,val)
end
returnret,count
end
--[[
random
Generate a random number
Usage:
{{#invoke: Math | random }}
{{#invoke: Math | random | maximum value }}
{{#invoke: Math | random | minimum value | maximum value }}
]]
functionwrap.random(args)
localfirst=p._cleanNumber(args[1])
localsecond=p._cleanNumber(args[2])
returnp._random(first,second)
end
functionp._random(first,second)
math.randomseed(mw.site.stats.edits+mw.site.stats.pages+os.time()+math.floor(os.clock()*1000000000))
-- math.random will throw an error if given an explicit nil parameter, so we need to use if statements to check the params.
iffirstandsecondthen
iffirst<=secondthen-- math.random doesn't allow the first number to be greater than the second.
returnmath.random(first,second)
end
elseiffirstthen
returnmath.random(first)
else
returnmath.random()
end
end
--[[
order
Determine order of magnitude of a number
Usage:
{{#invoke: Math | order | value }}
]]
functionwrap.order(args)
localinput_string=(args[1]orargs.xor'0');
localinput_number=p._cleanNumber(input_string);
ifinput_number==nilthen
returnerr('order of magnitude input appears non-numeric')
else
returnp._order(input_number)
end
end
functionp._order(x)
ifx==0thenreturn0end
returnmath.floor(math.log10(math.abs(x)))
end
--[[
precision
Detemines the precision of a number using the string representation
Usage:
{{ #invoke: Math | precision | value }}
]]
functionwrap.precision(args)
localinput_string=(args[1]orargs.xor'0');
localtrap_fraction=args.check_fraction;
localinput_number;
ifnotyesnothen
yesno=require('Module:Yesno')
end
ifyesno(trap_fraction,true)then-- Returns true for all input except nil, false, "no", "n", "0" and a few others. See [[Module:Yesno]].
localpos=string.find(input_string,'/',1,true);
ifpos~=nilthen
ifstring.find(input_string,'/',pos+1,true)==nilthen
localdenominator=string.sub(input_string,pos+1,-1);
localdenom_value=tonumber(denominator);
ifdenom_value~=nilthen
returnmath.log10(denom_value);
end
end
end
end
input_number,input_string=p._cleanNumber(input_string);
ifinput_string==nilthen
returnerr('precision input appears non-numeric')
else
returnp._precision(input_string)
end
end
functionp._precision(x)
iftype(x)=='number'then
x=tostring(x)
end
x=string.upper(x)
localdecimal=x:find('%.')
localexponent_pos=x:find('E')
localresult=0;
ifexponent_pos~=nilthen
localexponent=string.sub(x,exponent_pos+1)
x=string.sub(x,1,exponent_pos-1)
result=result-tonumber(exponent)
end
ifdecimal~=nilthen
result=result+string.len(x)-decimal
returnresult
end
localpos=string.len(x);
whilex:byte(pos)==string.byte('0')do
pos=pos-1
result=result-1
ifpos<=0then
return0
end
end
returnresult
end
--[[
max
Finds the maximum argument
Usage:
{{#invoke:Math| max | value1 | value2 |... }}
Note, any values that do not evaluate to numbers are ignored.
]]
functionwrap.max(args)
returnp._max(unpackNumberArgs(args))
end
functionp._max(...)
localfunctionmaxOfTwo(a,b)
ifa>bthen
returna
else
returnb
end
end
localmax_value=applyFuncToArgs(maxOfTwo,...)
ifmax_valuethen
returnmax_value
end
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.
]]
functionwrap.min(args)
returnp._min(unpackNumberArgs(args))
end
functionp._min(...)
localfunctionminOfTwo(a,b)
ifa<bthen
returna
else
returnb
end
end
localmin_value=applyFuncToArgs(minOfTwo,...)
ifmin_valuethen
returnmin_value
end
end
--[[
average
Finds the average
Usage:
{{#invoke:Math| average | value1 | value2 |... }}
OR
{{#invoke:Math| average }}
Note, any values that do not evaluate to numbers are ignored.
]]
functionwrap.average(args)
returnp._average(unpackNumberArgs(args))
end
functionp._average(...)
localfunctiongetSum(a,b)
returna+b
end
localsum,count=applyFuncToArgs(getSum,...)
ifnotsumthen
return0
else
returnsum/count
end
end
--[[
round
Rounds a number to specified precision
Usage:
{{#invoke:Math | round | value | precision }}
--]]
functionwrap.round(args)
localvalue=p._cleanNumber(args[1]orargs.valueor0)
localprecision=p._cleanNumber(args[2]orargs.precisionor0)
ifvalue==nilorprecision==nilthen
returnerr('round input appears non-numeric')
else
returnp._round(value,precision)
end
end
functionp._round(value,precision)
localrescale=math.pow(10,precisionor0);
returnmath.floor(value*rescale+0.5)/rescale;
end
--[[
mod
Implements the modulo operator
Usage:
{{#invoke:Math | mod | x | y }}
--]]
functionwrap.mod(args)
localx=p._cleanNumber(args[1])
localy=p._cleanNumber(args[2])
ifnotxthen
returnerr('first argument to mod appears non-numeric')
elseifnotythen
returnerr('second argument to mod appears non-numeric')
else
returnp._mod(x,y)
end
end
functionp._mod(x,y)
localret=x%y
ifnot(0<=retandret<y)then
ret=0
end
returnret
end
--[[
gcd
Calculates the greatest common divisor of multiple numbers
Usage:
{{#invoke:Math | gcd | value 1 | value 2 | value 3 |... }}
--]]
functionwrap.gcd(args)
returnp._gcd(unpackNumberArgs(args))
end
functionp._gcd(...)
localfunctionfindGcd(a,b)
localr=b
localoldr=a
whiler~=0do
localquotient=math.floor(oldr/r)
oldr,r=r,oldr-quotient*r
end
ifoldr<0then
oldr=oldr*-1
end
returnoldr
end
localresult,count=applyFuncToArgs(findGcd,...)
returnresult
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 }}
]]
functionwrap.precision_format(args)
localvalue_string=args[1]or0
localprecision=args[2]or0
returnp._precision_format(value_string,precision)
end
functionp._precision_format(value_string,precision)
-- For access to Mediawiki built-in formatter.
locallang=mw.getContentLanguage();
localvalue
value,value_string=p._cleanNumber(value_string)
precision=p._cleanNumber(precision)
-- Check for non-numeric input
ifvalue==nilorprecision==nilthen
returnerr('invalid input when rounding')
end
localcurrent_precision=p._precision(value)
localorder=p._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.
iforder+precision>=14then
orig_precision=p._precision(value_string)
iforder+orig_precision>=14then
precision=13-order;
end
end
-- If rounding off, truncate extra digits
ifprecision<current_precisionthen
value=p._round(value,precision)
current_precision=p._precision(value)
end
localformatted_num=lang:formatNum(math.abs(value))
localsign
-- Use proper unary minus sign rather than ASCII default
ifvalue<0then
sign='−'
else
sign=''
end
-- Handle cases requiring scientific notation
ifstring.find(formatted_num,'E',1,true)~=nilormath.abs(order)>=9then
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
ifcurrent_precision<precisionthen
localpadding
ifcurrent_precision<=0then
ifprecision>0then
localzero_sep=lang:formatNum(1.1)
formatted_num=formatted_num..zero_sep:sub(2,2)
padding=precision
ifpadding>20then
padding=20
end
formatted_num=formatted_num..string.rep('0',padding)
end
else
padding=precision-current_precision
ifpadding>20then
padding=20
end
formatted_num=formatted_num..string.rep('0',padding)
end
end
-- Add exponential notation, if necessary.
iforder~=0then
-- Use proper unary minus sign rather than ASCII default
iforder<0then
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
returnformatted_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.
]]
functionp._cleanNumber(number_string)
iftype(number_string)=='number'then
-- We were passed a number, so we don't need to do any processing.
returnnumber_string,tostring(number_string)
elseiftype(number_string)~='string'ornotnumber_string:find('%S')then
-- We were passed a non-string or a blank string, so exit.
returnnil,nil;
end
-- Attempt basic conversion
localnumber=tonumber(number_string)
-- If failed, attempt to evaluate input as an expression
ifnumber==nilthen
localsuccess,result=pcall(mw.ext.ParserFunctions.expr,number_string)
ifsuccessthen
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("^%+(.*)$")ornumber_string-- Trim any leading + signs.
ifnumber_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
returnnumber,number_string
end
--[[
Wrapper function that does basic argument processing. This ensures that all functions from #invoke can use either the current
frame or the parent frame, and it also trims whitespace for all arguments and removes blank arguments.
]]
localmt={__index=function(t,k)
returnfunction(frame)
ifnotgetArgsthen
getArgs=require('Module:Arguments').getArgs
end
returnwrap[k](getArgs(frame))-- Argument processing is left to Module:Arguments. Whitespace is trimmed and blank arguments are removed.
end
end}
returnsetmetatable(p,mt)