模組:Math

--[[This module provides a number of basic mathematical operations.]]localz={}-- Generate random numberfunctionz.random(frame)first=tonumber(frame.args[1])-- if it doesn't exist it's NaN, if not a number it's nilsecond=tonumber(frame.args[2])iffirstthen-- if NaN or nil, will skip down to final returniffirst<=secondthen-- could match if both nil, but already checked that first is a number in last linereturnmath.random(first,second)endreturnmath.random(first)endreturnmath.random()end--[[orderDetermine order of magnitude of a numberUsage: {{#invoke: Math | order | value }}]]functionz.order(frame)localinput_string=(frame.args[1]orframe.args.xor'0');localinput_number;input_number=z._cleanNumber(frame,input_string);ifinput_number==nilthenreturn'<strong class="error">Formatting error: Order of magnitude input appears non-numeric</strong>'elsereturnz._order(input_number)endendfunctionz._order(x)ifx==0thenreturn0endreturnmath.floor(math.log10(math.abs(x)))end--[[precisionDetemines the precision of a number using the string representationUsage: {{ #invoke: Math | precision | value }}]]functionz.precision(frame)localinput_string=(frame.args[1]orframe.args.xor'0');localtrap_fraction=frame.args.check_fractionorfalse;localinput_number;iftype(trap_fraction)=='string'thentrap_fraction=trap_fraction:lower();iftrap_fraction=='false'ortrap_fraction=='0'ortrap_fraction=='no'ortrap_fraction==''thentrap_fraction=false;elsetrap_fraction=true;endendiftrap_fractionthenlocalpos=string.find(input_string,'/',1,true);ifpos~=nilthenifstring.find(input_string,'/',pos+1,true)==nilthenlocaldenominator=string.sub(input_string,pos+1,-1);localdenom_value=tonumber(denominator);ifdenom_value~=nilthenreturnmath.log10(denom_value);endendendendinput_number,input_string=z._cleanNumber(frame,input_string);ifinput_string==nilthenreturn'<strong class="error">Formatting error: Precision input appears non-numeric</strong>'elsereturnz._precision(input_string)endendfunctionz._precision(x)x=string.upper(x)localdecimal=string.find(x,'.',1,true)localexponent_pos=string.find(x,'E',1,true)localresult=0;ifexponent_pos~=nilthenlocalexponent=string.sub(x,exponent_pos+1)x=string.sub(x,1,exponent_pos-1)result=result-tonumber(exponent)endifdecimal~=nilthenresult=result+string.len(x)-decimalreturnresultendlocalpos=string.len(x);whilex:byte(pos)==string.byte('0')dopos=pos-1result=result-1ifpos<=0thenreturn0endendreturnresultend--[[maxFinds the maximum argumentUsage: {{#invoke:Math| max | value1 | value2 | ... }}OR {{#invoke:Math| max }}When used with no arguments, it takes its input from the parentframe. Note, any values that do not evaluate to numbers are ignored.]]functionz.max(frame)localargs=frame.args;ifargs[1]==nilthenlocalparent=frame:getParent();args=parent.args;endlocalmax_value=nil;locali=1;whileargs[i]~=nildolocalval=z._cleanNumber(frame,args[i]);ifval~=nilthenifmax_value==nilorval>max_valuethenmax_value=val;endendi=i+1;endreturnmax_valueend--[[min Finds the minimum argumentUsage: {{#invoke:Math| min | value1 | value2 | ... }}OR {{#invoke:Math| min }}When used with no arguments, it takes its input from the parentframe. Note, any values that do not evaluate to numbers are ignored.]]functionz.min(frame)localargs=frame.args;ifargs[1]==nilthenlocalparent=frame:getParent();args=parent.args;endlocalmin_value=nil;locali=1;whileargs[i]~=nildolocalval=z._cleanNumber(frame,args[i]);ifval~=nilthenifmin_value==nilorval<min_valuethenmin_value=val;endendi=i+1;endreturnmin_valueend--[[average Finds the averageUsage: {{#invoke:Math| average | value1 | value2 | ... }}OR {{#invoke:Math| average }}When used with no arguments, it takes its input from the parentframe. Note, any values that do not evaluate to numbers are ignored.]]functionz.average(frame)localargs=frame.args;ifargs[1]==nilthenlocalparent=frame:getParent();args=parent.args;endlocalsum=0;localcount=0;locali=1;whileargs[i]~=nildolocalval=z._cleanNumber(frame,args[i]);ifval~=nilthensum=sum+valcount=count+1endi=i+1;endreturn(count==0and0orsum/count)end--[[roundRounds a number to specified precisionUsage: {{#invoke:Math | round | value | precision }}--]]functionz.round(frame)localvalue,precision;value=z._cleanNumber(frame,frame.args[1]orframe.args.valueor0);precision=z._cleanNumber(frame,frame.args[2]orframe.args.precisionor0);ifvalue==nilorprecision==nilthenreturn'<strong class="error">Formatting error: Round input appears non-numeric</strong>'elsereturnz._round(value,precision);endendfunctionz._round(value,precision)localrescale=math.pow(10,precision);returnmath.floor(value*rescale+0.5)/rescale;end--[[precision_formatRounds 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 }}]]functionz.precision_format(frame)-- For access to Mediawiki built-in formatter.locallang=mw.getContentLanguage();localvalue_string,value,precision;value,value_string=z._cleanNumber(frame,frame.args[1]or0);precision=z._cleanNumber(frame,frame.args[2]or0);-- Check for non-numeric inputifvalue==nilorprecision==nilthenreturn'<strong class="error">Formatting error: invalid input when rounding</strong>'endlocalcurrent_precision=z._precision(value);localorder=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.iforder+precision>=14thenorig_precision=z._precision(value_string);iforder+orig_precision>=14thenprecision=13-order;endend-- If rounding off, truncate extra digitsifprecision<current_precisionthenvalue=z._round(value,precision);current_precision=z._precision(value);endlocalformatted_num=lang:formatNum(math.abs(value));localsign;-- Use proper unary minus sign rather than ASCII defaultifvalue<0thensign='−';elsesign='';end-- Handle cases requiring scientific notationifstring.find(formatted_num,'E',1,true)~=nilormath.abs(order)>=9thenvalue=value*math.pow(10,-order);current_precision=current_precision+order;precision=precision+order;formatted_num=lang:formatNum(math.abs(value));elseorder=0;endformatted_num=sign..formatted_num;-- Pad with zeros, if needed ifcurrent_precision<precisionthenlocalpadding;ifcurrent_precision<=0thenifprecision>0thenlocalzero_sep=lang:formatNum(1.1);formatted_num=formatted_num..zero_sep:sub(2,2);padding=precision;ifpadding>20thenpadding=20;endformatted_num=formatted_num..string.rep('0',padding);endelsepadding=precision-current_precisionifpadding>20thenpadding=20;endformatted_num=formatted_num..string.rep('0',padding);endend-- Add exponential notation, if necessary.iforder~=0then-- Use proper unary minus sign rather than ASCII defaultiforder<0thenorder='−'..lang:formatNum(math.abs(order));elseorder=lang:formatNum(order);endformatted_num=formatted_num..'<span style="margin:0 .15em 0 .25em">×</span>10<sup>'..order..'</sup>'endreturnformatted_num;end--[[Helper function that interprets the input numerically. If the input does not appear to be a number, attempts evaluating it asa parser functions expression.]]functionz._cleanNumber(frame,number_string)ifnumber_string==nilornumber_string:len()==0thenreturnnil,nil;end-- Attempt basic conversionlocalnumber=tonumber(number_string)-- If failed, attempt to evaluate input as an expressionifnumber==nilthenlocalattempt=frame:preprocess('{{#expr: '..number_string..'}}');attempt=tonumber(attempt);ifattempt~=nilthennumber=attempt;number_string=tostring(number);elsenumber=nil;number_string=nil;endelse-- String is valid but may contain padding, clean it.number_string=number_string:match("^%s*(.-)%s*$");endreturnnumber,number_string;endreturnz