Documentation for this module may be created at Module:Math/doc

--<nowiki>

local p = {};

local shared = require( [[Module:Shared]] );
local stuff = {"'", "°", "%%", "−", "&minus;", "÷", "•", "×", "&times;", "{{", "}}", "{", "}", "%[", "%]", "E%+", "E%-", "e%+", "e%-", "γ", "φ", "π", "#e", "#gamma", "#phi", "#pi", "%)%(", "<sup>", "</sup>", "trunc%(", "floor%(", "ceil%(", "exp%(", "abs%(", "asin%(", "acos%(", "atan%(", "sin%(", "cos%(", "tan%(", "ln%(", "mod"};
local replaceStuff = {"", "%*#¡/180", "/100", "%-", "%-", "/", "%*", "%*", "%*", "%(", "%)", "%(", "%)", "%(", "%)", "%*10%^", "%*10%^-", "%*10%^", "%*10%^-", "#£", "#¢", "#¡", "#¤", "#£", "#¢", "#¡", "%)%*%(", "%^%(", "%)", "¥", "¦", "§", "¨", "©", "ª", "«", "¬", "®", "¯", "µ", "¶", "%%"};
local stuff2 = {"#¤" ,"#£", "#¢", "#¡", "¥", "¦", "§", "¨", "©", "ª", "«", "¬", "®", "¯", "µ", "¶", "%%"};
local replaceStuff2 = {"#e" ,"#gamma", "#phi", "#pi", "trunc%(", "floor%(", "ceil%(", "exp%(", "abs%(", "asin%(", "acos%(", "atan%(", "sin%(", "cos%(", "tan%(", "ln%(", "%)mod%("};
local absF = math.abs;
local logF = math.log;
local ceilF = math.ceil;
local floorF = math.floor;
	
