Added number.lua
file, to allow more informative representations of numbers.
This commit is contained in:
parent
b405afeae8
commit
cad8258a6f
118
number.lua
Normal file
118
number.lua
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
|
||||||
|
-- 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
|
24
pretty.lua
24
pretty.lua
|
@ -1,13 +1,17 @@
|
||||||
|
|
||||||
|
|
||||||
-- Ensure loading library, if it exists, no matter where pretty.lua was loaded from.
|
-- Ensure loading library, if it exists, no matter where pretty.lua was loaded from.
|
||||||
|
|
||||||
-- Load the library component
|
-- Load the library component
|
||||||
local LIBRARY
|
local LIBRARY, format_number
|
||||||
do
|
do
|
||||||
local thispath = ... and select('1', ...):match('.+%.') or ''
|
local thispath = ... and select('1', ...):match('.+%.') or ''
|
||||||
|
|
||||||
|
-- Load library info
|
||||||
local was_loaded, library = pcall(require, thispath..'library')
|
local was_loaded, library = pcall(require, thispath..'library')
|
||||||
LIBRARY = was_loaded and library or {}
|
LIBRARY = was_loaded and library or {}
|
||||||
|
|
||||||
|
-- Load number formatting
|
||||||
|
was_loaded, format_number = pcall(require, thispath..'number')
|
||||||
end
|
end
|
||||||
--
|
--
|
||||||
|
|
||||||
|
@ -114,10 +118,10 @@ local function compare_key_value_pairs (a, b)
|
||||||
local type_value_a, type_value_b = type(a[2]), type(b[2])
|
local type_value_a, type_value_b = type(a[2]), type(b[2])
|
||||||
|
|
||||||
-- Tons of compare
|
-- Tons of compare
|
||||||
if (type_key_a ~= 'string' or type_key_b ~= 'string') then
|
if (type_key_a == 'string' and type_key_b == 'string') then
|
||||||
return TYPE_SORT_ORDER[type_key_a] < TYPE_SORT_ORDER[type_key_b]
|
|
||||||
elseif (type_value_a == type_value_b) then
|
|
||||||
return alphanum_compare_strings(a[1], b[1])
|
return alphanum_compare_strings(a[1], b[1])
|
||||||
|
elseif (type_key_a == 'number' and type_key_b == 'number') then
|
||||||
|
return a[1] < b[1]
|
||||||
else
|
else
|
||||||
return TYPE_SORT_ORDER[type_value_a] < TYPE_SORT_ORDER[type_value_b]
|
return TYPE_SORT_ORDER[type_value_a] < TYPE_SORT_ORDER[type_value_b]
|
||||||
end
|
end
|
||||||
|
@ -470,10 +474,8 @@ local function format_string (str, options, depth, l)
|
||||||
local single_quote_index = str:find('\'')
|
local single_quote_index = str:find('\'')
|
||||||
local double_quote_index = str:find('\"')
|
local double_quote_index = str:find('\"')
|
||||||
|
|
||||||
|
-- ...
|
||||||
local chance_of_longform = is_long_string and (newline_or_tab_index <= NR_CHARS_IN_LONG_STRING) or double_quote_index and single_quote_index
|
local chance_of_longform = is_long_string and ((newline_or_tab_index or math.huge) <= NR_CHARS_IN_LONG_STRING) or double_quote_index and single_quote_index
|
||||||
|
|
||||||
|
|
||||||
local cut_string_index = options.cut_strings and (is_long_string or chance_of_longform)
|
local cut_string_index = options.cut_strings and (is_long_string or chance_of_longform)
|
||||||
and math.min(NR_CHARS_IN_LONG_STRING - 3, newline_or_tab_index or 1/0, double_quote_index or 1/0, single_quote_index or 1/0)
|
and math.min(NR_CHARS_IN_LONG_STRING - 3, newline_or_tab_index or 1/0, double_quote_index or 1/0, single_quote_index or 1/0)
|
||||||
|
|
||||||
|
@ -504,7 +506,8 @@ local function format_string (str, options, depth, l)
|
||||||
l[#l+1] = right
|
l[#l+1] = right
|
||||||
end
|
end
|
||||||
|
|
||||||
local function format_number (value, options, depth, l)
|
if not format_number then
|
||||||
|
function format_number (value, options, depth, l)
|
||||||
local shorthand = options.math_shorthand
|
local shorthand = options.math_shorthand
|
||||||
if value ~= value then l[#l+1] = shorthand and 'nan' or '0/0'
|
if value ~= value then l[#l+1] = shorthand and 'nan' or '0/0'
|
||||||
elseif value == 1/0 then l[#l+1] = shorthand and 'inf' or '1/0'
|
elseif value == 1/0 then l[#l+1] = shorthand and 'inf' or '1/0'
|
||||||
|
@ -512,6 +515,7 @@ local function format_number (value, options, depth, l)
|
||||||
else l[#l+1] = tostring(value)
|
else l[#l+1] = tostring(value)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
local function format_coroutine (value, options, depth, l)
|
local function format_coroutine (value, options, depth, l)
|
||||||
l[#l+1] = coroutine.status(value)
|
l[#l+1] = coroutine.status(value)
|
||||||
|
|
|
@ -131,6 +131,67 @@ format_test {
|
||||||
expect = '-2000',
|
expect = '-2000',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- Numbers, Other formats
|
||||||
|
|
||||||
|
format_test {
|
||||||
|
input = 10^5,
|
||||||
|
expect = '10^5',
|
||||||
|
}
|
||||||
|
|
||||||
|
format_test {
|
||||||
|
input = 10^-6,
|
||||||
|
expect = '10^-6',
|
||||||
|
}
|
||||||
|
|
||||||
|
format_test {
|
||||||
|
input = 2^17,
|
||||||
|
expect = '2^17',
|
||||||
|
}
|
||||||
|
|
||||||
|
format_test {
|
||||||
|
input = 2^-7,
|
||||||
|
expect = '2^-7',
|
||||||
|
}
|
||||||
|
|
||||||
|
format_test {
|
||||||
|
input = 1/3,
|
||||||
|
expect = '1/3',
|
||||||
|
}
|
||||||
|
|
||||||
|
format_test {
|
||||||
|
input = 9/17,
|
||||||
|
expect = '9/17',
|
||||||
|
}
|
||||||
|
|
||||||
|
format_test {
|
||||||
|
input = math.sqrt(2),
|
||||||
|
expect = 'math.sqrt(2)',
|
||||||
|
}
|
||||||
|
|
||||||
|
format_test {
|
||||||
|
input = 1/math.sqrt(2),
|
||||||
|
expect = '1/math.sqrt(2)',
|
||||||
|
}
|
||||||
|
|
||||||
|
format_test {
|
||||||
|
input = math.log(2),
|
||||||
|
expect = 'math.log(2)',
|
||||||
|
}
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- Numbers, Constants
|
||||||
|
|
||||||
|
format_test {
|
||||||
|
input = math.pi,
|
||||||
|
expect = 'math.pi',
|
||||||
|
}
|
||||||
|
|
||||||
|
format_test {
|
||||||
|
input = math.exp(1),
|
||||||
|
expect = 'math.exp(1)',
|
||||||
|
}
|
||||||
|
|
||||||
format_test {
|
format_test {
|
||||||
input = 1/0, -- Same as math.huge
|
input = 1/0, -- Same as math.huge
|
||||||
expect = '1/0',
|
expect = '1/0',
|
||||||
|
@ -149,13 +210,13 @@ format_test {
|
||||||
format_test {
|
format_test {
|
||||||
input = 1/0, -- Same as math.huge
|
input = 1/0, -- Same as math.huge
|
||||||
options = { math_shorthand = true },
|
options = { math_shorthand = true },
|
||||||
expect = 'inf',
|
expect = '∞',
|
||||||
}
|
}
|
||||||
|
|
||||||
format_test {
|
format_test {
|
||||||
input = -1/0, -- Same as -math.huge
|
input = -1/0, -- Same as -math.huge
|
||||||
options = { math_shorthand = true },
|
options = { math_shorthand = true },
|
||||||
expect = '-inf',
|
expect = '-∞',
|
||||||
}
|
}
|
||||||
|
|
||||||
format_test {
|
format_test {
|
||||||
|
@ -164,6 +225,23 @@ format_test {
|
||||||
expect = 'nan',
|
expect = 'nan',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
format_test {
|
||||||
|
input = math.pi,
|
||||||
|
options = { math_shorthand = true },
|
||||||
|
expect = 'π',
|
||||||
|
}
|
||||||
|
|
||||||
|
format_test {
|
||||||
|
input = math.exp(1),
|
||||||
|
options = { math_shorthand = true },
|
||||||
|
expect = 'e',
|
||||||
|
}
|
||||||
|
|
||||||
|
format_test {
|
||||||
|
input = { 1/2, 1/3, 1/4, 1/5 },
|
||||||
|
expect = '{ 1/2, 1/3, 1/4, 1/5 }',
|
||||||
|
}
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- Primitive types
|
-- Primitive types
|
||||||
|
|
||||||
|
@ -488,7 +566,6 @@ format_test { -- Can include very small tables
|
||||||
expect = '{ { 1 }, { 2 }, { 3 } }',
|
expect = '{ { 1 }, { 2 }, { 3 } }',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- Multi-line tables
|
-- Multi-line tables
|
||||||
|
|
||||||
|
@ -573,6 +650,47 @@ format_test {
|
||||||
expect = '{\n\t{ a = \'hello\', b = \'hi\' },\n\t{ a = \'hi\', b = \'hello\' }\n}',
|
expect = '{\n\t{ a = \'hello\', b = \'hi\' },\n\t{ a = \'hi\', b = \'hello\' }\n}',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- Table Sorting
|
||||||
|
|
||||||
|
format_test {
|
||||||
|
input = { 'c', 'b', 'a' },
|
||||||
|
expect = '{ \'c\', \'b\', \'a\' }',
|
||||||
|
}
|
||||||
|
|
||||||
|
format_test {
|
||||||
|
input = { a = 1, a1 = 0 },
|
||||||
|
expect = '{ a = 1, a1 = 0 }',
|
||||||
|
}
|
||||||
|
|
||||||
|
format_test {
|
||||||
|
input = { a10 = 1, a1 = 0 },
|
||||||
|
expect = '{ a1 = 0, a10 = 1 }',
|
||||||
|
}
|
||||||
|
|
||||||
|
format_test {
|
||||||
|
input = { a00 = 0, a1 = 1 },
|
||||||
|
expect = '{ a00 = 0, a1 = 1 }',
|
||||||
|
}
|
||||||
|
|
||||||
|
format_test {
|
||||||
|
input = { a = {}, b = true },
|
||||||
|
expect = '{ b = true, a = {} }',
|
||||||
|
}
|
||||||
|
|
||||||
|
format_test {
|
||||||
|
input = { a = {}, b = true, b1 = false },
|
||||||
|
expect = '{ b = true, b1 = false, a = {} }',
|
||||||
|
}
|
||||||
|
|
||||||
|
format_test {
|
||||||
|
input = { {}, true, false, 5 },
|
||||||
|
expect = '{ {}, true, false, 5 }',
|
||||||
|
}
|
||||||
|
|
||||||
|
-- TODO: Add more tests for sorting.
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- CDATA
|
-- CDATA
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user