1
0
pretty/number.lua

119 lines
3.2 KiB
Lua

-- Constants
local MAXIMUM_INT = 2^53 -- The maximum double for where all integers can be represented exactly.
local MAXIMUM_ZERO = 10^-7 -- Used when attempting to determine fraction. Anything below is counted as 0.
--------------------------------------------------------------------------------
-- Util
local function is_integer (n)
-- Predicate. Neither +inf, -inf nor nan are integer.
return n == math.floor(n) and n ~= 1/0 and n ~= -1/0
end
local function factorial (n)
-- Returns the factorial of n.
-- That is n! = n * (n-1) * (n-2) * ... * 1
local a = 1
for i = 2, n do a = a * i end
return a
end
local function calculate_fraction (n)
-- Returns x and y such that x/y = n. If none could be found, returns nil.
local a, b = 1, n % 1
while MAXIMUM_ZERO < b and a <= MAXIMUM_INT do
local r = math.pow(b, -1)
a, b = a * r, r % 1
-- Invariant: n * a / a = n
end
-- Check the values make sense.
local numerator, denumberator = math.floor(n * a), math.floor(a)
if numerator / denumberator == n then
return numerator, denumberator
end
end
local MATH_SHORTHAND = {
-- Only used if options.math_shorthand = true
['0/0'] = 'nan',
['1/0'] = '',
['-1/0'] = '-∞',
['math.pi'] = 'π',
['math.exp(1)'] = 'e',
}
--------------------------------------------------------------------------------
-- Generate special number table
-- TODO: Restructure this.
local SPECIAL_NUMBER = {}
do
SPECIAL_NUMBER[ 1/0] = '1/0'
SPECIAL_NUMBER[math.pi] = 'math.pi'
SPECIAL_NUMBER[math.exp(1)] = 'math.exp(1)'
for i = -10, -6 do
SPECIAL_NUMBER[math.pow(10, i)] = '10^'..i
end
for i = -15, -7 do
SPECIAL_NUMBER[math.pow( 2, i)] = '2^'..i
end
for i = 5, 308 do
SPECIAL_NUMBER[math.pow(10, i)] = '10^'..i
end
for i = 17, 1023 do
SPECIAL_NUMBER[math.pow( 2, i) ] = '2^'..i
--SPECIAL_NUMBER[math.pow( 2, i) - 1] = '2^'..i..'-1'
--SPECIAL_NUMBER[math.pow( 2, i) + 1] = '2^'..i..'+1'
end
for i = 9, 170 do
SPECIAL_NUMBER[factorial(i)] = i..'!'
end
for i = 1, 100 do
local sqrt = math.sqrt(i)
if not is_integer(sqrt) then
SPECIAL_NUMBER[1/sqrt] = '1/math.sqrt('..i..')'
SPECIAL_NUMBER[sqrt] = 'math.sqrt('..i..')'
end
end
for i = 1, 100 do
local log = math.log(i)
if not is_integer(log) then
SPECIAL_NUMBER[log] = 'math.log('..i..')'
end
end
end
--------------------------------------------------------------------------------
local function format_number (n)
-- Returns a string of n formatted as to allow easy back into a lua interpreter.
if n ~= n then return '0/0'
elseif n < 0 then return '-' .. format_number(-n)
elseif SPECIAL_NUMBER[n] then return SPECIAL_NUMBER[n]
end
-- Maybe it's an integer?
if is_integer(n) then return tostring(n) end
-- Maybe it's a fraction?
local numerator, denumberator = calculate_fraction(n)
if numerator then
return format_number(numerator)..'/'..format_number(denumberator)
end
return ( tostring(n):gsub('e%+?', '*10^') ) -- Parantheses to return first result.
end
return function (value, options, depth, l)
l[#l+1] = format_number(value)
if options.math_shorthand then
l[#l] = MATH_SHORTHAND[ l[#l] ]
end
end