local function replace(str)
    local i;
    local j;
    
    i = string.find(str, "%[%[File:");
    _, j = string.find(str, "%]%]&thinsp;");
    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, "%]%]&thinsp;");
    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, #stuff do
        str = string.gsub(str, stuff[i], replaceStuff[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, #stuff2 do
        str = string.gsub(str, stuff2[i], replaceStuff2[i]);
    end
    
    str = string.gsub(str, "#e", math.exp(1));
    str = string.gsub(str, "#gamma", 0.57721566490153);
    str = string.gsub(str, "#phi", (1 + math.sqrt(5))/2);
    str = string.gsub(str, "#pi", math.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
    
    mw.log(str);
    return str;
end

function p.replace(frame)
    return '('..replace(frame.args[1])..')';
end


local function replace2(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

function p.replace2(frame)
    return replace2(frame.args[1]);
end


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

function p.round(frame)
    local val = frame.args[1] + 0;
    if absF(val) <= 10^-14 then
        return 0;
    end
    
    local power = logF(absF(val))/logF(10);
    if power >= 0 then
        power = ceilF(power);
    else
        power = floorF(power);
    end
    
    if power <= -5 or power >= 14 then
        val = val * 10^(-power);
    end
    
    val = round(val, frame.args[2] + 0);
    
    if power <= -5 or power >= 14 then
        if power >= 0 then
            return val..'E+'..power;
        else
            return val..'E-'..-power;
        end
    else
        return shared.formatnum(val);
    end
end

function p._round(val, decimal, percent, degree)
    if val == 0 then
        return 0;
    end
    
    if percent == true then
        val = 100*val;
    end
    if degree == true then
        val = val*180/math.pi;
    end
    
    local power = logF(absF(val))/logF(10);
    if power >= 0 then
        power = ceilF(power);
    else
        power = floorF(power);
    end
    
    if power <= -5 or power >= 14 then
        val = val * 10^(-power);
    end
    
    val = round(val, decimal);
    
    if power <= -5 or power >= 14 then
        if power >= 0 then
            return val..'E+'..power;
        else
            return val..'E-'..-power;
        end
    else
        return shared.formatnum(val);
    end
end


local function trunc(num)
    local val = 0;
    if (num > 0) then
        val = num - floorF(num);
    else
        val = num - ceilF(num);
    end
    val = num - val;
    
    return val;
end

function p.trunc(frame)
    return trunc(frame.args[1] + 0);
end


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

local function findFrac(val, factor, str, epsilon)
    epsilon = epsilon + ceilF(logF(val/factor)/logF(10));
    local num = val/factor - floorF(val/factor);
    num = absF(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 = floorF(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 = absF(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 = floorF(absF(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

--function p.frac(val, factor, epsilon)
--[
function p.frac(frame)
    local val = frame.args[1] + 0;
    local factor = frame.args[2];
    local epsilon = frame.args[3] + 0;
--]]
    
    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' then
        str = '';
        factor = 1;
    else
        str = '\\text{('..(replace(factor) + 0)..')}';
    end
    
    factor = replace(factor) + 0;
    if factor == 0 then
        factor = 1;
    end
    
    if val ~= 0 then
        local fraction = findFrac(val, factor, str, epsilon);
        return fraction;
    else
        return 0;
    end
end


local function mod(num, by)
    return ceilF(num % by);
end

function p.mod(frame)
    return mod(frame.args[1] + 0, frame.args[2] + 0);
end


local function ceil(num)
    local val = ceilF(num);
    return val;
end

function p.ceil(frame)
    return ceil(frame.args[1] + 0);
end


local function floor(num)
    local val = floorF(num);
    return val;
end

function p.floor(frame)
    return floor(frame.args[1] + 0);
end


local function abs(num)
    local val = absF(num);
    return val;
end

function p.abs(frame)
    return abs(frame.args[1] + 0);
end


local function pow(num, expo)
    local val = math.pow(num, expo);
    return val;
end

function p.pow(frame)
    return pow(frame.args[1] + 0, frame.args[2] + 0);
end


local function exp(expo)
    local val = math.exp(expo);
    return val;
end

function p.exp(frame)
    return exp(frame.args[1] + 0);
end


local function ln(num)
    local val = logF(num);
    return val;
end

function p.ln(frame)
    return ln(frame.args[1] + 0);
end


local function log(base, num)
    local val = logF(num) / logF(base);
    return val;
end

function p.log(frame)
    return log(frame.args[1] + 0, frame.args[2] + 0);
end


local function sin(num)
    local val = math.sin(num);
    return val;
end

function p.sin(frame)
    return sin(frame.args[1] + 0);
end


local function cos(num)
    local val = math.cos(num);
    return val;
end

function p.cos(frame)
    return cos(frame.args[1] + 0);
end


local function tan(num)
    local val = math.tan(num);
    return val;
end

function p.tan(frame)
    return tan(frame.args[1] + 0);
end


local function asin(num)
    local val = math.asin(num);
    return val;
end

function p.asin(frame)
    return asin(frame.args[1] + 0);
end


local function acos(num)
    local val = math.acos(num);
    return val;
end

function p.acos(frame)
    return acos(frame.args[1] + 0);
end


local function atan(num)
    local val = math.atan(num);
    return val;
end

function p.atan(frame)
    return atan(frame.args[1] + 0);
end


local function func(x, z)
    local power = (z-1)*logF(x/(1-x))/logF(10) - (x/(1-x))/logF(10) - 2*logF(1-x)/logF(10);
    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, floorF(n/2) do
        pows[i] = old_pows[j] + log(10, 1 + 10^(old_pows[j+1] - old_pows[j]));
        j = j + 2;
    end
    if n % 2 == 1 then
        pows[floorF(n/2)+1] = old_pows[n];
    end

    return log_sum(pows);
end

local function gamma_(z)
    if z <= 1 then
        return gamma_(z+1) - log(10, 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) - logF(n)/logF(10);
    for i = 2, n-1 do
        pows_[i] = func(i*d, z) - logF(n)/logF(10);
        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, percent, degree)
    local power = gamma_(z);
    local p = 0;
    
    if absF(power + p) > 7 then
        if (power + p) < 0 then
            return round(10^(power - floorF(power)), 10^-7), 'E', floorF(power + p);
        else
            return round(10^(power - floorF(power)), 10^-7), 'E+', floorF(power + p);
        end
    else
        return round(10^(power + p), 10^-7);
    end
end

function p.gamma(frame)
    return gamma(frame.args[1] + 0, frame.args[2], frame.args[3]);
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

function p.ncr(frame)
    return ncr(frame.args[1] + 0, frame.args[2] + 0);
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

function p.npr(frame)
    return npr(frame.args[1] + 0, frame.args[2] + 0);
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 p.binomial(frame)
    return binomial(frame.args[1] + 0, frame.args[2] + 0, frame.args[3] + 0);
end


local 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 j = 1;
    for i = 1, #array do
    	num = array[i] + 0;
        if num > 0 then
            probs[j] = num;
            j = j + 1;
        end
    end
    
    local sumP = sum(probs);
    
    if sumP > 1 then
        for i = 1, #probs do
            probs[i] = probs[i]/sumP;
        end
    end
    
    return probs;
end

local function expected(array)
    local probs = getProbs(array);
    
    local sumP = sum(probs);
    
    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

function p.expected(frame)
    local runs = expected(frame.args);
    
    if floorF(runs) == ceilF(runs) or (frame.args["range"] ~= nil and frame.args["range"] ~= '')then
        return shared.formatnum(runs);
    else
        return floorF(runs)..'&nbsp;&ndash;&nbsp;'..ceilF(runs);
    end
end

function p._expected(frame)
    local runs = expected(frame);
    
    if floorF(runs) == ceilF(runs) then
        return shared.formatnum(runs);
    else
        return floorF(runs)..'&nbsp;&ndash;&nbsp;'..ceilF(runs);
    end
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 = absF((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 = round(nine99, 1)..'&nbsp;&#177;&nbsp;'..round((nine999 - nine9)/2, 1);
    end
    return range;
end

function p.nearlyGuaranteed(frame)
    return nearlyGuaranteed(frame.args);
end

function p._nearlyGuaranteed(frame)
    return nearlyGuaranteed(frame);
end


return p;
Community content is available under CC-BY-SA unless otherwise noted.