-- Table helpers function map_index(table, func) return setmetatable(table, { __index = function(table, key) return rawget(table, func(key)) end, __newindex = function(table, key, value) rawset(table, func(key), value) end }) end function set_case_insensitive_index(table) return map_index(table, string.lower) end function nilget(table, key, ...) assert(key ~= nil) local function nilget(table, key, ...) if key == nil then return table end local value = table[key] if value == nil then return nil end return nilget(value, ...) end return nilget(table, key, ...) end -- Fisher-Yates function shuffle(table) for index = 1, #table - 1 do local index_2 = math.random(index + 1, #table) table[index], table[index_2] = table[index_2], table[index] end return table end function equals_noncircular(table_1, table_2) local is_equal = table_1 == table_2 if is_equal or type(table_1) ~= "table" or type(table_2) ~= "table" then return is_equal end if #table_1 ~= #table_2 then return false end local table_keys = {} for key_1, value_1 in pairs(table_1) do local value_2 = table_2[key_1] if not equals_noncircular(value_1, value_2) then if type(key_1) == "table" then table_keys[key_1] = value_1 else return false end end end for key_2, value_2 in pairs(table_2) do if type(key_2) == "table" then local found for table, value in pairs(table_keys) do if equals_noncircular(key_2, table) and equals_noncircular(value_2, value) then table_keys[table] = nil found = true break end end if not found then return false end else if table_1[key_2] == nil then return false end end end return true end function tablecopy(t) return table.copy(t) end copy = tablecopy function shallowcopy(table) local copy = {} for key, value in pairs(table) do copy[key] = value end return copy end function deepcopy_noncircular(table) local function _copy(value) if type(value) == "table" then return deepcopy_noncircular(value) end return value end local copy = {} for key, value in pairs(table) do copy[_copy(key)] = _copy(value) end return copy end function deepcopy(table) local copies = {} local function _deepcopy(table) if copies[table] then return copies[table] end local copy = {} copies[table] = copy local function _copy(value) if type(value) == "table" then if copies[value] then return copies[value] end return _deepcopy(value) end return value end for key, value in pairs(table) do copy[_copy(key)] = _copy(value) end return copy end return _deepcopy(table) end function count(table) local count = 0 for _ in pairs(table) do count = count + 1 end return count end function is_empty(table) return next(table) == nil end function foreach(table, func) for k, v in pairs(table) do func(k, v) end end function foreach_value(table, func) for _, v in pairs(table) do func(v) end end function call(table, ...) for _, func in pairs(table) do func(...) end end function icall(table, ...) for _, func in ipairs(table) do func(...) end end function foreach_key(table, func) for key, _ in pairs(table) do func(key) end end function map(table, func) for key, value in pairs(table) do table[key] = func(value) end return table end function map_keys(table, func) local new_tab = {} for key, value in pairs(table) do new_tab[func(key)] = value end return new_tab end function process(table, func) local results = {} for key, value in pairs(table) do table.insert(results, func(key,value)) end return results end function call(funcs, ...) for _, func in ipairs(funcs) do func(...) end end function find(list, value) for index, other_value in pairs(list) do if value == other_value then return index end end return false end contains = find function difference(table, other_table) local result = {} for key, value in pairs(other_table) do if table[value] ~= value then result[key] = value end end return result end function add_all(table, additions) for key, value in pairs(additions) do table[key] = value end return table end function complete(table, completions) for key, value in pairs(completions) do if table[key] == nil then table[key] = value end end return table end function deepcomplete(table, completions) for key, value in pairs(completions) do if table[key] == nil then table[key] = value elseif type(table[key]) == "table" and type(value) == "table" then deepcomplete(table[key], value) end end return table end function merge_tables(table, other_table) return add_all(copy(table), other_table) end union = merge_tables function intersection(table, other_table) local result = {} for key, value in pairs(table) do if other_table[key] then result[key] = value end end return result end function append(table, other_table) local length = #table for index, value in ipairs(other_table) do table[length + index] = value end return table end function keys(table) local keys = {} for key, _ in pairs(table) do keys[#keys + 1] = key end return keys end function values(table) local values = {} for _, value in pairs(table) do values[#values + 1] = value end return values end function flip(table) local flipped = {} for key, value in pairs(table) do flipped[value] = key end return flipped end function set(table) local flipped = {} for _, value in pairs(table) do flipped[value] = true end return flipped end function unique(table) local lookup = {} for _, value in pairs(table) do lookup[value] = true end return keys(lookup) end function rpairs(table) local index = #table return function() if index >= 1 then local value = table[index] index = index - 1 if value ~= nil then return index + 1, value end end end end function best_value(table, is_better_func) local best = next(table) if best == nil then return end local candidate = best while true do candidate = next(table, candidate) if candidate == nil then return best end if is_better_func(candidate, best) then best = candidate end end error() end function min(table) return best_value(table, function(value, other_value) return value < other_value end) end function max(table) return best_value(table, function(value, other_value) return value > other_value end) end function default_comparator(value, other_value) if value == other_value then return 0 end if value > other_value then return 1 end return -1 end --> index if element found --> -index for insertion if not found function binary_search_comparator(comparator) return function(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(table) local l = #table + 1 for index = 1, math.floor(#table / 2) do table[l - index], table[index] = table[index], table[l - index] end return table end function repetition(value, count) local table = {} for index = 1, count do table[index] = value end return table end