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
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
|
-- Copyright 2012 by Till Tantau
--
-- This file may be distributed an/or modified
--
-- 1. under the LaTeX Project Public License and/or
-- 2. under the GNU Public License
--
-- See the file doc/generic/pgf/licenses/LICENSE for more information
-- @release $Header: /cvsroot/pgf/pgf/generic/pgf/graphdrawing/lua/pgf/gd/lib.lua,v 1.6 2014/02/24 10:40:32 tantau Exp $
---
-- Basic library functions
local lib = {}
-- Declare namespace
require("pgf.gd").lib = lib
-- General lib functions:
---
-- Finds the first value in the |array| for which |test| is true.
--
-- @param array An array to search in.
-- @param test A function that is applied to each element of the
-- array together with the index of the element and the
-- whole table.
--
-- @return The value of the first value where the test is true.
-- @return The index of the first value where the test is true.
-- @return The function value of the first value where the test is
-- true (only returned if test is a function).
--
function lib.find(array, test)
for i=1,#array do
local t = array[i]
local result = test(t,i,array)
if result then
return t,i,result
end
end
end
---
-- Finds the first value in the |array| for which a function
-- returns a minimal value
--
-- @param array An array to search in.
-- @param f A function that is applied to each element of the
-- array together with the index of the element and the
-- whole table. It should return an integer and, possibly, a value.
--
-- Among all elements for which a non-nil integer is returned, let |i|
-- by the index of the element where this integer is minimal.
--
-- @return |array[i]|
-- @return |i|
-- @return The return value(s) of the function at |array[i]|.
--
function lib.find_min(array, f)
local best = math.huge
local best_result
local best_index
for i=1,#array do
local t = array[i]
local result, p = f(t,i,array)
if result and p < best then
best = p
best_result = result
best_index = i
end
end
if best_index then
return array[best_index],best_index,best_result,best
end
end
---
-- Copies a table while preserving its metatable.
--
-- @param source The table to copy.
-- @param target The table to which values are to be copied or |nil| if a new
-- table is to be allocated.
--
-- @return The |target| table or a newly allocated table containing all
-- keys and values of the |source| table.
--
function lib.copy(source, target)
if not target then
target = {}
end
for key, val in pairs(source) do
target[key] = val
end
return setmetatable(target, getmetatable(source))
end
---
-- Copies an array while preserving its metatable.
--
-- @param source The array to copy.
-- @param target The array to which values are to be copied or |nil| if a new
-- table is to be allocated. The elements of the
-- |source| array will be added at the end.
--
-- @return The |target| table or a newly allocated table containing all
-- keys and values of the |source| table.
--
function lib.icopy(source, target)
target = target or {}
for _, val in ipairs(source) do
target[#target+1] = val
end
return setmetatable(target, getmetatable(source))
end
---
-- Apply a function to all pairs of a table, resulting in a new table.
--
-- @param source The table.
-- @param fun A function taking two arguments (|val| and |key|, in
-- that order). Should return two values (a |new_val| and a
-- |new_key|). This pair will be inserted into the new table. If,
-- however, |new_key| is |nil|, the |new_value| will be inserted at
-- the position |key|. This means, in particular, that if the |fun|
-- takes only a single argument and returns only a single argument,
-- you have a ``classical'' value mapper. Also note that if
-- |new_value| is |nil|, the value is removed from the table.
--
-- @return The new table.
--
function lib.map(source, fun)
local target = {}
for key, val in pairs(source) do
local new_val, new_key = fun(val, key)
if new_key == nil then
new_key = key
end
target[new_key] = new_val
end
return target
end
---
-- Apply a function to all elements of an array, resulting in a new
-- array.
--
-- @param source The array.
-- @param fun A function taking two arguments (|val| and |i|, the
-- current index). This function is applied to all elements of the
-- array. The result of this function is placed at the end of a new
-- array, expect when the function returns |nil|, in which case the
-- element is skipped. If this function is not provided (is |nil|),
-- the identity function is used.
-- @param new The target array (if |nil|, a new array is create).
--
--\begin{codeexample}[code only]
-- local a = lib.imap(array, function(v) if some_test(v) then return v end end)
--\end{codeexample}
--
-- The above code is a filter that will remove all elements from the
-- array that do not pass |some_test|.
--
--\begin{codeexample}[code only]
-- lib.imap(a, lib.id, b)
--\end{codeexample}
--
-- The above code has the same effect as |lib.icopy(a,b)|.
--
-- @return The new array
--
function lib.imap(source, fun, new)
if not new then
new = { }
end
for i, v in ipairs(source) do
new[#new+1] = fun(v, i)
end
return new
end
---
-- Generate a random permutation of the numbers $1$ to $n$ in time
-- $O(n)$. Knuth's shuffle is used for this.
--
-- @param n The desired size of the table
-- @return A random permutation
function lib.random_permutation(n)
local p = {}
for i=1,n do
p[i] = i
end
for i=1,n-1 do
local j = math.random(i,n)
p[i], p[j] = p[i], p[j]
end
return p
end
---
-- The identity function, so you can write |lib.id| instead of
-- |function (x) return x end|.
--
function lib.id(...)
return ...
end
---
-- Tries to find an option in different objects that have an
-- options field.
--
-- This function iterates over all objects given as parameters. In
-- each, it tries to find out whether the options field of the object
-- contains the option |name| and, if so,
-- returns the value. The important point is that checking whether the
-- option table of an object contains the name field is done using
-- |rawget| for all but the last parameter. This means that when you
-- write
--\begin{codeexample}[code only]
--lib.lookup_option("foo", vertex, graph)
--\end{codeexample}
-- and if |/graph drawin/foo| has an inital value set, if the
-- parameter is not explicitly set in a vertex, you will get the value
-- set for the graph or, if it is not set there either, the initial
-- value. In contrast, if you write
--\begin{codeexample}[code only]
-- vertex.options["foo"] or graph.options["foo"]
--\end{codeexample}
-- what happens is that the first access to |.options| will
-- \emph{always} return something when an initial parameter has been
-- set for the option |foo|.
--
-- @param name The name of the options
-- @param ... Any number of objects. Each must have an options
-- field.
--
-- @return The found option
function lib.lookup_option(name, ...)
local list = {...}
for i=1,#list-1 do
local o = list[i].options
if o then
local v = rawget(o, name)
if v then
return v
end
end
end
return list[#list].options[name]
end
---
-- Turns a table |t| into a class in the sense of object oriented
-- programming. In detail, this means that |t| is augmented by
-- a |new| function, which takes an optional table of |initial| values
-- and which outputs a new table whose metatable is the
-- class. The |new| function will call the function |constructor| if
-- it exsist. Furthermore, the class object's |__index| is set to itself
-- and its meta table is set to the |base_class| field of the
-- table. If |t| is |nil|, a new table is created.
--
-- Here is a typical usage of this function:
--
--\begin{codeexample}[code only]
--local Point = lib.class {}
--
--function Point:length()
-- return math.sqrt(self.x*self.x + self.y*self.y)
--end
--
--local p = Point.new { x = 5, y = 6 }
--
--print(p:length())
--\end{codeexample}
-- We can subclass this as follows:
--
--\begin{codeexample}[code only]
--local Point3D = lib.class { base_class = Point }
--
--function Point3D:length()
-- local l = Point.length(self) -- Call base class's function
-- return math.sqrt(l*l + self.z*self.zdy)
--end
--
--local p = Point3D.new { x = 5, y = 6, z = 6 }
--
--print(p:length())
--\end{codeexample}
--
-- @param t A table that gets augmented to a class. If |nil|, a new
-- table is created.
-- @return The augmented table.
function lib.class(t)
t = t or {}
-- First, setup indexing, if necessary
if not t.__index then
t.__index = t
end
-- Second, setup new method, if necessary
t.new = t.new or
function (initial)
-- Create new object
local obj = {}
for k,v in pairs(initial or {}) do
obj[k] = v
end
setmetatable(obj, t)
if obj.constructor then
obj:constructor()
end
return obj
end
-- Third, setup inheritence, if necessary
if not getmetatable(t) then
setmetatable(t, t.base_class)
end
return t
end
---
-- Returns a method that is loaded only on demand for a class.
--
-- The idea behind this function is that you may have a class (or just
-- a table) for which some methods are needed only seldomly. In this
-- case, you can put these methods in a separate file and then use
-- |ondemand| to indicate that the methods are found in a
-- another file.
--
--\begin{codeexample}[code only]
-- -- File Foo.lua
-- local Foo = {}
-- function Foo.bar () ... end
-- function Foo.bar2 () ... end
-- Foo.bar3 = lib.ondemand("Foo_extra", Foo, "bar3")
-- Foo.bar4 = lib.ondemand("Foo_extra", Foo, "bar4")
--
-- return Foo
--
-- -- Foo_extra.lua
-- local Foo = require "Foo"
-- function Foo.bar3 () ... end
-- function Foo.bar4 () ... end
--\end{codeexample}
--
-- @param filename The name of the file when extra methods are
-- located.
-- @param table The table for which the missing functions should be
-- loaded when they are accessed.
-- @param method The name of the method.
--
-- @return A function that, when called, loads the filename using
-- |require| and, then, forwards the call to the method.
function lib.ondemand(filename, table, name)
return function(...)
require (filename)
return table[name] (...)
end
end
-- Done
return lib
|