MathAddons+ || Useful functions all in one place!

Thank you for reporting! I overlooked that when copying between the minimum and maximum category.

Yeah its not only minimum and maximum, I remember multiple being like that. For example range uses the iqr function.

Update 1 - Imaginary Number Library
Sunday, June 19th, 2022


New Functions


Statistics & Probability

Linear Regression

Purpose: returns the function (or slope & y intercept) and correlation coefficient of a data set
Parameters:

  1. As many data points as you please in the form of {x,y}
  2. The last parameter would be a boolean to Toggle Function Format

Output: Function, Number (If Function Toggle is false: Number, Number, Number
Explanation:

  • *Calculates the line of best fit, and the correlation coefficient (the number that represents how good the line matches the set.
    Example:
local slope, yint, r = module.linreg({0,2},{1,2},{2,3},{5,9},{6,5},{4,7},{10,5})
-- slope = 0.4142857142857143
-- yint = 3.057142857142857
-- r = 0.5385164807134504
local func, r = module.linreg({1,2},{5,8},{4,5},true)
-- r = 0.9607689228305227
local predictedPoint = func(10) -- predictedPoint = 14.23076923076923

New Category


Imaginary Library

These functions assist you when working with arithmetic on complex numbers. It gets unreal.
Before the you read the documentation below, do note what a valid complex number is:

local complexNum = {1,2} -- This represents 1+2i
local realNum = 2 -- this represents 2+0i, this is a valid input, same thing as {2,0}
local imaginaryNum = {0,5} -- this represents 0+5i, this is a valid input
local invalidinput = {5} -- this will return an error
local complex = module.Complex -- this is how you call the library

Note that most of these functions require an input of 2 tables, with two values each


Addition

Purpose: combine two complex numbers
Parameters:

  1. Table - {a,b}
  2. Table - {c,d}
  3. String Format Toggle (OPTIONAL) || Defaulted to false

Output: Table/String
Explanation:

  • (a+bi)+(c+di) = (a+c)+(b+d)i

Example:

local 
local x = complex.add({2,1},{0,3},true) -- x = '2+4i'
local y = complex.add({-5,1},4) -- y = {-1,1}
Multiplication

Purpose: multiply two complex numbers
Parameters:

  1. Table - {a,b}
  2. Table - {c,d}
  3. String Format Toggle (OPTIONAL) || Defaulted to false

Output: Table/String
Explanation:

  • (a+bi)(c+di), you just distribute the binomial, anywhere you see an i^2, it becomes a -1 = ac-bd+(ad+cd)i

Example:

local 
local x = complex.mul({2,1},{0,3},true) -- x = '-3+6i'
local y = complex.mul({-5,1},4) -- y = {-20,4}
Division

Purpose: divide two complex numbers
Parameters:

  1. Table - {a,b}
  2. Table - {c,d}
  3. String Format Toggle (OPTIONAL) || Defaulted to false

Output: Table/String
Explanation:

  • (a+bi)/(c+di) multiply both sides by the conjugate as the numerator and denominator. you get (a+bi)(c-di)/(c+di)(c-di), which simplifies to (ac+bd+(bc-ad)i)/(c^2+d^2)

Example:

local 
local x = complex.div({2,1},{4,3},true) -- x = '0.44+-0.08i'
local y = complex.div({-5,1},4) -- y = {-1.25,.25}
To Polar Form

Purpose: convert a rectangular value to polar
Parameters:

  1. Table - {a,b}
  2. String Format Toggle (OPTIONAL) || Defaulted to false

Output: Number, Number
Explanation:

  • converting a rectangular form value to polar requires you to find the distance from the origin of the imaginary axis is that point: sqrt(a^2+b^2), and the angle if there was a line to be drawn from the origin to the point is the arctan(b/a)

Example:

local 
local x = complex.toPolar({2,1},true) -- x = '2.23606797749979e^0.4636476090008061i'
local distance,theta = complex.toPolar({-5,1})
-- distance = 5.0990195135927845,
-- theta = -0.19739555984988078
From Polar Form

Purpose: convert a rectangular value to polar
Parameters:

  1. Number - Distance
  2. Number - Angle
  3. String Format Toggle (OPTIONAL) || Defaulted to false

Output: Table/String
Explanation:

  • Converting polar form into rectangular form requires you to convert from polar form to Euler’s identity (re^iθ = r×cos(θ) + r×isin(θ), which can just be calculated using trig functions and arithmetic

Example:

local 
local x = complex.fromPolar(2.23606797749979,0.4636476090008061,true) -- x = '2+1i'
local y = complex.fromPolar(5.0990195135927845,0.19739555984988078) -- y = {-5,1}
Exponentiation

Purpose: set a complex number to a power of a complex number
Parameters:

  1. Table - {a,b}
  2. Table - {c,d}
  3. String Format Toggle (OPTIONAL) || Defaulted to false

Output: Table/String
Explanation:

  • Too tired to explain how this works, go check out the source code

Example:

local 
local x = complex.pow({0,1},{0,1},true) -- x = '0.20787957635+0i' (This is actually i^i)
local y = complex.pow(3,3,true) -- y = '27+0i' (just 3^3)
local z = complex.pow({0,1},{2,0},true) -- z = '-1+0i' (this is i^2 which is defined as -1'
local a = complex.pow(-1,.5,true) -- a = '-1+0i' (this is i which is defined as sqrt(-1)', note that with powers less than 1, its important to know that there are more than 1 solutions to these powers, the module has to decide on 1)
Mandelbrot Checker

Purpose: Check if a complex number is in the mandelbrot set
Parameters:

  1. Table - {a,b}
  2. Number - Power (OPTIONAL) || Defaulted to 2

Output: Boolean
Explanation:

  • This requires a bit of representation
Images

This below is the mandelbrot set


everything inside of it is a solution, so inputing any complex number inside the set will return true
What is the second parameter you ask?
Well, the mandelbrot set is generated by iterating this function an infinity amount of times
z = 0
z = z^2+c
where the z starts at 0 and c is the input. then z is equal to whatever the solution to 0^2+c is, and you repeat the process of subsituting whatever the output was back into z.
The second parameter is the power, you can tell in the equation that the set has the highest power of 2, the second parameter changes that number
Power of 3
image
Power of 4
image
Power of 5
image
Power of 6
image
With this function, you can actually generate these sets

and with a bit of modification to the function, you can get very detailed


and yes, even with different powers

How the function determines if something is in or out of the set, if checking if the real part of z to the power of 2 plus the imaginary part to the power of 2 is less than 4, if it is than its apart of the set, else, it isnt

Example:

local 
local x = complex.mandelbrot({0,1},2) -- x = true
local y = complex.mandelbrot({0,1},4) -- x = false
local z = complex.mandelbrot({-2,0}) -- x = true

Documentation Fixes


  • Some mistakes in the example code such as incorrect function names, thanks to
    @bitsplicer for bringing this to my attention :+1:

Thank you for all the support on this module :slight_smile:

2 Likes

Wow, this looks really useful, and will definitely consider installing it in my game!

1 Like

this my good sir is what i would call a banger

good stuff!!! super proud :smiley:

1 Like

Man that is pretty cool. I do not even think that the calculus functions are useless. In fact I was able to approximate pi up to 7 decimal digits correct with just three lines of code :

local Math = require(game.ReplicatedStorage.MathAddons)

s = 2 * Math.integral(-1,1, function(x) return math.sqrt(1-x^2) end)
print(s)

image

For anyone interested in math here is why this works:

Assume we have a circle with radius 1. Its area is then the following: pi * r^2 = pi * 1 = pi
So, by knowing the area of such a circle we can theoretically calculate pi. We also know that there is a function that describes the curve of the half of the circle which in our case is the following:


Thus if we can find the area of the region between the points -1 and 1 and multiply it by two we can then find the area of our whole circle which in our case is equal to pi. How can we do this? Luckily for us there is the first fundamental theorem of calculus which states that

Let f(x) be a continuous positive function between a and b and consider the region below the curve y = f(x), above the x-axis and between the vertical lines x = a and x = b. Then denote the area of this region by
image
and call this the definite integral of f(x) from a to b.

We know our function which is y=f(x)=sqrt(1-x^2) and our points a and b are -1 and 1 respectively. Thus the area under the curve is :
image
The problem with this though is that the indefinite integral of sqrt(1-x^2) is the following:
image
Now, you will ask what is the problem with the above? The answer is that the arcsin formula is the following:
arcsin(x) = π/2 - arccos(x) So as you understand we can not find pi using pi because we will end in something like this 0=0. Now, you might ask, how did the above module calculate pi ? The answer is that it did not calculate it, but it approximated it with the following method:


As you can see, the integral function does the following things:
First it makes sure that the parameters are in fact two numbers and a function. Then it checks whether the upper bound is less than zero and if it is, it takes the absolute value of it. In our case the upper bound is equal to one so there is no need to take its absolute value. Next, it does the following thing:

s = sqrt(1-(-1))^2 * 1e-5 + sqrt(1-1^2)*1e-5 + sqrt(1-(1e-5)^2)*1e-5 which then becomes
s = 0 + 0 + sqrt(1-(1e-5)^2) * 1e-5 which when multiplied by two is an approximation of our area and hence of pi.
I have to admit that the methods that are used in both calculus functions are smart because in reality these seemingly random numbers (1e-12) and (1e-5) are small changes(which is exactly what calculus examines, changes) that will approximate the result.

3 Likes

Great work, mate! Would you consider adding a function to convert a string to an equation and an equation to a string?

Input: “3 + 3” OR 3 + 3
Output: 3 + 3 OR “3 + 3”

2 Likes

I was actually planning to make this with the previous version before my previous account was terminated, definitely will work on this next!

2 Likes

I remember I tried to make a pi generator with lebreniz formula. And it was innacurate as hell. Thank you for this.

By the way, in the integral function, you can lower the number “1e-5” so something like 1e-8, the function will take longer to run since its a loop, however this will get you better estimates of pi, or whatever you’re attempting to calculate

Hi, it’s me again. I tried doing some optimizations on your Solver function, and I managed to shave off a considerable amount of processing time.

Standalone module of the function with my changes
local module = {}

type math = (number) -> (number)
local flr = math.floor
local ts = tostring
local strL = string.lower
local inf = math.huge
function round(n: number, decimal: number?): number
	local factor: number = decimal or 1
	return flr(n * factor + .5) / factor
end

function module.Solve(f: math, iterations: number?): {number} --2nd parameter allows for user-set amount of iterations
	local iterations = iterations or 1e3 --default iteration amount of 1000
	local t: {number} = {}
	for i = -20, 20, .1 do
		table.insert(t, i)
	end
	for ii = 1, iterations do
		for i, a in ipairs(t) do
			local fa: number = f(a)
			local num: number = a - 1e-5*fa / (f(a + 1e-5) - fa)
			local num2: number = round(num, 1e14)
			t[i] = if strL(ts(num2)) == 'nan' then inf else if ii == iterations then num2 else num
		end
	end
	local bl: {true} = {}
	local n: {number} = {}
	for _, v in ipairs(t) do
		local num: number = round(v, 1e10)
		if not bl[num] then
			bl[num] = true
			table.insert(n, num)
		end
	end
	table.sort(n)
	return n
end

return module

As for actual output, it’s basically the same. The loss of precision should be negligible for any practical applications.

Edit 2: made some more changes to the script for even more performance

1 Like

I definitely overlooked performance when making the function, this will be apart of the next version of the module. Thanks for the help once again!

Update 2 - Miniscule Updates & a new module
Thursday, June 30th, 2022


New Functions


Algebra

Lambert W function

Purpose: find the value of x for an equation in the form of x*e^x when its equal to some number y
Parameters:

  1. Number - y

Output: Table
Explanation:

Example:

local x = module.lambert(5*math.exp(5)) -- x = 5
local y = module.lambert(3*math.exp(3)) -- y = 3
local z = module.lambert(2) -- z = 0.8526055020137

Imaginary Library

To Array

Purpose: convert a number (complex or not) to an array in the form of {a,b} from the form “a+bi”
Parameters:

  1. String/Number

Output: Table
Explanation:

  • “1+i” will convert to {1,1}, “5-2i” will convert to {5,-2}, “i” converts to {0,1}, this is a general idea

Example:

local x = complex.toArray("1+i") -- x = {1,1}
local y = complex.toArray("5-2i") -- y = {5,-2}
local z = complex.toArray("i") -- z = {0,1}
To String

Purpose: convert an array to an string/number in the form of “a+bi” from the form {a,b}
Parameters:

  1. Table

Output: String/Number
Explanation:

  • {1,1} will convert to “1+i”, {5,-2}will convert to “5-2i” , {0,1} converts to “i”, this is a general idea

Example:

local x = complex.toString({1,1}) -- x = "1+i"
local y = complex.toString({5,-2}) -- y = "5-2i"
local z = complex.toString({0,1}) -- z = "i"

Module Fixes


  • Solver function was very inefficient, thank you @Prototrode for bringing this to my attention and offering an improved version of the function
  • There were calls to print random variables scattered around the module due to debugging, those are all removed
  • Addition and Multiplication with complex values now support more than two inputs, these inputs can also be strings such as “1-2i” or “i”
  • All imaginary library functions that return a complex value, will now be more readable when the imaginary term has a negative coefficient, thanks to the toString and toArray functions.
    Before: "9+ -33i"
    Now: "9-33i"

New Module


Ive created a new module for graphing functions called Graphit which allows you to graph any function you’d like.

image


The next update may contain more math functions such as logarithms with negative and imaginary bases and inputs, complex trig functions, etc.

Thank you for the continued feedback and suggestions for the module :smile:

1 Like

Update 3 - More Complex Number Support
Saturday, July 2nd, 2022


New Functions


Algebra

Tetration

Purpose: calculate x^x, a certain amount of times
Parameters:

  1. Number -
  2. Iteration Count - how many times is x taken to the xth power (OPTIONAL) || Defaulted to 2
    Output: Number/Table
    Explanation:
  • if you input 5,2 as the parameters, it will return 5^5, if you input 2,4, it will return 2^2^2^2.
    Heres more on tetration:

    Tetration - Wikipedia

Example:

local x = module.tetration(math.sqrt(2),10000) -- x = 2 --  fun fact the infinite tetration of root 2 is 2!!
local y = module.tetration(3) -- y = 3^3=27
local z = module.tetration(i,10000) -- z = 0.43828293672702395+0.3605924718713796i

Imaginary Library

To Real

Purpose: convert a complex number into its real part
Parameters:

  1. Number

Output: String/Number
Explanation:

  • The real part of 1+i is 1, so it returns 1

Example:

local x = complex.Re(1+i) -- x = 1
local y = complex.Re(5-2*i) -- y = 5
local z = complex.Re(i) -- z = 0
To Imaginary

Purpose: convert a complex number into its imaginary part
Parameters:

  1. Number

Output: String/Number
Explanation:

  • The imaginary part of 1+2i is 2, so it returns 2

Example:

local x = complex.Im(1+i) -- x = 1
local y = complex.Im(5-2*i) -- y = -2
local z = complex.Im(i) -- z = 1

Module Fixes


Ive updated the module so that common math functions are compatible with inputs and outputs of complex numbers

  • Sine (sin)
  • Cosine (cos)
  • Tangent (tan)
  • Arcsine (asin)
  • Arccosine (acos)
  • Arctangent (atan)
  • Sine Hyperbolic (sinh)
  • Cosine Hyperbolic (cosh)
  • Tangent Hyperbolic (tanh)
  • Logarithm - The base and argument can be complex and/or negative (log)
  • Power (pow)
  • Square Root (sqrt)

And also makes some normal functions compatible with complex numbers

  • Nth Fibonacci Number (fibonacci)
  • Summation/Product (summation/product)
  • Vertex Calculator (vertex)
  • Tetration (tetration)

  • Now, you can use i as if its a number, to call it, you can do:
local i = module.Complex.i
print(i^2) -- -1

Removed Functions


We are all gathered here to witness the removal of several complex number functions

  • Addition - Due to now i being a datatype, adding is as simple as 1+i
  • Multiplication - For the same reason as Additions removal
  • Division - Same reason

I dont know what the next update should have, let me know if you have an idea! :slight_smile:

It would be helpful if you mentioned some functions (especially the one in the statistics and probability section) mutates the table pointer especially if I expect it to not mutate (MathFunctions.min, MathFunctions.max).

print(MathAddons.min({0, -0}))
print(MathAddons.min({-0, 0}))

prints 0 and -0 rather than -0 and -0. Was this intentional?

MathAddons.mode actually returns a string

print(type(MathAddons.mode({ 1 })[1]))

even though the documentation stated that the function returns tables of numbers (IEEE).


I don’t think formatting belongs in math. I don’t recommend parsing formatted string (inverse format strings) as formatting is supposed to display numbers and parsing numbers that’s only supposed to show in the UI feels weird to me. What’s your motivation of the parsing functions - fromComma, fromKMBT, fromNumeral, etc?
The semantics is also an issue.
toKMBT return a number for values below ±1000 yet in the docs it’s stated that it returns a string?

print(MathAddons.toKMBT(1) == 1)

prints true. This is a serious bug in my opinion, as using string methods could error saying attempting to index number value.
toComma disregards signed zeroes.

ToFraction also prints 2^-1022 incorrectly as 1/20001 false, and did not simplify the denominator of 2^53 when finite IEEE 754 binary64 values is a subset rational with the denominator of 2^k.

print(Math.ToFraction(2 ^ -1022))
print(Math.ToFraction(2 ^ 53))

the first is not even a close approximation, and in the second, the denominator could be simplified to 9007199254740992/1.


Can I ask what’s the point of MathAddons.pi when math.pi already exists? Adding more digits doesn’t make it any more precise:

MathFunctions.pi = 3.14159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214808651328230664709384460955058223172535940812848111745028410270193852110555

as math.pi is 0x1.921fb54442d18p1 which is represented in IEEE 754 binary64 as 0 10000000000 1001001000011111101101010100010001000010110100011000 and at a VM-level the number data type in Luau is IEEE 754 binary64 in Roblox.


I noticed that you used pairs on tables with only ordinal indices, do note that as pairs has the same behaviour as next, the order of pairs and next is explicitly undefined. You should use ipairs or generalized iteration instead.


I think semantics of many (or majority) of these is something you should improve in your library.

2 Likes

Thanks for the feedback,

Not necessarily intentional but not a bug either, normal math functions like math.min and math.max behave the same way,

print(math.min(0,-0)) -- = 0
print(math.min(-0,0)) -- = -0
print(math.max(0,-0)) -- = 0
print(math.max(-0,0)) -- = -0

this is just how table.sort functions, having -0 deemed greater than 0 is not an actual problem, since when being compared to non 0 numbers, they will function normally since -0 is equal to 0, and order wont matter.


This was my mistake! I forget that although you can still do and inequalities on numbers in forms of strings, that they wouldn’t remain strings. This will be patched in the next update.


These functions are more applicable compared to other functions, although they perform no actual manipulation or computation of the input, there still is math behind it.

This also will be fixed in the next update.

The module runs a loop which plays a game of hot or cold to guess the fraction, it runs a loop of 20,000 times to get the best approximation possible. for numbers less than 1/20001
I have fixed this however, now the function will scale the input so it becomes larger than 1/20001, this is what the new values now are

print(Math.ToFraction(2 ^ -1022)) -- = 174619484/7.84780619e315
print(Math.ToFraction(2 ^ 53)) -- = 9007199254740992/1

I’m not sure why you would need the first one in any case since any number with over 308 digits is just considered math.huge,

print(1/math.huge) -- = 0

You make a point, this will be removed in the coming update


This doesn’t make any real difference in the code, however this does seem safer to use in case something does get sorted incorrectly.


I appreciate the criticism, most of your suggestions will be implemented in the next update.

Update 4 - Minor Changes
Saturday, July 9th, 2022


Module Fixes


  • The mode function now returns a table of numbers, instead of strings
  • Most formatting functions now return string outputs when -1000 < x < 1000
  • Fraction function now supports numbers smaller than 1/20001
  • Solver function now supports being set equal to two functions f(x) = g(x), solve for x

Removed Objects


  • Pi - math.pi

What’s Next


Development of this module will go on a hiatus until the next big update.

3 Likes

Small personal update regarding the module, yesterday my pc overheated (from Roblox haha) and is refusing to turn on. I may have to buy a new one if the circumstances don’t change, which may take a while considering my budget and specifications I need.

To sum it up the next update for the module is probably in september.

(typing from my phone so disregard the organization)

This is an amazing module, still wondering. Has it been updated and/or are you still maintaining it?

sorry for bump

1 Like

thanks, and it isnt dead! there will be a few more functions coming out like prime factorization, logical operations like xor, nCr, and others things which are only good for mathematical use.

Im still here taking suggestions just i dont have the same time as i did in the summer when i was out of school

1 Like