-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathnumber.lua
More file actions
131 lines (120 loc) · 4.24 KB
/
Copy pathnumber.lua
File metadata and controls
131 lines (120 loc) · 4.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
-- number metatable
local math = require 'ext.math'
-- flatten ext.math into this just like we flatten lua's string into ext.string
local hasutf8, utf8 = pcall(require, 'utf8')
local number = {}
number.__index = number
for k,v in pairs(math) do number[k] = math[k] end
-- [[ tostring machine precision of arbitrary base
number.alphabets = {
{('0'):byte(), ('9'):byte()}, -- latin numbers
{('a'):byte(), ('z'):byte()}, -- english
{0x3b1, 0x3c9}, -- greek
{0x430, 0x45f}, -- cyrillic
{0x561, 0x586}, -- armenian
{0x905, 0x939}, -- devanagari
{0x2f00, 0x2fd5}, -- kangxi
{0x3041, 0x3096}, -- hiragana
{0x30a1, 0x30fa}, -- katakana
{0x4e00, 0x9fd0}, -- chinese, japanese, korean characters
}
function number.charfor(digit)
local table = require 'ext.table'
for _,alphabet in ipairs(number.alphabets) do
local start,fin = table.unpack(alphabet)
if digit <= fin - start then
digit = digit + start
if hasutf8 then
return utf8.char(digit)
else
-- TODO this will fail with utf8 chars beyond ascii
return string.char(digit)
end
end
digit = digit - (fin - start + 1)
end
error 'you need more alphabets to represent that many digits'
end
-- TODO rename above function to 'tochar' ?
function number.todigit(ch)
local table = require 'ext.table'
local indexInAlphabet
if hasutf8 then
indexInAlphabet = utf8.codepoint(ch)
else
-- TODO this will fail with utf8 chars beyond ascii
indexInAlphabet = string.byte(ch)
end
local lastTotalIndex = 0
for _,alphabet in ipairs(number.alphabets) do
local start,fin = table.unpack(alphabet)
if indexInAlphabet >= start and indexInAlphabet <= fin then
return lastTotalIndex + (indexInAlphabet - start)
end
lastTotalIndex = lastTotalIndex + (fin - start + 1)
end
error"couldn't find the character in all the alphabets"
end
number.base = 10
number.maxdecimals = 50
-- I'm not going to set this as __tostring by default, but I will leave it as part of the meta
-- feel free to use it with a line something like (function(m)m.__tostring=m.tostring end)(debug.getmetatable(0))
number.tostring = function(t, base, maxdecimals)
local s = {}
if t < 0 then
t = -t
table.insert(s, '-')
end
if t == 0 then
table.insert(s, '0')
table.insert(s, '.')
else
if not base then base = number.base end
if not maxdecimals then maxdecimals = number.maxdecimals end
local i = math.floor(math.log(t,base))+1
if i == math.huge then error'infinite number of digits' end
t = t / base^i
local dot
while true do
if i < 1 then
if not dot then
dot = true
table.insert(s, '.')
if i < 0 then
table.insert(s, ('0'):rep(-i))
end
end
if t == 0 then break end
if i <= -maxdecimals then break end
end
t = t * base
local digit = math.floor(t)
t = t - digit
-- at this point 'digit' holds an integer value in [0,base)
-- there's two ways we can go about it:
-- 1) traditionally, where each digit represents an integer, times some base^i for some i
-- in this case, for fractional bases, the last (fraction) digit is only considered up to the fraction
-- i.e. in base 2.5, from 0b..1b we have the span of 1, partitioned by digits 0.1b at 2/5ths the distance, 0.2b at 4/5ths the distance, and 1.0 and 5/5ths the distance
-- from 1b..2b we have the same span,
-- and from 2b..10b we have half that span, from 2 to 2.5
-- therefore the value 2.2b would represent 2.8, which is also represetned by 10.0112210002002002...
-- so as long as the span between digits can represent fractions of base^i rather than whole base^i's
-- we can have multiple representations of numbers
-- 2) stretched. in this case a fractional span, such as from 2b to 10b in base 2.5, would be stretched
-- this is harder to convey when descibing the number system
table.insert(s, number.charfor(digit))
i = i - 1
end
end
if s[#s] == '.' then s[#s] = nil end
return table.concat(s)
end
--]]
-- ('a'):byte():char() == 'a'
number.char = string.char
-- me getting tired of typing 'tostring(16)' etc ...
function number.bin(x, ...) return number.tostring(x, 2, ...) end
function number.oct(x, ...) return number.tostring(x, 8, ...) end
function number.hex(x, ...) return number.tostring(x, 16, ...) end
-- so the lookup goes: primitive number -> number -> math
return number