Implemented more advanced utf8 support + moved fractional support to the SPECIAL_NUMBER list.
This commit is contained in:
parent
ed252643e9
commit
2a652a4c92
66
number.lua
66
number.lua
|
@ -1,4 +1,6 @@
|
|||
|
||||
local utf8 = require "utf8"
|
||||
|
||||
-- Constants
|
||||
|
||||
local MAXIMUM_INT = 2^53 -- The maximum double for where all integers can be represented exactly.
|
||||
|
@ -46,30 +48,22 @@ local function calculate_fraction (n)
|
|||
end
|
||||
end
|
||||
|
||||
local UNICODE_SUPERSCRIPT = {
|
||||
['0'] = '⁰', ['1'] = '¹', ['2'] = '²', ['3'] = '³', ['4'] = '⁴',
|
||||
['5'] = '⁵', ['6'] = '⁶', ['7'] = '⁷', ['8'] = '⁸', ['9'] = '⁹',
|
||||
['-'] = '⁻', ['+'] = '⁺', ['='] = '⁼', ['('] = '⁽', [')'] = '⁾',
|
||||
}
|
||||
local function to_superscript (n)
|
||||
return tostring(n):gsub('.', function (a) return UNICODE_SUPERSCRIPT[a] end)
|
||||
end
|
||||
|
||||
local UNICODE_CHAR_PATTERN = '[\01-\127\192-\255][\128-\191]*'
|
||||
local function unicode_len (str)
|
||||
local len = 0
|
||||
for char in str:gmatch(UNICODE_CHAR_PATTERN) do len = len + 1 end
|
||||
return len
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
local format_num
|
||||
|
||||
local SPECIAL_NUMBER = {
|
||||
-- x = ∞
|
||||
{ est = function (...) return ... end,
|
||||
real = function (a) return math.huge end,
|
||||
repr = function (a) return '1/0' end,
|
||||
short = function (a) return '∞' end,
|
||||
},
|
||||
-- x = a/b
|
||||
{ est = calculate_fraction,
|
||||
real = function (a, b) return b ~= 1 and (a/b) end,
|
||||
repr = function (a, b) return format_num(a)..'/'..format_num(b) end,
|
||||
short = function (a, b) return format_num(a)..'/'..format_num(b) end,
|
||||
},
|
||||
-- Factorial
|
||||
{ est = function (n) return inverse_factorial(n) end,
|
||||
|
@ -81,19 +75,19 @@ local SPECIAL_NUMBER = {
|
|||
{ est = function (n) return math.log(n)/math.log(2) end,
|
||||
real = function (a) return 2^a end,
|
||||
repr = function (a) return '2^'..a end,
|
||||
short = function (a) return '2'..to_superscript(a) end,
|
||||
short = function (a) return '2'..utf8.superscript(a) end,
|
||||
},
|
||||
-- x = 10^a
|
||||
{ est = function (n) return math.log(n)/math.log(10) end,
|
||||
real = function (a) return 10^a end,
|
||||
repr = function (a) return '10^'..a end,
|
||||
short = function (a) return '10'..to_superscript(a) end,
|
||||
short = function (a) return '10'..utf8.superscript(a) end,
|
||||
},
|
||||
-- x = 1/√a
|
||||
{ est = function (n) return 1/(n^2) end,
|
||||
real = function (a) return 1/math.sqrt(a) end,
|
||||
repr = function (a) return ('1/math.sqrt(%i)'):format(a) end,
|
||||
short = function (a) return '1/√'..a end,
|
||||
short = function (a) return '1/√'..utf8.overline(a) end,
|
||||
},
|
||||
-- x = lg a
|
||||
{ est = function (n) return math.exp(n) end,
|
||||
|
@ -105,7 +99,7 @@ local SPECIAL_NUMBER = {
|
|||
{ est = function (n) return math.log(n) end,
|
||||
real = function (a) return math.exp(a) end,
|
||||
repr = function (a) return ('math.exp(%i)'):format(a) end,
|
||||
short = function (a) return a == 1 and 'ℯ' or 'ℯ'..to_superscript(a) end,
|
||||
short = function (a) return a == 1 and 'ℯ' or 'ℯ'..utf8.superscript(a)end,
|
||||
},
|
||||
-- x = aπ
|
||||
{ est = function (n) return n/math.pi end,
|
||||
|
@ -117,7 +111,7 @@ local SPECIAL_NUMBER = {
|
|||
{ est = function (n) return n^2 end,
|
||||
real = function (a) return math.sqrt(a) end,
|
||||
repr = function (a) return ('math.sqrt(%i)'):format(a) end,
|
||||
short = function (a) return '√'..a end,
|
||||
short = function (a) return '√'..utf8.overline(a) end,
|
||||
},
|
||||
-- a = x
|
||||
{ est = function (n) return n end,
|
||||
|
@ -129,39 +123,29 @@ local SPECIAL_NUMBER = {
|
|||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
local LONG_ASS_CONSTANT = '___________________________________________________'
|
||||
|
||||
local function format_number (n, shorthand)
|
||||
function format_num (n, shorthand)
|
||||
if n ~= n then return shorthand and 'NaN' or '0/0'
|
||||
elseif n < 0 then return '-' .. format_number(-n, shorthand)
|
||||
elseif n < 0 then return '-' .. format_num(-n, shorthand)
|
||||
end
|
||||
|
||||
-- Finding the shortest
|
||||
local shortest, length = LONG_ASS_CONSTANT, unicode_len(LONG_ASS_CONSTANT)
|
||||
local shortest, length = nil, math.huge
|
||||
local function alternative_repr (repr)
|
||||
local repr_len = unicode_len(repr)
|
||||
local repr_len = utf8.width(repr)
|
||||
if repr_len < length then shortest, length = repr, repr_len end
|
||||
end
|
||||
|
||||
-- Maybe it's a "special" number?
|
||||
for _, special_number_tests in pairs(SPECIAL_NUMBER) do
|
||||
local a = special_number_tests.est(n)
|
||||
if a then
|
||||
local a_near = math.floor(a + 0.5)
|
||||
if n == special_number_tests.real(a_near) then
|
||||
alternative_repr( special_number_tests[shorthand and 'short' or 'repr'](a_near) )
|
||||
local a = { special_number_tests.est(n) }
|
||||
if a[1] then
|
||||
for i = 1, #a do a[i] = math.floor(a[i] + 0.5) end
|
||||
if n == special_number_tests.real(unpack(a)) then
|
||||
alternative_repr( special_number_tests[shorthand and 'short' or 'repr'](unpack(a)) )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Maybe it's a fractional number?
|
||||
do
|
||||
local numerator, denumberator = calculate_fraction(n)
|
||||
if numerator and denumberator ~= 1 then
|
||||
alternative_repr( format_number(numerator)..'/'..format_number(denumberator) )
|
||||
end
|
||||
end
|
||||
|
||||
-- Maybe it's a decimal number?
|
||||
alternative_repr( tostring(n):gsub('([^e]+)e%+?(%-?)0+', function(a, b) return (a == '1' and '' or a..'*')..'10^'..b end))
|
||||
|
||||
|
@ -170,5 +154,5 @@ local function format_number (n, shorthand)
|
|||
end
|
||||
|
||||
return function (value, options, depth, l)
|
||||
l[#l+1] = format_number(value, options.math_shorthand)
|
||||
l[#l+1] = format_num(value, options.math_shorthand)
|
||||
end
|
||||
|
|
|
@ -101,13 +101,13 @@ number_test {
|
|||
number_test {
|
||||
input = math.sqrt(8),
|
||||
expect = 'math.sqrt(8)',
|
||||
shorthand = '√8',
|
||||
shorthand = '√̅8',
|
||||
}
|
||||
|
||||
number_test {
|
||||
input = 1/math.sqrt(8),
|
||||
expect = '1/math.sqrt(8)',
|
||||
shorthand = '1/√8',
|
||||
shorthand = '1/√̅8',
|
||||
}
|
||||
|
||||
number_test {
|
||||
|
@ -169,6 +169,12 @@ number_test {
|
|||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
number_test {
|
||||
-- A half should always be represented using 1/2, never with 2⁻¹.
|
||||
input = 1/2,
|
||||
expect = '1/2',
|
||||
}
|
||||
|
||||
format_test {
|
||||
input = { 1/2, 1/3, 1/4, 1/5 },
|
||||
expect = '{ 1/2, 1/3, 1/4, 1/5 }',
|
||||
|
|
Loading…
Reference in New Issue
Block a user