]> source.dussan.org Git - rspamd.git/commitdiff
Import lua-functional for plugins stuff.
authorVsevolod Stakhov <vsevolod@highsecure.ru>
Mon, 16 Feb 2015 16:35:18 +0000 (16:35 +0000)
committerAndrew Lewis <nerf@judo.za.org>
Wed, 25 Feb 2015 12:34:45 +0000 (14:34 +0200)
CMakeLists.txt
contrib/lua-fun/COPYING.md [new file with mode: 0644]
contrib/lua-fun/fun.lua [new file with mode: 0644]

index 6b6c0d4e8f3d70c816dbd0d489b086d5dc283c5e..ca1e23326fa2439eed27370b4cc4d413ff43f385 100644 (file)
@@ -911,6 +911,9 @@ FOREACH(LUA_PLUGIN ${LUA_PLUGINS})
        INSTALL(FILES "src/plugins/lua/${LUA_PLUGIN}" DESTINATION ${PLUGINSDIR}/lua/${_rp})
 ENDFOREACH(LUA_PLUGIN)
 
+# Install lua fun library
+INSTALL(FILES "contrib/lua-fun/fun.lua" DESTINATION ${PLUGINSDIR}/fun.lua)
+
 # Lua config
 INSTALL(CODE "FILE(MAKE_DIRECTORY \$ENV{DESTDIR}${CONFDIR}/lua)")
 FILE(GLOB_RECURSE LUA_CONFIGS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}/conf/lua" 
