--OmegaNum port by Mathguy
Big = {
    array = {},
    sign = 1
}

maxArrow = 1e3

OmegaMeta = {}
OmegaMeta.__index = Big

external = true

omegaNumError = "[OmegaNumError] "
invalidArgument = omegaNumError .. "Invalid argument: "

isOmegaNum = "/^[-\\+]*(Infinity|NaN|(10(\\^+|\\{[1-9]\\d*\\})|\\(10(\\^+|\\{[1-9]\\d*\\})\\)\\^[1-9]\\d* )*((\\d+(\\.\\d*)?|\\d*\\.\\d+)?([Ee][-\\+]*))*(0|\\d+(\\.\\d*)?|\\d*\\.\\d+))$/"

MAX_SAFE_INTEGER = 9007199254740991
MAX_E = math.log(MAX_SAFE_INTEGER, 10)
LONG_STRING_MIN_LENGTH = 17

R = {}

R.ZERO = 0
R.ONE = 1
R.E = math.exp(1)
R.LN2 = math.log(2, R.E)
R.LN10 = math.log(10, R.E)
R.LOG2E = math.log(R.E, 2)
R.LOG10E = math.log(R.E, 0)
R.PI = math.pi
R.SQRT1_2 = math.sqrt(0.5)
R.SQRT2 = math.sqrt(2)
R.MAX_SAFE_INTEGER=MAX_SAFE_INTEGER
R.MIN_SAFE_INTEGER=-9007199254740992
R.MAX_DISP_INTEGER=1000000
R.NaN=0/0
R.NEGATIVE_INFINITY = -1/0
R.POSITIVE_INFINITY = 1/0
R.E_MAX_SAFE_INTEGER="e"..tostring(R.MAX_SAFE_INTEGER)
R.EE_MAX_SAFE_INTEGER="ee"..tostring(R.MAX_SAFE_INTEGER)
R.TETRATED_MAX_SAFE_INTEGER="10^^"..tostring(R.MAX_SAFE_INTEGER)

-- this will be populated with bignum equivalents of R's values at the end of the file
B = {}

--------------make the numbers look good----------------------
function thousands_format(number)
    return string.format("%.2f", number)
end

function AThousandNotation(n, places)
    local raw = string.format("%." .. places .."f", n)
    local result = ""
    local comma = string.find(raw, "%.")

    if comma == nil then
        comma = #raw
    else
        comma = comma - 1
    end

    for i = 1, #raw do
        result = result .. string.sub(raw, i, i)
        if (comma - i) % 3 == 0 and i < comma then
            result = result .. ","
        end
    end
    return result
end

------------------------------------------------------

function Big:arraySize()
    local total = 0
    for i, v in pairs(self.array) do
        if type(i) == "number" and v ~= 0 then
            total = i
        end
    end
    return total
end

function Big:new(arr)
    return setmetatable({array = arr, sign = 1}, OmegaMeta):normalize()
end

function Big:isNaN()
    return self.array[1] ~= self.array[1]
end

function Big:isInfinite()
    return (self.array[1] == R.POSITIVE_INFINITY) or (self.array[1] == R.NEGATIVE_INFINITY)
end

function Big:isFinite()
    return (not self:isInfinite() and not self:isNaN())
end

function Big:isint()
    if (self.sign==-1) then
        return self:abs():isint()
    end
    if (self:gt(B.MAX_SAFE_INTEGER)) then
        return true;
    end
    local num = self:to_number()
    if (math.floor(num) == num) then
        return true
    end
    return Big:create(math.floor(self:to_number())) == self;
end

