diff --git a/class.lua b/class.lua
index a46fdac..cbd3012 100644
--- a/class.lua
+++ b/class.lua
@@ -7,7 +7,7 @@ function define(name, def)
 end
 function new(classname, ...)
     local obj = get(classname).new(...)
-    obj = setmetatable(obj, {__index = classes[classname]}) -- TODO ? metatable add __call with setfenv
+    obj = setmetatable(obj, {__index = classes[classname]})
     return obj
 end
 function get(classname)
diff --git a/func.lua b/func.lua
new file mode 100644
index 0000000..50fe6d7
--- /dev/null
+++ b/func.lua
@@ -0,0 +1,6 @@
+function curry(func, ...)
+    local args = {...}
+    return function(...)
+        return func(unpack(args), ...)
+    end
+end
\ No newline at end of file
diff --git a/init.lua b/init.lua
index df874d3..73eb3dc 100644
--- a/init.lua
+++ b/init.lua
@@ -3,7 +3,7 @@ if _VERSION then
     if _VERSION < "Lua 5" then
         error("Outdated Lua version! modlib requires Lua 5 or greater.")
     end
-    if _VERSION > "Lua 5.1" then -- TODO automatically use _ENV instead of s/getfenv if _VERSION > 5.1
+    if _VERSION > "Lua 5.1" then
         -- not throwing error("Too new Lua version! modlib requires Lua 5.1 or smaller.") anymore
         unpack = unpack or table.unpack -- unpack was moved to table.unpack in Lua 5.2
         loadstring = load
@@ -47,14 +47,13 @@ local function loadfile_exports(filename)
     return env
 end
 
