Current File : //usr/share/texlive/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/lib.lua |
-- 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$
---
-- 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 = lib.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 drawing/foo| has an initial 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 exists. 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 inheritance, 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
---
-- This implements the a random number generator similar to the one
-- provided by Lua, but based on the tex.uniformdeviate primitive to
-- avoid differences in random numbers due to platform specifics.
--
-- @param l Lower bound
-- @param u Upper bound
-- @return A random number
function lib.random(l,u)
local fraction_one = 268435456
local r = tex.uniform_rand(fraction_one)/fraction_one
if l and u then
assert(l <= u)
return math.floor(r*(u-l+1)) + l
elseif l then
assert(1.0 <= l)
return math.floor(r*l) + 1.0
else
return r
end
end
---
-- Provide the seed for the random number generator
--
-- @param seed random seed
function lib.randomseed(seed)
tex.init_rand(seed)
end
-- Done
return lib