File: libmath.xml

package info (click to toggle)
enigma 1.30%2Bdfsg-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 76,132 kB
  • sloc: xml: 162,251; cpp: 67,393; ansic: 28,606; makefile: 1,986; sh: 1,298; yacc: 288; perl: 84; sed: 16
file content (333 lines) | stat: -rw-r--r-- 13,413 bytes parent folder | download | duplicates (5)
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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<el:level xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://enigma-game.org/schema/level/1 level.xsd" xmlns:el="http://enigma-game.org/schema/level/1">
  <el:protected>
    <el:info el:type="library">
      <el:identity el:title="" el:id="lib/libmath"/>
      <el:version el:score="1" el:release="1" el:revision="7" el:status="released"/>
      <el:author  el:name="Enigma Team" el:email="" el:homepage=""/>
      <el:copyright>Copyright © 2007, 2008 Enigma Team</el:copyright>
      <el:license el:type="GPL v2.0 or above" el:open="true"/>
      <el:compatibility el:enigma="1.10">
        <el:dependency el:path="lib/liblua" el:id="lib/liblua" el:release="1" el:preload="true"/>
      </el:compatibility>
      <el:modes el:easy="false" el:single="false" el:network="false"/>
      <el:comments>
      </el:comments>
      <el:score el:easy="-" el:difficult="-"/>
    </el:info>
    <el:luamain><![CDATA[

---------------------------------------------------------------------
-- libmath includes mathematical and functions and algorithms
-- of various origins.
---------------------------------------------------------------------
--
-- libmath provides the following functions:
--   lib.math.manhattan_distance(x1, y1, x2, y2)
--   lib.math.digits(number, base)
--   lib.math.combinations(depth, digits)
--   lib.math.cubic_polynomial(a, x, y)
--   lib.math.permutation(n)
--   lib.math.cyclic_permutation(n)  
--   lib.math.random_vector(number, ...)
--   lib.math.mark_components(group, attribute_name, neighborhood, exclusive_attribute)
-- Additional mathematically relevant functions are part of liblua and api2init:
--   lib.lua.mod(value, modul)
--   cond(condition, iftrue, iffalse)
--
-- libmath depends on liblua, as lib.math.combinations makes
-- use of lib.lua.deep_copy.
--

lib.math = {}
setmetatable(lib.math, getmetatable(lib))

---------------------------------------------------------------------
--  ADVANCED  POSITION  HANDLING  AND  CALCULATIONS
---------------------------------------------------------------------

-- manhattan_distance calculates the Manhattan-distance between
-- (X1,Y1) and (X2, Y2), which is |X1 - X2| + |Y1 - Y2|.
-- If X2 and Y2 are nil, X1 and Y1 are assumed to be positions
-- instead of coordinates.
function lib.math.manhattan_distance(x1, y1, x2, y2)
    if x1 and y1 and x2 and y2 then
        -- x1, y1, x2, y2 are coordinates
        return math.abs(x1 - x2) + math.abs(y1 - y2)
    end
    if x1 and y1 then
        -- x1 and y1 are positions, possibly tables.
        local p1 = x1
        local p2 = y1
        if type(p1) == "table" then
            p1 = po(p1)
        end
        if type(p2) == "table" then
            p2 = po(p2)
        end
        return math.abs(p1.x - p2.x) + math.abs(p1.y - p2.y)
    end
    error("manhattan_distance: Too less arguments.", 2)
end

---------------------------------------------------------------------
--  MATHEMATICAL  FUNCTIONS
---------------------------------------------------------------------

-- digits returns a table whose elements are the digits of NUMBER
-- in base BASE. BASE can be a number (e.g. 3 to get ternary)
-- as well as a table (then the table entries with numerical
-- keys will be used as digits).
-- Examples:
--   lib.math.digits(13, 2) = {1, 0, 1, 1}
--   lib.math.digits(15, 16) = {15}
--   lib.math.digits(17, 3) = {2, 2, 1}
--   lib.math.digits(17, {2, "b", 5}) = {5, 5, "b"}
-- Hexadecimal would be:
--   lib.math.digits(x, {0,1,2,3,4,5,6,7,8,9,"A","B","C","D","E","F"})
-- NUMBER is supposed to be a non-negative integer.
-- If NUMBER is zero, lib.math.digits returns the zero-digit as
-- result, unless ZERO_IS_EMPTY is true - in this case, it returns
-- an empty table.
function lib.math.digits(number, base, zero_is_empty)
    -- Check arguments and calculate fullbase and exponent
    assert_type(number, "lib.math.digits first argument", 2, "natural")
    assert_type(base, "lib.math.digits second argument", 2, "integer", "table")
    assert_type(zero_is_empty, "lib.math.digits third argument", 2, "boolean", "nil")
    local fullbase = {}
    local exponent = 0
    if type(base) == "number" then
        if base < 2 then
            error("lib.math.digits: Second argument out of range, is "..base..", must be at least 2.", 2)
        end
        for j = 1, base do
            table.insert(fullbase, j - 1)
        end
    else -- type(base) == "table"
        if table.getn(base) < 2 then
            error("lib.math.digits: Second argument has not enough elements, must be at least 2.", 2)
        end
        fullbase = base
    end
    exponent = table.getn(fullbase)
    -- Handle zero
    if number == 0 then
        return cond(zero_is_empty, {}, {fullbase[1]})
    end
    -- Decompose NUMBER
    local remains = number
    local result = {}
    while remains > 0 do
        local d = remains % exponent
        table.insert(result, fullbase[d + 1])
        remains = (remains - d) / exponent
        if remains ~= math.ceil(remains) then
            error("lib.math.digits: Internal error during calculation (remains = "..remains..").")
        end
    end
    return result
end

-- Return a table of all combinations of DEPTH entries,
-- each of which is chosen from DIGITS.
-- Example: lib.math.combinations(3, {7, 8, "a"}) will return
--   { {7,7,7}, {7,7,8}, {7,7,"a"}, {7,8,7}, {7,8,8}, {7,8,"a"},
--     {7,"a",7}, {7,"a",8}, {7,"a","a"}, {8,7,7}, ... }
-- Mathematically, it builds the leafs of an #DIGITS-ary tree
-- of depth DEPTH. Note that liblua has to be loaded to use
-- lib.math.combinations.
function lib.math.combinations(depth, digits)
    local all_combinations = {{}}
    local digs = digits
    assert_type(depth, "lib.math.combinations first argument (depth)", 2, "positive integer")
    assert_type(digits, "lib.math.combinations second argument (digits)", 2, "positive integer", "table")
    if type(digits) == "number" then
        digs = {}
        for j = 1, digits do
            digs[j] = j
        end
    end
    for _ = 1, depth do
        local next_step = {}
        for _, old_combination in pairs(all_combinations) do
            for _, new_digit in pairs(digs) do
                local new_combination = lib.lua.deep_copy(old_combination)
                table.insert(new_combination, new_digit)
                table.insert(next_step, new_combination)
            end
        end
        all_combinations = next_step
    end
    return all_combinations
end

-- cubic_polynomial returns the result of the
-- following polynomial with coefficients in A:
-- a[10]*y*y*y + a[9]*x*y*y + a[8]*x*x*y + a[7]*x*x*x
--   + a[6]*y*y + a[5]*x*y + a[4]*x*x + a[3]*y + a[2]*x + a[1]
-- You can use lib.math.random_vector(10, ...) and
-- a modulo operation to easily form a random
-- pattern of a floor, or choose the coefficients
-- to your own liking. Entries in A which are not
-- numbers are considered zero.
function lib.math.cubic_polynomial(a, x, y)
    assert_type(a, "lib.math.cubic_polynomial first argument", 2, "table")
    assert_type(x, "lib.math.cubic_polynomial second argument", 2, "number")
    assert_type(y, "lib.math.cubic_polynomial third argument", 2, "number")
    return   (a[10] or 0)*y*y*y + (a[9] or 0)*x*y*y + (a[8] or 0)*x*x*y
           + (a[7] or 0)*x*x*x + (a[6] or 0)*y*y + (a[5] or 0)*x*y
           + (a[4] or 0)*x*x + (a[3] or 0)*y + (a[2] or 0)*x + (a[1] or 0)
end

-- steps takes DISCRIMINATOR and returns a value dependend on where
-- in the table STEPS of integers the discriminator is located.
-- Example: lib.math.steps(x, {4, 8, 13}) returns:
--     0: If        x < 4
--     1: If   4 <= x < 8
--     2: If   8 <= x < 13
--     3: If  13 <= x
-- Negative and non-integer values are allowed.
function lib.math.steps(discriminator, steps)
    assert_type(discriminator, "lib.math.steps first argument", 2, "number")
    assert_type(steps, "lib.math.steps second argument", 2, "table")
    local result = 0
    for _, height in ipairs(steps) do
        assert_type(height, "lib.math.steps entry in second argument (height)", 2, "number")
        if discriminator < height then
            return result
        else
            result = result + 1
        end
    end
    return result
end

---------------------------------------------------------------------
--  PERMUTATIONS  AND  RANDOM  NUMBERS
---------------------------------------------------------------------

-- Return a random permutation of n elements.
-- This function outputs a table with integer entries between
-- 1 and n at positions 1 to n.
function lib.math.permutation(n)
    assert_type(n, "lib.math.permutation first argument", 2, "positive integer")
    if n == 1 then
        return {1}
    end
    local sequence = {}
    for j = 1, n do
        table.insert(sequence, j)
    end
    for n = table.getn(sequence), 2, -1 do
        local m = math.random(n)
        sequence[n], sequence[m] = sequence[m], sequence[n]
    end
    return sequence
end

-- Return a random cyclic permutation (i.e. with only one cycle) of n elements.
function lib.math.cyclic_permutation(n)
    assert_type(n, "lib.math.cyclic_permutation first argument", 2, "positive integer")
    local sequence1 = lib.math.permutation(n)
    local sequence2 = {}
    for j = 1, n - 1 do
        sequence2[sequence1[j]] = sequence1[j+1]
    end
    sequence2[sequence1[n]] = sequence1[1]
    return sequence2
end

-- Return a table with NUMBER random entries.
-- Additional arguments like with math.random.
function lib.math.random_vector(number, ...)
    assert_type(number, "lib.math.random_vector first argument", 2, "natural")
    local result = {}
    for j = 1, number do
        result[j] = math.random(...)
    end
    return result
end

-- Analyze the connected components of a group GROUP of floors.
-- Two floors are directly connected if their difference is in the polist
-- NEIGHBORHOOD, which has to be symmetric. A table is returned holding a
-- list of groups, each group is a complete connected component of GROUP.
-- In turn, each object in GROUP is attached an attribute with title
-- ATTRIBUTE_NAME, which holds the number of its component.
-- Note that any prior information in this attribute is lost.
-- If the attribute is not used by any other floor of the level, you may
-- set EXCLUSIVE_ATTRIBUTE to "true", which will result in a faster
-- algorithm.
-- Default NEIGHBORHOOD is NEIGHBORS_4.
function lib.math.mark_components(group, attribute_name, neighborhood, exclusive_attribute)
    assert_type(group, "lib.math.mark_components first argument (object group)", 2, "group")
    assert_type(attribute_name, "lib.math.mark_components second argument (attribute name)", 2, "non-empty string")
    assert_type(neighborhood, "lib.math.mark_components third argument (neighborhood)", 2, "polist", "nil")
    assert_type(exclusive_attribute, "lib.math.mark_components fourth argument (attribute name is exclusive)",
                2, "boolean", "nil")
    -- Check that GROUP is a group of floors.
    for obj in group do
        if not obj:is("fl") then
            error("lib.math.mark_components first argument must be a group of "
             .. "floors, but contains " .. obj:kind() .. ".", 2)
        end
    end
    -- Check that NEIGHBORHOOD is symmetric.
    if neighborhood and (neighborhood ~= NEIGHBORS_8) and (neighborhood ~= NEIGHBORS_CHESS) then
        for j = 1, #neighborhood do
            local found = false
            for k = 1, #neighborhood do
                found = found or (neighborhood[j] == -neighborhood[k])
            end
            if not found then
                error("lib.math.mark_components second argument must be a "
                      .. "symmetric polist, i.e. each negative of each position must be in the "
                      .. "polist as well.", 2)
            end
        end
    end
    local neigh = neighborhood or NEIGHBORS_4
    -- Start analyzing the components.
    local component = {}
    group[attribute_name] = 0
    -- Default recursive function.
    local function mark_component(obj, current)
        if (obj[attribute_name] or -1) == 0 then
            obj[attribute_name] = current
            component[current] = component[current] + obj
            for next_obj in fl(neigh + po(obj)) * group do
                mark_component(next_obj, current)
            end
        end
    end
    -- If we know that the attribute ATTRIBUTE_NAME is not used by any
    -- other floor, we might choose another, faster algorithm.
    local function mark_component_use_exclusiveness(obj, current)
        obj[attribute_name] = current
        component[current] = component[current] + obj
        for next_obj in fl(neigh + po(obj)) do
            if (next_obj[attribute_name] or -1) == 0 then
                mark_component_use_exclusiveness(next_obj, current)
            end
        end
    end
    -- Now loop over all floors of the group.
    for obj in group do
        if obj[attribute_name] == 0 then
            component[1 + #component] = grp()
            if exclusive_attribute then
                mark_component_use_exclusiveness(obj, #component)
            else
                mark_component(obj, #component)
            end
        end    
    end
    return component    
end

    ]]></el:luamain>
    <el:i18n>
    </el:i18n>
  </el:protected>
</el:level>