├── .gitignore ├── BigDecimal.lua ├── BigInteger.lua ├── Complex.lua ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Lua sources 2 | luac.out 3 | 4 | # luarocks build files 5 | *.src.rock 6 | *.zip 7 | *.tar.gz 8 | 9 | # Object files 10 | *.o 11 | *.os 12 | *.ko 13 | *.obj 14 | *.elf 15 | 16 | # Precompiled Headers 17 | *.gch 18 | *.pch 19 | 20 | # Libraries 21 | *.lib 22 | *.a 23 | *.la 24 | *.lo 25 | *.def 26 | *.exp 27 | 28 | # Shared objects (inc. Windows DLLs) 29 | *.dll 30 | *.so 31 | *.so.* 32 | *.dylib 33 | 34 | # Executables 35 | *.exe 36 | *.out 37 | *.app 38 | *.i*86 39 | *.x86_64 40 | *.hex 41 | 42 | -------------------------------------------------------------------------------- /BigDecimal.lua: -------------------------------------------------------------------------------- 1 | --[=[ 2 | Version 1.0.0a3 3 | This is intended for Roblox ModuleScripts 4 | BSD 2-Clause Licence 5 | Copyright ©, 2020 - Blockzez (devforum.roblox.com/u/Blockzez and github.com/Blockzez) 6 | All rights reserved. 7 | 8 | Redistribution and use in source and binary forms, with or without 9 | modification, are permitted provided that the following conditions are met: 10 | 11 | 1. Redistributions of source code must retain the above copyright notice, this 12 | list of conditions and the following disclaimer. 13 | 14 | 2. Redistributions in binary form must reproduce the above copyright notice, 15 | this list of conditions and the following disclaimer in the documentation 16 | and/or other materials provided with the distribution. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | ]=]-- 29 | -- The BigInt module is required 30 | local bigint = require(script.Parent:WaitForChild("BigInteger")); 31 | 32 | -- Lua 5.3 33 | local function math_type(x) 34 | if math.type then 35 | return math.type(x); 36 | end; 37 | return 'float'; 38 | end; 39 | 40 | local function scale_val(val, exp) 41 | val = ('0'):rep(-exp) .. val .. ('0'):rep(exp); 42 | local unscaled = (val:gsub("[.,]", '')); 43 | local len = #val; 44 | local dpos = (val:find("[.,]") or (len + 1)) + exp; 45 | return unscaled:sub(1, dpos - 1) .. '.' .. unscaled:sub(dpos); 46 | end; 47 | 48 | local function exp(val) 49 | local negt, intg, frac = val:gsub('^0+', ''):match('^(-?)([%d%a()]*)[.]?(%d*)$'); 50 | if intg:match('%D') then 51 | return val; 52 | end; 53 | if #intg == 0 then 54 | local fsize = #frac:gsub('[^0]*$', ''); 55 | return negt .. (frac:sub(fsize + 1, fsize + 1) == '' and '0' or frac:sub(fsize + 1, fsize + 1)) .. ('.' .. frac:sub(fsize + 2)):gsub('[.]0*$', '') .. ('E-' .. fsize + 1):gsub('-0+$', '0'); 56 | end; 57 | return negt .. (intg:sub(1, 1) == '' and '0' or intg:sub(1, 1)) .. ('.' .. intg:sub(2) .. frac):gsub('[.]0*$', '') .. 'E' .. (#intg - 1); 58 | end; 59 | 60 | local proxy = setmetatable({ }, { __mode = "k" }); 61 | local bd = { }; 62 | local current_context = 63 | { 64 | Precision = 27, 65 | MidpointRounding = 0, 66 | DivisionByZeroError = false, 67 | InvalidValueReturnsNaN = true, 68 | }; 69 | 70 | local function checkcontext(context, canbenil) 71 | if context == nil then 72 | return bd.GetContext(); 73 | end; 74 | if type(context) ~= "table" then 75 | error("The context argument are expected to be table, not " .. typeof(context), 2); 76 | end; 77 | if (type(context.Precision) ~= "number" or (context.Precision < 0) or (context.Precision % 1 ~= 0)) 78 | and ((not canbenil) or context.Precision ~= nil) then 79 | error("Precision property is invalid", 2); 80 | elseif ((not bd.MidpointRounding[context.Rounding]) and (type(context.Rounding) ~= "number" 81 | or (context.Rounding > 4 or context.Rounding < 0 or (context.Rounding % 1) ~= 0))) 82 | and ((not canbenil) or context.Rounding ~= nil) then 83 | error("MidpointRounding property is invalid", 2); 84 | elseif type(context.DivisionByZeroError) ~= "boolean" and ((not canbenil) or context.DivisionByZeroError ~= nil) then 85 | error("DivisionByZeroError property is invalid", 2); 86 | elseif type(context.InvalidValueReturnsNaN) ~= "boolean" and ((not canbenil) or context.InvalidValueReturnsNaN ~= nil) then 87 | error("InvalidValueReturnsNaN property is invalid", 2); 88 | end; 89 | return context; 90 | end; 91 | 92 | local function override_if_not_nil(value, value_to_override) 93 | if value == nil then 94 | return value_to_override; 95 | end; 96 | return value; 97 | end; 98 | 99 | function bd.SetContext(context) 100 | if context == nil then 101 | error("The context argument cannot be empty or nil", 2); 102 | end; 103 | checkcontext(context, true); 104 | current_context = 105 | { 106 | Precision = context.Precision or current_context.Precision, 107 | MidpointRounding = bd.MidpointRounding[context.MidpointRounding] or context.MidpointRounding or current_context.MidpointRounding, 108 | DivisionByZeroError = override_if_not_nil(context.DivisionByZeroError, current_context.DivisionByZeroError); 109 | InvalidValueReturnsNaN = override_if_not_nil(context.InvalidValueReturnsNaN, current_context.InvalidValueReturnsNaN); 110 | }; 111 | end; 112 | 113 | function bd.GetContext() 114 | return 115 | { 116 | Precision = current_context.Precision, 117 | MidpointRounding = current_context.MidpointRounding, 118 | DivisionByZeroError = current_context.DivisionByZeroError; 119 | InvalidValueReturnsNaN = current_context.InvalidValueReturnsNaN; 120 | }; 121 | end; 122 | 123 | bd.MidpointRounding = setmetatable( 124 | { }, 125 | { 126 | __index = 127 | { 128 | ToEven = 0, 129 | AwayFromZero = 1, 130 | ToZero = 2, 131 | ToNegativeInfinity = 3, 132 | ToPositiveInfinity = 4, 133 | }; 134 | __newindex = function() 135 | error("Attempt to modify read-only table"); 136 | end; 137 | __metatable = "The metatable is locked"; 138 | } 139 | ); 140 | 141 | --[=[ Check ]=]-- 142 | local function check_bigdecimal(func, params, err) 143 | return function(...) 144 | local argc = select('#', ...); 145 | if argc < math.abs(params) then 146 | error(("missing argument #%d (BigDecimal expected)"):format(argc + 1)); 147 | end; 148 | local args = { }; 149 | for i = 1, math.abs(params) do 150 | local v = constructor(select(i, ...)); 151 | if v then 152 | args[i] = v; 153 | else 154 | if params == 2 then 155 | local arg0, arg1 = ...; 156 | arg0, arg1 = typeof(arg0), typeof(arg1); 157 | error((err:gsub("{0}", (arg0 == arg1) and arg0 or (arg0 .. ' and ' .. arg1))), 2); 158 | else 159 | error(((err or "invalid argument #{0} (BigDecimal expected, got {1})"):gsub("{0}", i):gsub("{1}", typeof(v))), 2); 160 | end; 161 | end; 162 | end; 163 | for i = params + 1, argc do 164 | args[i] = (select(i, ...)); 165 | end; 166 | return func(unpack(args)); 167 | end; 168 | end; 169 | local zero_bigint = bigint.new(0); 170 | local one_bigint = bigint.new(1); 171 | local ten_bigint = bigint.new(10); 172 | local function get_value_scale(self, other) 173 | if other then 174 | local s0, s1 = proxy[self].scale, proxy[other].scale; 175 | local v0, v1 = proxy[self].value, proxy[other].value; 176 | if type(s1) ~= "number" then 177 | error("This method doesn't support NaN or Infinite values", 3); 178 | end; 179 | if s0 < s1 then 180 | return v0 * (ten_bigint ^ (s1 - s0)), v1, s1, s1 - s0; 181 | elseif s1 < s0 then 182 | return v0, v1 * (ten_bigint ^ (s0 - s1)), s0, s0 - s1; 183 | end; 184 | return v0, v1, s0, 0; 185 | end; 186 | return proxy[self].value, proxy[self].scale; 187 | end; 188 | 189 | --[=[ Direct methods ]=]-- 190 | local function add(self, other) 191 | if self:IsNaN() or other:IsNaN() then 192 | return constructor('NaN'); 193 | elseif self:IsInfinity() and other:IsInfinity() then 194 | if self ~= other then 195 | return constructor('NaN'); 196 | end 197 | return constructor(self); 198 | elseif self:IsInfinity() then 199 | return constructor(self); 200 | elseif other:IsInfinity() then 201 | return constructor(other); 202 | end; 203 | local r0, r1, s = get_value_scale(self, other); 204 | return constructor(r0 + r1, s); 205 | end; 206 | local function unm(self) 207 | if self:IsNaN() then 208 | return constructor('NaN'); 209 | elseif self:IsInfinity() then 210 | return constructor(self:IsPositiveInfinity() and '-Infinity' or 'Infinity'); 211 | end; 212 | return constructor(-proxy[self].value, proxy[self].scale); 213 | end; 214 | local values = { }; 215 | local function mul(self, other) 216 | if self:IsNaN() or other:IsNaN() then 217 | return constructor('NaN'); 218 | elseif self:IsInfinity() then 219 | local sign = other:Sign(); 220 | if sign == 0 then 221 | return constructor('NaN'); 222 | end; 223 | return constructor((self:Sign() == sign) and 'Infinity' or '-Infinity'); 224 | end; 225 | return constructor(proxy[self].value * proxy[other].value, proxy[self].scale + proxy[other].scale); 226 | end; 227 | local function divrem(self, other) 228 | local r0, r1, s = get_value_scale(self, other); 229 | local ret, rem = r0:divrem(r1); 230 | return ret, rem / (ten_bigint ^ s); 231 | end; 232 | local function idiv(self, other) 233 | local r0, r1 = get_value_scale(self, other); 234 | return r0 / r1; 235 | end; 236 | local function rem(self, other) 237 | local r0, r1, s = get_value_scale(self, other); 238 | return constructor(r0 % r1, s); 239 | end; 240 | local function div(self, other, context) 241 | context = (type(context == "number") and context) 242 | or bd.GetContext(context); 243 | if self:IsNaN() or other:IsNaN() then 244 | return constructor('NaN'); 245 | elseif other == values.zero then 246 | if (type(context) == "number" and bd.GetContext()) or (type(context) == "table" and context.DivisionByZeroError) then 247 | error("Division by zero", 2); 248 | end; 249 | if self == values.zero then 250 | return constructor('NaN'); 251 | end; 252 | return constructor(self:Sign() and 'Infinity' or '-Infinity'); 253 | elseif self:IsInfinity() then 254 | return constructor((self:Sign() == other:Sign()) and '-Infinity' or 'Infinity'); 255 | end; 256 | local s0, s1 = proxy[self].scale, proxy[other].scale; 257 | local v0, v1 = proxy[self].value, proxy[other].value; 258 | local precision = type(context) == "number" or context.Precision; 259 | if s0 < precision then 260 | v0 = v0 * (ten_bigint ^ ((precision + 1) - s0)); 261 | elseif s0 > precision then 262 | v0 = v0 / (ten_bigint ^ (s0 - (precision + 1))); 263 | end; 264 | if s1 < (precision + 1) then 265 | local delta = ((precision + 1) - s1) - ((precision + 1) - s0); 266 | if delta < 0 then 267 | v1 = v1 / (ten_bigint ^ -delta); 268 | else 269 | v1 = v1 * (ten_bigint ^ delta); 270 | end; 271 | elseif s1 > precision then 272 | v1 = v1 / (ten_bigint ^ ((s1 - (precision + 1)) + ((precision + 1) - s0))); 273 | end; 274 | return constructor(v0 / v1, precision + 1):Quantize(precision); 275 | end; 276 | local function compare(self, other) 277 | if self:IsNaN() or other:IsNaN() then 278 | -- C# Double.Compare and Java's Compare consider NaN bigger than all values (including Positive Infinity) 279 | return 1; 280 | end; 281 | if self:IsPositiveInfinity() then 282 | return other:IsPositiveInfinity() and 0 or 1; 283 | elseif other:IsPositiveInfinity() then 284 | return -1; 285 | elseif self:IsNegativeInfinity() then 286 | return other:IsNegativeInfinity() and 0 or -1; 287 | elseif other:IsNegativeInfinity() then 288 | return 1; 289 | end; 290 | local r0, r1 = get_value_scale(self, other); 291 | return bigint.compare(r0, r1); 292 | end; 293 | local function quantize(self, places, midpoint_rounding) 294 | if (not tonumber(places)) and places ~= nil then 295 | error("invalid argument #2 (number expected, got " .. typeof(places) .. ')', 2); 296 | end; 297 | places = tonumber(places); 298 | if places and (places % 1 ~= 0) then 299 | error("invalid argument #2 (places out of range)", 2); 300 | end; 301 | midpoint_rounding = (type(midpoint_rounding ~= "table") and (bd.MidpointRounding[midpoint_rounding] or midpoint_rounding)) 302 | or bd.GetContext(midpoint_rounding).MidpointRounding; 303 | if self:IsNaN() then 304 | return constructor('NaN'); 305 | elseif self:IsInfinity() then 306 | return nil; 307 | elseif self == values.zero then 308 | return constructor(0); 309 | end; 310 | local r, increase = nil, 0; 311 | local v, s = get_value_scale(self); 312 | local delta = (places or 0) - s; 313 | if delta >= 0 then 314 | return self; 315 | end; 316 | local sign = v:sign(); 317 | v = v:abs(); 318 | local ten_delta = ten_bigint ^ -delta; 319 | local ret, rem = v:divrem(ten_delta); 320 | s = s + delta; 321 | if r ~= zero_bigint and midpoint_rounding ~= 3 then 322 | if midpoint_rounding == 4 then 323 | increase = 1; 324 | else 325 | local midpoint = ten_delta / 2; 326 | if rem > midpoint then 327 | increase = 1; 328 | elseif rem == midpoint then 329 | if midpoint_rounding == 1 then 330 | increase = 1; 331 | elseif (midpoint_rounding ~= 2) and (ret % 2 == one_bigint) then 332 | increase = 1; 333 | end; 334 | end; 335 | end; 336 | end; 337 | if not places then 338 | return (ret + increase) * sign; 339 | end; 340 | return constructor((ret + increase) * sign, s); 341 | end; 342 | local function tostr(self) 343 | if self:IsNaN() then 344 | return 'NaN'; 345 | elseif self:IsPositiveInfinity() then 346 | return 'Infinity'; 347 | elseif self:IsNegativeInfinity() then 348 | return '-Infinity'; 349 | end; 350 | local sign; 351 | local value, scale = get_value_scale(self); 352 | sign, value = tostring(value):match('(-?)(%d*)'); 353 | local val_delta = #value - scale; 354 | local r = (val_delta < 0 and ('.' .. ('0'):rep(-val_delta)) or (value:sub(1, val_delta) .. '.')) .. (value:sub(math.max(val_delta + 1, 0)):gsub('0+$', '')); 355 | 356 | r = sign .. r:gsub('^[.]', '0.'):gsub('[.]$', ''); 357 | return r; 358 | end; 359 | local function todouble(self) 360 | if self:IsNaN() then 361 | return tonumber('nan'); 362 | elseif self:IsInfinity() then 363 | return math.huge * (self:Sign()); 364 | end; 365 | return proxy[self].value:todouble() / (10 ^ proxy[self].scale); 366 | end; 367 | 368 | --[=[ Class methods ]=]-- 369 | setmetatable(bd, { 370 | __newindex = function(self, ind, func) 371 | rawset(self, ind, check_bigdecimal(func, 1)); 372 | end; 373 | }); 374 | 375 | function bd.IsNaN(self) 376 | return proxy[self].scale == 'NaN'; 377 | end; 378 | 379 | function bd.IsInfinity(self) 380 | return proxy[self].scale == 'Infinity'; 381 | end; 382 | 383 | function bd.IsPositiveInfinity(self) 384 | return proxy[self].scale == 'Infinity' and proxy[self].value; 385 | end; 386 | 387 | function bd.IsNegativeInfinity(self) 388 | return proxy[self].scale == 'Infinity' and (not proxy[self].value); 389 | end; 390 | 391 | function bd.Sign(self) 392 | if self:IsNaN() then 393 | return 0; 394 | elseif self:IsInfinity() then 395 | return self:IsPositiveInfinity() and 1 or -1; 396 | end; 397 | return proxy[self].value:sign(); 398 | end; 399 | 400 | rawset(bd, 'CopySign', check_bigdecimal(function(self, sign) 401 | if self:IsNaN() then 402 | return constructor('NaN'); 403 | elseif sign:IsNaN() then 404 | sign = values.zero; 405 | end; 406 | return constructor(proxy[self].value:copysign(sign:Sign()), proxy[self].scale); 407 | end, -2)); 408 | 409 | bd.Quantize = quantize; 410 | bd.Round = quantize; 411 | function bd.Floor(self) 412 | return quantize(self, 0, bd.MidpointRounding.ToNegativeInfinity); 413 | end; 414 | function bd.Ceiling(self) 415 | return quantize(self, 0, bd.MidpointRounding.ToPositiveInfinity); 416 | end; 417 | bd.Compare = compare; 418 | 419 | function bd.Abs(self) 420 | if self:IsNaN() then 421 | return 0; 422 | elseif self:IsInfinity() then 423 | constructor('Infinity'); 424 | end 425 | return constructor(proxy[self].value:abs(), proxy[self].scale); 426 | end; 427 | 428 | bd.Div = div; 429 | 430 | function bd.ToExpString(self) 431 | return exp(tostring(self)); 432 | end; 433 | 434 | --[=[ Metamethods ]=]-- 435 | local bigdecimal_mt = { __metatable = "The metatable is locked" }; 436 | bigdecimal_mt.__index = bd; 437 | function bigdecimal_mt.__newindex(self, index, value) 438 | if type(index) ~= "string" and type(index) ~= "number" then 439 | error("invalid argument #2 (string expected, got " .. typeof(index) .. ')', 2); 440 | end; 441 | error(index .. " cannot be assigned to", 2); 442 | end; 443 | bigdecimal_mt.__tostring = tostr; 444 | function bigdecimal_mt.__concat(left, right) 445 | return tostring(left) .. tostring(right); 446 | end; 447 | 448 | bigdecimal_mt.__add = check_bigdecimal(add, 2, "attempt to perform arithmetic (add) on {0}"); 449 | bigdecimal_mt.__unm = unm; 450 | bigdecimal_mt.__sub = check_bigdecimal(function(left, right) 451 | return add(left, unm(right)); 452 | end, 2, "attempt to perform arithmetic (sub) on {0}"); 453 | bigdecimal_mt.__mul = check_bigdecimal(mul, 2, "attempt to perform arithmetic (mul) on {0}"); 454 | bigdecimal_mt.__div = check_bigdecimal(function(left, right) return div(left, right); end, 2, "attempt to perform arithmetic (div) on {0}"); 455 | bigdecimal_mt.__mod = check_bigdecimal(function(left, right) return rem(left, right); end, 2, "attempt to perform arithmetic (rem) on {0}"); 456 | bigdecimal_mt.__pow = function() 457 | error("The power function isn't supported for BigDecimal", 2); 458 | end; 459 | 460 | bigdecimal_mt.__lt = check_bigdecimal(function(left, right) 461 | return compare(left, right) < 0; 462 | end, 2, "attempt to compare {0}"); 463 | bigdecimal_mt.__le = check_bigdecimal(function(left, right) 464 | return compare(left, right) <= 0; 465 | end, 2, "attempt to compare {0}"); 466 | bigdecimal_mt.__eq = check_bigdecimal(function(left, right) 467 | return compare(left, right) == 0; 468 | end, 2, "attempt to compare {0}"); 469 | 470 | -- 5.3 471 | bigdecimal_mt.__idiv = check_bigdecimal(idiv, 2, "attempt to perform arithmetic (idiv) on {0}"); 472 | 473 | --[=[ Constructor ]=]-- 474 | function constructor(...) 475 | local value, scale = ...; 476 | if select('#', ...) == 0 then 477 | error("missing argument #1", 2); 478 | elseif proxy[value] then 479 | scale = proxy[value].scale; 480 | value = proxy[value].value; 481 | else 482 | if scale ~= nil and not tonumber(scale) then 483 | error("invalid argument #2 (number expected, got " .. typeof(scale) .. ')', 2); 484 | elseif scale ~= nil and (scale % 1 ~= 0) then 485 | error("invalid argument #2 (scale out of range)", 2); 486 | end; 487 | if scale or (type(value) ~= "string" and type(value) ~= "number") then 488 | value = bigint.new(value); 489 | scale = scale and (scale < 0 and (#tostring(value) + scale) or scale) or 0; 490 | else 491 | if type(value) == "number" then 492 | -- Not completey accurate sadly as lua doesn't allow values over %.99f :( 493 | -- TODO Implement full double parser. 494 | value = math_type(value) == "float" and ("%.99f"):format(value) or tostring(value); 495 | else 496 | value = tostring(value); 497 | end; 498 | local negt, post = value:lower():match("([%+%-]?)(.*)"); 499 | if negt == '+' then 500 | negt = ''; 501 | end; 502 | if post == "infinity" or post == "inf" then 503 | scale = "Infinity"; 504 | value = negt == ''; 505 | else 506 | if post then 507 | local val, exp = post:match('(%d*[.,]?%d*)e([-+]?%d+)'); 508 | if val then 509 | exp = tonumber(exp); 510 | if not exp then 511 | return constructor('nil'); 512 | end; 513 | if val == '' then 514 | return constructor('nil'); 515 | end; 516 | post = scale_val((val:gsub(',', '.')), exp); 517 | end; 518 | post = (post:gsub("(%d?)[  _](%d?)", '%1%2')); 519 | post = post:match("(%d*[.,]?%d*)"); 520 | end; 521 | if post and post ~= '' then 522 | value = bigint.new(negt .. (post:gsub('[.,]', ''))); 523 | scale = #post - ((post:find('[.,]')) or #post); 524 | elseif current_context.InvalidValueReturnsNaN 525 | or post == "nan" then 526 | scale = 'NaN'; 527 | value = nil; 528 | else 529 | return nil; 530 | end; 531 | end; 532 | end; 533 | end; 534 | 535 | -- Lua 5.1 536 | if newproxy then 537 | local pointer = newproxy(true); 538 | proxy[pointer] = { value = value; scale = scale; }; 539 | 540 | local pointer_mt = getmetatable(pointer); 541 | for k, v in next, bigdecimal_mt do 542 | pointer_mt[k] = v; 543 | end; 544 | return pointer; 545 | end; 546 | -- Lua 5.2/5.3/5.4 547 | local mt = setmetatable({ }, bigdecimal_mt); 548 | proxy[mt] = { value = value, scale = scale }; 549 | return mt; 550 | end; 551 | 552 | values.zero = constructor(0); 553 | 554 | return setmetatable( 555 | { }, 556 | { 557 | __index = function(_, ind) 558 | if ind == "new" then 559 | return constructor; 560 | elseif ind == "NaN" then 561 | -- If rawequal(value0, value1) is true, the __eq metamethod will not run (applies to all Lua 5.1, 5.3 and 5.4) 562 | -- Don't know if I can bypass this 563 | return constructor('NaN'); 564 | elseif ind == "PositiveInfinity" then 565 | return constructor('+Inf'); 566 | elseif ind == "NegativeInfinity" then 567 | return constructor('-Infinity'); 568 | elseif not ind:match("to.*locale.*string") then 569 | return bd[ind]; 570 | end; 571 | return nil; 572 | end, 573 | __newindex = function() error("Attempt to modify a readonly table", 2); end, 574 | __metatable = "The metatable is locked" 575 | } 576 | ); 577 | -------------------------------------------------------------------------------- /BigInteger.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Version 2.0.0a2 3 | This is intended for Roblox ModuleScripts. 4 | It works on vanilla Lua, but there are far superior implementations you should use instead. 5 | BSD 2-Clause Licence 6 | Copyright ©, 2020 - Blockzez (devforum.roblox.com/u/Blockzez and github.com/Blockzez) 7 | All rights reserved. 8 | 9 | Redistribution and use in source and binary forms, with or without 10 | modification, are permitted provided that the following conditions are met: 11 | 12 | 1. Redistributions of source code must retain the above copyright notice, this 13 | list of conditions and the following disclaimer. 14 | 15 | 2. Redistributions in binary form must reproduce the above copyright notice, 16 | this list of conditions and the following disclaimer in the documentation 17 | and/or other materials provided with the distribution. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | ]]-- 30 | local weakkey = { __mode = 'k' }; 31 | local proxy = { bi = setmetatable({ }, weakkey), byte = setmetatable({ }, weakkey) }; 32 | 33 | -- Hash 34 | local weakval = { __mode = 'v', }; 35 | local hashes = { [false] = setmetatable({ }, weakval), [true] = setmetatable({ }, weakval) }; 36 | local strong_ref = { }; 37 | 38 | local bigint_mt = { __metatable = "The metatable is locked"; }; 39 | 40 | --[=[ Quick functions ]=]-- 41 | local function div(left, right) 42 | return math.floor(left / right); 43 | end; 44 | 45 | local function divmod(left, right) 46 | return div(left, right), left % right; 47 | end; 48 | 49 | local function intband(left, right) 50 | local p, ret = 1, 0; 51 | while left > 0 and right > 0 do 52 | local r0, r1 = left % 2, right % 2; 53 | if (r0 + r1) > 1 then 54 | ret = ret + p; 55 | end; 56 | left, right, p = (left - r0) / 2, (right - r1) / 2, p * 2; 57 | end; 58 | return ret; 59 | end; 60 | 61 | -- Lua 5.3 62 | local typeof = typeof or type; 63 | local function math_type(x) 64 | if math.type then 65 | return math.type(x); 66 | end; 67 | return 'float'; 68 | end; 69 | 70 | -- For Lua 5.3 users 71 | local is51 = ('%15d'):format(9007199254740992 + 1) == "9007199254740992"; 72 | local function newproxy_or_setmetatable(mt) 73 | -- It doesn't really matter which one 74 | -- If you prefer metatables you can replace it with "if mt ~= bigint_mt then" instead (if false doesn't work). 75 | if is51 and newproxy then 76 | local pointer = newproxy(true); 77 | local pointer_mt = getmetatable(pointer); 78 | for k, v in next, mt do 79 | pointer_mt[k] = v; 80 | end; 81 | return pointer; 82 | end; 83 | return setmetatable({ }, mt); 84 | end; 85 | 86 | --[=[ Hash ]=]-- 87 | local function gethash(bytes, sign) 88 | assert(type(sign) == "boolean", "sign must be either true or false"); 89 | local p0 = hashes[sign]; 90 | for _, v in ipairs(bytes) do 91 | if not p0[v] then 92 | p0[v] = setmetatable({ }, weakval); 93 | strong_ref[p0[v]] = true; 94 | end; 95 | p0 = p0[v]; 96 | end; 97 | return p0; 98 | end; 99 | 100 | --[=[ Byte handler ]=]-- 101 | --[==[ 102 | This is getting the byte of the value, in little endian order. 103 | Byte 54 is the sign, as the minimum safe integer is -9'007'199'254'740'991 and the maximum 104 | is 9'007'199'254'740'991 on Lua 5.1 105 | -3 = (0; 1; ...; 0) 106 | -1 = (0; ...; 0) 107 | 0 = (0; ...; 1) 108 | 1 = (1; ...; 1) 109 | 3 = (0; 1; ...; 1) 110 | I don't know why I haven't gone this method on version 1 111 | I might even consider strings or full 64 bit of double (if you're in Roblox Lua) for future versions! 112 | ]==]-- 113 | local function bytehandler_rawset(self, index, value) 114 | proxy.byte[self].value[index + 1] = value; 115 | return self; 116 | end; 117 | 118 | local function bytehandler_rawget(self, index) 119 | return proxy.byte[self].value[index + 1]; 120 | end; 121 | 122 | local function bytehandler_readonly(self) 123 | while proxy.byte[self].value[#proxy.byte[self].value] == -1 do 124 | table.remove(proxy.byte[self].value); 125 | end; 126 | proxy.byte[self].readonly = true; 127 | return self; 128 | end; 129 | 130 | local function bytehandler_gethash(self) 131 | return gethash(proxy.byte[self].value, self.sign); 132 | end; 133 | 134 | local function bytehandler_iter(self) 135 | return function(self, i) 136 | i = i + 1; 137 | if i >= #self then 138 | return nil; 139 | end; 140 | return i, self[i]; 141 | end, self, -1; 142 | end; 143 | 144 | local function bytehandler_increase(self) 145 | if proxy.byte[self].readonly then 146 | error("It is now readonly to ensure immutibility", 2); 147 | end; 148 | local p = -1; 149 | repeat 150 | p = p + 1; 151 | self[p] = (self[p] == 0) and 1 or 0; 152 | until self[p] == 1; 153 | return self; 154 | end; 155 | 156 | local function bytehandler_decrease(self) 157 | if proxy.byte[self].readonly then 158 | error("It is now readonly to ensure immutibility", 2); 159 | end; 160 | local p = -1; 161 | local len = #self; 162 | repeat 163 | if p >= len then 164 | error("Stack Underflow", 2); 165 | end; 166 | p = p + 1; 167 | self[p] = (self[p] == 0) and 1 or 0; 168 | until self[p] == 0; 169 | return self; 170 | end; 171 | 172 | local function bytehandler_index(self, index) 173 | if index == "rawset" then 174 | return bytehandler_rawset; 175 | elseif index == "readonly" then 176 | return bytehandler_readonly; 177 | elseif index == "gethash" then 178 | return bytehandler_gethash; 179 | elseif index == "iter" then 180 | return bytehandler_iter; 181 | elseif index == "sign" then 182 | return proxy.byte[self].sign; 183 | elseif index == "copy" then 184 | return bytehandler_copy; 185 | elseif index == "increase" or index == "decrease" then 186 | return index == "increase" and bytehandler_increase or bytehandler_decrease; 187 | end; 188 | local resultind, byteind = divmod(index, is51 and 54 or 64); 189 | local byte = proxy.byte[self].value[resultind + 1] or -1; 190 | if byteind == (is51 and 53 or 63) then 191 | return byte < 0 and 0 or 1; 192 | end; 193 | if byte < 0 then 194 | return div((byte + 1), -(2 ^ byteind)) % 2; 195 | end; 196 | return div(byte, (2 ^ byteind)) % 2; 197 | end; 198 | 199 | local function bytehandler_newindex(self, index, value) 200 | if proxy.byte[self].readonly then 201 | error("It is now readonly to ensure immutibility", 2); 202 | elseif index == "sign" then 203 | proxy.byte[self].sign = value; 204 | elseif self[index] ~= value then 205 | local b = proxy.byte[self].value; 206 | local resultind, byteind = divmod(index, is51 and 54 or 64); 207 | if b[resultind + 1] or value ~= 0 then 208 | if resultind > #b then 209 | for i = #b + 1, resultind do 210 | b[i] = -1; 211 | end; 212 | end; 213 | local byte = b[resultind + 1] or -1; 214 | if byteind == (is51 and 53 or 63) then 215 | b[resultind + 1] = (-byte) - 1; 216 | else 217 | b[resultind + 1] = byte + ((2 ^ byteind) * ((value == 0 and -1 or 1) * (byte < 0 and -1 or 1))); 218 | end; 219 | end; 220 | end; 221 | end; 222 | 223 | local function bytehandler_len(self) 224 | return (#proxy.byte[self].value) * (is51 and 54 or 64); 225 | end; 226 | 227 | local bytehandler_mt = 228 | { 229 | __index = bytehandler_index; 230 | __newindex = bytehandler_newindex; 231 | __len = bytehandler_len; 232 | }; 233 | 234 | function bytehandler_copy(self) 235 | local value = proxy.byte[self].value; 236 | 237 | local pointer = newproxy_or_setmetatable(bytehandler_mt) 238 | proxy.byte[pointer] = { readonly = false, value = table.move(value, 1, #value, 1, table.create(#value)) }; 239 | return pointer; 240 | end; 241 | 242 | local function createbits() 243 | local pointer = newproxy_or_setmetatable(bytehandler_mt); 244 | proxy.byte[pointer] = { readonly = false, value = { } }; 245 | 246 | return pointer; 247 | end; 248 | 249 | --[=[ Base bit creation ]=]-- 250 | local function generatebits(tbl, sign) 251 | local ret = createbits(); 252 | for k, v in ipairs(tbl) do 253 | ret[k - 1] = v; 254 | end; 255 | ret.sign = sign; 256 | return ret; 257 | end; 258 | 259 | --[==[ Pseudo BigInteger, this is NOT immutable, and NOT for consumer use ]==]-- 260 | local pseudo_mt = 261 | { 262 | __add = function(self, other) 263 | if other == 0 then 264 | return self; 265 | end; 266 | assert(self.base == other.base, "base is inconsistent"); 267 | local c = 0; 268 | for i, v in ipairs(other.value) do 269 | c, self.value[i] = divmod(c + (self.value[i] or 0) + v, self.base); 270 | end; 271 | if c > 0 then 272 | self.value[#other.value + 1] = 1; 273 | end; 274 | return self; 275 | end; 276 | __sub = function(self, other) 277 | if other == 0 then 278 | return self; 279 | end; 280 | assert(self.base == other.base, "base is inconsistent"); 281 | if self < other then 282 | return nil; 283 | end; 284 | local c = 0; 285 | for i, v in ipairs(other.value) do 286 | c, self.value[i] = divmod(c + self.value[i] - v, self.base); 287 | end; 288 | if c < 0 then 289 | table.remove(self.value); 290 | end; 291 | while self.value[#self.value] == 0 do 292 | table.remove(self.value); 293 | end; 294 | return self; 295 | end; 296 | __mul = function(self, other) 297 | for i = 2, other do 298 | self = self + self; 299 | end; 300 | return self; 301 | end; 302 | __compare = function(self, other) 303 | assert(self.base == other.base, "base is inconsistent"); 304 | if #self.value ~= #other.value then 305 | return (#self.value < #other.value) and -1 or 1; 306 | end; 307 | for i = #self.value, 1, -1 do 308 | if self.value[i] ~= other.value[i] then 309 | return (self.value[i] < other.value[i]) and -1 or 1; 310 | end; 311 | end; 312 | return 0; 313 | end; 314 | __lt = function(self, other) 315 | return getmetatable(self).__compare(self, other) < 0; 316 | end; 317 | __le = function(self, other) 318 | return getmetatable(self).__compare(self, other) <= 0; 319 | end; 320 | __eq = function(self, other) 321 | return getmetatable(self).__compare(self, other) == 0; 322 | end; 323 | }; 324 | local function pseudo_bigint(...) 325 | local value, base; 326 | if select('#', ...) == 1 then 327 | base = ...; 328 | value = { }; 329 | else 330 | value, base = ...; 331 | end; 332 | return setmetatable({ value = value, base = base }, pseudo_mt); 333 | end; 334 | 335 | local function copy_pseudo_bigint(value, base) 336 | return pseudo_bigint(table.move(value.value, 1, #value.value, 1, table.create(#value.value)), base); 337 | end; 338 | 339 | local function to_bits(v0, base, sign) 340 | if base == 2 then 341 | return generatebits(v0, sign):readonly(); 342 | end; 343 | local ret = { }; 344 | local v1 = pseudo_bigint({ 1 }, base); 345 | local v2 = (pseudo_bigint(v0, base) - (sign and 0 or v1)); 346 | -- negative zero 347 | if not v2 then 348 | v2 = v0; 349 | sign = true; 350 | end; 351 | local value_access = { pseudo_bigint({ 1 }, base) }; 352 | while v1 <= v2 do 353 | table.insert(value_access, copy_pseudo_bigint(v1 * 2, base)); 354 | end; 355 | for i = #value_access, 1, -1 do 356 | local v3 = v2 - value_access[i]; 357 | ret[i] = v3 and 1 or 0; 358 | end; 359 | 360 | return generatebits(ret, sign):readonly(); 361 | end; 362 | 363 | local function from_bits(v0, base) 364 | if base == 2 then 365 | return v0; 366 | end; 367 | if base ~= nil and not tonumber(base) then 368 | error("invalid argument #2 (number expected, got " .. typeof(base) .. ')', 2); 369 | elseif base ~= nil and base < 2 and base > 36 then 370 | error("invalid argument #2 (base out of range)"); 371 | end; 372 | local ret = pseudo_bigint(base); 373 | local v1 = pseudo_bigint({ 1 }, base); 374 | if not v0.sign then 375 | ret = ret + v1; 376 | end; 377 | for i, v in v0:iter() do 378 | if (v == 1) or (v == true) then 379 | ret = ret + v1; 380 | end; 381 | v1 = v1 * 2; 382 | end; 383 | return ret.value; 384 | end; 385 | 386 | --[=[ Main ]=]-- 387 | local function check_bigint(func, params, err) 388 | return function(...) 389 | local argc = select('#', ...); 390 | if argc < math.abs(params) then 391 | error(("missing argument #%d (bigint expected)"):format(argc + 1)); 392 | end; 393 | local args = { }; 394 | for i = 1, math.abs(params) do 395 | local v = constructor(select(i, ...)); 396 | if v then 397 | args[i] = v; 398 | else 399 | if params == 2 then 400 | local arg0, arg1 = ...; 401 | arg0, arg1 = typeof(arg0), typeof(arg1); 402 | error((err:gsub("{0}", (arg0 == arg1) and arg0 or (arg0 .. ' and ' .. arg1))), 2); 403 | else 404 | error(((err or "invalid argument #{0} (bigint expected, got {1})"):gsub("{0}", i):gsub("{1}", typeof(v))), 2); 405 | end; 406 | end; 407 | end; 408 | for i = params + 1, argc do 409 | args[i] = (select(i, ...)); 410 | end; 411 | return func(unpack(args)); 412 | end; 413 | end; 414 | 415 | --[==[ Direct methods ]==]-- 416 | local function rawnew(bits) 417 | bits:readonly(); 418 | local hash = bits:gethash(); 419 | if hash.hash then 420 | return hash.hash; 421 | end; 422 | 423 | local pointer = newproxy_or_setmetatable(bigint_mt); 424 | hash.hash = pointer; 425 | proxy.bi[pointer] = { name = "bigint", bits = bits; }; 426 | 427 | return pointer; 428 | end; 429 | local values = { }; 430 | 431 | local function add(self, other) 432 | if self == values.zero then 433 | return other; 434 | elseif other == values.zero then 435 | return self; 436 | end; 437 | 438 | local data0 = proxy.bi[self].bits; 439 | local data1 = proxy.bi[other].bits; 440 | local ret = createbits(); 441 | ret.sign = data0.sign; 442 | local carry = 0; 443 | for i = 0, math.max(#data0, #data1) - 1 do 444 | local diff; 445 | diff = data0[i] + (data1[i] * ((data0.sign == data1.sign) and 1 or -1)) + carry; 446 | carry, ret[i] = divmod(diff, 2); 447 | end; 448 | if not data1.sign then 449 | (data0.sign and bytehandler_decrease or bytehandler_increase)(ret); 450 | end; 451 | 452 | if carry == -1 then 453 | carry = 0; 454 | local ret1 = createbits(); 455 | ret1.sign = not ret.sign; 456 | local len = #ret; 457 | for i = 0, len do 458 | carry, ret1[i] = divmod(((i == len and 0 or 1) - ret[i]) + carry, 2); 459 | end; 460 | ret = ret1; 461 | end; 462 | 463 | return rawnew(ret); 464 | end; 465 | local function unm(self) 466 | if self == values.zero then 467 | return self; 468 | end; 469 | local ret = proxy.bi[self].bits:copy(); 470 | ret.sign = not proxy.bi[self].bits.sign; 471 | (ret.sign and bytehandler_increase or bytehandler_decrease)(ret); 472 | return rawnew(ret); 473 | end; 474 | local function mul(self, other) 475 | if self == values.zero or other == values.zero then 476 | return values.zero; 477 | elseif self == values.one then 478 | return other; 479 | elseif other == values.one then 480 | return self; 481 | elseif self == values.negative_one then 482 | return unm(other); 483 | elseif other == values.negative_one then 484 | return unm(self); 485 | end; 486 | 487 | local positive = (proxy.bi[self].bits.sign == proxy.bi[other].bits.sign); 488 | local data0 = proxy.bi[self:abs()].bits; 489 | local data1 = proxy.bi[other:abs()].bits; 490 | local p, q = #data0, #data1; 491 | local ret = createbits(); 492 | local tot = 0; 493 | for ri = 0, p + q do 494 | for bi = math.max(0, ri - p), math.min(ri, q) do 495 | local ai = ri - bi; 496 | tot = tot + (data0[ai] * data1[bi]); 497 | end; 498 | tot, ret[ri] = divmod(tot, 2); 499 | end; 500 | ret[p + q] = tot % 2; 501 | ret.sign = positive; 502 | if not positive then 503 | ret:decrease(); 504 | end; 505 | return rawnew(ret); 506 | end; 507 | local function shl(self, ...) 508 | local other = ...; 509 | if proxy.bi[other] then 510 | other = other:todouble(); 511 | elseif select('#', ...) == 0 then 512 | error("missing argument #2 (number expected)", 2); 513 | elseif (not tonumber(other)) then 514 | error("invalid argument #2 (bigint/number expected got " .. typeof(other) .. ')', 2); 515 | else 516 | other = math.floor(other); 517 | end; 518 | local data = proxy.bi[self].bits; 519 | if not data.sign then 520 | data = data:copy():increase(); 521 | end; 522 | local ret = createbits(); 523 | local contain_one = true; 524 | for i = 0, #data + other - 1 do 525 | ret[i] = data[i - other]; 526 | contain_one = contain_one or (ret[i] ~= 0); 527 | end; 528 | if (not data.sign) and contain_one then 529 | ret:decrease(); 530 | ret.sign = false; 531 | else 532 | ret.sign = true; 533 | end; 534 | return rawnew(ret); 535 | end; 536 | local function shr(self, ...) 537 | local other = ...; 538 | if proxy.bi[other] then 539 | other = other:todouble(); 540 | elseif select('#', ...) == 0 then 541 | error("missing argument #2 (number expected)", 2); 542 | elseif (not tonumber(other)) then 543 | error("invalid argument #2 (bigint/number expected got " .. typeof(other) .. ')', 2); 544 | else 545 | other = math.floor(other); 546 | end; 547 | local data = proxy.bi[self]; 548 | local ret = createbits(); 549 | for i = other, #data.bits do 550 | ret[i] = data.bits[i + other]; 551 | end; 552 | if #ret == 0 and data.sign == - 1 then 553 | return values.negative_one; 554 | end; 555 | return rawnew(ret, data.sign); 556 | end; 557 | local function band(self, other) 558 | local p, ret = 1, 0; 559 | while self > values.zero and other > values.zero do 560 | local r0, r1 = self % 2, other % 2; 561 | if (r0 + r1) > values.one then 562 | ret = ret + p; 563 | end; 564 | self, other, p = (self - r0) / 2, (other - r1) / 2, p * 2; 565 | end; 566 | return ret; 567 | end; 568 | local function bnot(self) 569 | local p, ret = 1 , 0; 570 | while self > values.zero do 571 | local r = self % 2; 572 | if r < values.one then 573 | ret = ret + p 574 | end 575 | self, p = (self - r) / 2, p * 2; 576 | end; 577 | return ret; 578 | end; 579 | local function bor(self, other) 580 | local p, ret = 1, 0; 581 | while self + other > values.zero do 582 | local r0, r1 = self % 2, other % 2; 583 | if r0 + r1 > values.zero then 584 | ret = ret + p; 585 | end; 586 | self, other, p = (self - r0) / 2, (other - r1) / 2, p * 2; 587 | end; 588 | return ret; 589 | end; 590 | local function bxor(self, other) 591 | local p, ret = 1, 0; 592 | while self > values.zero and other > values.zero do 593 | local r0, r1 = self % 2, other % 2; 594 | if r0 ~= r1 then 595 | ret = ret + p; 596 | end; 597 | self, other, p = (self - r0) / 2, (other - r1) / 2, p * 2; 598 | end 599 | if self < other then 600 | self = other; 601 | end; 602 | while self > values.zero do 603 | local r = self % 2; 604 | if r > values.zero then 605 | ret = ret + p; 606 | end; 607 | self, p = (self - r) / 2, p * 2 608 | end 609 | return ret; 610 | end; 611 | local function divrem(self, other) 612 | -- NOT modulus, this is C remainder 613 | -- If you're looking for modulus use the :Modulus() metamethod 614 | -- https://rob.conery.io/2018/08/21/mod-and-remainder-are-not-the-same/ 615 | -- https://stackoverflow.com/questions/13683563/whats-the-difference-between-mod-and-remainder 616 | if other == values.zero then 617 | error("division or remainder by zero", 2); 618 | elseif self == other then 619 | return values.one, values.zero; 620 | elseif self == values.zero then 621 | return values.zero, values.zero; 622 | elseif self == values.one then 623 | return values.zero, values.one; 624 | elseif self == values.negative_one then 625 | return values.zero, values.negative_one; 626 | elseif other == values.one then 627 | return self, values.zero; 628 | elseif other == values.negative_one then 629 | return unm(self), values.zero; 630 | end; 631 | 632 | local sign0 = proxy.bi[self].bits.sign; 633 | self = self:abs(); 634 | local data0 = proxy.bi[self].bits; 635 | local sign1 = proxy.bi[other].bits.sign and 1 or - 1; 636 | other = other * sign1; 637 | 638 | if self < other then 639 | return values.zero, self * (sign0 and 1 or -1); 640 | end; 641 | 642 | local ret, rem = values.zero, values.zero; 643 | for i = #data0 - 1, 0, -1 do 644 | rem = shl(rem, 1); 645 | local b = proxy.bi[rem].bits:copy(); 646 | b[0] = data0[i]; 647 | b.sign = true; 648 | rem = rawnew(b); 649 | if rem >= other then 650 | rem = rem - other; 651 | b = proxy.bi[ret].bits:copy(); 652 | b[i] = 1; 653 | b.sign = true; 654 | ret = rawnew(b); 655 | end; 656 | end; 657 | return ret * ((sign0 and 1 or -1) * sign1), rem * (sign0 and 1 or -1); 658 | end; 659 | local function concat(self, other) 660 | return tostring(self) .. tostring(other); 661 | end; 662 | local function islessthanbit(left, right) 663 | -- Positive value override negative 664 | local sign0, sign1 = math.sign(left), math.sign(right); 665 | if sign0 ~= sign1 then 666 | return sign0 < sign1; 667 | end; 668 | -- Check the absolute value 669 | return math.abs(left) < math.abs(right); 670 | end; 671 | 672 | local function compare(self, other) 673 | if proxy.bi[self].bits.sign ~= proxy.bi[other].bits.sign then 674 | return proxy.bi[self].bits.sign and 1 or -1; 675 | end; 676 | local data0 = proxy.byte[proxy.bi[self].bits].value; 677 | local data1 = proxy.byte[proxy.bi[other].bits].value; 678 | 679 | if #data0 ~= #data1 then 680 | return ((#data0 < #data1) == proxy.bi[self].bits.sign) and -1 or 1; 681 | end; 682 | for i = #data0, 1, -1 do 683 | if data0[i] ~= data1[i] then 684 | return (islessthanbit(data0[i], data1[i]) == proxy.bi[self].bits.sign) and -1 or 1; 685 | end; 686 | end; 687 | return 0; 688 | end; 689 | local function toint53array(bits) 690 | local ret0 = { }; 691 | for i = 0, #bits - 1 do 692 | if i % 53 == 0 then 693 | table.insert(ret0, bits[i]); 694 | else 695 | ret0[#ret0] = ret0[#ret0] + (bits[i] * (2 ^ (i % 53))); 696 | end; 697 | end; 698 | if ret0[#ret0] == 0 then 699 | table.remove(ret0); 700 | end; 701 | return ret0; 702 | end; 703 | local function log(self, other) 704 | local data = toint53array(proxy.bi[self].bits); 705 | if proxy.bi[other] then 706 | other = other:todouble(); 707 | elseif (not tonumber(other)) and other ~= nil then 708 | error("invalid argument #2 (bigint/number expected got " .. typeof(other) .. ')', 2); 709 | else 710 | other = tonumber(other) or 2.71828182845905; 711 | end; 712 | if (not proxy.bi[self].bits.sign) or other == 1 then 713 | return tonumber('nan'); 714 | elseif self == values.one then 715 | return 0; 716 | elseif math.abs(other) == tonumber('inf') or other == 0 then 717 | return tonumber('nan'); 718 | end; 719 | 720 | local r0, r1 = 0, 0.5; 721 | local topbits = 0; 722 | local r2 = data[#data]; 723 | while r2 > 0 do 724 | r2 = math.floor(r2 / 2); 725 | topbits = topbits + 1; 726 | end; 727 | local bitlength = (#data - 1) * 53 + topbits; 728 | local indbit = (2 ^ (topbits - 1)); 729 | 730 | for i = #data, 1, -1 do 731 | while indbit ~= 0 do 732 | if (intband(data[i], indbit) ~= 0) then 733 | r0 = r0 + r1; 734 | end; 735 | r1 = r1 * 0.5; 736 | indbit = math.floor(indbit / 2); 737 | end; 738 | indbit = 2 ^ 53; 739 | end; 740 | return (math.log(r0) + (.69314718055994529 * bitlength)) / math.log(other); 741 | end; 742 | local function pow(self, ...) 743 | local other = (...); 744 | if proxy.bi[other] then 745 | other = other:todouble(); 746 | elseif select('#', ...) == 0 then 747 | error("missing argument #2 (number expected)", 2); 748 | elseif (not tonumber(other)) then 749 | error("invalid argument #2 (bigint/number expected got " .. typeof(other) .. ')', 2); 750 | else 751 | other = math.floor(other); 752 | end; 753 | if other == 0 then 754 | return values.one; 755 | elseif other == 1 then 756 | return self; 757 | elseif other < 0 then 758 | return values.zero; 759 | elseif self == values.one or self == values.zero then 760 | return self; 761 | elseif self == values.negative_one then 762 | return band(other, self) ~= 0 and 1 or -1; 763 | end; 764 | local ret = values.one; 765 | while other ~= 0 do 766 | if intband(other, 1) ~= 0 then 767 | ret = ret * self; 768 | end; 769 | other = math.floor(other / 2); 770 | self = self * self; 771 | end; 772 | return ret; 773 | end; 774 | 775 | --[=[ Class methods ]=]-- 776 | local bi = setmetatable({ }, { 777 | __newindex = function(self, ind, func) 778 | rawset(self, ind, check_bigint(func, 1)); 779 | end; 780 | }); 781 | 782 | -- For scientific notation, currencies and compact numbers tolocalestring is more than enough 783 | local base_char = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 784 | local function concat_base(bits, base) 785 | local tbl = from_bits(bits, base); 786 | if base <= 10 then 787 | return table.concat(tbl, ''):reverse():gsub('^0+', ''); 788 | end; 789 | local r = ''; 790 | for _, v in ipairs(tbl) do 791 | r = base_char:sub(v - 1, v - 1) .. r; 792 | end; 793 | return r:gsub('^0+', ''); 794 | end; 795 | function bi.tostring(self, options) 796 | local ret = concat_base(proxy.bi[self].bits, (type(options) == "number") and options or 10); 797 | if ret == '' then 798 | ret = '0'; 799 | end; 800 | if type(options) ~= "table" and type(options) ~= "number" and options ~= nil then 801 | error("invalid argument #2 (string expected, got " .. typeof(options) .. ')', 2); 802 | elseif options then 803 | local minimumIntegerDigits = (options.minimumIntegerDigits or 0); 804 | local maximumIntegerDigits = (options.maximumIntegerDigits or math.huge); 805 | if minimumIntegerDigits < options.maximumIntegerDigits then 806 | error("the maximumIntegerDigits is bigger than minimumIntegerDigits"); 807 | end; 808 | if maximumIntegerDigits and #ret > maximumIntegerDigits then 809 | ret = ret:sub(-options.maximumIntegerDigits); 810 | end; 811 | if minimumIntegerDigits and #ret < minimumIntegerDigits then 812 | ret = ('0'):rep(minimumIntegerDigits - #ret) .. ret; 813 | end; 814 | if #ret > 2 + (options.minimumGroupingDigits or 1) then 815 | local rem; 816 | options.groupSize = options.groupSize or { }; 817 | options.groupSize[1] = options.groupSize[1] or 3; 818 | options.groupSize[2] = options.groupSize[2] or options.groupSize[1] or 3; 819 | ret, rem = (options.groupSymbol or ' ') .. ret:sub(-options.groupSize[1]), ret:sub(1, -(options.groupSize[1] + 1)); 820 | while #rem > options.groupSize[2] do 821 | ret, rem = (options.groupSymbol or ' ') .. ret:sub(-options.groupSize[2]), ret:sub(1, -(options.groupSize[2] + 1)); 822 | end; 823 | ret = rem .. ret; 824 | end; 825 | if options.minimumFractionDigits then 826 | ret = ret .. (options.decimalSymbol or '.') .. ('0'):rep(options.minimumFractionDigits); 827 | end; 828 | end; 829 | return (proxy.bi[self].bits.sign and '' or '-') .. ret; 830 | end; 831 | function bi.bin(self) 832 | return (('0b' .. concat_base(proxy.bi[self].bits, 2)):gsub('^0b$', '0b0')); 833 | end; 834 | function bi.hex(self) 835 | return (('0x' .. concat_base(proxy.bi[self].bits, 16)):gsub('^0x$', '0x0')); 836 | end; 837 | 838 | function bi.mod(self, other) 839 | local ret = self % other; 840 | return (ret < 0) and (ret + other) or ret; 841 | end; 842 | rawset(bi, 'mod', check_bigint(bi.modulus, 2, "attempt to perform arithmetic (mod) on {0}")); 843 | 844 | function bi.divmod(self, other) 845 | local r0, r1 = divrem(self, other); 846 | return r0 + (((r0 < values.zero) and (r1 == values.zero)) and 1 or 0), (r1 < values.zero) and (r1 + other) or r1; 847 | end; 848 | rawset(bi, 'divmod', check_bigint(bi.divmod, 2, "attempt to perform arithmetic (divmod) on {0}")); 849 | 850 | rawset(bi, 'divrem', check_bigint(divrem, 2, "attempt to perform arithmetic (divrem) on {0}")); 851 | rawset(bi, 'compare', check_bigint(compare, 2, "attempt to compare {0}")); 852 | 853 | rawset(bi, 'band', check_bigint(band, 2, check_bigint(band, 2, "attempt to perform bitwise operation (band) on {0}"))); 854 | rawset(bi, 'bor', check_bigint(band, 2, check_bigint(bor, 2, "attempt to perform bitwise operation (bor) on {0}"))); 855 | rawset(bi, 'bxor', check_bigint(band, 2, check_bigint(bxor, 2, "attempt to perform bitwise operation (bxor) on {0}"))); 856 | 857 | bi.bnot = bnot; 858 | 859 | function bi.iseven(self) 860 | return proxy.bi[self].bits[0] == (proxy.bi[self].bits.sign and 0 or 1); 861 | end; 862 | 863 | function bi.sign(self) 864 | if self == values.zero then 865 | return 0; 866 | end; 867 | return proxy.bi[self].bits.sign and 1 or -1; 868 | end; 869 | 870 | function bi.todouble(self) 871 | local ret = 0; 872 | for i, v in proxy.bi[self].bits:iter() do 873 | ret = ret + (v * (2 ^ i)); 874 | end; 875 | return (ret + (proxy.bi[self].bits.sign and 0 or 1)) * (proxy.bi[self].bits.sign and 1 or -1); 876 | end; 877 | 878 | function bi.toint32(self) 879 | return self:todouble() % (2 ^ 31); 880 | end; 881 | 882 | function bi.min(value, ...) 883 | local argc = select('#', ...); 884 | local min_val = value; 885 | for i = 1, argc do 886 | local v = constructor(select(i, ...)); 887 | if v then 888 | if v < min_val then 889 | min_val = v; 890 | end; 891 | else 892 | error((("invalid argument #{0} (bigint expected, got {1})"):gsub("{0}", i + 1):gsub("{1}", typeof(v))), 2); 893 | end; 894 | end; 895 | return min_val; 896 | end; 897 | 898 | function bi.max(value, ...) 899 | local argc = select('#', ...); 900 | local max_val = value; 901 | for i = 1, argc do 902 | local v = constructor(select(i, ...)); 903 | if v then 904 | if v < max_val then 905 | max_val = v; 906 | end; 907 | else 908 | error((("invalid argument #{0} (bigint expected, got {1})"):gsub("{0}", i + 1):gsub("{1}", typeof(v))), 2); 909 | end; 910 | end; 911 | return max_val; 912 | end; 913 | 914 | bi.pow = pow; 915 | bi.log = log; 916 | function bi.log10(self) 917 | return log(self, 10); 918 | end; 919 | function bi.log2(self) 920 | return log(self, 2); 921 | end 922 | function bi.log16(self) 923 | return log(self, 36); 924 | end; 925 | function bi.log12(self) 926 | return log(self, 12); 927 | end; 928 | function bi.log8(self) 929 | return log(self, 8); 930 | end; 931 | 932 | function bi.abs(self) 933 | if proxy.bi[self].bits.sign then 934 | return self; 935 | end; 936 | local ret = proxy.bi[self].bits:copy(); 937 | ret.sign = true; 938 | ret:increase(); 939 | return rawnew(ret); 940 | end; 941 | 942 | getmetatable(bi).__newindex = nil; 943 | bi.copysign = check_bigint(function(self, sign) 944 | local ret = proxy.bi[self].bits:copy(); 945 | ret.sign = not proxy.bi[sign].bits.sign; 946 | if ret.sign ~= proxy.bi[self].bits.sign then 947 | (ret.sign and bytehandler_decrease or bytehandler_increase)(ret); 948 | end; 949 | return rawnew(ret); 950 | end, -2); 951 | 952 | bi.clamp = check_bigint(function(self, min, max) 953 | if max < min then 954 | error("max must be greater than min", 2); 955 | elseif self < min then 956 | return min; 957 | elseif self > max then 958 | return max; 959 | end; 960 | return self; 961 | end, 3); 962 | 963 | --[==[ Metamethods ]==]-- 964 | bigint_mt.__index = bi; 965 | function bigint_mt.__tostring(self, options) 966 | return self:tostring(options); 967 | end; 968 | bigint_mt.__concat = concat; 969 | 970 | bigint_mt.__abs = bi.abs; 971 | bigint_mt.__log = bi.log; 972 | function bigint_mt.__round(self) 973 | return self; 974 | end; 975 | function bigint_mt.__repr(self) 976 | return ("n'%s'"):format(tostring(self)); 977 | end; 978 | 979 | setmetatable(bigint_mt, { __newindex = function(self, index, func) rawset(self, index, check_bigint(func, 2, " attempt to perform arithmetic (" .. index:sub(3):gsub('mod', 'rem') .. ") on {0}")) end; }) 980 | bigint_mt.__add = add; 981 | bigint_mt.__unm = unm; 982 | function bigint_mt.__sub(self, other) 983 | return add(self, unm(other)); 984 | end; 985 | bigint_mt.__mul = mul; 986 | function bigint_mt.__div(self, other) 987 | return (divrem(self, other)); 988 | end; 989 | function bigint_mt.__mod(self, other) 990 | return (select(2, divrem(self, other))); 991 | end; 992 | bigint_mt.__pow = pow; 993 | -- Lua 5.3 994 | bigint_mt.__idiv = bigint_mt.__div; 995 | 996 | -- Just in case for Lua 5.3 users 997 | getmetatable(bigint_mt).__newindex = function(self, index, func) rawset(self, index, check_bigint(func, 2, " attempt to compare {0}")) end; 998 | function bigint_mt.__lt(self, other) 999 | return compare(self, other) < 0; 1000 | end; 1001 | function bigint_mt.__le(self, other) 1002 | return compare(self, other) <= 0; 1003 | end; 1004 | function bigint_mt.__eq(self, other) 1005 | return compare(self, other) == 0; 1006 | end; 1007 | 1008 | getmetatable(bigint_mt).__newindex = function(self, index, func) rawset(self, index, check_bigint(func, 2, " attempt to perform bitwise operation (" .. index:sub(3) .. ") on {0}")); end; 1009 | bigint_mt.__band = band; 1010 | bigint_mt.__bor = bor; 1011 | bigint_mt.__bxor = bxor; 1012 | bigint_mt.__bnot = bnot; 1013 | bigint_mt.__shl = shl; 1014 | bigint_mt.__shr = shr; 1015 | 1016 | setmetatable(bigint_mt, nil); 1017 | 1018 | --[=[ Predefined value ]=] 1019 | -- Minus One 1020 | values.negative_one = createbits(); 1021 | values.negative_one.sign = false; 1022 | values.negative_one = rawnew(values.negative_one); 1023 | 1024 | -- Zero 1025 | values.zero = createbits(); 1026 | values.zero.sign = true; 1027 | values.zero = rawnew(values.zero); 1028 | 1029 | -- One 1030 | values.one = createbits():rawset(0, -2); 1031 | values.one.sign = true; 1032 | values.one = rawnew(values.one); 1033 | 1034 | --[==[ Constructor ]==]-- 1035 | function constructor(...) 1036 | if select('#', ...) == 0 then 1037 | error("missing argument #1", 2); 1038 | elseif proxy.bi[(...)] then 1039 | return (...); 1040 | end; 1041 | local value, base = ...; 1042 | if base ~= nil and not tonumber(base) then 1043 | error("invalid argument #2 (number expected, got " .. typeof(base) .. ')', 2); 1044 | elseif base ~= nil and (base < 2 or base > 36 or (base % 1) ~= 0) then 1045 | error("invalid argument #2 (base out of range)", 2); 1046 | end; 1047 | base = base or 10; 1048 | local bits, sign; 1049 | if value == true then 1050 | return values.one; 1051 | elseif value == false then 1052 | return values.zero; 1053 | elseif type(value) == "number" then 1054 | sign, value = value >= 0, math.abs(value); 1055 | if value < (2 ^ 53) then 1056 | bits = createbits():rawset(0, -(value + (sign and 1 or 0))); 1057 | bits.sign = sign; 1058 | else 1059 | return constructor(math_type(value) == "float" and ('%.0f'):format(value) or tostring(value)); 1060 | end; 1061 | else 1062 | if type(value) ~= "string" then 1063 | value = tostring(value); 1064 | end; 1065 | value = value:gsub('[.,]%d+$', ''):upper(); 1066 | 1067 | sign, value = value:match('^([%+%-]?)(.*)$'); 1068 | if sign == '+' then 1069 | sign = ''; 1070 | end; 1071 | 1072 | local int, frac, exp = value:match('(%d*)[.,]?(%d*)E([+]?%d+)'); 1073 | if int and base == 10 then 1074 | exp = tonumber(exp); 1075 | if not exp then 1076 | return nil; 1077 | end; 1078 | if int == '' and frac == '' then 1079 | return nil; 1080 | end; 1081 | if exp < -1 then 1082 | return values.zero; 1083 | end; 1084 | frac = frac:gsub('0+$', ''); 1085 | value = int .. frac .. ('0'):rep(exp - #frac); 1086 | end; 1087 | 1088 | if not (value and (value:match("^[^  _].*[^  _]$") or value:match('^[^  _]$'))) then 1089 | return nil; 1090 | end; 1091 | sign, value = sign == '', value:gsub('[  _]', ''):gsub('^0+', ''); 1092 | 1093 | if value == '' then 1094 | return values.zero; 1095 | end; 1096 | 1097 | local base_ret = { }; 1098 | for i = #value, 1, -1 do 1099 | local v = base_char:find(value:sub(i, i)); 1100 | if not v then 1101 | return nil; 1102 | end; 1103 | table.insert(base_ret, v - 1); 1104 | end; 1105 | bits = to_bits(base_ret, base, sign); 1106 | end; 1107 | 1108 | return rawnew(bits); 1109 | end; 1110 | 1111 | local function isbigint(...) 1112 | if select('#', ...) == 0 then 1113 | error("missing argument #1", 2); 1114 | end; 1115 | return not not proxy.bi[(...)]; 1116 | end; 1117 | 1118 | return setmetatable( 1119 | { }, 1120 | { 1121 | __index = function(_, ind) 1122 | if ind == "new" then 1123 | return constructor; 1124 | elseif ind == "isbigint" then 1125 | return isbigint; 1126 | end; 1127 | return bi[ind]; 1128 | end, 1129 | __metatable = "The metatable is locked", 1130 | __newindex = function() 1131 | error("Attempt to modify a readonly table", 2); 1132 | end; 1133 | } 1134 | ); 1135 | -------------------------------------------------------------------------------- /Complex.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Version 1.0.0 3 | This is intended for Roblox ModuleScripts. 4 | BSD 2-Clause Licence 5 | Copyright ©, 2020 - Blockzez (devforum.roblox.com/u/Blockzez and github.com/Blockzez) 6 | All rights reserved. 7 | 8 | Redistribution and use in source and binary forms, with or without 9 | modification, are permitted provided that the following conditions are met: 10 | 11 | 1. Redistributions of source code must retain the above copyright notice, this 12 | list of conditions and the following disclaimer. 13 | 14 | 2. Redistributions in binary form must reproduce the above copyright notice, 15 | this list of conditions and the following disclaimer in the documentation 16 | and/or other materials provided with the distribution. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | ]]-- 29 | local proxy = { }; 30 | local i = { }; 31 | local methods = setmetatable( 32 | { }, 33 | { 34 | __newindex = function(self, index, func) 35 | rawset(self, index, function(...) 36 | if select('#', ...) == 0 then 37 | error("missing argument #1 (Complex expected)", 2); 38 | elseif not proxy[(...)] then 39 | error("invalid argument #1 (Complex expected, got " .. typeof((...)) .. ')', 2); 40 | end; 41 | return func(...); 42 | end); 43 | end; 44 | } 45 | ); 46 | local i_mt = { __metatable = "The metatable is locked" }; 47 | 48 | --[=[ Metamethods ]=]-- 49 | function i_mt.__add(self, other) 50 | if type(self) == "number" then 51 | return i.new(self + proxy[other].Real, proxy[other].Imaginary); 52 | elseif type(other) == "number" then 53 | return i.new(proxy[self].Real + other, proxy[self].Imaginary); 54 | elseif proxy[self] and proxy[other] then 55 | return i.new(proxy[self].Real + proxy[other].Real, proxy[self].Imaginary + proxy[other].Imaginary); 56 | end; 57 | error("attempt to perform arithmetic (add) on " .. (typeof(self) .. " and " .. typeof(other)):gsub(" and " .. typeof(self), ''), 2); 58 | end; 59 | function i_mt.__sub(self, other) 60 | if type(self) == "number" then 61 | return i.new(self - proxy[other].Real, proxy[other].Imaginary); 62 | elseif type(other) == "number" then 63 | return i.new(proxy[self].Real - other, proxy[self].Imaginary); 64 | elseif proxy[self] and proxy[other] then 65 | return i.new(proxy[self].Real - proxy[other].Real, proxy[self].Imaginary - proxy[other].Imaginary); 66 | end; 67 | error("attempt to perform arithmetic (sub) on " .. (typeof(self) .. " and " .. typeof(other)):gsub(" and " .. typeof(self), ''), 2); 68 | end; 69 | function i_mt.__mul(self, other) 70 | if type(self) == "number" then 71 | return i.new(self * proxy[other].Real, self * proxy[other].Imaginary); 72 | elseif type(other) == "number" then 73 | return i.new(proxy[self].Real * other, proxy[self].Imaginary * other); 74 | elseif proxy[self] and proxy[other] then 75 | local a, b, c, d = proxy[self].Real, proxy[self].Imaginary, proxy[other].Real, proxy[other].Imaginary; 76 | return i.new((a * c) - (b * d), (a * d) + (b * c)); 77 | end; 78 | error("attempt to perform arithmetic (mul) on " .. (typeof(self) .. " and " .. typeof(other)):gsub(" and " .. typeof(self), ''), 2); 79 | end; 80 | function i_mt.__div(self, other) 81 | if type(self) == "number" then 82 | local a, c, d = self, proxy[other].Real, proxy[other].Imaginary; 83 | return i.new((a * c) / ((c ^ 2) + (d ^ 2)), (a * d) / ((c ^ 2) + (d ^ 2))); 84 | elseif type(other) == "number" then 85 | return i.new((proxy[self].Real * other) / (other ^ 2), (proxy[self].Imaginary * other) / (other ^ 2)); 86 | elseif proxy[self] and proxy[other] then 87 | local a, b, c, d = proxy[self].Real, proxy[self].Imaginary, proxy[other].Real, proxy[other].Imaginary; 88 | return i.new(((a * c) + (b * d)) / ((c ^ 2) + (d ^ 2)), ((b * c) - (a * d)) / ((c ^ 2) + (d ^ 2))); 89 | end; 90 | error("attempt to perform arithmetic (mul) on " .. (typeof(self) .. " and " .. typeof(other)):gsub(" and " .. typeof(self), ''), 2); 91 | end; 92 | local E = 2.7182818284590451; 93 | function i_mt.__pow(self, other) 94 | if type(other) == "number" or (proxy[self] and proxy[other]) then 95 | local a, b, c, d = proxy[self].Real, proxy[self].Imaginary, proxy[other] and proxy[other].Real or other, proxy[other] and proxy[other].Imaginary or 0; 96 | if (a == 0 and b == 0) or (c == 0 and d == 0) then 97 | return i.new(0, 0); 98 | end; 99 | local rho = self:Abs(); 100 | local theta = math.atan2(b, a); 101 | local newrho = (c * theta) + (d * math.log(rho)); 102 | local t = (rho ^ c) * (E ^ (-d * theta)); 103 | return i.new(t * math.cos(newrho), t * math.sin(newrho)); 104 | end; 105 | error("attempt to perform arithmetic (pow) on " .. (typeof(self) .. " and " .. typeof(other)):gsub(" and " .. typeof(self), ''), 2); 106 | end; 107 | function i_mt.__unm(self) 108 | return self * -1; 109 | end; 110 | function i_mt.__index(self, index) 111 | if proxy[self][index] then 112 | return proxy[self][index]; 113 | elseif methods[index] then 114 | return methods[index]; 115 | end; 116 | return nil; 117 | end; 118 | function i_mt.__newindex(self, index, value) 119 | if type(index) ~= "string" and type(index) ~= "number" then 120 | error("invalid argument #2 (string expected, got " .. typeof(index) .. ')', 2); 121 | end; 122 | error(index .. " cannot be assigned to", 2); 123 | end; 124 | function i_mt.__tostring(self) 125 | return (("%s+%si"):format(proxy[self].Real, proxy[self].Imaginary):gsub('%-nan%(ind%)', 'nan'):gsub('%+%-', '-'):gsub('^0%+*', '')); 126 | end; 127 | function i_mt.__eq(self, other) 128 | return (proxy[self].Real == proxy[other].Real) and (proxy[self].Imaginary == proxy[other].Imaginary); 129 | end; 130 | 131 | --[=[ Class methods ]=]-- 132 | function methods:Log(base) 133 | if type(base) == "number" then 134 | return self:Log() / i.new(base, 0):Log(); 135 | elseif base ~= nil then 136 | error("invalid argument #2 (number expected, got " .. typeof(base) .. ')', 2); 137 | end; 138 | return i.new(math.log(self:Abs()), math.atan2(proxy[self].Imaginary, proxy[self].Real)); 139 | end; 140 | function methods:Abs() 141 | local a, b = proxy[self].Real, proxy[self].Imaginary; 142 | if b == 0 then 143 | return math.abs(a); 144 | elseif a == 0 then 145 | return math.abs(b); 146 | elseif a > b then 147 | return a * math.sqrt(1 + ((b ^ 2)/(a ^ 2))); 148 | end; 149 | return b * math.sqrt(1 + ((a ^ 2)/(b ^ 2))); 150 | end; 151 | function methods:Sqrt() 152 | return i.fromPolarCoordinates(math.sqrt(self.Magnitude), self.Phase / 2); 153 | end; 154 | function methods:Sin() 155 | return i.new(math.sin(proxy[self].Real) * math.cosh(proxy[self].Imaginary), math.cos(proxy[self].Real) * math.sinh(proxy[self].Imaginary)); 156 | end; 157 | function methods:Cos() 158 | return i.new(math.cos(proxy[self].Real) * math.cosh(proxy[self].Imaginary), -(math.sin(proxy[self].Real) * math.sinh(proxy[self].Imaginary))); 159 | end; 160 | function methods:Tan() 161 | return self:Sin() / self:Cos(); 162 | end; 163 | function methods:Sinh() 164 | return i.new(math.sinh(proxy[self].Real) * math.cos(proxy[self].Imaginary), math.cosh(proxy[self].Real) * math.sin(proxy[self].Imaginary)); 165 | end; 166 | function methods:Cosh() 167 | return i.new(math.cosh(proxy[self].Real) * math.cos(proxy[self].Imaginary), math.sinh(proxy[self].Real) * math.sin(proxy[self].Imaginary)); 168 | end; 169 | function methods:Tanh() 170 | return self:Sinh() / self:Cosh(); 171 | end; 172 | function methods:Asin() 173 | return i.new(0, 1) * ((i.new(0, 1) * self) + (i.new(1, 0) - (self * self)):Sqrt()):Log(); 174 | end; 175 | function methods:Acos() 176 | return i.new(0, 1) * (i.new(0, 1) + (self * (i.new(1, 0) - (self * self)):Sqrt())):Log(); 177 | end; 178 | function methods:Atan() 179 | return (i.new(0, 1) / i.new(2, 0)) * ((i.new(1, 0) - (i.new(0, 1) * self)):Log() - (i.new(1, 0) + (i.new(0, 1) * self)):Log()); 180 | end; 181 | function methods:IsInfinity() 182 | return (math.abs(proxy[self].Real) == math.huge) or (math.abs(proxy[self].Imaginary) == math.huge); 183 | end; 184 | function methods:IsNaN() 185 | return (proxy[self].Real ~= proxy[self].Real) or (proxy[self].Imaginary ~= proxy[self].Imaginary); 186 | end; 187 | 188 | --[=[ Constructor ]=]-- 189 | function i.new(...) 190 | local argc = select('#', ...); 191 | if argc < 2 then 192 | error("missing argument #" .. (argc + 1) .. " (number expected)", 2); 193 | end; 194 | local real, imaginary = ...; 195 | if type(real) ~= "number" then 196 | error("invalid arguemnt #1 (number expected, got " .. typeof(real) .. ")", 2); 197 | end; 198 | if type(imaginary) ~= "number" then 199 | error("invalid arguemnt #2 (number expected, got " .. typeof(imaginary) .. ")", 2); 200 | end; 201 | 202 | local pointer; 203 | if newproxy then 204 | pointer = newproxy(true); 205 | local mt = getmetatable(pointer); 206 | for k, f in next, i_mt do 207 | mt[k] = f; 208 | end; 209 | else 210 | pointer = setmetatable({ }, i_mt); 211 | end; 212 | 213 | proxy[pointer] = { 214 | Real = real, 215 | Imaginary = imaginary, 216 | Magnitude = math.sqrt((real ^ 2) + (imaginary ^ 2)), 217 | Phase = math.atan2(imaginary, real); 218 | }; 219 | return pointer; 220 | end; 221 | function i.fromPolarCoordinates(...) 222 | local argc = select('#', ...); 223 | if argc < 2 then 224 | error("missing argument #" .. (argc + 1) .. " (number expected)", 2); 225 | end; 226 | local magnitude, phase = ...; 227 | if type(magnitude) ~= "number" then 228 | error("invalid arguemnt #1 (number expected, got " .. typeof(magnitude) .. ")", 2); 229 | end; 230 | if type(phase) ~= "number" then 231 | error("invalid arguemnt #2 (number expected, got " .. typeof(phase) .. ")", 2); 232 | end; 233 | 234 | local pointer; 235 | if newproxy then 236 | pointer = newproxy(true); 237 | local mt = getmetatable(pointer); 238 | for k, f in next, i_mt do 239 | mt[k] = f; 240 | end; 241 | else 242 | pointer = setmetatable({ }, i_mt); 243 | end; 244 | 245 | proxy[pointer] = { 246 | Real = magnitude * math.cos(phase), 247 | Imaginary = magnitude * math.sin(phase), 248 | Magnitude = magnitude, 249 | Phase = phase; 250 | }; 251 | return pointer; 252 | end; 253 | 254 | return setmetatable( 255 | { }, 256 | { 257 | __index = function(self, index) 258 | if i[index] then 259 | return i[index]; 260 | elseif methods[index] then 261 | return methods[index]; 262 | end; 263 | return nil; 264 | end, 265 | __metatable = "The metatable is locked", 266 | } 267 | ); 268 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2020, Blockzez 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NumericModules 2 | 3 | A repository for some of my modules. Intended for Roblox Lua 4 | 5 | ## Documentations 6 | BigInteger: https://devforum.roblox.com/t/1-0-0-biginteger-safely-store-and-represent-values-over-2/587199 7 | --------------------------------------------------------------------------------