summaryrefslogtreecommitdiff
path: root/xmake/core/base/heap.lua
blob: ac090c4b92faf32985bd07fbf5218fced3089623 (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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
--!A cross-platform build utility based on Lua
--
-- priority queue implemented as a binary heap.
-- written by Cosmin Apreutesei. Public Domain.
--
-- @see https://github.com/luapower/heap
--
-- modified by  ruki
-- @file        heap.lua
--

-- define module: heap
local heap  = heap or {}

local ffi --init on demand so that the module can be used without luajit
local assert, floor = assert, math.floor

-- heap algorithm working over abstract API that counts from one.
function heap._make(add, remove, swap, length, cmp)

    local function moveup(child)
        local parent = floor(child / 2)
        while child > 1 and cmp(child, parent) do
            swap(child, parent)
            child = parent
            parent = floor(child / 2)
        end
        return child
    end

    local function movedown(parent)
        local last = length()
        local child = parent * 2
        while child <= last do
            if child + 1 <= last and cmp(child + 1, child) then
                child = child + 1 -- sibling is smaller
            end
            if not cmp(child, parent) then break end
            swap(parent, child)
            parent = child
            child = parent * 2
        end
        return parent
    end

    local function push(...)
        add(...)
        return moveup(length())
    end

    local function pop(i)
        swap(i, length())
        remove()
        movedown(i)
    end

    local function rebalance(i)
        if moveup(i) == i then
            movedown(i)
        end
    end
    return push, pop, rebalance
end

-- create a heap working over a cdata array (FFI)
--
-- @param h     the options {size = N, ctype = "int", cmp = function(a, b), data = cdata, length = 0}
-- @return      push(val), pop() -> val, rebalance(index) functions
--
function heap.cdataheap(h)
    ffi = ffi or require("ffi")
    assert(h and h.size, "size expected")
    assert(h.size >= 2, "size too small")
    assert(h.ctype, "ctype expected")
    local ctype = ffi.typeof(h.ctype)
    h.data = h.data or ffi.new(ffi.typeof("$[?]", ctype), h.size)
    local t, n, maxn = h.data, h.length or 0, h.size - 1
    local function add(v) n = n + 1; t[n] = v end
    local function rem() n = n - 1 end
    local function swap(i, j) t[0] = t[i]; t[i] = t[j]; t[j] = t[0] end
    local function length() return n end
    local cmp = h.cmp and
        function(i, j) return h.cmp(t[i], t[j]) end or
        function(i, j) return t[i] < t[j] end
    local push, pop, rebalance = heap._make(add, rem, swap, length, cmp)

    local function get(i, box)
        assert(i >= 1 and i <= n, "invalid index")
        if box then
            box[0] = t[i]
        else
            return ffi.new(ctype, t[i])
        end
    end
    function h:push(v)
        assert(n < maxn, "buffer overflow")
        push(v)
    end
    function h:pop(i, box)
        assert(n > 0, "buffer underflow")
        local v = get(i or 1, box)
        pop(i or 1)
        return v
    end
    function h:peek(i, box)
        return get(i or 1, box)
    end
    function h:replace(i, v)
        assert(i >= 1 and i <= n, "invalid index")
        t[i] = v
        rebalance(i)
    end
    h.length = length
    return h
end

-- create a heap working over a Lua table
--
-- @param h     the options (optional) {cmp = function(a, b)}
-- @return      the heap table with push(val), pop() -> val, rebalance(index)
--
function heap.valueheap(h)
    h = h or {}
    local t, n = h, #h
    local function add(v) n = n + 1; t[n] = v end
    local function rem() t[n] = nil; n = n - 1 end
    local function swap(i, j) t[i], t[j] = t[j], t[i] end
    local function length() return n end
    local cmp = h.cmp and
        function(i, j) return h.cmp(t[i], t[j]) end or
        function(i, j) return t[i] < t[j] end
    local push, pop, rebalance = heap._make(add, rem, swap, length, cmp)

    local function get(i)
        assert(i >= 1 and i <= n, "invalid index")
        return t[i]
    end
    function h:push(v)
        assert(v ~= nil, "invalid value")
        push(v)
    end
    function h:pop(i)
        assert(n > 0, "buffer underflow")
        local v = get(i or 1)
        pop(i or 1)
        return v
    end
    function h:peek(i)
        return get(i or 1)
    end
    function h:replace(i, v)
        assert(i >= 1 and i <= n, "invalid index")
        t[i] = v
        rebalance(i)
    end
    h.length = length
    return h
end

-- return module: heap
return heap