--- TODO automatically know current mod
-
 local components = {
     mod = {},
     class = {},
     conf = {},
     data = {},
     file = {},
+    func = {},
     log = {},
     minetest = {},
     number = {},
@@ -80,4 +79,7 @@ end
 
 modlib.mod.loadfile_exports = loadfile_exports
 
+-- complete the string library (=metatable) with text helpers
+modlib.table.complete(string, modlib.text)
+
 _ml = modlib
\ No newline at end of file
diff --git a/minetest.lua b/minetest.lua
index 887256f..d88075f 100644
--- a/minetest.lua
+++ b/minetest.lua
@@ -1,5 +1,4 @@
 -- MT extension
--- TODO add formspec queues and event system + sync handler
 delta_times={}
 delays={}
 callbacks={}
diff --git a/mod.lua b/mod.lua
index 9f40cdb..f537ad1 100644
--- a/mod.lua
+++ b/mod.lua
@@ -9,12 +9,13 @@ function include(modname, file)
 end
 
 -- loadfile with table env
-function include_namespace(classname, filename)
-    _G[classname] = setmetatable(_G[classname] or {}, {__index = _G, __call = _G})
+function include_namespace(classname, filename, parent_namespace)
+    parent_namespace = parent_namespace or _G
+    parent_namespace[classname] = setmetatable(parent_namespace[classname] or {}, {__index = parent_namespace, __call = parent_namespace})
     local class = assert(loadfile(filename))
-    setfenv(class, _G[classname])
+    setfenv(class, parent_namespace[classname])
     class()
-    return _G[classname]
+    return parent_namespace[classname]
 end
 
 -- runs main.lua in table env
diff --git a/number.lua b/number.lua
index 2528167..7962e9b 100644
--- a/number.lua
+++ b/number.lua
@@ -3,3 +3,43 @@ function round(number, steps) -- Rounds a number
     steps = steps or 1
     return math.floor(number * steps + 0.5) / steps
 end
+local c0 = ("0"):byte()
+local cA = ("A"):byte()
+function default_digit_function(digit)
+    if digit <= 9 then
+        return string.char(c0+digit)
+    end
+    return string.char(cA+digit-10)
+end
+default_precision = 10
+function tostring(number, base, digit_function, precision)
+    digit_function = digit_function or default_digit_function
+    precision = precision or default_precision
+    local out = {}
+    if number < 0 then
+        table.insert(out, "-")
+        number = -number
+    end
+    local digit
+    while number >= base do
+        digit = math.floor(number % base)
+        table.insert(out, digit_function(digit))
+        number = number / base
+    end
+    digit = math.floor(number)
+    table.insert(out, digit_function(digit))
+    modlib.table.reverse(out)
+    number = number % 1
+    if number >= math.pow(base, precision) then
+        table.insert(out, ".")
+        -- precision >= 0 eventually redundant
+        while precision >= 0 and number >= math.pow(base, precision) do
+            number = number * base
+            digit = math.floor(number % base)
+            table.insert(out, digit_function(digit))
+            number = number - digit
+            precision = precision - 1
+        end
+    end
+    return table.concat(out)
+end
\ No newline at end of file
diff --git a/table.lua b/table.lua
index e370e4c..23e5cfa 100644
--- a/table.lua
+++ b/table.lua
@@ -47,6 +47,8 @@ function tablecopy(t)
     return table.copy(t)
 end
 
+copy = tablecopy
+
 function count(table)
     local count = 0
     for _ in pairs(table) do
@@ -59,10 +61,29 @@ function is_empty(table)
     return next(table) == nil
 end
 
+function foreach(t, func)
+    for k, v in pairs(t) do
+        func(k, v)
+    end
+end
+
+function foreach_value(t, func)
+    for _, v in pairs(t) do
+        func(v)
+    end
+end
+
+function foreach_key(t, func)
+    for k, _ in pairs(t) do
+        func(k)
+    end
+end
+
 function map(t, func)
     for k, v in pairs(t) do
         t[k]=func(v)
     end
+    return t
 end
 
 function process(t, func)
@@ -79,14 +100,17 @@ function call(funcs, ...)
     end
 end
 
-function merge_tables(table1, table2)
-    local table1copy = table.copy(table1)
-    for key, value in pairs(table2) do
-        table1copy[key] = value
+function find(list, value)
+    for index, other_value in pairs(list) do
+        if value == other_value then
+            return index
+        end
     end
-    return table1copy
+    return false
 end
 
+contains = find
+
 function difference(table1, table2)
     local result={}
     for k, v in pairs(table2) do
@@ -105,6 +129,31 @@ function add_all(dst, new)
     return dst
 end
 
+function complete(dst, new)
+    for key, value in pairs(new) do
+        if  dst[key] == nil then
+            dst[key] = value
+        end
+    end
+    return dst
+end
+
+function merge_tables(table1, table2)
+    return add_all(copy(table1), table2)
+end
+
+union = merge_tables
+
+function intersection(t1, t2)
+    local result = {}
+    for key, value in pairs(t1) do
+        if t2[key] then
+            result[key] = value
+        end
+    end
+    return result
+end
+
 function append(t1, t2)
     local l=#t1
     for k, v in ipairs(t2) do
@@ -192,15 +241,42 @@ function max(table)
     return best_value(table, function(v, m) return v > m end)
 end
 
-function binary_search(list, value)
-    local min, size = 1, #list
-    while size > 1 do
-        local s_half = math.floor(size / 2)
-        local pivot = min + s_half
-        local element = list[pivot]
-        if value > element then
-            min = pivot
-        end
-        size = s_half
+function default_comparator(a, b)
+    if a == b then
+        return 0
     end
+    if a > b then
+        return 1
+    end
+    return -1
+end
+
+function binary_search_comparator(comparator)
+    -- if found, returns index; if not found, returns -index for insertion
+    function binary_search(list, value)
+        local min, max = 1, #list
+        while min <= max do
+            local pivot = min + math.floor((max-min)/2)
+            local element = list[pivot]
+            local compared = comparator(value, element)
+            if compared == 0 then
+                return pivot
+            elseif compared > 0 then
+                min = pivot+1
+            else
+                max = pivot-1
+            end
+        end
+        return -min
+    end
+end
+
+binary_search = binary_search_comparator(default_comparator)
+
+function reverse(list)
+    local len = #list
+    for i = 1, math.floor(#list/2) do
+        list[len-i+1], list[i] = list[i], list[len-i+1]
+    end
+    return list
 end
\ No newline at end of file
diff --git a/text.lua b/text.lua
index c2ce358..1c91d0f 100644
--- a/text.lua
+++ b/text.lua
@@ -1,4 +1,3 @@
--- TODO probably set string metatables ?
 -- String helpers - split & trim at end & begin
 function upper_first(str)
     return str:sub(1,1):upper()..str:sub(2)