summaryrefslogtreecommitdiff
path: root/tex/context/base/mkiv/util-rnd.lua
blob: 7504965b21a814b6ae1a18375f6b68dc392b845a (plain)
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
if not modules then modules = { } end modules ['util-rnd'] = {
    version   = 1.001,
    comment   = "companion to luat-lib.mkiv",
    author    = "Tamara, Adriana, Tomáš Hála & Hans Hagen",
    copyright = "ConTeXt Development Team", -- umbrella
    license   = "see context related readme files"
}

-- The rounding code is a variant on Tomáš Hála <tomas.hala@mendelu.cz; mendelu@thala.cz>
-- code that is used in the statistical module. We use local variables and a tolerant name
-- resolver that also permits efficient local aliases. With code like this one
-- really have to make sure that locals are used because changing the rounding
-- can influence other code.

local floor, ceil, pow = math.floor, math.ceil, math.pow
local rawget, type = rawget, type
local gsub, lower = string.gsub, string.lower

local rounding = { }

local methods = {
    no = function(num)
        -- no rounding
        return num
    end,
    up = function(num,coef)
        -- ceiling rounding
        coef = coef and pow(10,coef) or 1
        return ceil(num * coef) / coef
    end,
    down = function(num,coef)
        -- floor rounding
        coef = coef and pow(10,coef) or 1
        return floor(num * coef) / coef
    end,
    halfup = function(num,coef)
        -- rounds decimal numbers as usual, numbers with 0.5 up, too (e.g. number -0.5 will be rounded to 0)
        coef = coef and pow(10,coef) or 1
        return floor(num * coef + 0.5) / coef
    end,
    halfdown = function(num,coef)
        -- rounds decimal numbers as usual, numbers with 0.5 down, too (e.g. number 0.5 will be rounded to 0)
        coef = coef and pow(10,coef) or 1
        return ceil(num * coef -0.5) / coef
    end,
    halfabsup = function(num,coef)
        -- 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
        coef = coef and pow(10,coef) or 1
        return (num >= 0 and floor(num * coef + 0.5) or ceil(num * coef - 0.5)) / coef
    end,
    halfabsdown = function(num,coef)
        -- 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
        coef = coef and pow(10,coef) or 1
        return (num <  0 and floor(num * coef + 0.5) or ceil(num * coef - 0.5)) / coef
    end,
    halfeven = function(num,coef)
       -- 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
        coef = coef and pow(10,coef) or 1
        num = num*coef
        return floor(num + (((num - floor(num)) ~= 0.5 and 0.5) or ((floor(num) % 2 == 1) and 1) or 0)) / coef
    end,
    halfodd = function(num,coef)
        -- 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
        coef = coef and pow(10,coef) or 1
        num = num * coef
        return floor(num + (((num - floor(num)) ~= 0.5 and 0.5) or ((floor(num) % 2 == 1) and 0) or 1)) / coef
    end,
}

methods.default = methods.halfup

rounding.methods = table.setmetatableindex(methods,function(t,k)
    local s = gsub(lower(k),"[^a-z]","")
    local v = rawget(t,s)
    if not v then
        v = t.halfup
    end
    t[k] = v
    return v
end)

-- If needed I can make a high performance one.

local defaultmethod = methods.halfup

rounding.round = function(num,dec,mode)
    if type(dec) == "string" then
        mode = dec
        dec  = 1
    end
    return (mode and methods[mode] or defaultmethods)(num,dec)
end

number.rounding = rounding

-- -- Tomáš' test numbers:

-- local list = { 5.49, 5.5, 5.51, 6.49, 6.5, 6.51, 0.5, 12.45 }
--
-- for method, round in table.sortedhash(number.rounding.methods) do
--     for i=1,#list do
--         local n = list[i]
--         print(n,method,round(n,k),round(n,k,3))
--     end
-- end
--
-- local myround = number.rounding.methods["HALF ABS DOWN"]
--
-- for i=1,#list do
--     local n = list[i]
--     print(n,"Half Abs Down",number.rounding.round(n,1,"Half Abs Down"))
--     print(n,"HALF_ABS_DOWN",number.rounding.round(n,1,"HALF_ABS_DOWN"))
--     print(n,"HALF_ABS_DOWN",myround(n,1))
-- end

return rounding