diff --git a/contrib/lua-fun/COPYING.md b/contrib/lua-fun/COPYING.md
new file mode 100644 (file)
index 0000000..42ee231
--- /dev/null
@@ -0,0 +1,27 @@
+Copying
+=======
+
+**Lua Fun** source codes, logo and documentation are distributed under the
+**[MIT/X11 License]** - same as Lua and LuaJIT.
+
+Copyright (c) 2013 Roman Tsisyk <roman@tsisyk.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+[MIT/X11 License]: http://www.opensource.org/licenses/mit-license.php
diff --git a/contrib/lua-fun/fun.lua b/contrib/lua-fun/fun.lua
new file mode 100644 (file)
index 0000000..5d019d6
--- /dev/null
@@ -0,0 +1,968 @@
+---
+--- Lua Fun - a high-performance functional programming library for LuaJIT
+---
+--- Copyright (c) 2013 Roman Tsisyk <roman@tsisyk.com>
+---
+--- Distributed under the MIT/X11 License. See COPYING.md for more details.
+---
+
+--------------------------------------------------------------------------------
+-- Tools
+--------------------------------------------------------------------------------
+
+local return_if_not_empty = function(state_x, ...)
+    if state_x == nil then
+        return nil
+    end
+    return ...
+end
+
+local call_if_not_empty = function(fun, state_x, ...)
+    if state_x == nil then
+        return nil
+    end
+    return state_x, fun(...)
+end
+
+local function deepcopy(orig) -- used by cycle()
+    local orig_type = type(orig)
+    local copy
+    if orig_type == 'table' then
+        copy = {}
+        for orig_key, orig_value in next, orig, nil do
+            copy[deepcopy(orig_key)] = deepcopy(orig_value)
+        end
+    else
+        copy = orig
+    end
+    return copy
+end
+
+--------------------------------------------------------------------------------
+-- Basic Functions
+--------------------------------------------------------------------------------
+
+local nil_gen = function(_param, _state)
+    return nil
+end
+
+local string_gen = function(param, state)
+    local state = state + 1
+    if state > #param then
+        return nil
+    end
+    local r = string.sub(param, state, state)
+    return state, r
+end
+
+local pairs_gen = pairs({ a = 0 }) -- get the generating function from pairs
+local map_gen = function(tab, key)
+    local value
+    local key, value = pairs_gen(tab, key)
+    return key, key, value
+end
+
+local iter = function(obj, param, state)
+    assert(obj ~= nil, "invalid iterator")
+    if (type(obj) == "function") then
+        return obj, param, state
+    elseif (type(obj) == "table" or type(obj) == "userdata") then
+        if #obj > 0 then
+            return ipairs(obj)
+        else
+            return map_gen, obj, nil
+        end
+    elseif (type(obj) == "string") then
+        if #obj == 0 then
+            return nil_gen, nil, nil
+        end
+        return string_gen, obj, 0
+    end
+    error(string.format('object %s of type "%s" is not iterable',
+          obj, type(obj)))
+end
+
+local iter_tab = function(obj)
+    if type(obj) == "function" then
+       return obj, nil, nil
+    elseif type(obj) == "table" and type(obj[1]) == "function" then
+        return obj[1], obj[2], obj[3]
+    else
+        return iter(obj)
+    end
+end
+
+local each = function(fun, gen, param, state)
+    local gen_x, param_x, state_x = iter(gen, param, state)
+    repeat
+        state_x = call_if_not_empty(fun, gen_x(param_x, state_x))
+    until state_x == nil
+end
+
+--------------------------------------------------------------------------------
+-- Generators
+--------------------------------------------------------------------------------
+
+local range_gen = function(param, state)
+    local stop, step = param[1], param[2]
+    local state = state + step
+    if state >= stop then
+        return nil
+    end
+    return state, state
+end
+
+local range_rev_gen = function(param, state)
+    local stop, step = param[1], param[2]
+    local state = state + step
+    if state <= stop then
+        return nil
+    end
+    return state, state
+end
+
+local range = function(start, stop, step)
+    if step == nil then
+        step = 1
+        if stop == nil then
+            stop = start
+            start = 0
+        end
+    end
+
+    assert(type(start) == "number", "start must be a number")
+    assert(type(stop) == "number", "stop must be a number")
+    assert(type(step) == "number", "step must be a number")
+    assert(step ~= 0, "step must not be zero")
+
+    if (step > 0) then
+        return range_gen, {stop, step}, start - step
+    elseif (step < 0) then
+        return range_rev_gen, {stop, step}, start - step
+    end
+end
+
+local duplicate_table_gen = function(param_x, state_x)
+    return state_x + 1, unpack(param_x)
+end
+
+local duplicate_fun_gen = function(param_x, state_x)
+    return state_x + 1, param_x(state_x)
+end
+
+local duplicate_gen = function(param_x, state_x)
+    return state_x + 1, param_x
+end
+
+local duplicate = function(...)
+    if select('#', ...) <= 1 then
+        return duplicate_gen, select(1, ...), 0
+    else
+        return duplicate_table_gen, {...}, 0 
+    end
+end
+
+local tabulate = function(fun)
+    assert(type(fun) == "function")
+    return duplicate_fun_gen, fun, 0
+end
+
+local zeros = function()
+    return duplicate_gen, 0, 0
+end
+
+local ones = function()
+    return duplicate_gen, 1, 0
+end
+
+local rands_gen = function(param_x, _state_x)
+    return 0, math.random(param_x[1], param_x[2])
+end
+
+local rands_nil_gen = function(_param_x, _state_x)
+    return 0, math.random()
+end
+
+local rands = function(n, m)
+    if n == nil and m == nil then
+        return rands_nil_gen, 0, 0
+    end
+    assert(type(n) == "number", "invalid first arg to rands")
+    if m == nil then
+        m = n
+        n = 0
+    else
+        assert(type(m) == "number", "invalid second arg to rands")
+    end
+    assert(n < m, "empty interval")
+    return rands_gen, {n, m - 1}, 0
+end
+
+--------------------------------------------------------------------------------
+-- Slicing
+--------------------------------------------------------------------------------
+
+local nth = function(n, gen, param, state)
+    assert(n > 0, "invalid first argument to nth")
+    local gen_x, param_x, state_x = iter(gen, param, state)
+    -- An optimization for arrays and strings
+    if gen_x == ipairs then
+        return param_x[n]
+    elseif gen_x == string_gen then
+        if n < #param_x then
+            return string.sub(param_x, n, n)
+        else
+            return nil
+        end
+    end
+    for i=1,n-1,1 do
+        state_x = gen_x(param_x, state_x)
+        if state_x == nil then
+            return nil
+        end
+    end
+    return return_if_not_empty(gen_x(param_x, state_x))
+end
+
+local head_call = function(state, ...)
+    if state == nil then
+        error("head: iterator is empty")
+    end
+    return ...
+end
+
+local head = function(gen, param, state)
+    local gen_x, param_x, state_x = iter(gen, param, state)
+    return head_call(gen_x(param_x, state_x))
+end
+
+local tail = function(gen, param, state)
+    local gen_x, param_x, state_x = iter(gen, param, state)
+    state_x = gen_x(param_x, state_x)
+    if state_x == nil then
+        return nil_gen, nil, nil
+    end
+    return gen_x, param_x, state_x
+end
+
+local take_n_gen_x = function(i, state_x, ...)
+    if state_x == nil then
+        return nil
+    end
+    return {i, state_x}, ...
+end
+
+local take_n_gen = function(param, state)
+    local n, gen_x, param_x = param[1], param[2], param[3]
+    local i, state_x = state[1], state[2]
+    if i >= n then
+        return nil
+    end
+    return take_n_gen_x(i + 1, gen_x(param_x, state_x))
+end
+
+local take_n = function(n, gen, param, state)
+    assert(n >= 0, "invalid first argument to take_n")
+    local gen_x, param_x, state_x = iter(gen, param, state)
+    return take_n_gen, {n, gen, param}, {0, state}
+end
+
+local take_while_gen_x = function(fun, state_x, ...)
+    if state_x == nil or not fun(...) then
+        return nil
+    end
+    return state_x, ...
+end
+
+local take_while_gen = function(param, state_x)
+    local fun, gen_x, param_x = param[1], param[2], param[3]
+    return take_while_gen_x(fun, gen_x(param_x, state_x))
+end
+
+local take_while = function(fun, gen, param, state)
+    assert(type(fun) == "function", "invalid first argument to take_while")
+    local gen_x, param_x, state_x = iter(gen, param, state)
+    return take_while_gen, {fun, gen, param}, state
+end
+
+local take = function(n_or_fun, gen, param, state)
+    if type(n_or_fun) == "number" then
+        return take_n(n_or_fun, gen, param, state)
+    else 
+        return take_while(n_or_fun, gen, param, state)
+    end
+end
+
+local drop_n = function(n, gen, param, state)
+    assert(n >= 0, "invalid first argument to drop_n")
+    local gen_x, param_x, state_x = iter(gen, param, state)
+    for i=1,n,1 do
+        state_x = gen_x(param_x, state_x)
+        if state_x == nil then
+            return nil_gen, nil, nil
+        end
+    end
+    return gen_x, param_x, state_x
+end
+
+local drop_while_x = function(fun, state_x, ...)
+    if state_x == nil or not fun(...) then
+        return state_x, false
+    end
+    return state_x, true, ...
+end
+
+local drop_while = function(fun, gen, param, state)
+    assert(type(fun) == "function", "invalid first argument to drop_while")
+    local gen_x, param_x, state_x = iter(gen, param, state)
+    local cont, state_x_prev
+    repeat
+        state_x_prev = deepcopy(state_x)
+        state_x, cont = drop_while_x(fun, gen_x(param_x, state_x))
+    until not cont
+    if state_x == nil then
+        return nil_gen, nil, nil
+    end
+    return gen_x, param_x, state_x_prev
+end
+
+local drop = function(n_or_fun, gen, param, state)
+    if type(n_or_fun) == "number" then
+        return drop_n(n_or_fun, gen, param, state)
+    else 
+        return drop_while(n_or_fun, gen, param, state)
+    end
+end
+
+local split = function(n_or_fun, gen, param, state)
+    return {take(n_or_fun, gen, param, state)},
+           {drop(n_or_fun, gen, param, state)}
+end
+
+--------------------------------------------------------------------------------
+-- Indexing
+--------------------------------------------------------------------------------
+
+local index = function(x, gen, param, state) 
+    local i = 1
+    for _k, r in iter(gen, param, state) do
+        if r == x then
+            return i
+        end
+        i = i + 1
+    end
+    return nil
+end
+
+local indexes_gen = function(param, state)
+    local x, gen_x, param_x = param[1], param[2], param[3]
+    local i, state_x = state[1], state[2]
+    local r
+    while true do
+        state_x, r = gen_x(param_x, state_x)
+        if state_x == nil then
+            return nil
+        end
+        i = i + 1
+        if r == x then
+            return {i, state_x}, i
+        end
+    end
+end
+
+local indexes = function(x, gen, param, state)
+    local gen_x, param_x, state_x = iter(gen, param, state)
+    return indexes_gen, {x, gen_x, param_x}, {0, state_x}
+end
+
+-- TODO: undocumented
+local find = function(fun, gen, param, state)
+    local gen_x, param_x, state_x = filter(fun, gen, param, state)
+    return return_if_not_empty(gen_x(param_x, state_x))
+end
+
+--------------------------------------------------------------------------------
+-- Filtering
+--------------------------------------------------------------------------------
+
+local filter1_gen = function(fun, gen_x, param_x, state_x, a)
+    while true do
+        if state_x == nil or fun(a) then break; end
+        state_x, a = gen_x(param_x, state_x)
+    end
+    return state_x, a
+end
+
+-- call each other
+local filterm_gen
+local filterm_gen_shrink = function(fun, gen_x, param_x, state_x)
+    return filterm_gen(fun, gen_x, param_x, gen_x(param_x, state_x))
+end
+
+filterm_gen = function(fun, gen_x, param_x, state_x, ...)
+    if state_x == nil then
+        return nil
+    end
+    if fun(...) then
+        return state_x, ...
+    end
+    return filterm_gen_shrink(fun, gen_x, param_x, state_x)
+end
+
+local filter_detect = function(fun, gen_x, param_x, state_x, ...)
+    if select('#', ...) < 2 then
+        return filter1_gen(fun, gen_x, param_x, state_x, ...)
+    else
+        return filterm_gen(fun, gen_x, param_x, state_x, ...)
+    end
+end
+
+local filter_gen = function(param, state_x)
+    local fun, gen_x, param_x = param[1], param[2], param[3]
+    return filter_detect(fun, gen_x, param_x, gen_x(param_x, state_x))
+end
+
+local filter = function(fun, gen, param, state)
+    local gen_x, param_x, state_x = iter(gen, param, state)
+    return filter_gen, {fun, gen_x, param_x}, state_x
+end
+
+local grep = function(fun_or_regexp, gen, param, state)
+    local fun = fun_or_regexp
+    if type(fun_or_regexp) == "string" then
+        fun = function(x) return string.find(x, fun_or_regexp) ~= nil end
+    end
+    return filter(fun, gen, param, state)
+end
+
+local partition = function(fun, gen, param, state)
+    local neg_fun = function(...)
+        return not fun(...)
+    end
+    local gen_x, param_x, state_x = iter(gen, param, state)
+    return {filter(fun, gen_x, param_x, state_x)},
+           {filter(neg_fun, gen_x, param_x, state_x)}
+end
+
+--------------------------------------------------------------------------------
+-- Reducing
+--------------------------------------------------------------------------------
+
+local foldl_call = function(fun, start, state, ...)
+    if state == nil then
+        return nil, start
+    end
+    return state, fun(start, ...)
+end
+
+local foldl = function(fun, start, gen, param, state)
+    local gen_x, param_x, state_x = iter(gen, param, state)
+    while true do
+        state_x, start = foldl_call(fun, start, gen_x(param_x, state_x))
+        if state_x == nil then
+            break;
+        end
+    end
+    return start
+end
+
+local length = function(gen, param, state)
+    local gen, param, state = iter(gen, param, state)
+    if gen == ipairs or gen == string_gen then
+        return #param
+    end
+    local len = 0
+    repeat
+        state = gen(param, state)
+        len = len + 1
+    until state == nil
+    return len - 1
+end
+
+local is_null = function(gen, param, state)
+    local gen_x, param_x, state_x = iter(gen, param, state)
+    return gen_x(param_x, deepcopy(state_x)) == nil
+end
+
+local is_prefix_of = function(iter_x, iter_y)
+    local gen_x, param_x, state_x = iter_tab(iter_x)
+    local gen_y, param_y, state_y = iter_tab(iter_y)
+
+    local r_x, r_y
+    for i=1,10,1 do
+        state_x, r_x = gen_x(param_x, state_x)
+        state_y, r_y = gen_y(param_y, state_y)
+        if state_x == nil then
+            return true
+        end
+        if state_y == nil or r_x ~= r_y then
+            return false
+        end
+    end
+end
+
+local all = function(fun, gen, param, state)
+    local gen_x, param_x, state_x = iter(gen, param, state)
+    local r
+    repeat
+        state_x, r = call_if_not_empty(fun, gen_x(param_x, state_x))
+    until state_x == nil or not r
+    return state_x == nil
+end
+
+local any = function(fun, gen, param, state)
+    local gen_x, param_x, state_x = iter(gen, param, state)
+    local r
+    repeat
+        state_x, r = call_if_not_empty(fun, gen_x(param_x, state_x))
+    until state_x == nil or r
+    return not not r
+end
+
+local sum = function(gen, param, state)
+    local gen, param, state = iter(gen, param, state)
+    local s = 0
+    local r = 0
+    repeat
+        s = s + r
+        state, r = gen(param, state)
+    until state == nil
+    return s
+end
+
+local product = function(gen, param, state)
+    local gen, param, state = iter(gen, param, state)
+    local p = 1
+    local r = 1
+    repeat
+        p = p * r
+        state, r = gen(param, state)
+    until state == nil
+    return p
+end
+
+local min_cmp = function(m, n)
+    if n < m then return n else return m end
+end
+
+local max_cmp = function(m, n)
+    if n > m then return n else return m end
+end
+
+local min = function(gen, param, state)
+    local gen, param, state = iter(gen, param, state)
+    local state, m = gen(param, state)
+    if state == nil then
+        error("min: iterator is empty")
+    end
+
+    local cmp
+    if type(m) == "number" then
+        -- An optimization: use math.min for numbers
+        cmp = math.min
+    else
+        cmp = min_cmp
+    end
+
+    for _, r in gen, param, state do
+        m = cmp(m, r)
+    end
+    return m
+end
+
+local min_by = function(cmp, gen, param, state)
+    local gen_x, param_x, state_x = iter(gen, param, state)
+    local state_x, m = gen_x(param_x, state_x)
+    if state_x == nil then
+        error("min: iterator is empty")
+    end
+
+    for _, r in gen_x, param_x, state_x do
+        m = cmp(m, r)
+    end
+    return m
+end
+
+local max = function(gen, param, state)
+    local gen_x, param_x, state_x = iter(gen, param, state)
+    local state_x, m = gen_x(param_x, state_x)
+    if state_x == nil then
+        error("max: iterator is empty")
+    end
+
+    local cmp
+    if type(m) == "number" then
+        -- An optimization: use math.max for numbers
+        cmp = math.max
+    else
+        cmp = max_cmp
+    end
+
+    for _, r in gen_x, param_x, state_x do
+        m = cmp(m, r)
+    end
+    return m
+end
+
+local max_by = function(cmp, gen, param, state)
+    local gen_x, param_x, state_x = iter(gen, param, state)
+    local state_x, m = gen_x(param_x, state_x)
+    if state_x == nil then
+        error("max: iterator is empty")
+    end
+
+    for _, r in gen_x, param_x, state_x do
+        m = cmp(m, r)
+    end
+    return m
+end
+
+local totable = function(gen, param, state)
+    local gen_x, param_x, state_x = iter(gen, param, state)
+    local tab, key, val = {}
+    while true do
+        state_x, val = gen_x(param_x, state_x)
+        if state_x == nil then
+            break
+        end
+        table.insert(tab, val)
+    end
+    return tab
+end
+
+local tomap = function(gen, param, state)
+    local gen_x, param_x, state_x = iter(gen, param, state)
+    local tab, key, val = {}
+    while true do
+        state_x, key, val = gen_x(param_x, state_x)
+        if state_x == nil then
+            break
+        end
+        tab[key] = val
+    end
+    return tab
+end
+
+--------------------------------------------------------------------------------
+-- Transformations
+--------------------------------------------------------------------------------
+
+local map_gen = function(param, state)
+    local gen_x, param_x, fun = param[1], param[2], param[3]
+    return call_if_not_empty(fun, gen_x(param_x, state))
+end
+
+local map = function(fun, gen, param, state)
+    local gen_x, param_x, state_x = iter(gen, param, state)
+    return map_gen, {gen_x, param_x, fun}, state_x
+end
+
+local enumerate_gen_call = function(state, i, state_x, ...)
+    if state_x == nil then
+        return nil
+    end
+    return {i + 1, state_x}, i, ...
+end
+
+local enumerate_gen = function(param, state)
+    local gen_x, param_x = param[1], param[2]
+    local i, state_x = state[1], state[2]
+    return enumerate_gen_call(state, i, gen_x(param_x, state_x))
+end
+
+local enumerate = function(gen, param, state)
+    local gen_x, param_x, state_x = iter(gen, param, state)
+    return enumerate_gen, {gen_x, param_x}, {0, state_x}
+end
+
+local intersperse_call = function(i, state_x, ...)
+    if state_x == nil then
+        return nil
+    end
+    return {i + 1, state_x}, ...
+end
+
+local intersperse_gen = function(param, state)
+    local x, gen_x, param_x = param[1], param[2], param[3]
+    local i, state_x = state[1], state[2]
+    if i % 2 == 1 then
+        return {i + 1, state_x}, x
+    else
+        return intersperse_call(i, gen_x(param_x, state_x))
+    end
+end
+
+-- TODO: interperse must not add x to the tail
+local intersperse = function(x, gen, param, state)
+    local gen_x, param_x, state_x = iter(gen, param, state)
+    return intersperse_gen, {x, gen_x, param_x}, {0, state_x}
+end
+
+--------------------------------------------------------------------------------
+-- Compositions
+--------------------------------------------------------------------------------
+
+local function zip_gen_r(param, state, state_new, ...)
+    if #state_new == #param / 2 then
+        return state_new, ...
+    end
+
+    local i = #state_new + 1
+    local gen_x, param_x = param[2 * i - 1], param[2 * i]
+    local state_x, r = gen_x(param_x, state[i])
+    -- print('i', i, 'state_x', state_x, 'r', r)
+    if state_x == nil then
+        return nil
+    end
+    table.insert(state_new, state_x)
+    return zip_gen_r(param, state, state_new, r, ...)
+end
+
+local zip_gen = function(param, state)
+    return zip_gen_r(param, state, {})
+end
+
+local zip = function(...)
+    local n = select('#', ...)
+    if n == 0 then
+        return nil_gen, nil, nil
+    end
+    local param = { [2 * n] = 0 }
+    local state = { [n] = 0 }
+
+    local i, gen_x, param_x, state_x
+    for i=1,n,1 do
+        local elem = select(n - i + 1, ...)
+        gen_x, param_x, state_x = iter_tab(elem)
+        param[2 * i - 1] = gen_x
+        param[2 * i] = param_x
+        state[i] = state_x
+    end
+
+    return zip_gen, param, state
+end
+
+local cycle_gen_call = function(param, state_x, ...)
+    if state_x == nil then
+        local gen_x, param_x, state_x0 = param[1], param[2], param[3]
+        return gen_x(param_x, deepcopy(state_x0))
+    end
+    return state_x, ...
+end
+
+local cycle_gen = function(param, state_x)
+    local gen_x, param_x, state_x0 = param[1], param[2], param[3]
+    return cycle_gen_call(param, gen_x(param_x, state_x))
+end
+
+local cycle = function(gen, param, state)
+    local gen_x, param_x, state_x = iter(gen, param, state)
+    return cycle_gen, {gen_x, param_x, state_x}, deepcopy(state_x)
+end
+
+-- call each other
+local chain_gen_r1
+local chain_gen_r2 = function(param, state, state_x, ...)
+    if state_x == nil then
+        local i = state[1]
+        i = i + 1
+        if i > #param / 3 then
+            return nil
+        end
+        local state_x = param[3 * i]
+        return chain_gen_r1(param, {i, state_x})
+    end
+    return {state[1], state_x}, ...
+end
+
+chain_gen_r1 = function(param, state)
+    local i, state_x = state[1], state[2]
+    local gen_x, param_x = param[3 * i - 2], param[3 * i - 1]
+    return chain_gen_r2(param, state, gen_x(param_x, state[2]))
+end
+
+local chain = function(...)
+    local n = select('#', ...)
+    if n == 0 then
+        return nil_gen, nil, nil
+    end
+
+    local param = { [3 * n] = 0 }
+    local i, gen_x, param_x, state_x
+    for i=1,n,1 do
+        local elem = select(i, ...)
+        gen_x, param_x, state_x = iter_tab(elem)
+        param[3 * i - 2] = gen_x
+        param[3 * i - 1] = param_x
+        param[3 * i] = state_x
+    end
+
+    return chain_gen_r1, param, {1, param[3]}
+end
+
+--------------------------------------------------------------------------------
+-- Operators
+--------------------------------------------------------------------------------
+
+operator = {
+    ----------------------------------------------------------------------------
+    -- Comparison operators
+    ----------------------------------------------------------------------------
+    lt  = function(a, b) return a < b end,
+    le  = function(a, b) return a <= b end,
+    eq  = function(a, b) return a == b end,
+    ne  = function(a, b) return a ~= b end,
+    ge  = function(a, b) return a >= b end,
+    gt  = function(a, b) return a > b end,
+
+    ----------------------------------------------------------------------------
+    -- Arithmetic operators
+    ----------------------------------------------------------------------------
+    add = function(a, b) return a + b end,
+    div = function(a, b) return a / b end,
+    floordiv = function(a, b) return math.floor(a/b) end,
+    intdiv = function(a, b)
+        local q = a / b
+        if a >= 0 then return math.floor(q) else return math.ceil(q) end
+    end,
+    mod = function(a, b) return a % b end,
+    mul = function(a, b) return a * b end,
+    neq = function(a) return -a end,
+    unm = function(a) return -a end, -- an alias
+    pow = function(a, b) return a ^ b end,
+    sub = function(a, b) return a - b end,
+    truediv = function(a, b) return a / b end,
+
+    ----------------------------------------------------------------------------
+    -- String operators
+    ----------------------------------------------------------------------------
+    concat = function(a, b) return a..b end,
+    len = function(a) return #a end,
+    length = function(a) return #a end, -- an alias
+
+    ----------------------------------------------------------------------------
+    -- Logical operators
+    ----------------------------------------------------------------------------
+    land = function(a, b) return a and b end,
+    lor = function(a, b) return a or b end,
+    lnot = function(a) return not a end,
+    truth = function(a) return not not a end,
+}
+
+--------------------------------------------------------------------------------
+-- module definitions
+--------------------------------------------------------------------------------
+
+local exports = {
+    ----------------------------------------------------------------------------
+    -- Basic
+    ----------------------------------------------------------------------------
+    iter = iter,
+    each = each,
+    for_each = each, -- an alias
+    foreach = each, -- an alias
+
+    ----------------------------------------------------------------------------
+    -- Generators
+    ----------------------------------------------------------------------------
+    range = range,
+    duplicate = duplicate,
+    xrepeat = duplicate, -- an alias
+    replicate = duplicate, -- an alias
+    tabulate = tabulate,
+    ones = ones,
+    zeros = zeros,
+    rands = rands,
+
+    ----------------------------------------------------------------------------
+    -- Slicing
+    ----------------------------------------------------------------------------
+    nth = nth,
+    head = head,
+    car = head, -- an alias
+    tail = tail,
+    cdr = tail, -- an alias
+    take_n = take_n,
+    take_while = take_while,
+    take = take,
+    drop_n = drop_n,
+    drop_while = drop_while,
+    drop = drop,
+    split = split,
+    split_at = split, -- an alias
+    span = split, -- an alias
+
+    ----------------------------------------------------------------------------
+    -- Indexing
+    ----------------------------------------------------------------------------
+    index = index,
+    index_of = index, -- an alias
+    elem_index = index, -- an alias
+    indexes = indexes,
+    indices = indexes, -- an alias
+    elem_indexes = indexes, -- an alias
+    elem_indices = indexes, -- an alias
+    find = find,
+
+    ----------------------------------------------------------------------------
+    -- Filtering
+    ----------------------------------------------------------------------------
+    filter = filter,
+    remove_if = filter, -- an alias
+    grep = grep,
+    partition = partition,
+
+    ----------------------------------------------------------------------------
+    -- Reducing
+    ----------------------------------------------------------------------------
+    foldl = foldl,
+    reduce = foldl, -- an alias
+    length = length,
+    is_null = is_null,
+    is_prefix_of = is_prefix_of,
+    all = all,
+    every = all, -- an alias
+    any = any,
+    some = any, -- an alias
+    sum = sum,
+    product = product,
+    min = min,
+    minimum = min, -- an alias
+    min_by = min_by,
+    minimum_by = min_by, -- an alias
+    max = max,
+    maximum = max, -- an alias
+    max_by = max_by,
+    maximum_by = max_by, -- an alias
+    totable = totable,
+    tomap = tomap,
+
+    ----------------------------------------------------------------------------
+    -- Transformations
+    ----------------------------------------------------------------------------
+    map = map,
+    enumerate = enumerate,
+    intersperse = intersperse,
+
+    ----------------------------------------------------------------------------
+    -- Compositions
+    ----------------------------------------------------------------------------
+    zip = zip,
+    cycle = cycle,
+    chain = chain,
+
+    ----------------------------------------------------------------------------
+    -- Operators
+    ----------------------------------------------------------------------------
+    operator = operator,
+    op = operator -- an alias
+}
+
+-- a special syntax sugar to export all functions to the global table
+setmetatable(exports, {
+    __call = function(t)
+        for k, v in pairs(t) do _G[k] = v end
+    end,
+})
+
+return exports