Math is an extension of the math STL, containing additional functionality and support. All Math, non-STL, functions are capable of evaluating mathemetical expression strings and provide friendlier error messages than the default error() function. While more powerful than their STL counterparts, Math's functions comes at the cost of performance. If complex math is done within long looping applications or Lua expense needs to be kept low, consider using the STL functions instead.

Math can be invoked directly ({{#invoke:Math|function|...}}), invoked from a template ({{template|function|...}}), or used within other modules.

All math STL objects can be accessed by adding two underscores before the name, e.g.
math.__abs(-7) - Original math STL abs function
math.abs(-7) - Math's abs function with error checking and string evaluation

On this Wiki, Math is used in:

Usage

Direct Invocation

{{#invoke:Math|function|input1|input2|...}}

Template

In template: {{#invoke:Math|__main}}
In articles: {{template|function|input1|input2|...}}

Module

local p = {}
local math = require('Module:Math')

local pi = math.pi
local absSTL = math.__abs

local function func(input)
    -- ...
    -- input -> stuff
    -- ...
    return math.eval(stuff)
end

Documentation

Package items

math.eval(num) (function)
Evaluates input
Parameter: num The input expression (string)
Returns: eval(num) (number)
math.formatnum(num, code, noCommafy) (function)
Evaluates and formats input
Parameters:
  • num The input expression (number, string)
  • code A language code | default 'en' (string; optional)
  • noCommafy Use comma separators | default false (boolean; optional)
Returns:
  • formatnum(num) (string)
  • formatnum(num, 'ar') (string)
  • formatnum(num, "", true) (string)
  • etc. (string)
math.abs(num) (function)
Evaluates input and returns the magnitude
Parameter: num The input value (number, string)
Returns: abs(num) (number)
math.acos(num) (function)
Evaluates input and returns the acos
Parameter: num The input value (number, string)
Returns: acos(num) (number)
math.asin(num) (function)
Evaluates input and returns the asin
Parameter: num The input value (number, string)
Returns: asin(num) (number)
math.atan(num, den) (function)
Evaluates inputs and returns the atan
Parameters:
  • num The input value (number, string)
  • den A second input value | default 1 (number, string; optional)
Returns:
  • atan(num) (number)
  • atan(num / den) (number)
math.binomial(p, n, r) (function)
Returns the binomial probability of three inputs
Parameters:
  • p The probability of success (string, number)
  • n Total number of trials (string, number)
  • r Number of successes (string, number)
Returns: Binomial of p, n, and r (number)
math.ceil(num) (function)
Evaluates input and returns the ceil
Parameter: num The input value (number, string)
Returns: ceil(num) (number)
math.cos(num) (function)
Evaluates input and returns the cos
Parameter: num The input value (number, string)
Returns: cos(num) (number)
math.cosh(num) (function)
Evaluates input and returns the cosh
Parameter: num The input value (number, string)
Returns: cosh(num) (number)
math.deg(num) (function)
Evaluates input and converts into degrees
Parameter: num The input value (number, string)
Returns: deg(num) (number)
math.ex(frame) (function)
Evaluates inputs and returns the expected value
Parameter: frame inputs (table)
Returns: Expectation range of inputs (string)
math.exp(num) (function)
Evaluates input and returns the exp
Parameter: num The input value (number, string)
Returns: exp(num) (number)
math.floor(num) (function)
Evaluates input and returns the floor
Parameter: num The input value (number, string)
Returns: floor(num) (number)
math.frac(num, factor, epsilon) (function)
Evaluates inputs and returns closest fraction
Parameters:
  • num The input value (number, string)
  • factor The value to factor out | default '1' (string; optional)
  • epsilon Number of decimal places to be accurate to | default -5 (10^-5) (number, string; optional)
Returns: Closest fraction in LaTeX (string)
math.gamma(num) (function)
Evaluates input and returns the factorial
Parameter: num The input value (number, string)
Returns:
  • (num - 1)! (number)
  • (num - 1)! scientific notation if larger than 107 (string)
math.gcd(num1, num2) (function)
Evaluates inputs and return greatest common divider
Parameters:
  • num1 The first input value (number, string)
  • num2 The second input value (number, string)
Returns: GCD(num1, num2) (number)
math.ln(num) (function)
Evaluates input and returns the natural log
Parameter: num The input value (number, string)
Returns: ln(num) (number)
math.log(num, base) (function)
Evaluates inputs and returns the log (base x)
Parameters:
  • num The input value (number, string)
  • base The logarithm base | default 10 (number, string; optional)
Returns:
  • log(num) (number)
  • log(num, base) (number)
math.max(nums) (function)
Evaluates inputs and returns the maximum value
Parameter: nums The input values (table)
Returns:
  • max(num1, num2, num3, ...) (number)
  • max(nums) (number)
math.min(nums) (function)
Evaluates inputs and returns the minimum value
Parameter: nums The input values (table)
Returns:
  • min(num1, num2, num3, ...) (number)
  • min(nums) (number)
math.mod(num, den) (function)
Evaluates inputs and returns the modulo
Parameters:
  • num The dividend (number, string)
  • den The divider (number, string)
Returns: num % den (number)
math.modf(num) (function)
Evaluates input and returns the integral, fractional, or both parts
Parameters:
  • num The input value (number, string)
    • frame.int Returns the integer | default false (boolean; optional)
    • frame.dec Returns the decimal | default true (boolean; optional)
Returns:
  • modf(num) (number)
  • modf(num, int=true) (number)
  • modf(num, int=true, dec=true) (returns both) (number)
math.ncr(n, r) (function)
Evaluates inputs and returns the combitorial
Parameters:
  • n The input value (number, string)
  • r The second input value (number, string)
Returns: nCr(n, r) (number)
math.ng(frame) (function)
Evaluates inputs and returns the nearly guaranteed value
Parameter: frame Inputs (table)
Returns: Nearly guaranteed range of inputs (string)
math.npr(n, r) (function)
Evaluates inputs and returns the permutation
Parameters:
  • n The input value (number, string)
  • r The second input value (number, string)
Returns: nPr(n, r) (number)
math.percentage(num) (function)
Evaluates inputs and returns as a percent
Parameter: num The input value (number)
Returns: number represented as a percentage (string)
math.pow(base, exponent) (function)
Evaluates inputs and returns the power of one to the other
Parameters:
  • base The input value (number, string)
  • exponent The second input value (number, string)
Returns: baseexponent (number)
math.rad(num) (function)
Evaluates input and converts into radians
Parameter: num The input value (number, string)
Returns: rad(num) (number)
math.rand(low, upp) (function)
Evaluates input and converts into radians
Parameters:
  • low The lower bound of random values | default 0 (number, string; optional)
  • upp The upper bound of random values | default 1 or low if it exists (number, string; optional)
    • frame.seed The seed to randomize from | default current OS time (number, string; optional)
Returns:
  • rand() [0 - 1] (number)
  • rand(low) [0 - low] (number)
  • rand(low, upp) [low - upp] (number)
  • rand(seed = 0) (number)
  • etc. (number)
math.replace(str) (function)
Replaces and deletes all non-expression characters in a string
Parameter: str expression (string)
Returns: An evaluatable expression (string)
math.replaceWithSymbol(str) (function)
Replaces constants with their symbol
Parameter: str expression (string)
Returns: #e, #gamma, #phi, and #pi replaced with e, γ, φ, and π (string)
math.round(num, multiple, percent, degree) (function)
Evaluates input and rounds to specified multiple
Parameters:
  • num The input value (number, string)
  • multiple The multiple to round to | default 0.0001 (number, string; optional)
  • percent Convert input from decimal to percentage | default false (boolean; optional)
  • degree Convert input into degrees | default false (boolean; optional)
Returns:
  • round(num) (string)
  • round(num, 0.125) (string)
  • round(num, "", true, false) (string)
  • etc. (string)
math.sin(num) (function)
Evaluates input and returns the sin
Parameter: num The input value (number, string)
Returns: sin(num) (number)
math.sinh(num) (function)
Evaluates input and returns the sinh
Parameter: num The input value (number, string)
Returns: sinh(num) (number)
math.sqrt(num) (function)
Evaluates input and returns the sqrt
Parameter: num The input value (number, string)
Returns: sqrt(num) (number)
math.sum(nums) (function)
Evaluates inputs and returns the summation
Parameter: nums inputs (table)
Returns:
  • sum(num1, num2, num3, ...) (number)
  • sum(nums) (number)
math.tan(num) (function)
Evaluates input and returns the tan
Parameter: num The input value (number, string)
Returns: tan(num) (number)
math.tanh(num) (function)
Evaluates input and returns the tanh
Parameter: num The input value (number, string)
Returns: tanh(num) (number)
math.trunc(num) (function)
Evaluates input and returns the truncation
Parameter: num The input value (number, string)
Returns: trunc(num) (number)

Created with Docbuntu


See Also


Code


--- '''Math''' is an extension of the math STL, containing additional functionality 
--  and support. All Math, non-[https://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#Math_library STL], functions 
--  are capable of evaluating mathemetical expression strings and provide 
--  friendlier error messages than the default `error()` function. While more 
--  powerful than their STL counterparts, Math's functions comes at the cost of 
--  performance. If complex math is done within long looping applications or Lua 
--  expense needs to be kept low, consider using the STL functions instead.
--  
--  Math can be invoked directly (`{{#invoke:Math|function|...}}`), 
--  invoked from a template (`{{template|function|...}}`), or used
--  within other modules.
--  
--  All math STL objects can be accessed by adding two underscores before the name, e.g.<br />
--	`math.__abs(-7)` - Original math STL abs function<br />
--	`math.abs(-7)` - Math's abs function with error checking and string evaluation<br />
--  
--  On this Wiki, Math is used in:
--  * [[Module:Acquisition]]
--  * [[Module:Shared]]
--  * [[Module:Warframes]]
--  * [[Module:Weapons]]
--  * [[Template:Math]]
--  
--  @module		math
--  @alias		p
--  @author		[[User:FINNER|FINNER]]
--  @image		MathLogo.png
--  @require	[[w:c:dev:Module:Arguments|Module:Arguments]]
--  @require	[[w:c:dev:Module:Entrypoint|Module:Entrypoint]]
--  @require	[[w:c:dev:Module:User error|Module:User error]]
--  @release	stable
--  <nowiki>
local p = {};

--  Module dependencies  -------------------------------------------------------
local args		 = require('Dev:Arguments');
local entrypoint = require('Dev:Entrypoint');
local userError  = require('Dev:User error');

--  Module variables  ----------------------------------------------------------
local patts = {"'", "°",        "%%",   "−",  "&minus;", "÷", "•",  "×",  "&times;", "{{", "}}", "{",  "}",  "%[", "%]", "E%+",    "E%-",     "e%+",    "e%-",     "γ",  "φ",  "π",  "#e", "#gamma", "#phi", "#pi", "%)%(",   "<sup>", "</sup>", "trunc%(", "floor%(", "ceil%(", "exp%(", "not%(", "abs%(", "asin%(", "acos%(", "atan%(", "sin%(", "cos%(", "tan%(", "ln%(", "mod"};
local reps  = {"",  "%*#¡/180", "/100", "%-", "%-",      "/", "%*", "%*", "%*",      "%(", "%)", "%(", "%)", "%(", "%)", "%*10%^", "%*10%^-", "%*10%^", "%*10%^-", "#£", "#¢", "#¡", "#¤", "#£",     "#¢",   "#¡",  "%)%*%(", "%^%(",  "%)",     "¥",       "¦",       "§",      "¨",     "©",     "ª",     "«",      "¬",      "®",      "¯",     "µ",     "¸",     "¶",    "%%"};
local patts2 = {"#¤", "#£",     "#¢",   "#¡",  "¥",       "¦",       "§",      "¨",     "©",     "ª",     "«",      "¬",      "®",      "¯",     "µ",     "¸",     "¶",    "%%"};
local reps2  = {"#e", "#gamma", "#phi", "#pi", "trunc%(", "floor%(", "ceil%(", "exp%(", "not%(", "abs%(", "asin%(", "acos%(", "atan%(", "sin%(", "cos%(", "tan%(", "ln%(", "%)mod%("};

--  Mathematical constants  ----------------------------------------------------
p.e   = math.exp(1);
p.gam = 0.57721566490153;
p.inf = math.huge;
p.phi = (1 + math.sqrt(5))/2;
p.pi  = math.pi;


-- Local functions  ------------------------------------------------------------
local eval = mw.ext.ParserFunctions.expr;

local function formatnum(num, options)
	if options and options.code and mw.language.isValidCode(options.code) then
		return mw.getLanguage(options.code):formatNum(num, options);
	end
	return mw.language.getContentLanguage():formatNum(num, options);
end

local function replace(str)
    local i;
    local j;
    
    i = string.find(str, "%[%[File:");
    _, j = string.find(str, "%]%]&nbsp;");
    while (i ~= nil) do
        str = string.sub(str, 0, i-1)..string.sub(str, j+1, #str);
        i = string.find(str, "%[%[File:");
        _, j = string.find(str, "%]%]&nbsp;");
    end
    
    i = string.find(str, "%[%[(%a+)");
    _, j = string.find(str, "(%a+)|");
    while (i ~= nil) do
        str = string.sub(str, 0, i+1)..string.sub(str, j+1, #str);
        i = string.find(str, "%[%[(%a+)");
        _, j = string.find(str, "(%a+)|");
    end
    
    str = string.gsub(str, "%s+", "");
    for i = 1, #patts do
        str = string.gsub(str, patts[i], reps[i]);
    end
    
    i = string.find(str, "<");
    j = string.find(str, ">");
    while (i ~= nil) do
        str = string.sub(str, 0, i-1)..string.sub(str, j+1, #str);
        i = string.find(str, "<");
        j = string.find(str, ">");
    end
    
    str = string.gsub(str, "%a+", "");
    for i = 1, #patts2 do
        str = string.gsub(str, patts2[i], reps2[i]);
    end
    
    str = string.gsub(str, "#e", p.e);
    str = string.gsub(str, "#gamma", p.gam);
    str = string.gsub(str, "#phi", p.phi);
    str = string.gsub(str, "#pi", p.pi);
    
    for i = 0, 9 do
        str = string.gsub(str, i.."%(", i.."%*%(");
        str = string.gsub(str, "%)"..i, "%)%*"..i);
    end
    
    patterns = {":", ";", "&", "|", "=", "\"", "\\", ",", "%?", "_", "`", "#", "%(%)"}
    for i = 1, #patterns do
        str = string.gsub(str, patterns[i], "");
    end
    
    return str;
end

local function replaceWithSymbol(str)
    str = string.gsub(str, "#e", "e");
    str = string.gsub(str, "#gamma", "γ");
    str = string.gsub(str, "#phi", "φ");
    str = string.gsub(str, "#pi", "π");
    
    return str;
end

local function round(num, to)
    local total = num + to / 2;
    local val = total - (total % to);
    return val;
end

local function trunc(num)
    local int, dec = math.modf(num);
    return int;
end

local function gcd(a, b)
    a = math.floor(a); b = math.floor(b);
    if b == 0 then
        return a;
    else
        return gcd(b, a % b);
    end
end

local function findFrac(val, factor, str, epsilon)
    epsilon = epsilon + math.ceil(math.log10(val/factor));
    local num = val/factor - math.floor(val/factor);
    num = math.abs(round(num, 10^epsilon));
    
    local inverse = false;
    if num > (1 - num) then
        num = 1 - num;
        inverse = true;
    end
    
    local currError = 1;
    local midNum = 1; local midDen = math.floor(1/num);
    
     if num <= (10^epsilon) then
        currError = 0;
        midNum = 0; midDen = 1;
    end
    
    local gcDen = 0;
    while currError > (10^epsilon) do
        if num < midNum/midDen then
            midDen = midDen + 1;
        elseif num > midNum/midDen then
            midNum = 10*midNum;
            midDen = 10*(midDen - 1);
        end
        
        currError = math.abs(midNum/midDen - num)/num;
        currError = trunc(currError*10^-epsilon)*10^epsilon;
    end
    
    if num > (10^epsilon) then
        gcDen = gcd(midNum, midDen);
        midNum = midNum/gcDen; midDen = midDen/gcDen;
    end
    
    if inverse then
        midNum = midDen - midNum;
    end
    
    num = math.floor(math.abs(round(val/factor, 10^epsilon)))*midDen;
    
    if (midDen == 1) then
        if (num + midNum) == 1 and str ~= '' then
            num = str;
        else
            num = '\\text{'..(num + midNum)..'}'..str;
        end
    else
        midDen = '}{\\text{'..midDen..'}}';
        if (num + midNum) == 1 and str ~= '' then
            num = '\\frac{'..str..midDen;
        else
            num = '\\frac{\\text{'..(num + midNum)..'}'..midDen..'\\!'..str;
        end
    end
    
    if (val < 0) then num = '-'..num; end
    
    return num;
end

local function asin(num)
    local val = math.asin(num);
    if math.abs(val) <= 10^-14 then val = 0; end
    return val;
end
local function acos(num)
    local val = math.acos(num);
    if math.abs(val) <= 10^-14 then val = 0; end
    return val;
end
local function atan(num, den)
    local val = math.atan2(num, den);
    if math.abs(val) <= 10^-14 then val = 0; end
    return val;
end

local function sin(num)
    local val = math.sin(num);
    if math.abs(val) <= 10^-14 then val = 0; end
    return val;
end
local function cos(num)
    local val = math.cos(num);
    if math.abs(val) <= 10^-14 then val = 0; end
    return val;
end
local function tan(num)
    local val = math.tan(num);
    if math.abs(val) <= 10^-14 then val = 0; end
    return val;
end

local function sinh(num)
    local val = math.sinh(num);
    if math.abs(val) <= 10^-14 then val = 0; end
    return val;
end
local function cosh(num)
    local val = math.cosh(num);
    if math.abs(val) <= 10^-14 then val = 0; end
    return val;
end
local function tanh(num)
    local val = math.tanh(num);
    if math.abs(val) <= 10^-14 then val = 0; end
    return val;
end

local function rand(low, upp, seed)
	math.randomseed(seed);
	local randomtable = {};
	
	if low == 0 and upp == 1 then
		for i = 1, 97 do
			randomtable[i] = math.random();
		end
	else
		for i = 1, 97 do
			randomtable[i] = math.random(low, upp);
		end
	end
	
	local i = math.ceil(97 * math.random());
	
	return randomtable[i];
end

local function func(x, z)
    local power = (z-1)*math.log10(x/(1-x)) - (x/(1-x))/math.log(10) - 2*math.log10(1-x);
    return power;
end
local function log_sum(old_pows)
    local n = table.getn(old_pows);
    if n == 1 then
        return old_pows[1];
    end
        
    local pows = {};
    local j = 1;
    for i = 1, math.floor(n/2) do
        pows[i] = old_pows[j] + math.log10(1 + 10^(old_pows[j+1] - old_pows[j]));
        j = j + 2;
    end
    if n % 2 == 1 then
        pows[math.floor(n/2)+1] = old_pows[n];
    end

    return log_sum(pows);
end
local function gamma_(z)
    if z <= 1 then
        return gamma_(z+1) - math.log10(z);
    end
        
    local n = 100000;
    local d = 1/n;

    local pows_ = {};
    local pows = {};
    local j = 1;
    local k = n-1;
    pows_[1] = func(d, z) - math.log10(n);
    for i = 2, n-1 do
        pows_[i] = func(i*d, z) - math.log10(n);
        if (pows_[i] >= -323) and (pows_[i-1] < -323) then
            j = i;
        end
        if (pows_[i] < -323) and (pows_[i-1] >= -323) then
            k = i-1;
            break;
        end
    end
    for i = 1, k-j+1 do
        pows[i] = pows_[i+j-1];
    end

    local power = log_sum(pows);
    return power;
end
local function gamma(z)
    local power = gamma_(z);
    
    if math.abs(power + 0) > 7 then
        if (power + 0) < 0 then
            return round(10^(power - math.floor(power)), 10^-7), 'E', math.floor(power + 0);
        else
            return round(10^(power - math.floor(power)), 10^-7), 'E+', math.floor(power + 0);
        end
    else
        return round(10^(power + 0), 10^-7);
    end
end

local function ncr(n, r)
    -- Error Checks --
    if (n < 0) then
        n = 0;
    end
    if (r > n) then
        r = n;
    end
    
    local val = 1;
    
    -- Special Cases --
    if (r <= 0 or r >= n) then
        return val;
    elseif (r == 1 or r == n - 1) then
        val = n;
        return val;
    end
    
    -- n! / r!(n-r)! --
    local total = n - r;
    local minimum = r + 1;
    local j = total;
    if (total > r) then
        minimum = total + 1;
        j = r;
    end
    
    for i = n, minimum, -1 do
        val = val * i / j;
        if (j >= 2) then
            j = j - 1;
        end
    end
    
    return val;
end

local function npr(n, r)
    -- Error Checks --
    if (n < 0) then
        n = 0;
    end
    if (r > n) then
        r = n;
    end
    
    local val = 1;
    
    -- Special Cases --
    if (r <= 0) then
        return val;
    elseif (r == 1) then
        val = n;
        return val;
    end
    
    -- n! / (n-r)! --
    local total = n - r;
    local minimum = total + 1;
    
    for i = n, minimum, -1 do
        val = val * i;
    end
    
    return val;
end

local function binomial(p, n, r)
    if p < 0 then
        p = 0;
    elseif p > 1 then
        p = 1;
    end
    
    local c = ncr(n, r);
    local val = c*(p^r)*(1-p)^(n-r);
    return val;
end

function sum(array)
    local sum = 0;
    for i = 1, #array do
        sum = sum + array[i];
    end

    return sum;
end
local function getProbs(array)
    local probs = {};
    local num;
    
    local i = 1;
    local j = 1;
    while array[i] ~= nil do
    	num = array[i] + 0;
        if num > 0 then
            probs[j] = num;
            j = j + 1;
        end
        i = i + 1;
    end
    
    local sumP = sum(probs);
    
    if sumP > 1 then
        for i = 1, #probs do
            probs[i] = probs[i]/sumP;
        end
    end
    
    return probs, sumP;
end
local function expected(array)
    local probs, sumP = getProbs(array);
    
    if sumP <= 0 then
        return 0;
    end
    
    local runs = 1;
    local probArray = {};
    
    for i = 1, #probs do
        probArray = getProbs(array);
        probArray[i] = 0;
        run = expected(probArray);
        runs = runs + probs[i]*run;
    end
    
    runs = runs/sumP;
    return runs;
end

local function func2(probs, dp, x)
    local ngFunction = 0;
    
    for i = 1, #probs do
        ngFunction = ngFunction + (1 - probs[i])^x;
    end
    
    return 1 - dp - ngFunction;
end
local function IQI(probs, dp, x_guess_one, x_guess_two, x_guess_three, error_desired, max_iterations)
	local x_old_old = x_guess_one;
	local x_old = x_guess_two;
	local x_current = x_guess_three;
	local error_approx = error_desired + 1;
	local num_iterations = 0;

	if func2(probs, dp, x_old_old) == 0 then
		x_root = x_old_old;
	elseif func2(probs, dp, x_old) == 0 then
		x_root = x_old;
	elseif func2(probs, dp, x_current) == 0 then
		x_root = x_current;
	else
		while (error_approx > error_desired) and (num_iterations < max_iterations) do
			num_iterations = num_iterations + 1;
			y_old_old = func2(probs, dp, x_old_old);
			y_old = func2(probs, dp, x_old)
			y_current = func2(probs, dp, x_current)
			x_new = (y_old*y_old_old/((y_current - y_old)*(y_current - y_old_old)))*x_current + (y_current*y_old_old/((y_old - y_current)*(y_old - y_old_old)))*x_old + (y_current*y_old/((y_old_old - y_current)*(y_old_old - y_old)))*x_old_old;

			if x_new ~= 0 then
				error_approx = math.abs((x_new - x_current)/x_new) * 100;
			end

			x_old_old = x_old;
			x_old = x_current;
			x_current = x_new;
		end
		x_root = x_new;
	end

	return x_root;
end
local function nearlyGuaranteed(array)
    local probs = getProbs(array);
    
    local range = 1;
    if table.getn(probs) ~= 1 or probs[1] ~= 1 then
        local nine9 = IQI(probs, 0.99, 0, 1, 2, 0.000001, 1000);
        local nine99 = IQI(probs, 0.999, 0, 1, 2, 0.000001, 1000);
        local nine999 = IQI(probs, 0.9999, 0, 1, 2, 0.000001, 1000);
    
        range = formatnum(round(nine99, 1))..'&nbsp;&#177;&nbsp;'..formatnum(round((nine999 - nine9)/2, 1));
    end
    return range;
end

local function getArgs(frame, options)
	if type(frame[1]) == 'table' and table.getn(frame) < 2 then frame = frame[1]; end
	if type(options) ~= 'table' then options = {options}; end
	
	local args = args.getArgs(frame, options);
	local tempArgs = {};
	local str;
	if not(options.noReplace) then
		for i, v in pairs(args) do
			if type(i) ~= 'number' or type(v) ~= 'string' then tempArgs[i] = v;
			else
				str = '('..replace(v)..')';
				if options.noEval then tempArgs[i] = str;
				else tempArgs[i] = tonumber(eval(str)); end
			end
		end
		args = tempArgs;
	end
	
	local j = 1;
	tempArgs = {};
	if options.noNil then
		for i, v in pairs(args) do
			if type(i) ~= 'number' then tempArgs[i] = v;
			elseif v then tempArgs[j] = v; j = j + 1; end
		end
		args = tempArgs;
	end
	
	return args;
end


--  Member functions  ----------------------------------------------------------

--- Evaluates input
--  @function		p.eval
--  @param			{string} num The input expression
--  @return			{number} eval(num)
function p.eval(...)
	local args = getArgs({...}, {noEval = true});
	if not(args[1]) then return userError('Not enough input arguments | eval(num)'); end
    return eval(args[1]);
end

--- Evaluates and formats input
--  @function		p.formatnum
--  @param			{number, string} num The input expression
--  @param[opt]		{string} code A language code | default 'en'
--  @param[opt]		{boolean} noCommafy Use comma separators | default false
--  @return			{string} formatnum(num)
--  @return			{string} formatnum(num, 'ar')
--  @return			{string} formatnum(num, "", true)
--  @return			{string} etc.
function p.formatnum(...)
	local args = getArgs({...}, {noReplace = true});
	local options = {};
	options.code = args[2] or 'en';
	options.noCommafy = args[3] or false;
	
	local args = getArgs({...});
	if not(args[1]) then return userError('Not enough input arguments | formatnum(num) or formatnum(num, code)'); end
    return formatnum(args[1], options);
end

--- Evaluates input and returns the magnitude
--  @function		p.abs
--  @param			{number, string} num The input value
--  @return			{number} abs(num)
function p.abs(...)
	local args = getArgs({...});
	if not(args[1]) then return userError('Not enough input arguments | abs(num)'); end
    return math.abs(args[1]);
end

--- Evaluates input and returns the acos
--  @function		p.acos
--  @param			{number, string} num The input value
--  @return			{number} acos(num)
function p.acos(...)
	local args = getArgs({...});
	if not(args[1]) then return userError('Not enough input arguments | acos(num)'); end
    return acos(args[1]);
end

--- Evaluates input and returns the asin
--  @function		p.asin
--  @param			{number, string} num The input value
--  @return			{number} asin(num)
function p.asin(...)
	local args = getArgs({...});
	if not(args[1]) then return userError('Not enough input arguments | asin(num)'); end
    return asin(args[1]);
end

--- Evaluates inputs and returns the atan
--  @function		p.atan
--  @param			{number, string} num The input value
--  @param[opt]		{number, string} den A second input value | default 1
--  @return			{number} atan(num)
--  @return			{number} atan(num / den)
function p.atan(...)
	local args = getArgs({...});
	if not(args[1]) then return userError('Not enough input arguments | atan(num) or atan(num, den)'); end
    return atan(args[1], args[2] or 1);
end

--- Returns the binomial probability of three inputs
--  @function		p.binomial
--  @param			{string, number} p The probability of success
--  @param			{string, number} n Total number of trials
--  @param			{string, number} r Number of successes
--  @return			{number} Binomial of p, n, and r
function p.binomial(...)
	local args = getArgs({...});
	if not(args[1]) or not(args[2]) or not(args[3]) then return userError('Not enough input arguments | binomial(prob, n, r)'); end
    return binomial(args[1], args[2], args[3]);
end

--- Evaluates input and returns the ceil
--  @function		p.ceil
--  @param			{number, string} num The input value
--  @return			{number} ceil(num)
function p.ceil(...)
	local args = getArgs({...});
	if not(args[1]) then return userError('Not enough input arguments | ceil(num)'); end
    return math.ceil(args[1]);
end

--- Evaluates input and returns the cos
--  @function		p.cos
--  @param			{number, string} num The input value
--  @return			{number} cos(num)
function p.cos(...)
	local args = getArgs({...});
	if not(args[1]) then return userError('Not enough input arguments | cos(num)'); end
    return cos(args[1]);
end

--- Evaluates input and returns the cosh
--  @function		p.cosh
--  @param			{number, string} num The input value
--  @return			{number} cosh(num)
function p.cosh(...)
	local args = getArgs({...});
	if not(args[1]) then return userError('Not enough input arguments | cosh(num)'); end
    return cosh(args[1]);
end

--- Evaluates input and converts into degrees
--  @function		p.deg
--  @param			{number, string} num The input value
--  @return			{number} deg(num)
function p.deg(...)
	local args = getArgs({...});
	if not(args[1]) then return userError('Not enough input arguments | deg(num)'); end
    return math.deg(args[1]);
end

--- Evaluates inputs and returns the expected value
--  @function		p.ex
--  @param			{table} frame inputs
--  @return			{string} Expectation range of inputs
function p.ex(...)
	local args = getArgs({...}, {noNil = true});
	if not(args[1]) then return userError('Not enough input arguments | ex(prob1, prob2, prob3, ...)'); end
    local runs = expected(args);
    
    if math.floor(runs) == math.ceil(runs) or (args["range"] ~= nil and args["range"] ~= '') then
        return formatnum(runs);
    else
        return formatnum(math.floor(runs))..'&nbsp;&ndash;&nbsp;'..formatnum(math.ceil(runs));
    end
end

--- Evaluates input and returns the exp
--  @function		p.exp
--  @param			{number, string} num The input value
--  @return			{number} exp(num)
function p.exp(...)
	local args = getArgs({...});
    return math.exp(args[1] or 1);
end

--- Evaluates input and returns the floor
--  @function		p.floor
--  @param			{number, string} num The input value
--  @return			{number} floor(num)
function p.floor(...)
	local args = getArgs({...});
	if not(args[1]) then return userError('Not enough input arguments | floor(num)'); end
    return math.floor(args[1]);
end

--- Evaluates inputs and returns closest fraction
--  @function		p.frac
--  @param			{number, string} num The input value
--  @param[opt]		{string} factor The value to factor out | default '1'
--  @param[opt]		{number, string} epsilon Number of decimal places to be accurate to | default -5 (10^-5)
--  @return			{string} Closest fraction in LaTeX
function p.frac(...)
	local args = getArgs({...}, {noReplace = true});
	local factor = args[2] or '1';
	
	local args = getArgs({...});
	if not(args[1]) then return userError('Not enough input arguments | frac(num) or frac(num, factor, epsilon)'); end
    local num = args[1];
    local epsilon = args[3] or -5;
    
    local str = '';
    if factor == '#e' then
        str = 'e';
    elseif factor == '#gamma' then
        str = '\\gamma';
    elseif factor == '#phi' then
        str = '\\varphi';
    elseif factor == '#pi' then
        str = '\\pi';
    elseif factor == '1' or factor == 'y' or factor == '' then
        str = '';
        factor = 1;
    else
        str = '\\text{('..(replace(factor) + 0)..')}';
    end
    
    factor = replace(factor) + 0;
    if factor == 0 then
        factor = 1;
    end
    
    if num ~= 0 then
        local fraction = findFrac(num, factor, str, epsilon);
        return fraction;
    else
        return 0;
    end
end

--- Evaluates input and returns the factorial
--  @function		p.gamma
--  @param			{number, string} num The input value
--  @return			{number} (num - 1)!
--  @return			{string} (num - 1)! scientific notation if larger than 10^7
function p.gamma(...)
	local args = getArgs({...});
	if not(args[1]) then return userError('Not enough input arguments | gamma(num)'); end
    return gamma(args[1]);
end

--- Evaluates inputs and return greatest common divider
--  @function		p.gcd
--  @param			{number, string} num1 The first input value
--  @param			{number, string} num2 The second input value
--  @return			{number} GCD(num1, num2)
function p.gcd(...)
	local args = getArgs({...});
	if not(args[1]) or not(args[2]) then return userError('Not enough input arguments | gcd(num1, num2)'); end
    return gcd(args[1], args[2]);
end

--- Evaluates input and returns the natural log
--  @function		p.ln
--  @param			{number, string} num The input value
--  @return			{number} ln(num)
function p.ln(...)
	local args = getArgs({...});
	if not(args[1]) then return userError('Not enough input arguments | ln(num)'); end
    return math.log(args[1]);
end

--- Evaluates inputs and returns the log (base x)
--  @function		p.log
--  @param			{number, string} num The input value
--  @param[opt]		{number, string} base The logarithm base | default 10
--  @return			{number} log(num)
--  @return			{number} log(num, base)
function p.log(...)
	local args = getArgs({...});
	if not(args[1]) then return userError('Not enough input arguments | log(num) or log(num, base)'); end
    return math.log(args[1]) / math.log(args[2] or 10);
end

--- Evaluates inputs and returns the maximum value
--  @function		p.max
--  @param			{table} nums The input values
--  @return			{number} max(num1, num2, num3, ...)
--  @return			{number} max(nums)
function p.max(...)
	local i = 1;
	local maxVal = -p.inf;
	local args = getArgs({...}, {noNil = true});
	if not(args[1]) then return userError('Not enough input arguments | max(num1, num2, num3, ...)'); end
	
	while args[i] ~= nil do
		maxVal = math.max(maxVal, args[i]);
		i = i + 1;
	end
	
    return maxVal;
end

--- Evaluates inputs and returns the minimum value
--  @function		p.min
--  @param			{table} nums The input values
--  @return			{number} min(num1, num2, num3, ...)
--  @return			{number} min(nums)
function p.min(...)
	local i = 1;
	local minVal = p.inf;
	local args = getArgs({...}, {noNil = true});
	if not(args[1]) then return userError('Not enough input arguments | min(num1, num2, num3, ...)'); end
	
	while args[i] ~= nil do
		minVal = math.min(minVal, args[i]);
		i = i + 1;
	end
	
    return minVal;
end

--- Evaluates inputs and returns the modulo
--  @function		p.mod
--  @param			{number, string} num The dividend
--  @param			{number, string} den The divider
--  @return			{number} num % den
function p.mod(...)
	local args = getArgs({...});
	if not(args[1]) or not(args[2]) then return userError('Not enough input arguments | mod(num, den)'); end
    return math.fmod(args[1], args[2]);
end

--- Evaluates input and returns the integral, fractional, or both parts
--  @function		p.modf
--  @param			{number, string} num The input value
--  @param[opt]		{boolean} frame.int Returns the integer | default false
--  @param[opt]		{boolean} frame.dec Returns the decimal | default true
--  @return			{number} modf(num)
--  @return			{number} modf(num, int=true)
--  @return			{number} modf(num, int=true, dec=true) (returns both)
function p.modf(...)
	local args = getArgs({...});
	if not(args[1]) then return userError('Not enough input arguments | modf(num)'); end
	local int, dec = math.modf(args[1]);
	if args["int"] and not(args["dec"]) then return int; end
	if args["int"] and args["dec"] then return int, dec; end
	return dec;
end

--- Evaluates inputs and returns the combitorial
--  @function		p.ncr
--  @param			{number, string} n The input value
--  @param			{number, string} r The second input value
--  @return			{number} nCr(n, r)
function p.ncr(...)
	local args = getArgs({...});
	if not(args[1]) or not(args[2]) then return userError('Not enough input arguments | ncr(n, r)'); end
    return ncr(args[1], args[2]);
end

--- Evaluates inputs and returns the nearly guaranteed value
--  @function		p.ng
--  @param			{table} frame Inputs
--  @return			{string} Nearly guaranteed range of inputs
function p.ng(...)
	local args = getArgs({...}, {noNil = true});
	if not(args[1]) then return userError('Not enough input arguments | ng(prob1, prob2, prob3, ...)'); end
    return nearlyGuaranteed(args);
end

--- Evaluates inputs and returns the permutation
--  @function		p.npr
--  @param			{number, string} n The input value
--  @param			{number, string} r The second input value
--  @return			{number} nPr(n, r)
function p.npr(...)
	local args = getArgs({...});
	if not(args[1]) or not(args[2]) then return userError('Not enough input arguments | npr(n, r)'); end
    return npr(args[1], args[2]);
end

--- Evaluates inputs and returns as a percent
--  @function		p.percentage
--  @param			{number} num The input value
--  @return			{string} number represented as a percentage
function p.percentage(...)
	local args = getArgs({...});
	if not(args[1]) then return userError('Not enough input arguments | percentage(num)'); end
	if not(args[2]) then args[2] = 0.01; end
    return formatnum(round(args[1] * 100, args[2]))..'%';
end

--- Evaluates inputs and returns the power of one to the other
--  @function		p.pow
--  @param			{number, string} base The input value
--  @param			{number, string} exponent The second input value
--  @return			{number} base^exponent
function p.pow(...)
	local args = getArgs({...});
	if not(args[1]) or not(args[2]) then return userError('Not enough input arguments | pow(base, exponent)'); end
	if args[2] == 0.5 then return math.sqrt(math.abs(args[1])); end
	if args[2] > 0 and args[2] < 1 then args[1] = math.abs(args[1]); end
    return math.pow(args[1], args[2]);
end

--- Evaluates input and converts into radians
--  @function		p.rad
--  @param			{number, string} num The input value
--  @return			{number} rad(num)
function p.rad(...)
	local args = getArgs({...});
	if not(args[1]) then return userError('Not enough input arguments | rad(num)'); end
    return math.rad(args[1]);
end

--- Evaluates input and converts into radians
--  @function		p.rand
--  @param[opt]		{number, string} low The lower bound of random values | default 0
--  @param[opt]		{number, string} upp The upper bound of random values | default 1 or low if it exists
--  @param[opt]		{number, string} frame.seed The seed to randomize from | default current OS time
--  @return			{number} rand() [0 - 1]
--  @return			{number} rand(low) [0 - low]
--  @return			{number} rand(low, upp) [low - upp]
--  @return			{number} rand(seed = 0)
--  @return			{number} etc.
function p.rand(...)
	local args = getArgs({...});
	local seed = tonumber(eval(replace(args["seed"] or '')));
	if not(seed) then seed = os.time(); end
	if not(args[1]) then args[2] = 1; args[1] = 0; end
	if not(args[2]) then args[2] = args[1]; args[1] = 1; end
    return rand(math.min(args[1], args[2]), math.max(args[1], args[2]), seed)..'_'..seed;
end

--- Replaces and deletes all non-expression characters in a string
--  @function		p.replace
--  @param			{string} str expression
--  @return			{string} An evaluatable expression
function p.replace(frame)
    return '('..replace(frame.args[1] or '')..')';
end

--- Replaces constants with their symbol
--  @function		p.replaceWithSymbol
--  @param			{string} str expression
--  @return			{string} #e, #gamma, #phi, and #pi replaced with e, γ, φ, and π
function p.replaceWithSymbol(frame)
    return replaceWithSymbol(frame.args[1] or '');
end

--- Evaluates input and rounds to specified multiple
--  @function		p.round
--  @param			{number, string} num The input value
--  @param[opt]		{number, string} multiple The multiple to round to | default 0.0001
--  @param[opt]		{boolean} percent Convert input from decimal to percentage | default false
--  @param[opt]		{boolean} degree Convert input into degrees | default false
--  @return			{string} round(num)
--  @return			{string} round(num, 0.125)
--  @return			{string} round(num, "", true, false)
--  @return			{string} etc.
function p.round(...)
	local args = getArgs({...});
	
	if not(args[1]) then return userError('Not enough input arguments | round(num) or round(num, multiple)'); end
    if math.abs(args[1]) <= 10^-14 then return 0; end
    
    if args[3] == true then args[1] = 100*args[1]; end
    if args[4] == true then args[1] = math.deg(args[1]); end
    
    local num = round(args[1], args[2] or 0.0001);
    
    if math.abs(num) <= 10^-14 then return 0; end
    
    local power = math.log10(math.abs(num));
    if power >= 0 then
        power = math.floor(power);
    else
        power = math.ceil(power);
    end
    if power <= -5 or power >= 14 then
        num = num * 10^(-power);
        
        if power >= 0 then
            return num..'E+'..power;
        else
            return num..'E-'..-power;
        end
    else
        return formatnum(num);
    end
end

--- Evaluates input and returns the sin
--  @function		p.sin
--  @param			{number, string} num The input value
--  @return			{number} sin(num)
function p.sin(...)
	local args = getArgs({...});
	if not(args[1]) then return userError('Not enough input arguments | sin(num)'); end
    return sin(args[1]);
end

--- Evaluates input and returns the sinh
--  @function		p.sinh
--  @param			{number, string} num The input value
--  @return			{number} sinh(num)
function p.sinh(...)
	local args = getArgs({...});
	if not(args[1]) then return userError('Not enough input arguments | sinh(num)'); end
    return sinh(args[1]);
end

--- Evaluates input and returns the sqrt
--  @function		p.sqrt
--  @param			{number, string} num The input value
--  @return			{number} sqrt(num)
function p.sqrt(...)
	local args = getArgs({...});
	if not(args[1]) then return userError('Not enough input arguments | sqrt(num)'); end
    return math.sqrt(math.abs(args[1]));
end

--- Evaluates inputs and returns the summation
--  @function		p.sum
--  @param			{table} nums inputs
--  @return			{number} sum(num1, num2, num3, ...)
--  @return			{number} sum(nums)
function p.sum(...)
	local args = getArgs({...}, {noNil = true});
	if not(args[1]) then return userError('Not enough input arguments | sum(num1, num2, num3, ...)'); end
	
	local i = 1;
	local sum = 0;
	while args[i] ~= nil do
		sum = sum + (args[i] or 0);
		i = i + 1;
	end

	return sum;
end

--- Evaluates input and returns the tan
--  @function		p.tan
--  @param			{number, string} num The input value
--  @return			{number} tan(num)
function p.tan(...)
	local args = getArgs({...});
	if not(args[1]) then return userError('Not enough input arguments | tan(num)'); end
    return tan(args[1]);
end

--- Evaluates input and returns the tanh
--  @function		p.tanh
--  @param			{number, string} num The input value
--  @return			{number} tanh(num)
function p.tanh(...)
	local args = getArgs({...});
	if not(args[1]) then return userError('Not enough input arguments | tanh(num)'); end
    return tanh(args[1]);
end

--- Evaluates input and returns the truncation
--  @function		p.trunc
--  @param			{number, string} num The input value
--  @return			{number} trunc(num)
function p.trunc(...)
	local args = getArgs({...});
	if not(args[1]) then return userError('Not enough input arguments | trunc(num)'); end
    return trunc(args[1]);
end


--  All Math STL functions  ----------------------------------------------------
-- https://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#Math_library
p.__abs = math.abs;
p.__acos = math.acos;
p.__asin = math.asin;
p.__atan = math.atan;
p.__atan2 = math.atan2;
p.__ceil = math.ceil;
p.__cos = math.cos;
p.__cosh = math.cosh;
p.__deg = math.deg;
p.__exp = math.exp;
p.__floor = math.floor;
p.__fmod = math.fmod;
p.__frexp = math.frexp;
p.__huge = math.huge;
p.__ldexp = math.ldexp;
p.__log = math.log;
p.__log10 = math.log10;
p.__log = math.log;
p.__max = math.max;
p.__min = math.min;
p.__mod = math.mod;
p.__modf = math.modf;
p.__pi = math.pi
p.__pow = math.pow;
p.__rad = math.rad;
p.__random = math.random;
p.__randomseed = math.randomseed;
p.__sin = math.sin;
p.__sinh = math.sinh;
p.__sqrt = math.sqrt;
p.__tan = math.tan;
p.__tanh = math.tanh;


p.__main = entrypoint(p);
return p;
Community content is available under CC-BY-SA unless otherwise noted.