util-rnd.lua /size: 4177 b    last modification: 2024-01-16 10:22
1if not modules then modules = { } end modules ['util-rnd'] = {
2    version   = 1.001,
3    comment   = "companion to luat-lib.mkiv",
4    author    = "Tamara, Adriana, Tomáš Hála & Hans Hagen",
5    copyright = "ConTeXt Development Team", -- umbrella
6    license   = "see context related readme files"
7}
8
9-- The rounding code is a variant on Tomáš Hála <tomas.hala@mendelu.cz; mendelu@thala.cz>
10-- code that is used in the statistical module. We use local variables and a tolerant name
11-- resolver that also permits efficient local aliases. With code like this one
12-- really have to make sure that locals are used because changing the rounding
13-- can influence other code.
14
15local floor, ceil, pow = math.floor, math.ceil, math.pow
16local rawget, type = rawget, type
17local gsub, lower = string.gsub, string.lower
18
19local rounding = { }
20
21local methods = {
22    no = function(num)
23        -- no rounding
24        return num
25    end,
26    up = function(num,coef)
27        -- ceiling rounding
28        coef = coef and pow(10,coef) or 1
29        return ceil(num * coef) / coef
30    end,
31    down = function(num,coef)
32        -- floor rounding
33        coef = coef and pow(10,coef) or 1
34        return floor(num * coef) / coef
35    end,
36    halfup = function(num,coef)
37        -- rounds decimal numbers as usual, numbers with 0.5 up, too (e.g. number -0.5 will be rounded to 0)
38        coef = coef and pow(10,coef) or 1
39        return floor(num * coef + 0.5) / coef
40    end,
41    halfdown = function(num,coef)
42        -- rounds decimal numbers as usual, numbers with 0.5 down, too (e.g. number 0.5 will be rounded to 0)
43        coef = coef and pow(10,coef) or 1
44        return ceil(num * coef -0.5) / coef
45    end,
46    halfabsup = function(num,coef)
47        -- rounds deciaml numbers as usual, numbers with 0.5 away from zero, e.g. numbers -0.5 and 0.5 will be rounded to -1 and 1
48        coef = coef and pow(10,coef) or 1
49        return (num >= 0 and floor(num * coef + 0.5) or ceil(num * coef - 0.5)) / coef
50    end,
51    halfabsdown = function(num,coef)
52        -- rounds deciaml numbers as usual, numbers with 0.5 towards zero, e.g. numbers -0.5 and 0.5 will be rounded both to 0
53        coef = coef and pow(10,coef) or 1
54        return (num <  0 and floor(num * coef + 0.5) or ceil(num * coef - 0.5)) / coef
55    end,
56    halfeven = function(num,coef)
57       -- rounds deciaml numbers as usual, numbers with 0.5 to the nearest even, e.g. numbers 1.5 and 2.5 will be rounded both to 2
58        coef = coef and pow(10,coef) or 1
59        num = num*coef
60        return floor(num + (((num - floor(num)) ~= 0.5 and 0.5) or ((floor(num) % 2 == 1) and 1) or 0)) / coef
61    end,
62    halfodd = function(num,coef)
63        -- rounds deciaml numbers as usual, numbers with 0.5 to the nearest odd (e.g. numbers 1.5 and 2.5 will be rounded to 1 and 3
64        coef = coef and pow(10,coef) or 1
65        num = num * coef
66        return floor(num + (((num - floor(num)) ~= 0.5 and 0.5) or ((floor(num) % 2 == 1) and 0) or 1)) / coef
67    end,
68}
69
70methods.default = methods.halfup
71
72rounding.methods = table.setmetatableindex(methods,function(t,k)
73    local s = gsub(lower(k),"[^a-z]","")
74    local v = rawget(t,s)
75    if not v then
76        v = t.halfup
77    end
78    t[k] = v
79    return v
80end)
81
82-- If needed I can make a high performance one.
83
84local defaultmethod = methods.halfup
85
86rounding.round = function(num,dec,mode)
87    if type(dec) == "string" then
88        mode = dec
89        dec  = 1
90    end
91    return (mode and methods[mode] or defaultmethods)(num,dec)
92end
93
94number.rounding = rounding
95
96-- -- Tomáš' test numbers:
97
98-- local list = { 5.49, 5.5, 5.51, 6.49, 6.5, 6.51, 0.5, 12.45 }
99--
100-- for method, round in table.sortedhash(number.rounding.methods) do
101--     for i=1,#list do
102--         local n = list[i]
103--         print(n,method,round(n,k),round(n,k,3))
104--     end
105-- end
106--
107-- local myround = number.rounding.methods["HALF ABS DOWN"]
108--
109-- for i=1,#list do
110--     local n = list[i]
111--     print(n,"Half Abs Down",number.rounding.round(n,1,"Half Abs Down"))
112--     print(n,"HALF_ABS_DOWN",number.rounding.round(n,1,"HALF_ABS_DOWN"))
113--     print(n,"HALF_ABS_DOWN",myround(n,1))
114-- end
115
116return rounding
117