local Module = {}
setmetatable(Module, {__index = math})
Module.Pi = math.pi
Module.Tau = math.pi * 2
Module.Huge = math.huge
Module.Euler = math.exp(1)
Module.Phi = (1 + math.sqrt(5)) / 2
Module.C = 299792458
Module.Epsilon = 1e-6
Module.Floor = math.floor
Module.Ceil = math.ceil
Module.Abs = math.abs
Module.Clamp = math.clamp
Module.Sign = math.sign
Module.Sin = math.sin
Module.Cos = math.cos
Module.Tan = math.tan
Module.Asin = math.asin
Module.Acos = math.acos
Module.Atan = math.atan
Module.Atan2 = math.atan2
Module.Sqrt = math.sqrt
Module.Exp = math.exp
Module.Log = math.log
Module.Log10 = math.log10
Module.Rad = math.rad
Module.Deg = math.deg
Module.Round = function(x: number, places: number?): number
local m = 10 ^ (places or 0)
return math.floor(x * m + 0.5) / m
end
Module.Add = function(...): number
local count = select("#", ...)
if count == 0 then return 0 end
local result = (select(1, ...))
for i = 2, count do
result += (select(i, ...))
end
return result
end
Module.Subtract = function(...): number
local count = select("#", ...)
if count == 0 then return 0 end
local result = (select(1, ...))
for i = 2, count do
result -= (select(i, ...))
end
return result
end
Module.Mult = function(...): number
local count = select("#", ...)
if count == 0 then return 0 end
local result = (select(1, ...))
for i = 2, count do
result *= (select(i, ...))
end
return result
end
Module.Divide = function(...): number
local count = select("#", ...)
if count == 0 then return 0 end
local result = (select(1, ...))
for i = 2, count do
local divisor = (select(i, ...))
if math.abs(divisor) < Module.Epsilon then
error("Math_Library: Divide — division by zero at argument " .. i)
end
result /= divisor
end
return result
end
Module.Fraction = function(a: number, b: number): number
if math.abs(b) < Module.Epsilon then
error("Math_Library: Fraction — denominator cannot be zero")
end
return a / b
end
Module.Reciprocal = function(x: number): number
if math.abs(x) < Module.Epsilon then
error("Math_Library: Reciprocal — cannot take reciprocal of zero")
end
return 1 / x
end
Module.Pow = function(x: number, power: number?): number
return x ^ (power or 2)
end
Module.Factorial = function(x: number): number
if x < 0 then error("Math_Library: Factorial — x must be non-negative") end
local c = 1
for i = 1, x do c *= i end
return c
end
Module.GetRemainder = function(value: number, divisor: number): (number, number)
if math.abs(divisor) < Module.Epsilon then
error("Math_Library: GetRemainder — divisor cannot be zero")
end
local quotient = math.floor(value / divisor)
local remainder = value - (quotient * divisor)
return quotient, remainder
end
Module.Percent = function(x: number): number
return x / 100
end
Module.InversePercent = function(x: number): number
return 1 - (x / 100)
end
Module.Sigma = function(start: number, goal: number, step: number?, callback: (i: number) -> number): number
local result = 0
local increment = step or 1
for i = start, goal, increment do
result += callback(i)
end
return result
end
Module.SlopeIntercept = function(m: number, x: number, b: number): number
return m * x + b
end
Module.Slope = function(...): number
local args = {...}
local n = #args
if n == 2 then
local v1, v2 = args[1], args[2]
if typeof(v1) == "Vector3" or typeof(v1) == "Vector2" then
local diff = v2 - v1
if math.abs(diff.X) < Module.Epsilon then
error("Math_Library: Slope — vertical line has undefined slope")
end
return diff.Y / diff.X
end
elseif n == 4 then
local x1, y1, x2, y2 = args[1], args[2], args[3], args[4]
if math.abs(x2 - x1) < Module.Epsilon then
error("Math_Library: Slope — vertical line has undefined slope")
end
return (y2 - y1) / (x2 - x1)
end
error("Math_Library: Slope — requires either 2 Vectors or 4 numbers")
end
Module.QuadFormula = function(a: number, b: number, c: number): (number, number)
local discriminant = Module.Pow(b) - 4 * a * c
if discriminant < 0 then
error("Math_Library: QuadFormula — no real solution (discriminant is negative)")
end
local root = Module.Sqrt(discriminant)
local denom = 2 * a
return (-b + root) / denom, (-b - root) / denom
end
Module.PythagoreanSq = function(a: number, b: number): number
return a * a + b * b
end
Module.Pythagorean = function(a: number, b: number): number
return math.sqrt(a * a + b * b)
end
Module.SolveLeg = function(knownSide: number, hypotenuse: number): number
if knownSide >= hypotenuse then
error("Math_Library: SolveLeg — side cannot be greater than or equal to the hypotenuse")
end
return math.sqrt(hypotenuse ^ 2 - knownSide ^ 2)
end
Module.AngleTo2D = function(from: Vector2 | Vector3, to: Vector2 | Vector3): number
local diff = to - from
return math.atan2(diff.Y, diff.X)
end
Module.RectangleArea = function(width: number, height: number): number
return width * height
end
Module.TriangleArea = function(base: number, height: number): number
return 0.5 * base * height
end
Module.CircleArea = function(radius: number): number
return Module.Pi * radius ^ 2
end
Module.Circumference = function(radius: number): number
return Module.Tau * radius
end
Module.BoxSurfaceArea = function(width: number, height: number, depth: number): number
return 2 * (width * height + height * depth + width * depth)
end
Module.BoxVolume = function(width: number, height: number, depth: number): number
return width * height * depth
end
Module.SphereSurfaceArea = function(radius: number): number
return 4 * Module.Pi * radius ^ 2
end
Module.SphereVolume = function(radius: number): number
return (4 / 3) * Module.Pi * radius ^ 3
end
Module.Lerp = function(start: number, goal: number, alpha: number): number
return start + (goal - start) * alpha
end
Module.RevLerp = function(a: number, b: number, value: number): number
if math.abs(b - a) < Module.Epsilon then
error("Math_Library: RevLerp — a and b cannot be equal")
end
return (value - a) / (b - a)
end
Module.Map = function(value: number, inMin: number, inMax: number, outMin: number, outMax: number): number
if math.abs(inMax - inMin) < Module.Epsilon then
error("Math_Library: Map — input range cannot be zero")
end
local t = (value - inMin) / (inMax - inMin)
return outMin + (outMax - outMin) * t
end
Module.Remap = function(value: number, inMin: number, inMax: number, outMin: number, outMax: number): number
if math.abs(inMax - inMin) < Module.Epsilon then
error("Math_Library: Remap — input range cannot be zero")
end
local t = math.clamp((value - inMin) / (inMax - inMin), 0, 1)
return outMin + (outMax - outMin) * t
end
Module.Wrap = function(value: number, min: number, max: number): number
local range = max - min
return ((value - min) % range + range) % range + min
end
Module.Half = function(x: any): any
local t = typeof(x)
if t == "number" or t:find("Vector") then
return x * 0.5
end
if t == "CFrame" then
return CFrame.identity:Lerp(x, 0.5)
end
if t == "UDim2" then
return UDim2.new(x.X.Scale * 0.5, x.X.Offset * 0.5, x.Y.Scale * 0.5, x.Y.Offset * 0.5)
end
if t == "UDim" then
return UDim.new(x.Scale * 0.5, x.Offset * 0.5)
end
if t == "Rect" then
return Rect.new(x.Min * 0.5, x.Max * 0.5)
end
if t == "Color3" then
return Color3.new(x.R * 0.5, x.G * 0.5, x.B * 0.5)
end
if t == "BrickColor" then
return BrickColor.new(Module.Half(x.Color))
end
if t == "ColorSequence" then
local keypoints = table.create(#x.Keypoints)
for i, kp in ipairs(x.Keypoints) do
keypoints[i] = ColorSequenceKeypoint.new(kp.Time, Color3.new(
kp.Value.R * 0.5, kp.Value.G * 0.5, kp.Value.B * 0.5
))
end
return ColorSequence.new(keypoints)
end
if t == "NumberRange" then
return NumberRange.new(x.Min * 0.5, x.Max * 0.5)
end
if t == "NumberSequence" then
local keypoints = {}
for _, kp in ipairs(x.Keypoints) do
table.insert(keypoints, NumberSequenceKeypoint.new(kp.Time, kp.Value * 0.5, kp.Envelope * 0.5))
end
return NumberSequence.new(keypoints)
end
if t == "Ray" then
return Ray.new(x.Origin, x.Direction * 0.5)
end
error("Math_Library: Half — unsupported type '" .. t .. "'")
end
Module.GetComponents = function(x: any): ...any
local t = typeof(x)
if t == "Vector3" then return x.X, x.Y, x.Z end
if t == "Vector2" then return x.X, x.Y end
if t == "Color3" then return x.R, x.G, x.B end
if t == "UDim2" then return x.X.Scale, x.X.Offset, x.Y.Scale, x.Y.Offset end
if t == "UDim" then return x.Scale, x.Offset end
if t == "CFrame" then return x:GetComponents() end
if t == "NumberRange" then return x.Min, x.Max end
if t == "Rect" then return x.Min.X, x.Min.Y, x.Max.X, x.Max.Y end
if t == "PhysicalProperties" then return x.Density, x.Friction, x.Elasticity end
if t == "BrickColor" then return x.Color.R, x.Color.G, x.Color.B end
warn("Math_Library: GetComponents — unsupported type '" .. t .. "'")
return x
end
Module.Calc2DMagnitude = function(faceNormalID: Enum.NormalId, start: Vector3, finish: Vector3): number
if not faceNormalID then
return (finish - start).Magnitude
end
local v1, v2
if faceNormalID == Enum.NormalId.Front or faceNormalID == Enum.NormalId.Back then
v1 = Vector2.new(start.X, start.Y)
v2 = Vector2.new(finish.X, finish.Y)
elseif faceNormalID == Enum.NormalId.Right or faceNormalID == Enum.NormalId.Left then
v1 = Vector2.new(start.Y, start.Z)
v2 = Vector2.new(finish.Y, finish.Z)
elseif faceNormalID == Enum.NormalId.Top or faceNormalID == Enum.NormalId.Bottom then
v1 = Vector2.new(start.X, start.Z)
v2 = Vector2.new(finish.X, finish.Z)
else
return 0
end
return (v2 - v1).Magnitude
end
Module.Distance = function(...): number
local a, b = ...
if typeof(a) == typeof(b) and typeof(a):find("Vector") then
return (b - a).Magnitude
end
local x1, y1, x2, y2 = ...
return Module.Sqrt(Module.Pow(x2 - x1) + Module.Pow(y2 - y1))
end
Module.UnitVector = function(vector: Vector3): Vector3
if vector.Magnitude < Module.Epsilon then
error("Math_Library: UnitVector — cannot normalize a zero-length vector")
end
return vector / vector.Magnitude
end
Module.Delta = function(initial: number, final: number): number
return final - initial
end
Module.IsNearlyZero = function(value: number): boolean
return math.abs(value) < Module.Epsilon
end
Module.IsNearlyEqual = function(a: number, b: number, tolerance: number?): boolean
return math.abs(a - b) < (tolerance or Module.Epsilon)
end
Module.ToGrid = function(index: number, columns: number): (number, number)
local x = (index - 1) % columns
local y = math.floor((index - 1) / columns)
return x, y
end
Module.InBounds = function(point: Vector3, min: Vector3, max: Vector3): boolean
return point.X >= min.X and point.X <= max.X and
point.Y >= min.Y and point.Y <= max.Y and
point.Z >= min.Z and point.Z <= max.Z
end
Module.GetHexValue = function(value: number): string
return string.format("0x%X", math.floor(math.abs(value)))
end
Module.GetNumberFromHexString = function(hex: string): number
return tonumber(hex, 16) or 0
end
Module.AbbreviateNumber = function(n: number): string
local suffixes = {"", "K", "M", "B", "T", "Qd", "Qn"}
if n < 1000 then return tostring(n) end
local exp = math.floor(math.log10(n) / 3)
local suffix = suffixes[exp + 1] or ("e+" .. exp)
local short = n / (10 ^ (exp * 3))
return string.format("%.2f%s", short, suffix):gsub("%.?0+$", "")
end
Module.StandardDeviation = function(numbers: {number}): number
if #numbers == 0 then error("Math_Library: StandardDeviation — dataset cannot be empty") end
local sum = 0
local count = #numbers
for _, v in ipairs(numbers) do sum += v end
local mean = sum / count
sum = 0
for _, v in ipairs(numbers) do sum += (v - mean) ^ 2 end
return math.sqrt(sum / count)
end
Module.FahrenheitToCelsius = function(fahrenheit: number): number
return Module.Fraction(5, 9) * (fahrenheit - 32)
end
Module.CelsiusToFahrenheit = function(celsius: number): number
return Module.Fraction(9, 5) * celsius + 32
end
Module.IsEven = function(n: number): boolean
return n % 2 == 0
end
Module.IsOdd = function(n: number): boolean
return n % 2 ~= 0
end
Module.IsPrime = function(n: number): boolean
if n <= 1 then return false end
if n <= 3 then return true end
if n % 2 == 0 or n % 3 == 0 then return false end
local i = 5
while i * i <= n do
if n % i == 0 or n % (i + 2) == 0 then return false end
i += 6
end
return true
end
Module.GeneratePrimes = function(limit: number): {number}
local isPrime = table.create(limit, true)
isPrime[1] = false
for p = 2, math.sqrt(limit) do
if isPrime[p] then
for i = p * p, limit, p do
isPrime[i] = false
end
end
end
local primes = {}
for p = 2, limit do
if isPrime[p] then table.insert(primes, p) end
end
return primes
end
Module.Bezier = function(t: number, p0: Vector3, p1: Vector3, p2: Vector3): Vector3
local l1 = p0:Lerp(p1, t)
local l2 = p1:Lerp(p2, t)
return l1:Lerp(l2, t)
end
Module.CreatePID = function(kp: number, ki: number, kd: number): (number, number, number) -> number
local state = {kp = kp, ki = ki, kd = kd, lastError = 0, integral = 0}
return function(dt: number, setpoint: number, pv: number): number
local err = setpoint - pv
state.integral += err * dt
local derivative = (err - state.lastError) / dt
state.lastError = err
return (err * state.kp) + (state.integral * state.ki) + (derivative * state.kd)
end
end
Module.Mu = function(time: number, x: number, y: number, z: number): Vector3
return Vector3.new(x, y, z).Unit * Module.C * time
end
Module.IntervalGap = function(index: number, range: number): number
return math.clamp(1 - (index - 1) * range, 0, 1)
end
return Module :: typeof(math) & typeof(Module)