function Big:compareTo(other)
    other = Big:ensureBig(other)
    local other_array_size = other:arraySize()
    local self_array_size = self:arraySize()
    if ((self.array[1] ~= self.array[1]) or (other.array[1] ~= other.array[1])) then
        return R.NaN;
    end
    if ((self.array[1]==R.POSITIVE_INFINITY) and (other.array[1]~=R.POSITIVE_INFINITY)) then
        return self.sign
    end
    if ((self.array[1]~=R.POSITIVE_INFINITY) and (other.array[1]==R.POSITIVE_INFINITY)) then
        return other.sign
    end
    if ((self_array_size==1) and (self.array[1]==other.array[1]) and (other_array_size==1)) then
        --return 0
    end
    if (self.sign~=other.sign) then
        return self.sign
    end
    local m = self.sign;
    local r = nil;
    if (self_array_size>other_array_size) then
        r = 1;
    elseif (self_array_size<other_array_size) then
        r = -1;
    else
        if self_array_size == 1 then 
            if self.array[1] > other.array[1]  then
                return 1 * m
            elseif self.array[1] < other.array[1] then
                return -1 * m
            else
                return 0
            end
        end
        local barray = {}
        for i, v in pairs(self.array) do
            barray[#barray+1]=i
        end
        --make sure to include both sets of indeces so that it actually checks every non 0 value
        for i, v in pairs(other.array) do
            barray[#barray+1]=i
        end
        table.sort(barray, function(a,b) return a > b end)
        for i, v in pairs(barray) do
            if ((self.array[v] or 0)>(other.array[v] or 0)) then
                r = 1;
                break;
            elseif ((self.array[v] or 0)<(other.array[v] or 0)) then
                r = -1
                break
            end
            r = r or 0;
        end
    end
    return r * m;
end

function Big:lt(other)
    return self:compareTo(other) < 0
end

function Big:gt(other)
    return self:compareTo(other) > 0
end

function Big:lte(other)
    return self:compareTo(other) <= 0
end

function Big:gte(other)
    return self:compareTo(other) >= 0
end

function Big:eq(other)
    return self:compareTo(other) == 0
end

function Big:neg()
    local x = self:clone();
    x.sign = x.sign * -1;
    return x;
end

function Big:abs()
    local x = self:clone();
    x.sign = 1;
    return x;
end

function Big:min(other)
    if (self:lt(other)) then
        return self:clone()
    else
        return Big:create(other)
    end
end

function Big:max(other)
    if (self:gt(other)) then
        return self:clone()
    else
        return Big:create(other)
    end
end

function Big:normalize()
    local b = nil
    local x = self
    if ((x.array == nil) or (type(x.array) ~= "table") or (x:arraySize() == 0)) then
        x.array = {0}
    end
    if (x:arraySize() == 1) and (x.array[1] == 0) then
        x.sign = 1
        return x
    end
    if (x:arraySize() == 1) and (x.array[1] < 0) then
        x.sign = -1
        x.array[1] = -x.array[1]
    end
    if ((x.sign~=1) and (x.sign~=-1)) then
    --   if (typeof x.sign!="number") x.sign=Number(x.sign);
        if (x.sign < 0) then
            x.sign = -1;
        else
            x.sign = 1;
        end
    end
    for i, v in pairs(x.array) do
        local e = x.array[i] or 0;
        if (e ~= e) then
            x.array={R.NaN};
            return x;
        end
        if (e == R.POSITIVE_INFINITY) or (e == R.NEGATIVE_INFINITY) then
            x.array = {R.POSITIVE_INFINITY};
            return x;
        end
        if (i ~= 1) then
            x.array[i]=math.floor(e)
        end
        --first 3 values kept because they are hardcoded in a few places
        --it also doesnt hurt memory that much to keep them anyway
        if ((e == 0)) and i > 3 then
            x.array[i] = nil
        end
    end
    local doOnce = true
    while (doOnce or b) do
    --   if (OmegaNum.debug>=OmegaNum.ALL) console.log(x.toString());
        b=false;
        while ((x:arraySize() ~= 0) and (x.array[x:arraySize()]==0)) do
            x.array[x:arraySize()] = nil;
            b=true;
        end
        if ((x.array[1] or 0) > R.MAX_DISP_INTEGER) then --modified, should make printed values easier to display
            x.array[2]=(x.array[2] or 0) + 1;
            x.array[1]= math.log(x.array[1], 10);
            b=true;
        end
        while (((x.array[1] or 0) < math.log(R.MAX_DISP_INTEGER,10)) and ((x.array[2] ~= nil) and (x.array[2] ~= 0))) do
            x.array[1] = math.pow(10,x.array[1], 10);
            x.array[2] = x.array[2] - 1
            b=true;
        end
        -- if ((x:arraySize()>2) and ((x.array[2] == nil) or (x.array[2] == 0))) then
        --     local i = 3
        --     while (x.array[i] == nil) or (x.array[i] == 0) do
        --         i = i + 1
        --     end
        --     x.array[i-1]=x.array[1];
        --     x.array[1]=1;
        --     x.array[i] = x.array[i] - 1
        --     b=true;
        -- end
        doOnce = false;
        --l = x:arraySize()
        for i, v in pairs(x.array) do
            if type(i) == "number" then
                if ((x.array[i] or 0)>R.MAX_SAFE_INTEGER) then
                    x.array[i+1]=(x.array[i+1] or 0)+1;
                    x.array[1]=x.array[i]+1;
                    for j=2,i do
                        x.array[j]=0;
                    end
                    b=true;
                end
            end
        end
    end
    if (x:arraySize() == 0) then
        x.array = {0}
    end
    return x;
end

function Big:toString()
    if (self.sign==-1) then
        return "-" .. self:abs():toString()
    end
    if (self.array[1] ~= self.array[1]) then
        return "NaN"
    end
    -- if (!isFinite(this.array[0])) return "Infinity";
    local s = "";
    if (self:arraySize()>=2) then
        for i=self:arraySize(),3,-1 do
            local q = nil
            if (i >= 6) then
                q = "{"..(i-1).."}"
            else
                q = string.rep("^", i-1)
            end
            if (self.array[i]>1) then
                s = s .."(10" .. q .. ")^" .. AThousandNotation(self.array[i], 0) .. " "
            elseif (self.array[i]==1) then
                s= s .."10" .. q;
            end
        end
    end
    if (self.array[2] == nil) or (self.array[2] == 0) then
        if (self.array[1] <= 9e9) then
            s = s .. AThousandNotation(self.array[1], 2)
        else
            local exponent = math.floor(math.log(self.array[1], 10))
            local mantissa = math.floor((self.array[1] / (10^exponent))*100)/100
            s = s .. AThousandNotation(mantissa, 2) .. "e" .. AThousandNotation(exponent, 0)
        end
    elseif (self.array[2]<3) then
        s = s .. string.rep("e", self.array[2]-1) .. AThousandNotation(math.pow(10,self.array[1]-math.floor(self.array[1])), 2) .. "e" .. AThousandNotation(math.floor(self.array[1]), 0);
    elseif (self.array[2]<8) then
        s = s .. string.rep("e", self.array[2]) .. AThousandNotation(self.array[1], 0)
    else
        s = s .. "(10^)^" .. AThousandNotation(self.array[2], 0) .. " " .. AThousandNotation(self.array[1],0)
    end
    return s
end

function log10LongString(str)
    return math.log(tonumber(string.sub(str, 1, LONG_STRING_MIN_LENGTH)), 10)+(string.len(str)- LONG_STRING_MIN_LENGTH);
end

function Big:parse(input)
    -- if (typeof input!="string") throw Error(invalidArgument+"Expected String");
    -- var isJSON=false;
    -- if (typeof input=="string"&&(input[0]=="["||input[0]=="{")){
    --   try {
    --     JSON.parse(input);
    --   }finally{
    --     isJSON=true;
    --   }
    -- }
    -- if (isJSON){
    --   return OmegaNum.fromJSON(input);
    -- }
    local x = Big:new({0})
    -- if (!isOmegaNum.test(input)){
    --   console.warn(omegaNumError+"Malformed input: "+input);
    --   x.array=[NaN];
    --   return x;
    -- }
    local negateIt = false
    while ((string.sub(input, 1, 1)=="-") or (string.sub(input, 1, 1)=="+")) do
        if (string.sub(input, 1, 1)=="-") then
            negateIt = not negateIt
        end
        input = string.sub(input, 2);
    end
    if (input=="NaN") or (input=="nan") then
        x.array = {R.NaN}
    elseif (input=="Infinity") or (input=="inf") then
        x.array = {R.POSITIVE_INFINITY}
    else
        local a = 0
        local b = 0
        local c = 0
        local d = 0
        local i = 0
        while (string.len(input) > 0) do
            local passTest = false
            if true then
                local j = 1
                if string.sub(input, 1, 1) == "(" then
                    j = j + 1
                end
                if (string.sub(input, j, j+1) == "10") and ((string.sub(input, j+2, j+2) == "^") or (string.sub(input, j+2, j+2) == "{")) then
                    passTest = true
                end
            end
            if (passTest) then
                if (string.sub(input, 1, 1) == "(") then
                input = string.sub(input, 2);
                end
                local arrows = -1;
                if (string.sub(input, 3, 3)=="^") then
                    arrows = 3
                    while (string.sub(input, arrows, arrows) == "^") do
                        arrows = arrows + 1
                    end
                    arrows = arrows - 3
                    a = arrows
                    b = arrows + 2;
                else
                    a = 1
                    while (string.sub(input, a, a) ~= "}") do
                        a = a + 1
                    end
                    arrows=tonumber(string.sub(input, 4, a - 1))+1;
                    b = a + 1
                end
                --[[if (arrows >= maxArrow) then
                -- console.warn("Number too large to reasonably handle it: tried to "+arrows.add(2)+"-ate.");
                    x.array = {R.POSITIVE_INFINITY};
                    break;
                end--]]
                input = string.sub(input, b + 1);
                if (string.sub(input, 1, 1) == ")") then
                    a = 1
                    while (string.sub(input, a, a) ~= " ") do
                        a = a + 1
                    end
                    c = tonumber(string.sub(input, 3, a - 1));
                    input = string.sub(input, a+1);
                else
                    c = 1
                end
                if (arrows==1) then
                    x.array[2] = (x.array[2] or 0) + c;
                elseif (arrows==2) then
                    a = x.array[2] or 0;
                    b = x.array[1] or 0;
                    if (b>=1e10) then
                        a = a + 1
                    end
                    if (b>=10) then
                        a = a + 1
                    end
                    x.array[1]=a;
                    x.array[2]=0;
                    x.array[3]=(x.array[3] or 0)+c;
                else
                    a=x.array[arrows] or 0;
                    b=x.array[arrows-1] or 0;
                    if (b>=10) then
                        a = a + 1
                    end
                    for i=1, arrows do
                        x.array[i] = 0;
                    end
                    x.array[1]=a;
                    x.array[arrows+1] = (x.array[arrows+1] or 0) + c;
                end
            else
                break
            end
        end
        a = {""}
        while (string.len(input) > 0) do
            if ((string.sub(input, 1, 1) == "e") or (string.sub(input, 1, 1) == "E")) then
                a[#a + 1] = ""
            else
                a[#a] = a[#a] .. string.sub(input, 1, 1)
            end
            input = string.sub(input, 2);
        end
        if a[#a] == "" then
            a[#a] = nil
        end
        b={x.array[1],0};
        c=1;
        for i=#a, 1, -1 do
            if ((b[1] < MAX_E) and (b[2]==0)) then
                b[1] = math.pow(10,c*b[1]);
            elseif (c==-1) then
                if (b[2]==0) then
                    b[1]=math.pow(10,c*b[1]);
                elseif ((b[2]==1) and (b[1]<=308)) then
                    b[1] = math.pow(10,c*math.pow(10,b[1]));
                else
                    b[1]=0;
                end
                b[2]=0;
            else
                b[2] = b[2] + 1;
            end
            local decimalPointPos = 1;
            while ((string.sub(a[i], decimalPointPos, decimalPointPos) ~= ".") and (decimalPointPos <= #a[i])) do
                decimalPointPos = decimalPointPos + 1
            end
            if decimalPointPos == #a[i] + 1 then
                decimalPointPos = -1
            end
            local intPartLen = -1
            if (decimalPointPos == -1) then
                intPartLen = #a[i] + 1
            else
                intPartLen = decimalPointPos
            end
            if (b[2] == 0) then
                if (intPartLen - 1 >= LONG_STRING_MIN_LENGTH) then
                    b[1] = math.log10(b[1]) + log10LongString(string.sub(a[i], 1, intPartLen - 1))
                    b[2] = 1;
                elseif ((a[i] ~= nil) and (a[i] ~= "") and (tonumber(a[i]) ~= nil)) then
                    b[1] = b[1] * tonumber(a[i]);
                end
            else
                d=-1
                if (intPartLen - 1 >= LONG_STRING_MIN_LENGTH) then
                    d = log10LongString(string.sub(a[i], 1,intPartLen - 1))
                else
                    if (a[i] ~= nil) and (a[i] ~= "") and (tonumber(a[i]) ~= nil) then
                        d = math.log(tonumber(a[i]), 10)
                    else
                        d = 0
                    end
                end
                if (b[2]==1) then
                    b[1] = b[1] + d;
                elseif ((b[2]==2) and (b[1]<MAX_E+math.log(d, 10))) then
                    b[1] = b[1] + math.log(1+math.pow(10,Math.log10(d)-b[0]), 10);
                end
            end
            if ((b[1]<MAX_E) and (b[2] ~= 0) and (b[2] ~= nil)) then
                b[1]=math.pow(10,b[1]);
                b[2] = b[2] - 1;
            elseif (b[1]>MAX_SAFE_INTEGER) then
                b[1] = math.log(b[1], 10);
                b[2] = b[2] + 1;
            end
        end
        x.array[1]= b[1];
        x.array[2]= (x.array[2] or 0) + b[2];
    end
    if (negateIt) then
        x.sign = x.sign * -1
    end
    x:normalize();
    return x;
end

function Big:to_number()
    -- //console.log(this.array);
    if (self.sign==-1) then
        return -1*(self:neg():to_number());
    end
    if not self.array[1] then return 0 end
    if self.array[2] == nil then self.array[2] = 0 end
    if ((self:arraySize()>=2) and ((self.array[2]>=2) or (self.array[2]==1) and (self.array[1]>308))) then
        return R.POSITIVE_INFINITY;
    end
    if (self:arraySize() >= 3) and ((self.array[1] >= 3) or (self.array[2] >= 1) or (self.array[3] >= 1)) then
        return R.POSITIVE_INFINITY;
    end
    if (self:arraySize() >= 4) and ((self.array[1] > 1) or (self.array[2] >= 1) or (self.array[3] >= 1)) then
        for i, v in pairs(self.array) do
            if self.array[i] > 0 and i > 4 then
                return R.POSITIVE_INFINITY;
            end
        end
    end
    if (type(self.array[1]) == "table") then
        self.array[1] = self.array[1]:to_number()
    end
    if (self.array[2]==1) then
        return math.pow(10,self.array[1]);
    end
    return self.array[1];
end

function Big:floor()
    if (self:isint(true)) then
        return self:clone()
    end
    return Big:create(math.floor(self:to_number()));
end

function Big:ceil()
    if (self:isint()) then
        return self:clone()
    end
    return Big:create(math.ceil(self:to_number()));
end

function Big:clone()
    local newArr = {}
    for i, j in pairs(self.array) do
        newArr[i] = j
    end
    local result = Big:new(newArr)
    result.sign = self.sign
    return result
end

function Big:create(input)
    if ((type(input) == "number")) then
        return Big:new({input})
    elseif ((type(input) == "string")) then
        return Big:parse(input)
    elseif ((type(input) == "table") and getmetatable(input) == OmegaMeta) then
        return input:clone()
    else
        return Big:new(input)
    end
end

function Big:ensureBig(input)
    if ((type(input) == "table") and getmetatable(input) == OmegaMeta) then
        return input
    else
        return Big:create(input)
    end
end

function Big:add(other)
    local x = self
    other = Big:ensureBig(other)
    -- if (OmegaNum.debug>=OmegaNum.NORMAL){
    --   console.log(this+"+"+other);
    --   if (!debugMessageSent) console.warn(omegaNumError+"Debug output via 'debug' is being deprecated and will be removed in the future!"),debugMessageSent=true;
    -- }
    if (x.sign==-1) then
        return x:neg():add(other:neg()):neg()
    end
    if (other.sign==-1) then
        return x:sub(other:neg());
    end
    if (x:eq(B.ZERO)) then
        return other:clone();
    end
    if (other:eq(B.ZERO)) then
        return x:clone();
    end
    if (x:isNaN() or other:isNaN() or (x:isInfinite() and other:isInfinite() and x:eq(other:neg()))) then
        return Big:create(B.NaN);
    end
    if (x:isInfinite()) then
        return x:clone();
    end
    if (other:isInfinite()) then
        return other:clone();
    end
    local p=x:min(other);
    local q=x:max(other);
    local t = -1;
    if (p.array[2] == 2) and not p:gt(B.E_MAX_SAFE_INTEGER) then
        p.array[2] = 1
        p.array[1] = 10 ^ p.array[1]
    end
    if (q.array[2] == 2) and not q:gt(B.E_MAX_SAFE_INTEGER) then
        q.array[2] = 1
        q.array[1] = 10 ^ q.array[1]
    end
    if (q:gt(B.E_MAX_SAFE_INTEGER) or q:div(p):gt(B.MAX_SAFE_INTEGER)) then
        t = q;
    elseif (q.array[2] == nil) or (q.array[2] == 0) then
        t= Big:create(x:to_number()+other:to_number());
    elseif (q.array[2]==1) then
        if (p.array[2] ~= nil) and (p.array[2] ~= 0) then
            a = p.array[1]
        else
            a = math.log(p.array[1], 10)
        end
        t = Big:new({a+math.log(math.pow(10,q.array[1]-a)+1, 10),1});
    end
    p = nil
    q = nil
    return t;
end

function Big:sub(other)
    local x = self
    other = Big:ensureBig(other)
    -- if (OmegaNum.debug>=OmegaNum.NORMAL) console.log(x+"-"+other);
    if (x.sign==-1) then
        return x:neg():sub(other:neg()):neg()
    end
    if (other.sign==-1) then
        return x:add(other:neg())
    end
    if (x:eq(other)) then
        return Big:create(B.ZERO)
    end
    if (other:eq(B.ZERO)) then
        return x:clone();
    end
    if (x:isNaN() or other:isNaN() or (x:isInfinite() and other:isInfinite() and x:eq(other:neg()))) then
        return Big:create(B.NaN)
    end
    if (x:isInfinite()) then
        return x:clone()
    end
    if (other:isInfinite()) then
        return other:neg()
    end
    local p = x:min(other);
    local q = x:max(other);
    local n = other:gt(x);
    local t = -1;
    if (p.array[2] == 2) and not p:gt(B.E_MAX_SAFE_INTEGER) then
        p.array[2] = 1
        p.array[1] = 10 ^ p.array[1]
    end
    if (q.array[2] == 2) and not q:gt(B.E_MAX_SAFE_INTEGER) then
        q.array[2] = 1
        q.array[1] = 10 ^ q.array[1]
    end
    if (q:gt(B.E_MAX_SAFE_INTEGER) or q:div(p):gt(B.MAX_SAFE_INTEGER)) then
        t = q;
        if n then
            t = t:neg()
        else
            t = t
        end
    elseif (q.array[2] == nil) or (q.array[2] == 0) then
        t = Big:create(x:to_number()-other:to_number());
    elseif (q.array[2]==1) then
        if (p.array[2] ~= nil) and (p.array[2] ~= 0) then
            a = p.array[1]
        else
            a = math.log(p.array[1], 10)
        end

        t = Big:new({a+math.log(math.pow(10,q.array[1]-a)-1, 10),1});
        if n then
            t = t:neg()
        else
            t = t
        end
    end
    p = nil
    q = nil
    return t;
end

function Big:div(other)
    local x = self;
    other = Big:ensureBig(other);
    if (x.sign*other.sign==-1) then
        return x:abs():div(other:abs()):neg()
    end
    if (x.sign==-1) then
        return x:abs():div(other:abs())
    end
    if (x:isNaN() or other:isNaN() or (x:isInfinite() and other:isInfinite() and x:eq(other:neg()))) then
        return Big:create(B.NaN)
    end
    if (other:eq(B.ZERO)) then
        return Big:create(B.POSITIVE_INFINITY)
    end
    if (other:eq(B.ONE)) then
        return x:clone()
    end
    if (x:eq(other)) then
        return Big:create(B.ONE)
    end
    if (x:isInfinite()) then
        return x:clone()
    end
    if (other:isInfinite()) then
        return Big:create(B.ZERO)
    end
    if (x:max(other):gt(B.EE_MAX_SAFE_INTEGER)) then
        if x:gt(other) then
            return x:clone()
        else
            return Big:create(B.ZERO)
        end
    end
    local n = x:to_number()/other:to_number();
    if (n<=MAX_SAFE_INTEGER) then
        return Big:create(n)
    end
    local pw = Big:create(10):pow(x:log10():sub(other:log10()))
    local fp = pw:floor()
    if (pw:sub(fp):lt(Big:create(1e-9))) then
        return fp
    end
    return pw
end

function Big:mul(other)
    local x = Big:ensureBig(self);
    other = Big:ensureBig(other);
    -- if (OmegaNum.debug>=OmegaNum.NORMAL) console.log(x+"*"+other);
    if (x.sign*other.sign==-1) then
        return x:abs():mul(other:abs()):neg()
    end
    if (x.sign==-1) then
        return x:abs():mul(other:abs())
    end
    if (x:isNaN() or other:isNaN() or (x:isInfinite() and other:isInfinite() and x:eq(other:neg()))) then
        return Big:create(B.NaN)
    end
    if (other:eq(B.ZERO)) or x:eq(B.ZERO) then
        return Big:create(B.ZERO)
    end
    if (other:eq(B.ONE)) then
        return x:clone()
    end
    if (x:eq(B.ONE)) then
        return other:clone()
    end
    if (x:isInfinite()) then
        return x:clone()
    end
    if (other:isInfinite()) then
        return other:clone()
    end
    if (x:max(other):gt(B.EE_MAX_SAFE_INTEGER)) then
        return x:max(other)
    end
    local n = x:to_number()*other:to_number()
    if (n<=MAX_SAFE_INTEGER) then
        return Big:create(n)
    end
    return Big:create(10):pow(x:log10():add(other:log10()));
end

function Big:rec()
    if (self:isNaN() or self:eq(B.ZERO)) then
        return Big:create(B.NaN)
    end
    if (self:abs():gt("2e323")) then
        return Big:create(B.ZERO)
    end
    return Big:create(B.ONE):div(self)
end

function Big:logBase(base)
    if base == nil then
        base = Big:create(B.E)
    end
    return self:log10():div(base:log10())
end

function Big:log10()
    local x = self;
    -- if (OmegaNum.debug>=OmegaNum.NORMAL) console.log("log"+this);
    if (x:lt(B.ZERO)) then
        return Big:create(B.NaN)
    end
    if (x:eq(B.ZERO)) then
        return Big:create(B.NEGATIVE_INFINITY)
    end
    if (x:lte(B.MAX_SAFE_INTEGER)) then
        return Big:create(math.log(x:to_number(), 10))
    end
    if (not x:isFinite()) then
        return x:clone();
    end
    if (x:gt(B.TETRATED_MAX_SAFE_INTEGER)) then
        return x:clone();
    end
    x = x:clone()
    x.array[2] = (x.array[2] or 0) - 1;
    return x:normalize()
end

function Big:ln()
    base = Big:create(B.E)
    return self:log10():div(base:log10())
end

function Big:pow(other)
    other = Big:ensureBig(other);
    -- if (OmegaNum.debug>=OmegaNum.NORMAL) console.log(this+"^"+other);
    if (other:eq(B.ZERO)) then
        return Big:create(B.ONE)
    end
    if (other:eq(B.ONE)) then
        return self:clone()
    end
    if (other:lt(B.ZERO)) then
        return self:pow(other:neg()):rec()
    end
    if (self:lt(B.ZERO) and other:isint()) then
        if (other:mod(2):lt(B.ONE)) then
            return self:abs():pow(other)
        end
        return self:abs():pow(other):neg()
    end
    if (self:lt(B.ZERO)) then
        --return Big:create(B.NaN)
        --Override this interaction to always make positive numbers
        return self:abs():pow(other)
    end
    if (self:eq(B.ONE)) then
        return Big:create(B.ONE)
    end
    if (self:eq(B.ZERO)) then
        return Big:create(B.ZERO)
    end
    if (self:max(other):gt(B.TETRATED_MAX_SAFE_INTEGER)) then
        return self:max(other);
    end
    if (self:eq(10)) then
        if (other:gt(B.ZERO)) then
            other = other:clone();
            other.array[2] = (other.array[2] or 0) + 1;
            other:normalize();
            return other;
        else
            return Big:create(math.pow(10,other:to_number()));
        end
    end
    if (other:lt(B.ONE)) then
        return self:root(other:rec())
    end
    local n = math.pow(self:to_number(),other:to_number())
    if (n<=MAX_SAFE_INTEGER) then
        return Big:create(n);
    end
    return Big:create(10):pow(self:log10():mul(other));
end

function Big:exp()
    return Big:create(B.E, self)
end

function Big:root(other)
    other = Big:ensureBig(other)
    -- if (OmegaNum.debug>=OmegaNum.NORMAL) console.log(this+"root"+other);
    if (other:eq(B.ONE)) then
        return self:clone()
    end
    if (other:lt(B.ZERO)) then
        return self:root(other:neg()):rec()
    end
    if (other:lt(B.ONE)) then
        return self:pow(other:rec())
    end
    if (self:lt(B.ZERO) and other:isint() and other:mod(2):eq(B.ONE)) then
        return self:neg():root(other):neg()
    end
    if (self:lt(B.ZERO)) then
        return Big:create(B.NaN)
    end
    if (self:eq(B.ONE)) then
        return Big:create(B.ONE)
    end
    if (self:eq(B.ZERO)) then
        return Big:create(B.ZERO)
    end
    if (self:max(other):gt(B.TETRATED_MAX_SAFE_INTEGER)) then
        if self:gt(other) then
            return self:clone()
        else
            Big:create(B.ZERO)
        end
    end
    return Big:create(10):pow(self:log10():div(other));
end

function Big:slog(base)
    if base == nil then
        base = 10
    end
    local x = self
    base = Big:ensureBig(base)
    if (x:isNaN() or base:isNaN() or (x:isInfinite() and base:isInfinite())) then
        return Big:create(B.NaN)
    end
    if (x:isInfinite()) then
        return x:clone();
    end
    if (base:isInfinite()) then
        return Big:create(B.ZERO)
    end
    if (x:lt(B.ZERO)) then
        return Big:create(-R.ONE)
    end
    if (x:lt(B.ONE)) then
        return Big:create(B.ZERO)
    end
    if (x:eq(base)) then
        return Big:create(B.ONE)
    end
    if (base:lt(math.exp(1/R.E))) then
        local a = base:tetrate(1/0)
        if (x:eq(a)) then
            return Big:create(B.POSITIVE_INFINITY)
        end
        if (x:gt(a)) then
            return Big:create(B.NaN)
        end
    end
    if (x:max(base):gt("10^^^" .. R.MAX_SAFE_INTEGER)) then
        if (x:gt(base)) then
            return x:clone();
        end
        return Big:create(B.ZERO)
    end
    if (x:max(base):gt(B.TETRATED_MAX_SAFE_INTEGER)) then
        if x:gt(base) then
            x = x:clone()
            x.array[3] = (x.array[3] or 0) - 1
            x:normalize()
            return x:sub(x.array[2])
        end
        return Big:create(B.ZERO)
    end
    x = x:clone()
    local r = 0
    local t = (x.array[2] or 0) - (base.array[2] or 0)
    if (t > 3) then
        local l = t - 3
        r = r + l
        x.array[2] = x.array[2] - l
    end
    for i = 0, 99 do
        if x:lt(B.ZERO) then
            x = base:pow(x)
            r = r - 1
        elseif (x:lte(B.ONE)) then
            return Big:create(r + x:to_number() - 1)
        else
            r = r + 1
            x = x:logBase(base)
        end
    end
    if (x:gt(10)) then
        return Big:create(r)
    end
end

function Big:tetrate(other)
    local t = self
    if other == 1 then return Big:create(self) end
    other = Big:ensureBig(other)
    local negln = nil
    if (t:isNaN() or other:isNaN()) then
        return Big:create(B.NaN)
    end
    if (other:isInfinite() and other.sign > 0) then
        negln = t:ln():neg()
        return negln:lambertw():div(negln)
    end
    if (other:lte(-2)) then
        return Big:create(B.NaN)
    end
    if (t:eq(B.ZERO)) then
        if (other:eq(B.ZERO)) then
            return Big:create(B.NaN)
        end
        if (other:mod(2):eq(B.ZERO)) then
            return Big:create(B.ZERO)
        end
        return Big:create(B.ONE)
    end
    if (t:eq(B.ONE)) then
        if (other:eq(-1)) then
            return Big:create(B.NaN)
        end
        return Big:create(B.ONE)
    end
    if (other:eq(-1)) then
        return Big:create(B.ZERO)
    end
    if other:eq(B.ZERO) then
        return Big:create(B.ONE)
    end
    if other:eq(B.ONE) then
        return t:clone()
    end
    if other:eq(2) then
        return t:pow(t)
    end
    if t:eq(2) then
        if other:eq(3) then
            return Big:create({16})
        end
        if other:eq(4) then
            return Big:create({65536})
        end
    end
    local m = t:max(other)
    if (m:gt(Big:create("10^^^" .. tostring(R.MAX_SAFE_INTEGER)))) then
        return m
    end
    if (m:gt(B.TETRATED_MAX_SAFE_INTEGER) or other:gt(R.MAX_SAFE_INTEGER)) then
        if (t:lt(math.exp(1/R.E))) then
            negln = t:ln():neg()
            return negln:lambertw():div(negln)
        end
        local j = t:slog(10):add(other)
        j.array[3]=(j.array[3] or 0) + 1
        j:normalize()
        return j
    end
    local y = other:to_number()
    local f = math.floor(y)
    local r = t:pow(y-f)
    local l = B.NaN
    local i = 0
    local m = B.E_MAX_SAFE_INTEGER
    while ((f ~= 0) and r:lt(m) and (i < 100)) do
        if (f > 0) then
            r = t:pow(r)
            if (l:eq(r)) then
                f = 0
                break
            end
            l = r
            f = f - 1
        else
            r = r:logBase(t)
            if (l:eq(r)) then
                f = 0
                break
            end
            l = r
            f = f + 1
        end
    end
    if ((i == 100) or t:lt(math.exp(1/R.E))) then
        f = 0
    end
    r.array[2] = (r.array[2] or 0) + f
    r:normalize()
    return r;
end

function Big:max_for_op(arrows)
    if type(arrows) == "table" then
        arrows = arrows:to_number()
    end
    if arrows < 1 or arrows ~= arrows or arrows == R.POSITIVE_INFINITY then
        return B.NaN:clone()
    end
    if arrows == 1 then
        return B.E_MAX_SAFE_INTEGER:clone()
    end
    if arrows == 2 then
        return B.TETRATED_MAX_SAFE_INTEGER:clone()
    end

    local arr = {}
    arr[1] = 10e9
    arr[arrows] = R.MAX_SAFE_INTEGER - 2
    for i = 2, math.min(arrows - 1, 1e6) do
        arr[i] = 8
    end
    if arrows > 1e6 then
        local limit = math.floor(math.log(arrows, 10))
        for i = 6, limit do
            arr[10^i] = 8
        end 
    end
    arr[arrows - 1] = 8

    local res = Big:new({0})
    res.array = arr
    return res
end

function Big:arrow(arrows, other)
    local t = self:clone()
    local oldarrows = Big:ensureBig(arrows)
    if oldarrows > Big:ensureBig(1e308) then --if too big return infinity
        return Big:create(R.POSITIVE_INFINITY)
    end
    oldarrows = oldarrows:to_number()
    arrows = Big:ensureBig(arrows)
    if oldarrows >= 1e6 then --needed to stop "Infinity"
        arrows = arrows:floor()
    end
    if oldarrows == 1 then
        return Big:create(self)
    end
    if self:eq(B.ONE) then return 1 end
    if self:eq(B.ZERO) then return 0 end
    --idk why but arrows above 1e6 just sometimes randomly get treated as non ints even though they are
    --this is technically inaccurate now but i think 1e7 +0.1 counting as an integer amount of arrow here is fine
    if (not arrows:isint() or arrows:lt(B.ZERO)) and arrows:lt(1e6) then
        return Big:create(B.NaN)
    end
    if type(oldarrows) == "number" and oldarrows ~= math.floor(oldarrows) and oldarrows < 1e6 then
        return Big:create(B.NaN)
    end
    if arrows:eq(B.ZERO) then
        return t:mul(other)
    end
    if arrows:eq(B.ONE) or oldarrows == 1 then
        return t ^ other--t:pow(other) idk why this was causing issues but it was so now theres this
    end
    if arrows:eq(2) or oldarrows == 2 then
        return t:tetrate(other)
    end
    other = Big:create(other)
    if (other:lt(B.ZERO)) then
        return Big:create(B.NaN)
    end
    if (other:eq(B.ZERO)) then
        return Big:create(B.ONE)
    end
    if (other:eq(B.ONE)) then
        return t:clone()
    end
    if self:eq(2) and other:eq(2) then
        -- handle infinite arrows
        if arrows:isInfinite() then return Big:create(R.POSITIVE_INFINITY) end

        return Big:create(4)
    end
    --[[if (arrows:gte(maxArrow)) then
        return Big:create(B.POSITIVE_INFINITY)
    end--]]

    --remove potential error from before
    local arrowsNum = math.floor(oldarrows)
    if (other:eq(2)) then
        return t:arrow(arrowsNum - 1, t)
    end
    local limit_plus = Big:max_for_op(arrowsNum+1)
    local limit = Big:max_for_op(arrowsNum)
    local limit_minus = Big:max_for_op(arrowsNum-1)
    if (t:max(other):gt(limit_plus)) then
        return t:max(other)
    end
    local r = nil
    -- if arrows >= Big:ensureBig(3500) then
    --     if self:arrow(100, self) > other then
    --         return Big:new({
    --             [1] = math.min(to_number(math.log(other, 10)), 1e300),
    --             [2] = 0,
    --             [3] = 0,
    --             [to_number(arrows)+1] = 1
    --         })
    --     end
    -- end
    if (t:gt(limit) or other:gt(B.MAX_SAFE_INTEGER)) or arrows >= Big:ensureBig(350) then --just kinda chosen randomly
        if (t:gt(limit)) then
            r = t:clone()
            r.array[arrowsNum + 1] = r.array[arrowsNum + 1] - 1
            if arrowsNum < 25000 then --arbitrary, normalisation is just extra steps when you get high enough
                r:normalize()
            end
        elseif (t:gt(limit_minus)) then
            r = Big:create(t.array[arrowsNum])
        else
            r = Big:create(B.ZERO)
        end
        local j = r:add(other)
        j.array[arrowsNum+1] = (j.array[arrowsNum+1] or 0) + 1
        j:normalize()
        return j
    end
    local y = other:to_number()
    local f = math.floor(y)
    local arrows_m1 = arrows:sub(B.ONE)
    local i = 0
    local m = limit_minus
    r = t:arrow(arrows_m1, y-f)
    while (f ~= 0) and r:lt(m) and (i<100) do
        if (f > 0) then
            r = t:arrow(arrows_m1, r)
            f = f - 1
        end
        i = i + 1
    end
    if (i == 100) then
        f = 0
    end
    r.array[arrowsNum] = (r.array[arrowsNum] or 0) + f
    r:normalize()
    return r
end

function Big:mod(other)
    other = Big:ensureBig(other)
    if (other:eq(B.ZERO)) then
        Big:create(B.ZERO)
    end
    if (self.sign*other.sign == -1) then
        return self:abs():mod(other:abs()):neg()
    end
    if (self.sign==-1) then
        return self:abs():mod(other:abs())
    end
    return self:sub(self:div(other):floor():mul(other))
end

function Big:lambertw()
    local x = self
    if (x:isNaN()) then
        return x:clone();
    end
    if (x:lt(-0.3678794411710499)) then
        print("lambertw is unimplemented for results less than -1, sorry!")
        local a = nil
        return a.b
    end
    if (x:gt(B.TETRATED_MAX_SAFE_INTEGER)) then
        return x:clone();
    end
    if (x:gt(B.EE_MAX_SAFE_INTEGER)) then
        x.array[1] = x.array[1] - 1
        return x:clone();
    end
    if (x:gt(B.E_MAX_SAFE_INTEGER)) then
        return Big:d_lambertw(x)
    else
        return Big:create(Big:f_lambertw(x.sign*x.array[1]))
    end
end

function Big:f_lambertw(z)
    local tol = 1e-10
    local w = nil
    local wn = nil
    local OMEGA = 0.56714329040978387299997
    if (not Big:ensureBig(z):isFinite()) then
        return z;
    end
    if z == 0 then
        return z;
    end
    if z == 1 then
        return OMEGA
    end
    if (z < 10) then
        w = 0
    else
        w = math.log(z) - math.log(math.log(z))
    end
    for i=0,99 do
        wn = (z*math.exp(-w)+w*w)/(w+1)
        if (math.abs(wn-w)<tol*math.abs(wn)) then
            return wn
        end
        w=wn
    end
    print("Iteration failed to converge: "+z)
    local a = nil
    return a.b
end

function Big:d_lambertw(z)
    local tol = 1e-10
    z = Big:ensureBig(z)
    local w = nil
    local ew = nil
    local wewz = nil
    local wn = nil
    local OMEGA = 0.56714329040978387299997
    if (not z:isFinite()) then
        return z:clone();
    end
    if (z == 0) then
        return z:clone();
    end
    if (z == 1) then
        return OMEGA
    end
    w = z:ln()
    for i=0, 99 do
        ew = w:neg():exp()
        wewz = w:sub(z:mul(ew))
        wn = w:sub(wewz:div(w:add(B.ONE):sub((w:add(2)):mul(wewz):div((w:mul(2):add(2))))))
        if (wn:sub(w):abs():lt(wn:abs():mul(tol))) then
            return wn
        end
        w = wn
    end
    print("Iteration failed to converge: "+z)
    local a = nil
    return a.b
end

------------------------metastuff----------------------------

function OmegaMeta.__add(b1, b2)
    if type(b1) == "number" then
        return Big:create(b1):add(b2)
    end
    return b1:add(b2)
end

function OmegaMeta.__sub(b1, b2)
    if type(b1) == "number" then
        return Big:create(b1):sub(b2)
    end
    return b1:sub(b2)
end

function OmegaMeta.__mul(b1, b2)
    if type(b1) == "number" then
        return Big:create(b1):mul(b2)
    end
    return b1:mul(b2)
end

function OmegaMeta.__div(b1, b2)
    if type(b1) == "number" then
        return Big:create(b1):div(b2)
    end
    return b1:div(b2)
end
function OmegaMeta.__mod(b1, b2)
    if type(b1) == "number" then
        return Big:create(b1):mod(b2)
    end
    return b1:mod(b2)
end

function OmegaMeta.__unm(b)
    return b:neg()
end

function OmegaMeta.__pow(b1, b2)
    if type(b1) == "number" then
        return Big:ensureBig(b1):pow(b2)
    end
    return b1:pow(b2)
end

function OmegaMeta.__le(b1, b2)
    b1 = Big:ensureBig(b1)
    return b1:lte(b2)
end

function OmegaMeta.__lt(b1, b2)
    b1 = Big:ensureBig(b1)
    return b1:lt(b2)
end

function OmegaMeta.__ge(b1, b2)
    b1 = Big:ensureBig(b1)
    return b1:gte(b2)
end

function OmegaMeta.__gt(b1, b2)
    b1 = Big:ensureBig(b1)
    return b1:gt(b2)
end

function OmegaMeta.__eq(b1, b2)
    b1 = Big:ensureBig(b1)
    return b1:eq(b2)
end

function OmegaMeta.__tostring(b)
    return number_format(b)
end

function OmegaMeta.__concat(a, b)
    a = Big:ensureBig(a)
    return tostring(a) .. tostring(b)
end


---------------------------------------

for i,v in pairs(R) do
    B[i] = Big:ensureBig(v)
end

return Big


