Created
June 9, 2024 06:48
-
-
Save Brugarolas/f818066c2ff3ac36bbea9cece81bc963 to your computer and use it in GitHub Desktop.
Lua Stuff (WIP)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| -- @file adv[anced]_clean_m[odules].lua | |
| -- @brief A clean module system for Lua, with caching | |
| -- @author Andrés Brugarolas Martínez (2023) | |
| -- Store original environment for possible future uses | |
| _G.main_env = getfenv(1) | |
| -- Store function references for performance optimization | |
| local getfenv_local = getfenv | |
| local setfenv_local = setfenv | |
| local setmetatable_local = setmetatable | |
| local g_require_local = require | |
| local pcall_local = pcall | |
| local loadfile_local = loadfile | |
| local ipairs_local = ipairs | |
| local rawset_local = rawset | |
| -- Cache for compiled modules | |
| local __compiled_cache = {} | |
| -- Track globals set by each module | |
| local __module_globals = {} | |
| -- List of the files which won't be unloaded | |
| local __keep_in_memory = { | |
| -- Lua globals | |
| ["base"] = true, | |
| ["string"] = true, | |
| ["package"] = true, | |
| ["_G"] = true, | |
| ["os"] = true, | |
| ["table"] = true, | |
| ["math"] = true, | |
| ["coroutine"] = true, | |
| ["debug"] = true, | |
| ["io"] = true, | |
| -- LuaJIT | |
| ["bit"] = true, | |
| ["ffi"] = true, | |
| ["jit"] = true | |
| } | |
| -- Returns a functions that wraps the function passed as argument and | |
| -- logs if an exception is thrown during execution returning nil and allowing | |
| -- Lua continuing its execution flow, because we don't want a "corrupted" module | |
| -- to affect the rest of the modules and compromise the execution of the whole | |
| -- app/game/framework/server. If the returned nil is not handled in calling module | |
| -- or if the module is a core module and it is needed for other modules to work, | |
| -- app will crash somewhere else and execution will be stopped, but it's important | |
| -- it does NOT happen here | |
| -- Note: in case you are wondering why, this library was initially made as | |
| -- a framework for modding a game | |
| local function __createLoggingHighOrderFunction(func) | |
| return function (...) | |
| local ok, err = pcall_local(func, ...) | |
| if not ok then | |
| return nil, false | |
| end | |
| return ok, true | |
| end | |
| end | |
| -- Function to load a file (with loadfile, not with require, | |
| -- so we should handle package.loaded manually in calling | |
| -- functions), compile it, and save the compiled module | |
| -- in a cache to save time if we are loading it again | |
| local function __loadfile_compiled(name) | |
| local path = package.searchpath(name, package.path) | |
| if not path then | |
| error("Module '" .. name .. "' not found") | |
| end | |
| -- Check if already compiled and cached | |
| if __compiled_cache[path] then | |
| return __compiled_cache[path] | |
| end | |
| -- If not in cache, compile and store | |
| local f, err = loadfile_local(path) | |
| if not f then | |
| error("Error loading module '" .. name .. "' from file '" .. path .. "':\n\t" .. err) | |
| end | |
| __compiled_cache[path] = f | |
| return f | |
| end | |
| -- Merge the properties of the second table into the first (main) table | |
| local function __merge_table(main_table, secondary_table) | |
| if not secondary_table then return end | |
| for k, v in pairs(secondary_table) do main_table[k] = v end | |
| end | |
| -- Declare module cleanly: | |
| -- module is registered in package.loaded, | |
| -- but not inserted in the global namespace | |
| -- so it does not pollute global environment | |
| -- it only gets loaded into _G if specifically | |
| -- declared as "_G.name = value" | |
| local function _module(modname, ...) | |
| -- Convert module name to all lower case to avoid cases like "Main" and "main" potentially leading to separate tables | |
| local modulename = string.lower(modname) | |
| local _M = {} -- namespace for module | |
| setfenv_local(2, _M) | |
| -- Define for partial compatibility with module() | |
| _M._M = _M | |
| _M._NAME = modulename | |
| -- Keeps track of global defined vars with "_G.name = value" | |
| local global_vars = {} | |
| -- Apply decorators to the module, like module_compiled.seeall | |
| if ... then | |
| for _, func in ipairs_local({...}) do | |
| local decorator_defined_global_vars = func(_M) | |
| __merge_table(global_vars, decorator_defined_global_vars) | |
| end | |
| end | |
| package.loaded[modulename] = _M | |
| __module_globals[modulename] = global_vars | |
| end | |
| -- Modified _seeall function to allow specific writes to _G | |
| local function _seeall(_M) | |
| local priv = {} -- private environment for module | |
| local global_vars = {} -- keep track of global vars defined with "_G.name = value" | |
| local privmt = { | |
| __index = function(privat, k) | |
| return _M[k] or _G[k] | |
| end, | |
| __newindex = function(t, k, v) | |
| if string.sub(k, 1, 3) == "_G." then | |
| local propertyName = string.sub(k, 4) | |
| -- TODO Would this also works if the "global" var is defined inside a function? | |
| -- TODO I think it will, but I have to check it anyway | |
| _G[propertyName] = v | |
| global_vars[propertyName] = v | |
| else | |
| _M[k] = v | |
| end | |
| end | |
| } | |
| setmetatable_local(priv, privmt) | |
| setfenv_local(3, priv) | |
| return global_vars -- return global vars so "module" function can register and keep track of them | |
| end | |
| -- Require module with logging and cache | |
| local function _require(name) | |
| -- Convert module name to all lower case to avoid cases like "Main" and "main" potentially leading to separate tables | |
| local modulename = string.lower(name) | |
| -- Check if the module is already loaded | |
| if package.loaded[modulename] then | |
| return package.loaded[modulename] | |
| end | |
| -- Use our compile and cache mechanism | |
| local loader = __loadfile_compiled(modulename) | |
| local result = loader(modulename) | |
| package.loaded[modulename] = result | |
| rawset_local(getfenv_local(2), modulename, result) | |
| return result | |
| end | |
| -- Unload a module and clear | |
| local function _unrequire(name) | |
| -- Convert module name to all lower case to avoid cases like "Main" and "main" potentially leading to separate tables | |
| local modulename = string.lower(name) | |
| -- Remove global variables set by this module | |
| local globals = __module_globals[modulename] | |
| if globals then | |
| for k, _ in pairs(globals) do | |
| _G[k] = nil | |
| end | |
| __module_globals[modulename] = nil | |
| end | |
| -- Remove the module from package.loaded | |
| package.loaded[modulename] = nil | |
| end | |
| -- Clean all loaded packages except the ones we have | |
| -- defined to keep in memory (mostly lua core stuff and | |
| -- framework core stuff to) | |
| local function _unrequire_all() | |
| -- Convert module name to all lower case to avoid cases like "Main" and "main" potentially leading to separate tables | |
| for module_name, _ in pairs(package.loaded) do | |
| if not __keep_in_memory[module_name] then | |
| _unrequire(module_name) | |
| end | |
| end | |
| end | |
| -- Force require a module without recompiling, | |
| -- it loads and executes again an already loaded module, | |
| -- but it skips the actual "loading" and compilating part | |
| local function _force_require(name) | |
| -- Convert module name to all lower case to avoid cases like "Main" and "main" potentially leading to separate tables | |
| local modulename = string.lower(name) | |
| package.loaded[modulename] = nil | |
| return _require(modulename) | |
| end | |
| -- It is the same as regular _require but: | |
| -- 1) It allows you to pass as an argument the namespace table where all variables will be saved | |
| -- 2) So multiple modules can share the same namespace | |
| -- 3) It does not checks if module is already loaded in package.loaded and does not register module there | |
| local function _namespace_require(modulename, namespace_table) | |
| if package.loaded[modulename] then | |
| return package.loaded[modulename] | |
| end | |
| local loader = __loadfile_compiled(modulename) | |
| local t = namespace_table or {} | |
| setmetatable_local(t, { __index = getfenv(1) }) | |
| setfenv_local(loader, t) | |
| return loader(modulename) | |
| end | |
| -- Ironically, this module is not itself clean, so that it can be used with 'require' | |
| module(...) | |
| module = __createLoggingHighOrderFunction(_module) | |
| seeall = _seeall | |
| require = __createLoggingHighOrderFunction(_require) | |
| unrequire = __createLoggingHighOrderFunction(_unrequire) | |
| unrequire_all = __createLoggingHighOrderFunction(_unrequire_all) | |
| force_require = __createLoggingHighOrderFunction(_force_require) | |
| namespace_require = __createLoggingHighOrderFunction(_namespace_require) | |
| -- Explicit return is not necessary, but can be kept for clarity | |
| return _M |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| -- @file prototype.lua | |
| -- @brief Lua prototype-based object-oriented programming system. | |
| -- @author Andrés Brugarolas Martínez (2024) | |
| local ClassFactory = {} | |
| local useGlobalEnv = false | |
| -- Start a namespace and execute the provided callback within it. | |
| -- The garbage collector is stopped during the execution of the callback and resumed afterward. | |
| -- If no namespace is provided, a new one is created. | |
| -- The JIT threshold is lowered to minimum, and then restored, but before restoring the test | |
| -- function is run twice, effectively JIT compiling whatever the function is running from what | |
| -- is created in the callback. | |
| function ClassFactory.defineCompiledInNamespace(callback, test, namespace) | |
| assert(type(callback) == "function", "Expected a function for the callback.") | |
| -- If no namespace is provided, create a new one | |
| local actualNamespace | |
| if useGlobalEnv then | |
| actualNamespace = namespace or _G | |
| else | |
| actualNamespace = namespace or {} | |
| end | |
| -- Create the environment | |
| local environment = setmetatable({}, { | |
| __index = actualNamespace, | |
| __newindex = actualNamespace | |
| }) | |
| -- Save current JIT options | |
| local original_opts = {} | |
| for _, opt in ipairs{"hotloop", "hotexit", "tracerecord", "instunroll", "loopunroll", "callunroll", "recunroll", "sizemcode", "maxmcode", "maxtrace", "maxrecord", "maxirconst", "maxside", "maxsnap", "maxdse", "maxmcodegap", "maxmcodeuse", "maxphitopic", "maxtopicuse"} do | |
| original_opts[opt] = jit.opt.get(opt) | |
| end | |
| -- Stop the garbage collector | |
| collectgarbage("stop") | |
| -- Set the environment for the callback and execute it | |
| setfenv(callback, environment)() | |
| -- Set the environment fand execute the test twice to force JIT compile what's defined on callback | |
| local dummyEnvironment = {} | |
| setmetatable(dummyEnvironment, { __index = environment }) | |
| -- Execute the test twice in its own environment | |
| setfenv(test, dummyEnvironment) | |
| test() | |
| test() | |
| -- Resume the garbage collector | |
| collectgarbage("restart") | |
| -- Restore the original JIT options | |
| local opts_str = "" | |
| for opt, value in pairs(original_opts) do | |
| opts_str = opts_str .. opt .. "=" .. tostring(value) .. " " | |
| end | |
| jit_opt.start(opts_str:match("^%s*(.-)%s*$")) | |
| -- Return the namespaces | |
| return environment, dummyEnvironment | |
| end | |
| -- Clean up a namespace by setting all of its entries to nil. | |
| -- If an entry has a destroy method, it is called before the entry is set to nil. | |
| -- The garbage collector is stopped during the cleanup and resumed afterward. | |
| function ClassFactory.cleanAutomaticallyNamespace(namespace, callback) | |
| assert(type(callback) == "function", "Expected a function for the callback.") | |
| assert(type(namespace) == "table", "Expected a table for the namespace.") | |
| -- Stop the garbage collector | |
| collectgarbage("stop") | |
| -- Execute the callback to allow for manual cleanup | |
| callback(namespace) | |
| -- Clean up the namespace | |
| for k, v in pairs(namespace) do | |
| if type(v) == "table" and v.destroy and type(v.destroy) == "function" then | |
| v:destroy() | |
| end | |
| namespace[k] = nil | |
| end | |
| -- Resume the garbage collector | |
| collectgarbage("restart") | |
| end | |
| function ClassFactory.defineClassSystem() | |
| -- Metaclass for creating new classes | |
| Prototype = { | |
| prototype = {}, | |
| mixins = {} | |
| } | |
| Mixin = {} | |
| -- Setting Class's metatable to itself | |
| setmetatable(Prototype, Prototype) | |
| setmetatable(Mixin, Mixin) | |
| -- Method to NOT instantiate a mixin | |
| function Mixin:new(...) | |
| error("You cannot instantiate a mixin") | |
| end | |
| -- Method to create a new mixin/trait | |
| function Mixin:create(extendFunc) | |
| local newMixin = {} | |
| newMixin.super = self | |
| setmetatable(newMixin, newMixin) | |
| if extendFunc then | |
| extendFunc(newMixin) | |
| end | |
| return newMixin | |
| end | |
| -- Method to extend a mixin/trait | |
| function Mixin:extend(extendFunc) | |
| local newMixin = {} | |
| newMixin.super = self | |
| -- Allows newMixin to use methods defined in self.super mixin | |
| setmetatable(newMixin, { __index = self.super }) | |
| if extendFunc then | |
| extendFunc(newMixin) | |
| end | |
| return newMixin | |
| end | |
| -- Method to create a new class | |
| function Prototype:extend(extendFunc) | |
| local newClass = {} | |
| newClass.super = self | |
| newClass.mixins = {} | |
| newClass.prototype = {} | |
| setmetatable(newClass, newClass) | |
| -- Allows newClass to use methods defined in Class.prototype | |
| setmetatable(newClass.prototype, { __index = self.prototype }) | |
| -- Allows newClass to use methods defined in Class.mixins | |
| setmetatable(newClass.mixins, { __index = self.mixins }) | |
| if extendFunc then | |
| extendFunc(newClass) | |
| end | |
| return newClass | |
| end | |
| -- Method to implement a mixin in a class | |
| function Prototype:implement(mixin) | |
| self.mixins[#self.mixins + 1] = mixin | |
| return self | |
| end | |
| -- Method to instantiate a class | |
| function Prototype:new(...) | |
| if self == Prototype then | |
| error("You cannot instantiate a meta-prototype") | |
| end | |
| local instance = setmetatable({}, self.prototype) | |
| for _, mixin in ipairs(self.mixins) do | |
| for propertyName, propertyValue in pairs(mixin) do | |
| if type(propertyValue) == "function" then | |
| instance[propertyName] = function(self, ...) | |
| return propertyValue(self, ...) | |
| end | |
| else | |
| instance[propertyName] = propertyValue | |
| end | |
| end | |
| end | |
| instance.class = self | |
| instance.type = "instance" | |
| if instance.constructor then | |
| instance:constructor(...) | |
| end | |
| return instance | |
| end | |
| -- Basic methods for all classes | |
| Prototype.prototype.isSame = function(self, other) | |
| return self.class == other.class | |
| end | |
| Prototype.prototype.isEqual = function(self, other) | |
| local function tables_are_equal(t1, t2) | |
| -- Check if both are not tables or are nil | |
| if type(t1) ~= 'table' or type(t2) ~= 'table' then | |
| return t1 == t2 | |
| end | |
| -- Check if they have the same keys and values | |
| for k1, v1 in pairs(t1) do | |
| local v2 = t2[k1] | |
| if v2 == nil or not tables_are_equal(v1, v2) then | |
| return false | |
| end | |
| end | |
| -- Check if t2 has keys not in t1 | |
| for k2, _ in pairs(t2) do | |
| if t1[k2] == nil then | |
| return false | |
| end | |
| end | |
| return true | |
| end | |
| return tables_are_equal(self, other) | |
| end | |
| Prototype.prototype.isDeepEqual = function(self, other) | |
| if type(self) ~= 'function' or type(other) ~= 'function' then | |
| return false | |
| end | |
| return string.dump(self) == string.dump(other) | |
| end | |
| Prototype.prototype.toString = function(self) | |
| local function tableToString(tbl) | |
| local result = {} | |
| local isArray = #tbl > 0 | |
| if isArray then | |
| for i, v in ipairs(tbl) do | |
| table.insert(result, tostring(v)) | |
| end | |
| return "[" .. table.concat(result, ", ") .. "]" | |
| else | |
| for k, v in pairs(tbl) do | |
| local keyStr = type(k) == "string" and '"' .. k .. '"' or "[" .. tostring(k) .. "]" | |
| table.insert(result, keyStr .. ": " .. tostring(v)) | |
| end | |
| return "{" .. table.concat(result, ", ") .. "}" | |
| end | |
| end | |
| local valueType = type(self) | |
| if valueType == "table" then | |
| return tableToString(self) | |
| elseif valueType == "string" then | |
| return '"' .. self .. '"' | |
| elseif valueType == "number" or valueType == "boolean" then | |
| return tostring(self) | |
| elseif valueType == "function" then | |
| return "<function>" | |
| elseif valueType == "userdata" or valueType == "thread" then | |
| return "<" .. valueType .. ">" | |
| else | |
| return "nil" | |
| end | |
| end | |
| Prototype.prototype.isInstanceOf = function(self, aClass) | |
| local currentClass = self.class | |
| while currentClass ~= nil do | |
| if currentClass == aClass then | |
| return true | |
| end | |
| currentClass = currentClass.super | |
| end | |
| return false | |
| end | |
| -- Object prototype, more user-friendly and with table methods | |
| Object = Prototype:extend(function (Object) | |
| -- Populate Object.prototype with table methods | |
| for k, v in pairs(table) do | |
| Object.prototype[k] = function(self, ...) | |
| return v(self, ...) | |
| end | |
| end | |
| end) | |
| -- Array Implementation | |
| Array = Object:extend(function(Array) | |
| -- Constructor | |
| function Array.constructor(self, ...) | |
| self.elements = {...} | |
| end | |
| -- Length method | |
| function Array:length() | |
| return #self.elements | |
| end | |
| -- Push method | |
| function Array:push(element) | |
| table.insert(self.elements, element) | |
| end | |
| -- Pop method | |
| function Array:pop() | |
| return table.remove(self.elements) | |
| end | |
| -- Map method | |
| function Array:map(callback) | |
| local newArray = Array:new() | |
| for i, v in ipairs(self.elements) do | |
| newArray:push(callback(v, i)) | |
| end | |
| return newArray | |
| end | |
| -- Filter method | |
| function Array:filter(callback) | |
| local newArray = Array:new() | |
| for i, v in ipairs(self.elements) do | |
| if callback(v, i) then | |
| newArray:push(v) | |
| end | |
| end | |
| return newArray | |
| end | |
| -- Reduce method | |
| function Array:reduce(callback, initialValue) | |
| local accumulator = initialValue | |
| for i, v in ipairs(self.elements) do | |
| accumulator = callback(accumulator, v, i) | |
| end | |
| return accumulator | |
| end | |
| -- Divide method | |
| function Array:divide(conditionFunction) | |
| local match = Array:new() | |
| local others = Array:new() | |
| for _, v in ipairs(self.elements) do | |
| if conditionFunction(v) then | |
| match:push(v) | |
| else | |
| others:push(v) | |
| end | |
| end | |
| return { match = match, others = others } | |
| end | |
| end) | |
| -- End Array Implementation | |
| -- Set Implementation | |
| Set = Object:extend(function(Set) | |
| -- Constructor | |
| function Set.constructor(self, elements) | |
| self.elements = {} | |
| if elements then | |
| for _, element in ipairs(elements) do | |
| self.elements[element] = true | |
| end | |
| end | |
| end | |
| -- Add method | |
| function Set:add(element) | |
| self.elements[element] = true | |
| end | |
| -- Delete method | |
| function Set:delete(element) | |
| self.elements[element] = nil | |
| end | |
| -- Has method | |
| function Set:has(element) | |
| return self.elements[element] ~= nil | |
| end | |
| -- Clear method | |
| function Set:clear() | |
| self.elements = {} | |
| end | |
| -- Size method | |
| function Set:size() | |
| local count = 0 | |
| for _ in pairs(self.elements) do | |
| count = count + 1 | |
| end | |
| return count | |
| end | |
| -- Union method | |
| function Set:union(otherSet) | |
| local resultSet = Set:new() | |
| for element in pairs(self.elements) do | |
| resultSet:add(element) | |
| end | |
| for element in pairs(otherSet.elements) do | |
| resultSet:add(element) | |
| end | |
| return resultSet | |
| end | |
| -- Intersection method | |
| function Set:intersection(otherSet) | |
| local resultSet = Set:new() | |
| for element in pairs(self.elements) do | |
| if otherSet:has(element) then | |
| resultSet:add(element) | |
| end | |
| end | |
| return resultSet | |
| end | |
| -- Difference method | |
| function Set:difference(otherSet) | |
| local resultSet = Set:new() | |
| for element in pairs(self.elements) do | |
| if not otherSet:has(element) then | |
| resultSet:add(element) | |
| end | |
| end | |
| return resultSet | |
| end | |
| -- FilterBySet method | |
| function Set:filterBySet(anotherSet) | |
| local resultSet = Set:new() | |
| for element in pairs(self.elements) do | |
| if not anotherSet:has(element) then | |
| resultSet:add(element) | |
| end | |
| end | |
| return resultSet | |
| end | |
| -- IsIncludedIn method | |
| function Set:isIncludedIn(element) | |
| for item in pairs(self.elements) do | |
| if string.find(element, item) then | |
| return true | |
| end | |
| end | |
| return false | |
| end | |
| -- ToTable method (utility to convert Set to table) | |
| function Set:toTable() | |
| local tbl = {} | |
| for element in pairs(self.elements) do | |
| table.insert(tbl, element) | |
| end | |
| return tbl | |
| end | |
| end) | |
| -- End Set Implementation | |
| -- String Implementation | |
| String = Object:extend(function(String) | |
| -- Constructor | |
| function String.constructor(self, value) | |
| self.value = value or "" | |
| end | |
| -- Length method | |
| function String:length() | |
| return #self.value | |
| end | |
| -- CharAt method | |
| function String:charAt(index) | |
| return self.value:sub(index, index) | |
| end | |
| -- Substring method | |
| function String:substring(startIndex, endIndex) | |
| return String:new(self.value:sub(startIndex, endIndex)) | |
| end | |
| -- ToUpperCase method | |
| function String:toUpperCase() | |
| return String:new(self.value:upper()) | |
| end | |
| -- ToLowerCase method | |
| function String:toLowerCase() | |
| return String:new(self.value:lower()) | |
| end | |
| -- Split method | |
| function String:split(separator) | |
| local result = {} | |
| for match in (self.value .. separator):gmatch("(.-)" .. separator) do | |
| table.insert(result, String:new(match)) | |
| end | |
| return result | |
| end | |
| -- Trim method | |
| function String:trim() | |
| return String:new(self.value:match("^%s*(.-)%s*$")) | |
| end | |
| -- HashCode method | |
| function String:hashCode() | |
| local hash = 0 | |
| if self.value == "" then | |
| return tostring(hash) | |
| end | |
| for i = 1, #self.value do | |
| local char = self.value:byte(i) | |
| hash = ((hash << 5) - hash) + char | |
| hash = hash & hash -- convert to 32bit integer | |
| end | |
| hash = hash >> 0 -- convert signed to unsigned | |
| return tostring(hash):gsub("%-",""):format("%x") -- remove any negative sign and convert to hex | |
| end | |
| end) | |
| -- End String Implementation | |
| -- Start HashMap Implementation | |
| HashMap = Object:extend(function(HashMap) | |
| -- Constructor | |
| function HashMap.constructor(self) | |
| self.map = {} | |
| self.size = 0 | |
| end | |
| -- Set method | |
| function HashMap:set(key, value) | |
| local keyString = tostring(key) | |
| if self.map[keyString] == nil then | |
| self.size = self.size + 1 | |
| end | |
| self.map[keyString] = value | |
| end | |
| -- Get method | |
| function HashMap:get(key) | |
| return self.map[tostring(key)] | |
| end | |
| -- Has method | |
| function HashMap:has(key) | |
| return self.map[tostring(key)] ~= nil | |
| end | |
| -- Delete method | |
| function HashMap:delete(key) | |
| local keyString = tostring(key) | |
| if self.map[keyString] ~= nil then | |
| self.map[keyString] = nil | |
| self.size = self.size - 1 | |
| return true | |
| end | |
| return false | |
| end | |
| -- Clear method | |
| function HashMap:clear() | |
| self.map = {} | |
| self.size = 0 | |
| end | |
| -- Size method | |
| function HashMap:size() | |
| return self.size | |
| end | |
| -- Keys method | |
| function HashMap:keys() | |
| local keys = {} | |
| for k, _ in pairs(self.map) do | |
| table.insert(keys, k) | |
| end | |
| return keys | |
| end | |
| -- Values method | |
| function HashMap:values() | |
| local values = {} | |
| for _, v in pairs(self.map) do | |
| table.insert(values, v) | |
| end | |
| return values | |
| end | |
| -- Entries method | |
| function HashMap:entries() | |
| local entries = {} | |
| for k, v in pairs(self.map) do | |
| table.insert(entries, { key = k, value = v }) | |
| end | |
| return entries | |
| end | |
| -- ForEach method | |
| function HashMap:forEach(callback) | |
| for k, v in pairs(self.map) do | |
| callback(k, v) | |
| end | |
| end | |
| -- Clone method | |
| function HashMap:clone() | |
| local newMap = HashMap:new() | |
| for k, v in pairs(self.map) do | |
| newMap:set(k, v) | |
| end | |
| return newMap | |
| end | |
| -- ToString method (useful for debugging) | |
| function HashMap:toString() | |
| local result = "{" | |
| for k, v in pairs(self.map) do | |
| result = result .. k .. ": " .. tostring(v) .. ", " | |
| end | |
| if result:sub(-2) == ", " then | |
| result = result:sub(1, -3) | |
| end | |
| result = result .. "}" | |
| return result | |
| end | |
| end) | |
| -- End HashMap Implementation | |
| -- Start Queue Implementation | |
| Queue = Object:extend(function(Queue) | |
| -- Constructor | |
| function Queue.constructor(self) | |
| self.elements = {} | |
| self.front = 0 | |
| self.back = -1 | |
| end | |
| -- Enqueue method | |
| function Queue:enqueue(element) | |
| self.back = self.back + 1 | |
| self.elements[self.back] = element | |
| end | |
| -- Dequeue method | |
| function Queue:dequeue() | |
| if self.front > self.back then | |
| return nil | |
| end | |
| local element = self.elements[self.front] | |
| self.elements[self.front] = nil | |
| self.front = self.front + 1 | |
| return element | |
| end | |
| -- Peek method | |
| function Queue:peek() | |
| return self.elements[self.front] | |
| end | |
| -- IsEmpty method | |
| function Queue:isEmpty() | |
| return self.front > self.back | |
| end | |
| -- Clear method | |
| function Queue:clear() | |
| self.elements = {} | |
| self.front = 0 | |
| self.back = -1 | |
| end | |
| -- Size method | |
| function Queue:size() | |
| return self.back - self.front + 1 | |
| end | |
| end) | |
| -- End Queue Implementation | |
| -- Start Stack Implementation | |
| Stack = Object:extend(function(Stack) | |
| -- Constructor | |
| function Stack.constructor(self) | |
| self.elements = {} | |
| self.top = 0 | |
| end | |
| -- Push method | |
| function Stack:push(element) | |
| self.top = self.top + 1 | |
| self.elements[self.top] = element | |
| end | |
| -- Pop method | |
| function Stack:pop() | |
| if self.top == 0 then | |
| return nil | |
| end | |
| local element = self.elements[self.top] | |
| self.elements[self.top] = nil | |
| self.top = self.top - 1 | |
| return element | |
| end | |
| -- Peek method | |
| function Stack:peek() | |
| return self.elements[self.top] | |
| end | |
| -- IsEmpty method | |
| function Stack:isEmpty() | |
| return self.top == 0 | |
| end | |
| -- Clear method | |
| function Stack:clear() | |
| self.elements = {} | |
| self.top = 0 | |
| end | |
| -- Size method | |
| function Stack:size() | |
| return self.top | |
| end | |
| end) | |
| -- End Stack Implementation | |
| -- Start Deque Implementation | |
| Deque = Object:extend(function(Deque) | |
| -- Constructor | |
| function Deque.constructor(self) | |
| self.elements = {} | |
| self.front = 0 | |
| self.back = -1 | |
| end | |
| -- PushFront method | |
| function Deque:pushFront(element) | |
| self.front = self.front - 1 | |
| self.elements[self.front] = element | |
| end | |
| -- PushBack method | |
| function Deque:pushBack(element) | |
| self.back = self.back + 1 | |
| self.elements[self.back] = element | |
| end | |
| -- PopFront method | |
| function Deque:popFront() | |
| if self.front > self.back then | |
| return nil | |
| end | |
| local element = self.elements[self.front] | |
| self.elements[self.front] = nil | |
| self.front = self.front + 1 | |
| return element | |
| end | |
| -- PopBack method | |
| function Deque:popBack() | |
| if self.front > self.back then | |
| return nil | |
| end | |
| local element = self.elements[self.back] | |
| self.elements[self.back] = nil | |
| self.back = self.back - 1 | |
| return element | |
| end | |
| -- PeekFront method | |
| function Deque:peekFront() | |
| return self.elements[self.front] | |
| end | |
| -- PeekBack method | |
| function Deque:peekBack() | |
| return self.elements[self.back] | |
| end | |
| -- IsEmpty method | |
| function Deque:isEmpty() | |
| return self.front > self.back | |
| end | |
| -- Clear method | |
| function Deque:clear() | |
| self.elements = {} | |
| self.front = 0 | |
| self.back = -1 | |
| end | |
| -- Size method | |
| function Deque:size() | |
| return self.back - self.front + 1 | |
| end | |
| end) | |
| -- End Deque Implementation | |
| -- Start PriorityQueue Implementation | |
| PriorityQueue = Object:extend(function(PriorityQueue) | |
| -- Constructor | |
| function PriorityQueue.constructor(self) | |
| self.elements = {} | |
| self.size = 0 | |
| end | |
| -- Enqueue method | |
| function PriorityQueue:enqueue(element, priority) | |
| self.size = self.size + 1 | |
| local index = self.size | |
| self.elements[index] = { element = element, priority = priority } | |
| while index > 1 do | |
| local parentIndex = math.floor(index / 2) | |
| if self.elements[index].priority >= self.elements[parentIndex].priority then | |
| break | |
| end | |
| self.elements[index], self.elements[parentIndex] = self.elements[parentIndex], self.elements[index] | |
| index = parentIndex | |
| end | |
| end | |
| -- Dequeue method | |
| function PriorityQueue:dequeue() | |
| if self.size == 0 then | |
| return nil | |
| end | |
| local element = self.elements[1].element | |
| self.elements[1] = self.elements[self.size] | |
| self.elements[self.size] = nil | |
| self.size = self.size - 1 | |
| local index = 1 | |
| while true do | |
| local leftIndex = index * 2 | |
| local rightIndex = index * 2 + 1 | |
| local smallestIndex = index | |
| if leftIndex <= self.size and self.elements[leftIndex].priority < self.elements[smallestIndex].priority then | |
| smallestIndex = leftIndex | |
| end | |
| if rightIndex <= self.size and self.elements[rightIndex].priority < self.elements[smallestIndex].priority then | |
| smallestIndex = rightIndex | |
| end | |
| if smallestIndex == index then | |
| break | |
| end | |
| self.elements[index], self.elements[smallestIndex] = self.elements[smallestIndex], self.elements[index] | |
| index = smallestIndex | |
| end | |
| return element | |
| end | |
| -- Peek method | |
| function PriorityQueue:peek() | |
| return self.elements[1].element | |
| end | |
| -- IsEmpty method | |
| function PriorityQueue:isEmpty() | |
| return self.size == 0 | |
| end | |
| -- Clear method | |
| function PriorityQueue:clear() | |
| self.elements = {} | |
| self.size = 0 | |
| end | |
| -- Size method | |
| function PriorityQueue:size() | |
| return self.size | |
| end | |
| end) | |
| -- End PriorityQueue Implementation | |
| -- Start Graph Implementation | |
| Graph = Object:extend(function(Graph) | |
| -- Constructor | |
| function Graph.constructor(self) | |
| self.vertices = {} | |
| self.edges = {} | |
| end | |
| -- AddVertex method | |
| function Graph:addVertex(vertex) | |
| self.vertices[vertex] = true | |
| self.edges[vertex] = {} | |
| end | |
| -- AddEdge method | |
| function Graph:addEdge(vertex1, vertex2, weight) | |
| if not self.vertices[vertex1] or not self.vertices[vertex2] then | |
| return | |
| end | |
| if not self.edges[vertex1][vertex2] then | |
| self.edges[vertex1][vertex2] = weight | |
| end | |
| end | |
| -- RemoveVertex method | |
| function Graph:removeVertex(vertex) | |
| self.vertices[vertex] = nil | |
| self.edges[vertex] = nil | |
| for _, edges in pairs(self.edges) do | |
| edges[vertex] = nil | |
| end | |
| end | |
| -- RemoveEdge method | |
| function Graph:removeEdge(vertex1, vertex2) | |
| if not self.vertices[vertex1] or not self.vertices[vertex2] then | |
| return | |
| end | |
| if self.edges[vertex1][vertex2] then | |
| self.edges[vertex1][vertex2] = nil | |
| end | |
| end | |
| -- HasVertex method | |
| function Graph:hasVertex(vertex) | |
| return self.vertices[vertex] ~= nil | |
| end | |
| -- HasEdge method | |
| function Graph:hasEdge(vertex1, vertex2) | |
| return self.edges[vertex1][vertex2] ~= nil | |
| end | |
| -- GetWeight method | |
| function Graph:getWeight(vertex1, vertex2) | |
| return self.edges[vertex1][vertex2] | |
| end | |
| -- GetNeighbors method | |
| function Graph:getNeighbors(vertex) | |
| local neighbors = {} | |
| for neighbor in pairs(self.edges[vertex]) do | |
| table.insert(neighbors, neighbor) | |
| end | |
| return neighbors | |
| end | |
| end) | |
| -- End Graph Implementation | |
| -- Start DirectedGraph Implementation | |
| DirectedGraph = Object:extend(function(DirectedGraph) | |
| -- Constructor | |
| function DirectedGraph.constructor(self) | |
| self.vertices = {} | |
| self.edges = {} | |
| end | |
| -- AddVertex method | |
| function DirectedGraph:addVertex(vertex) | |
| self.vertices[vertex] = true | |
| self.edges[vertex] = {} | |
| end | |
| -- AddEdge method | |
| function DirectedGraph:addEdge(vertex1, vertex2, weight) | |
| if not self.vertices[vertex1] or not self.vertices[vertex2] then | |
| return | |
| end | |
| if not self.edges[vertex1][vertex2] then | |
| self.edges[vertex1][vertex2] = weight | |
| end | |
| end | |
| -- RemoveVertex method | |
| function DirectedGraph:removeVertex(vertex) | |
| self.vertices[vertex] = nil | |
| self.edges[vertex] = nil | |
| for _, edges in pairs(self.edges) do | |
| edges[vertex] = nil | |
| end | |
| end | |
| -- RemoveEdge method | |
| function DirectedGraph:removeEdge(vertex1, vertex2) | |
| if not self.vertices[vertex1] or not self.vertices[vertex2] then | |
| return | |
| end | |
| if self.edges[vertex1][vertex2] then | |
| self.edges[vertex1][vertex2] = nil | |
| end | |
| end | |
| -- HasVertex method | |
| function DirectedGraph:hasVertex(vertex) | |
| return self.vertices[vertex] ~= nil | |
| end | |
| -- HasEdge method | |
| function DirectedGraph:hasEdge(vertex1, vertex2) | |
| return self.edges[vertex1][vertex2] ~= nil | |
| end | |
| -- GetWeight method | |
| function DirectedGraph:getWeight(vertex1, vertex2) | |
| return self.edges[vertex1][vertex2] | |
| end | |
| -- GetNeighbors method | |
| function DirectedGraph:getNeighbors(vertex) | |
| local neighbors = {} | |
| for neighbor in pairs(self.edges[vertex]) do | |
| table.insert(neighbors, neighbor) | |
| end | |
| return neighbors | |
| end | |
| end) | |
| -- End DirectedGraph Implementation | |
| -- Start DirectedAcyclicGraph Implementation | |
| DirectedAcyclicGraph = Object:extend(function(DirectedAcyclicGraph) | |
| -- Constructor | |
| function DirectedAcyclicGraph.constructor(self) | |
| self.vertices = {} | |
| self.edges = {} | |
| end | |
| -- AddVertex method | |
| function DirectedAcyclicGraph:addVertex(vertex) | |
| self.vertices[vertex] = true | |
| self.edges[vertex] = {} | |
| end | |
| -- AddEdge method | |
| function DirectedAcyclicGraph:addEdge(vertex1, vertex2, weight) | |
| if not self.vertices[vertex1] or not self.vertices[vertex2] then | |
| return | |
| end | |
| if not self.edges[vertex1][vertex2] then | |
| self.edges[vertex1][vertex2] = weight | |
| end | |
| end | |
| -- RemoveVertex method | |
| function DirectedAcyclicGraph:removeVertex(vertex) | |
| self.vertices[vertex] = nil | |
| self.edges[vertex] = nil | |
| for _, edges in pairs(self.edges) do | |
| edges[vertex] = nil | |
| end | |
| end | |
| -- RemoveEdge method | |
| function DirectedAcyclicGraph:removeEdge(vertex1, vertex2) | |
| if not self.vertices[vertex1] or not self.vertices[vertex2] then | |
| return | |
| end | |
| if self.edges[vertex1][vertex2] then | |
| self.edges[vertex1][vertex2] = nil | |
| end | |
| end | |
| -- HasVertex method | |
| function DirectedAcyclicGraph:hasVertex(vertex) | |
| return self.vertices[vertex] ~= nil | |
| end | |
| -- HasEdge method | |
| function DirectedAcyclicGraph:hasEdge(vertex1, vertex2) | |
| return self.edges[vertex1][vertex2] ~= nil | |
| end | |
| -- GetWeight method | |
| function DirectedAcyclicGraph:getWeight(vertex1, vertex2) | |
| return self.edges[vertex1][vertex2] | |
| end | |
| -- GetNeighbors method | |
| function DirectedAcyclicGraph:getNeighbors(vertex) | |
| local neighbors = {} | |
| for neighbor in pairs(self.edges[vertex]) do | |
| table.insert(neighbors, neighbor) | |
| end | |
| return neighbors | |
| end | |
| -- TopologicalSort method | |
| function DirectedAcyclicGraph:topologicalSort() | |
| local visited = {} | |
| local stack = Stack:new() | |
| local function visit(vertex) | |
| if visited[vertex] then | |
| return | |
| end | |
| visited[vertex] = true | |
| for neighbor in pairs(self.edges[vertex]) do | |
| visit(neighbor) | |
| end | |
| stack:push(vertex) | |
| end | |
| for vertex in pairs(self.vertices) do | |
| visit(vertex) | |
| end | |
| local result = {} | |
| while not stack:isEmpty() do | |
| table.insert(result, stack:pop()) | |
| end | |
| return result | |
| end | |
| end) | |
| -- End DirectedAcyclicGraph Implementation | |
| -- Start RedBlackTree Implementation | |
| -- Define colors | |
| local RED = true | |
| local BLACK = false | |
| -- Node class | |
| local RedBlackTreeNode = Object:extend(function(RedBlackTreeNode) | |
| function RedBlackTreeNode.constructor(self, key, value, color) | |
| self.key = key | |
| self.value = value | |
| self.color = color | |
| self.left = nil | |
| self.right = nil | |
| self.parent = nil | |
| end | |
| function RedBlackTreeNode:isRed() | |
| return self.color == RED | |
| end | |
| function RedBlackTreeNode:setColor(color) | |
| self.color = color | |
| end | |
| end) | |
| -- RedBlackTree class | |
| RedBlackTree = Object:extend(function(RedBlackTree) | |
| function RedBlackTree.constructor(self) | |
| self.root = nil | |
| end | |
| function RedBlackTree:rotateLeft(node) | |
| local rightChild = node.right | |
| node.right = rightChild.left | |
| if rightChild.left ~= nil then | |
| rightChild.left.parent = node | |
| end | |
| rightChild.parent = node.parent | |
| if node.parent == nil then | |
| self.root = rightChild | |
| elseif node == node.parent.left then | |
| node.parent.left = rightChild | |
| else | |
| node.parent.right = rightChild | |
| end | |
| rightChild.left = node | |
| node.parent = rightChild | |
| end | |
| function RedBlackTree:rotateRight(node) | |
| local leftChild = node.left | |
| node.left = leftChild.right | |
| if leftChild.right ~= nil then | |
| leftChild.right.parent = node | |
| end | |
| leftChild.parent = node.parent | |
| if node.parent == nil then | |
| self.root = leftChild | |
| elseif node == node.parent.right then | |
| node.parent.right = leftChild | |
| else | |
| node.parent.left = leftChild | |
| end | |
| leftChild.right = node | |
| node.parent = leftChild | |
| end | |
| function RedBlackTree:fixInsert(node) | |
| while node.parent and node.parent:isRed() do | |
| if node.parent == node.parent.parent.left then | |
| local uncle = node.parent.parent.right | |
| if uncle and uncle:isRed() then | |
| node.parent:setColor(BLACK) | |
| uncle:setColor(BLACK) | |
| node.parent.parent:setColor(RED) | |
| node = node.parent.parent | |
| else | |
| if node == node.parent.right then | |
| node = node.parent | |
| self:rotateLeft(node) | |
| end | |
| node.parent:setColor(BLACK) | |
| node.parent.parent:setColor(RED) | |
| self:rotateRight(node.parent.parent) | |
| end | |
| else | |
| local uncle = node.parent.parent.left | |
| if uncle and uncle:isRed() then | |
| node.parent:setColor(BLACK) | |
| uncle:setColor(BLACK) | |
| node.parent.parent:setColor(RED) | |
| node = node.parent.parent | |
| else | |
| if node == node.parent.left then | |
| node = node.parent | |
| self:rotateRight(node) | |
| end | |
| node.parent:setColor(BLACK) | |
| node.parent.parent:setColor(RED) | |
| self:rotateLeft(node.parent.parent) | |
| end | |
| end | |
| end | |
| self.root:setColor(BLACK) | |
| end | |
| function RedBlackTree:insert(key, value) | |
| local newNode = RedBlackTreeNode:new(key, value, RED) | |
| if self.root == nil then | |
| self.root = newNode | |
| self.root:setColor(BLACK) | |
| return | |
| end | |
| local currentNode = self.root | |
| local parentNode = nil | |
| while currentNode ~= nil do | |
| parentNode = currentNode | |
| if key < currentNode.key then | |
| currentNode = currentNode.left | |
| elseif key > currentNode.key then | |
| currentNode = currentNode.right | |
| else | |
| currentNode.value = value | |
| return | |
| end | |
| end | |
| newNode.parent = parentNode | |
| if key < parentNode.key then | |
| parentNode.left = newNode | |
| else | |
| parentNode.right = newNode | |
| end | |
| self:fixInsert(newNode) | |
| end | |
| function RedBlackTree:search(key) | |
| local currentNode = self.root | |
| while currentNode ~= nil do | |
| if key < currentNode.key then | |
| currentNode = currentNode.left | |
| elseif key > currentNode.key then | |
| currentNode = currentNode.right | |
| else | |
| return currentNode.value | |
| end | |
| end | |
| return nil | |
| end | |
| function RedBlackTree:inOrderTraversal(node, callback) | |
| if node ~= nil then | |
| self:inOrderTraversal(node.left, callback) | |
| callback(node) | |
| self:inOrderTraversal(node.right, callback) | |
| end | |
| end | |
| function RedBlackTree:preOrderTraversal(node, callback) | |
| if node ~= nil then | |
| callback(node) | |
| self:preOrderTraversal(node.left, callback) | |
| self:preOrderTraversal(node.right, callback) | |
| end | |
| end | |
| function RedBlackTree:postOrderTraversal(node, callback) | |
| if node ~= nil then | |
| self:postOrderTraversal(node.left, callback) | |
| self:postOrderTraversal(node.right, callback) | |
| callback(node) | |
| end | |
| end | |
| function RedBlackTree:traverseInOrder(callback) | |
| self:inOrderTraversal(self.root, callback) | |
| end | |
| function RedBlackTree:traversePreOrder(callback) | |
| self:preOrderTraversal(self.root, callback) | |
| end | |
| function RedBlackTree:traversePostOrder(callback) | |
| self:postOrderTraversal(self.root, callback) | |
| end | |
| end) | |
| -- End RedBlackTree Implementation | |
| -- Start Trie Implementation | |
| -- TrieNode class | |
| local TrieNode = Object:extend(function(TrieNode) | |
| function TrieNode.constructor(self) | |
| self.children = {} | |
| self.isEndOfWord = false | |
| end | |
| end) | |
| -- Trie class | |
| Trie = Object:extend(function(Trie) | |
| function Trie.constructor(self) | |
| self.root = TrieNode:new() | |
| end | |
| function Trie:insert(word) | |
| local currentNode = self.root | |
| for i = 1, #word do | |
| local char = word:sub(i, i) | |
| if not currentNode.children[char] then | |
| currentNode.children[char] = TrieNode:new() | |
| end | |
| currentNode = currentNode.children[char] | |
| end | |
| currentNode.isEndOfWord = true | |
| end | |
| function Trie:search(word) | |
| local currentNode = self.root | |
| for i = 1, #word do | |
| local char = word:sub(i, i) | |
| if not currentNode.children[char] then | |
| return false | |
| end | |
| currentNode = currentNode.children[char] | |
| end | |
| return currentNode.isEndOfWord | |
| end | |
| function Trie:delete(word) | |
| local function deleteHelper(node, word, index) | |
| if index == #word then | |
| if not node.isEndOfWord then | |
| return false | |
| end | |
| node.isEndOfWord = false | |
| return next(node.children) == nil | |
| end | |
| local char = word:sub(index + 1, index + 1) | |
| if not node.children[char] then | |
| return false | |
| end | |
| local shouldDeleteNode = deleteHelper(node.children[char], word, index + 1) | |
| if shouldDeleteNode then | |
| node.children[char] = nil | |
| return next(node.children) == nil | |
| end | |
| return false | |
| end | |
| deleteHelper(self.root, word, 0) | |
| end | |
| end) | |
| -- End Trie Implementation | |
| -- Start BinaryHeap Implementation | |
| -- BinaryHeap class | |
| BinaryHeap = Object:extend(function(BinaryHeap) | |
| function BinaryHeap.constructor(self, compareFunction) | |
| self.elements = {} | |
| self.compareFunction = compareFunction or function(a, b) return a < b end | |
| end | |
| function BinaryHeap:insert(element) | |
| table.insert(self.elements, element) | |
| local index = #self.elements | |
| while index > 1 do | |
| local parentIndex = math.floor(index / 2) | |
| if self.compareFunction(self.elements[index], self.elements[parentIndex]) then | |
| break | |
| end | |
| self.elements[index], self.elements[parentIndex] = self.elements[parentIndex], self.elements[index] | |
| index = parentIndex | |
| end | |
| end | |
| function BinaryHeap:extract() | |
| if #self.elements == 0 then | |
| return nil | |
| end | |
| local element = self.elements[1] | |
| self.elements[1] = self.elements[#self.elements] | |
| table.remove(self.elements) | |
| local index = 1 | |
| while true do | |
| local leftIndex = index * 2 | |
| local rightIndex = index * 2 + 1 | |
| local smallestIndex = index | |
| if leftIndex <= #self.elements and self.compareFunction(self.elements[leftIndex], self.elements[smallestIndex]) then | |
| smallestIndex = leftIndex | |
| end | |
| if rightIndex <= #self.elements and self.compareFunction(self.elements[rightIndex], self.elements[smallestIndex]) then | |
| smallestIndex = rightIndex | |
| end | |
| if smallestIndex == index then | |
| break | |
| end | |
| self.elements[index], self.elements[smallestIndex] = self.elements[smallestIndex], self.elements[index] | |
| index = smallestIndex | |
| end | |
| return element | |
| end | |
| function BinaryHeap:peek() | |
| return self.elements[1] | |
| end | |
| function BinaryHeap:isEmpty() | |
| return #self.elements == 0 | |
| end | |
| function BinaryHeap:size() | |
| return #self.elements | |
| end | |
| end) | |
| -- End BinaryHeap Implementation | |
| -- Start AVLTree Implementation | |
| -- AVLTreeNode class | |
| local AVLTreeNode = Object:extend(function(AVLTreeNode) | |
| function AVLTreeNode.constructor(self, key, value) | |
| self.key = key | |
| self.value = value | |
| self.height = 1 | |
| self.left = nil | |
| self.right = nil | |
| end | |
| end) | |
| -- AVLTree class | |
| AVLTree = Object:extend(function(AVLTree) | |
| function AVLTree.constructor(self) | |
| self.root = nil | |
| end | |
| function AVLTree:height(node) | |
| return node and node.height or 0 | |
| end | |
| function AVLTree:rotateRight(node) | |
| local leftChild = node.left | |
| node.left = leftChild.right | |
| leftChild.right = node | |
| node.height = math.max(self:height(node.left), self:height(node.right)) + 1 | |
| leftChild.height = math.max(self:height(leftChild.left), self:height(leftChild.right)) + 1 | |
| return leftChild | |
| end | |
| function AVLTree:rotateLeft(node) | |
| local rightChild = node.right | |
| node.right = rightChild.left | |
| rightChild.left = node | |
| node.height = math.max(self:height(node.left), self:height(node.right)) + 1 | |
| rightChild.height = math.max(self:height(rightChild.left), self:height(rightChild.right)) + 1 | |
| return rightChild | |
| end | |
| function AVLTree:rotateLeftRight(node) | |
| node.left = self:rotateLeft(node.left) | |
| return self:rotateRight(node) | |
| end | |
| function AVLTree:rotateRightLeft(node) | |
| node.right = self:rotateRight(node.right) | |
| return self:rotateLeft(node) | |
| end | |
| function AVLTree:balance(node) | |
| local balanceFactor = self:height(node.left) - self:height(node.right) | |
| if balanceFactor > 1 then | |
| if self:height(node.left.left) >= self:height(node.left.right) then | |
| node = self:rotateRight(node) | |
| else | |
| node = self:rotateLeftRight(node) | |
| end | |
| elseif balanceFactor < -1 then | |
| if self:height(node.right.right) >= self:height(node.right.left) then | |
| node = self:rotateLeft(node) | |
| else | |
| node = self:rotateRightLeft(node) | |
| end | |
| end | |
| return node | |
| end | |
| function AVLTree:insert(node, key, value) | |
| if node == nil then | |
| return AVLTreeNode:new(key, value) | |
| end | |
| if key < node.key then | |
| node.left = self:insert(node.left, key, value) | |
| elseif key > node.key then | |
| node.right = self:insert(node.right, key, value) | |
| else | |
| node.value = value | |
| return node | |
| end | |
| node.height = math.max(self:height(node.left), self:height(node.right)) + 1 | |
| return self:balance(node) | |
| end | |
| function AVLTree:insertNode(key, value) | |
| self.root = self:insert(self.root, key, value) | |
| end | |
| function AVLTree:search(node, key) | |
| if node == nil then | |
| return nil | |
| end | |
| if key < node.key then | |
| return self:search(node.left, key) | |
| elseif key > node.key then | |
| return self:search(node.right, key) | |
| else | |
| return node.value | |
| end | |
| end | |
| function AVLTree:searchNode(key) | |
| return self:search(self.root, key) | |
| end | |
| function AVLTree:inOrderTraversal(node, callback) | |
| if node ~= nil then | |
| self:inOrderTraversal(node.left, callback) | |
| callback(node) | |
| self:inOrderTraversal(node.right, callback) | |
| end | |
| end | |
| function AVLTree:preOrderTraversal(node, callback) | |
| if node ~= nil then | |
| callback(node) | |
| self:preOrderTraversal(node.left, callback) | |
| self:preOrderTraversal(node.right, callback) | |
| end | |
| end | |
| function AVLTree:postOrderTraversal(node, callback) | |
| if node ~= nil then | |
| self:postOrderTraversal(node.left, callback) | |
| self:postOrderTraversal(node.right, callback) | |
| callback(node) | |
| end | |
| end | |
| function AVLTree:traverseInOrder(callback) | |
| self:inOrderTraversal(self.root, callback) | |
| end | |
| function AVLTree:traversePreOrder(callback) | |
| self:preOrderTraversal(self.root, callback) | |
| end | |
| function AVLTree:traversePostOrder(callback) | |
| self:postOrderTraversal(self.root, callback) | |
| end | |
| function AVLTree:delete(node, key) | |
| if node == nil then | |
| return nil | |
| end | |
| if key < node.key then | |
| node.left = self:delete(node.left, key) | |
| elseif key > node.key then | |
| node.right = self:delete(node.right, key) | |
| else | |
| if node.left == nil or node.right == nil then | |
| local temp = node.left or node.right | |
| if temp == nil then | |
| node = nil | |
| else | |
| node = temp | |
| end | |
| else | |
| local temp = node.right | |
| while temp.left do | |
| temp = temp.left | |
| end | |
| node.key = temp.key | |
| node.value = temp.value | |
| node.right = self:delete(node.right, temp.key) | |
| end | |
| end | |
| if node == nil then | |
| return node | |
| end | |
| node.height = math.max(self:height(node.left), self:height(node.right)) + 1 | |
| return self:balance(node) | |
| end | |
| function AVLTree:deleteNode(key) | |
| self.root = self:delete(self.root, key) | |
| end | |
| function AVLTree:clear() | |
| self.root = nil | |
| end | |
| function AVLTree:isEmpty() | |
| return self.root == nil | |
| end | |
| function AVLTree:size() | |
| local count = 0 | |
| local function countNodes(node) | |
| if node ~= nil then | |
| countNodes(node.left) | |
| count = count + 1 | |
| countNodes(node.right) | |
| end | |
| end | |
| countNodes(self.root) | |
| return count | |
| end | |
| function AVLTree:height() | |
| return self:height(self.root) | |
| end | |
| function AVLTree:isBalanced() | |
| local function checkHeight(node) | |
| if node == nil then | |
| return 0 | |
| end | |
| local leftHeight = checkHeight(node.left) | |
| if leftHeight == -1 then | |
| return -1 | |
| end | |
| local rightHeight = checkHeight(node.right) | |
| if rightHeight == -1 then | |
| return -1 | |
| end | |
| local heightDiff = leftHeight - rightHeight | |
| if math.abs(heightDiff) > 1 then | |
| return -1 | |
| else | |
| return math.max(leftHeight, rightHeight) + 1 | |
| end | |
| end | |
| return checkHeight(self.root) ~= -1 | |
| end | |
| function AVLTree:isComplete() | |
| local function checkComplete(node, index, count) | |
| if node == nil then | |
| return true | |
| end | |
| if index >= count then | |
| return false | |
| end | |
| return checkComplete(node.left, 2 * index + 1, count) and checkComplete(node.right, 2 * index + 2, count) | |
| end | |
| local function countNodes(node) | |
| if node == nil then | |
| return 0 | |
| end | |
| return 1 + countNodes(node.left) + countNodes(node.right) | |
| end | |
| return checkComplete(self.root, 0, countNodes(self.root)) | |
| end | |
| function AVLTree:isFull() | |
| local function checkFull(node) | |
| if node == nil then | |
| return true | |
| end | |
| if node.left == nil and node.right == nil then | |
| return true | |
| end | |
| if node.left and node.right then | |
| return checkFull(node.left) and checkFull(node.right) | |
| end | |
| return false | |
| end | |
| return checkFull(self.root) | |
| end | |
| function AVLTree:isPerfect() | |
| local function checkPerfect(node, depth, level) | |
| if node == nil then | |
| return true | |
| end | |
| if node.left == nil and node.right == nil then | |
| return depth == level + 1 | |
| end | |
| if node.left == nil or node.right == nil then | |
| return false | |
| end | |
| return checkPerfect(node.left, depth, level + 1) and checkPerfect(node.right, depth, level + 1) | |
| end | |
| return checkPerfect(self.root, self:height(self.root), 0) | |
| end | |
| function AVLTree:isSymmetric() | |
| local function checkSymmetric(left, right) | |
| if left == nil and right == nil then | |
| return true | |
| end | |
| if left == nil or right == nil then | |
| return false | |
| end | |
| return left.key == right.key and checkSymmetric(left.left, right.right) and checkSymmetric(left.right, right.left) | |
| end | |
| return checkSymmetric(self.root, self.root) | |
| end | |
| end) | |
| -- End AVLTree Implementation | |
| -- Start DoublyLinkedList Implementation | |
| -- DoublyLinkedListNode class | |
| local DoublyLinkedListNode = Object:extend(function(DoublyLinkedListNode) | |
| function DoublyLinkedListNode.constructor(self, value) | |
| self.value = value | |
| self.next = nil | |
| self.prev = nil | |
| end | |
| end) | |
| -- DoublyLinkedList class | |
| DoublyLinkedList = Object:extend(function(DoublyLinkedList) | |
| function DoublyLinkedList.constructor(self) | |
| self.head = nil | |
| self.tail = nil | |
| self.size = 0 | |
| end | |
| function DoublyLinkedList:pushFront(value) | |
| local newNode = DoublyLinkedListNode:new(value) | |
| if self.head == nil then | |
| self.head = newNode | |
| self.tail = newNode | |
| else | |
| newNode.next = self.head | |
| self.head.prev = newNode | |
| self.head = newNode | |
| end | |
| self.size = self.size + 1 | |
| end | |
| function DoublyLinkedList:pushBack(value) | |
| local newNode = DoublyLinkedListNode:new(value) | |
| if self.tail == nil then | |
| self.head = newNode | |
| self.tail = newNode | |
| else | |
| newNode.prev = self.tail | |
| self.tail.next = newNode | |
| self.tail = newNode | |
| end | |
| self.size = self.size + 1 | |
| end | |
| function DoublyLinkedList:popFront() | |
| if self.head == nil then | |
| return nil | |
| end | |
| local value = self.head.value | |
| self.head = self.head.next | |
| if self.head == nil then | |
| self.tail = nil | |
| else | |
| self.head.prev = nil | |
| end | |
| self.size = self.size - 1 | |
| return value | |
| end | |
| function DoublyLinkedList:popBack() | |
| if self.tail == nil then | |
| return nil | |
| end | |
| local value = self.tail.value | |
| self.tail = self.tail.prev | |
| if self.tail == nil then | |
| self.head = nil | |
| else | |
| self.tail.next = nil | |
| end | |
| self.size = self.size - 1 | |
| return value | |
| end | |
| function DoublyLinkedList:peekFront() | |
| return self.head and self.head.value or nil | |
| end | |
| function DoublyLinkedList:peekBack() | |
| return self.tail and self.tail.value or nil | |
| end | |
| function DoublyLinkedList:isEmpty() | |
| return self.size == 0 | |
| end | |
| function DoublyLinkedList:size() | |
| return self.size | |
| end | |
| function DoublyLinkedList:clear() | |
| self.head = nil | |
| self.tail = nil | |
| self.size = 0 | |
| end | |
| function DoublyLinkedList:toArray() | |
| local array = {} | |
| local currentNode | |
| currentNode = self.head | |
| while currentNode do | |
| table.insert(array, currentNode.value) | |
| currentNode = currentNode.next | |
| end | |
| return array | |
| end | |
| end) | |
| -- End DoublyLinkedList Implementation | |
| -- Start SkipList Implementation | |
| local MAX_LEVEL = 16 -- Max level for the skip list | |
| -- SkipListNode class | |
| local SkipListNode = Object:extend(function(SkipListNode) | |
| function SkipListNode.constructor(self, key, value, level) | |
| self.key = key | |
| self.value = value | |
| self.forward = {} | |
| for i = 1, level do | |
| self.forward[i] = nil | |
| end | |
| end | |
| end) | |
| -- SkipList class | |
| SkipList = Object:extend(function(SkipList) | |
| function SkipList.constructor(self) | |
| self.level = 1 | |
| self.header = SkipListNode:new(nil, nil, MAX_LEVEL) | |
| end | |
| function SkipList:randomLevel() | |
| local level = 1 | |
| while math.random() < 0.5 and level < MAX_LEVEL do | |
| level = level + 1 | |
| end | |
| return level | |
| end | |
| function SkipList:insert(key, value) | |
| local update = {} | |
| local current = self.header | |
| for i = self.level, 1, -1 do | |
| while current.forward[i] and current.forward[i].key < key do | |
| current = current.forward[i] | |
| end | |
| update[i] = current | |
| end | |
| current = current.forward[1] | |
| if current and current.key == key then | |
| current.value = value | |
| else | |
| local level = self:randomLevel() | |
| if level > self.level then | |
| for i = self.level + 1, level do | |
| update[i] = self.header | |
| end | |
| self.level = level | |
| end | |
| local newNode = SkipListNode:new(key, value, level) | |
| for i = 1, level do | |
| newNode.forward[i] = update[i].forward[i] | |
| update[i].forward[i] = newNode | |
| end | |
| end | |
| end | |
| function SkipList:search(key) | |
| local current = self.header | |
| for i = self.level, 1, -1 do | |
| while current.forward[i] and current.forward[i].key < key do | |
| current = current.forward[i] | |
| end | |
| end | |
| current = current.forward[1] | |
| if current and current.key == key then | |
| return current.value | |
| else | |
| return nil | |
| end | |
| end | |
| function SkipList:delete(key) | |
| local update = {} | |
| local current = self.header | |
| for i = self.level, 1, -1 do | |
| while current.forward[i] and current.forward[i].key < key do | |
| current = current.forward[i] | |
| end | |
| update[i] = current | |
| end | |
| current = current.forward[1] | |
| if current and current.key == key then | |
| for i = 1, self.level do | |
| if update[i].forward[i] ~= current then | |
| break | |
| end | |
| update[i].forward[i] = current.forward[i] | |
| end | |
| while self.level > 1 and self.header.forward[self.level] == nil do | |
| self.level = self.level - 1 | |
| end | |
| end | |
| end | |
| function SkipList:traverse(callback) | |
| local current = self.header.forward[1] | |
| while current do | |
| callback(current) | |
| current = current.forward[1] | |
| end | |
| end | |
| end) | |
| -- End SkipList Implementation | |
| -- Start TrieMap Implementation | |
| -- TrieMap class | |
| TrieMap = Object:extend(function(TrieMap) | |
| function TrieMap.constructor(self) | |
| self.root = {} | |
| end | |
| function TrieMap:insert(key, value) | |
| local current = self.root | |
| for i = 1, #key do | |
| local char = key:sub(i, i) | |
| if not current[char] then | |
| current[char] = {} | |
| end | |
| current = current[char] | |
| end | |
| current.value = value | |
| end | |
| function TrieMap:search(key) | |
| local current = self.root | |
| for i = 1, #key do | |
| local char = key:sub(i, i) | |
| if not current[char] then | |
| return nil | |
| end | |
| current = current[char] | |
| end | |
| return current.value | |
| end | |
| function TrieMap:delete(key) | |
| local function deleteHelper(node, key, index) | |
| if index == #key then | |
| if not node.value then | |
| return false | |
| end | |
| node.value = nil | |
| return next(node) == nil | |
| end | |
| local char = key:sub(index + 1, index + 1) | |
| if not node[char] then | |
| return false | |
| end | |
| local shouldDeleteNode = deleteHelper(node[char], key, index + 1) | |
| if shouldDeleteNode then | |
| node[char] = nil | |
| return next(node) == nil | |
| end | |
| return false | |
| end | |
| deleteHelper(self.root, key, 0) | |
| end | |
| end) | |
| -- End TrieMap Implementation | |
| -- Start Callable Implementation | |
| -- Callable class | |
| Callable = Object:extend(function(Callable) | |
| function Callable.constructor(self, func) | |
| self.func = func | |
| end | |
| function Callable:call(...) | |
| return self.func(...) | |
| end | |
| end) | |
| -- End Callable Implementation | |
| -- Start Thenable Implementation | |
| -- Thenable class | |
| Thenable = Object:extend(function(Thenable) | |
| function Thenable.constructor(self, func) | |
| self.func = func | |
| end | |
| function Thenable:thenAfter(onFulfilled, onRejected) | |
| return Thenable:new(function(resolve, reject) | |
| self.func(function(value) | |
| if onFulfilled then | |
| resolve(onFulfilled(value)) | |
| else | |
| resolve(value) | |
| end | |
| end, function(reason) | |
| if onRejected then | |
| resolve(onRejected(reason)) | |
| else | |
| reject(reason) | |
| end | |
| end) | |
| end) | |
| end | |
| end) | |
| -- End Thenable Implementation | |
| -- Start Promise Implementation | |
| -- Promise class | |
| Promise = Object:extend(function(Promise) | |
| function Promise.constructor(self, executor) | |
| self.status = "pending" | |
| self.value = nil | |
| self.reason = nil | |
| self.onFulfilled = {} | |
| self.onRejected = {} | |
| local function resolve(value) | |
| if self.status == "pending" then | |
| self.status = "fulfilled" | |
| self.value = value | |
| for _, callback in ipairs(self.onFulfilled) do | |
| callback(value) | |
| end | |
| end | |
| end | |
| local function reject(reason) | |
| if self.status == "pending" then | |
| self.status = "rejected" | |
| self.reason = reason | |
| for _, callback in ipairs(self.onRejected) do | |
| callback(reason) | |
| end | |
| end | |
| end | |
| executor(resolve, reject) | |
| end | |
| function Promise:thenAfter(onFulfilled, onRejected) | |
| return Promise:new(function(resolve, reject) | |
| if self.status == "fulfilled" then | |
| if onFulfilled then | |
| resolve(onFulfilled(self.value)) | |
| else | |
| resolve(self.value) | |
| end | |
| elseif self.status == "rejected" then | |
| if onRejected then | |
| resolve(onRejected(self.reason)) | |
| else | |
| reject(self.reason) | |
| end | |
| else | |
| table.insert(self.onFulfilled, function(value) | |
| if onFulfilled then | |
| resolve(onFulfilled(value)) | |
| else | |
| resolve(value) | |
| end | |
| end) | |
| table.insert(self.onRejected, function(reason) | |
| if onRejected then | |
| resolve(onRejected(reason)) | |
| else | |
| reject(reason) | |
| end | |
| end) | |
| end | |
| end) | |
| end | |
| end) | |
| -- End Promise Implementation | |
| -- Start Future Implementation | |
| -- Future class | |
| Future = Object:extend(function(Future) | |
| function Future.constructor(self, executor) | |
| self.status = "pending" | |
| self.value = nil | |
| self.reason = nil | |
| self.onFulfilled = {} | |
| self.onRejected = {} | |
| local function resolve(value) | |
| if self.status == "pending" then | |
| self.status = "fulfilled" | |
| self.value = value | |
| for _, callback in ipairs(self.onFulfilled) do | |
| callback(value) | |
| end | |
| end | |
| end | |
| local function reject(reason) | |
| if self.status == "pending" then | |
| self.status = "rejected" | |
| self.reason = reason | |
| for _, callback in ipairs(self.onRejected) do | |
| callback(reason) | |
| end | |
| end | |
| end | |
| executor(resolve, reject) | |
| end | |
| function Future:thenAfter(onFulfilled, onRejected) | |
| return Future:new(function(resolve, reject) | |
| if self.status == "fulfilled" then | |
| if onFulfilled then | |
| resolve(onFulfilled(self.value)) | |
| else | |
| resolve(self.value) | |
| end | |
| elseif self.status == "rejected" then | |
| if onRejected then | |
| resolve(onRejected(self.reason)) | |
| else | |
| reject(self.reason) | |
| end | |
| else | |
| table.insert(self.onFulfilled, function(value) | |
| if onFulfilled then | |
| resolve(onFulfilled(value)) | |
| else | |
| resolve(value) | |
| end | |
| end) | |
| table.insert(self.onRejected, function(reason) | |
| if onRejected then | |
| resolve(onRejected(reason)) | |
| else | |
| reject(reason) | |
| end | |
| end) | |
| end | |
| end) | |
| end | |
| end) | |
| -- End Future Implementation | |
| -- Start Deferred Implementation | |
| -- Deferred class | |
| Deferred = Object:extend(function(Deferred) | |
| function Deferred.constructor(self) | |
| self.promise = Promise:new(function(resolve, reject) | |
| self.resolve = resolve | |
| self.reject = reject | |
| end) | |
| return self | |
| end | |
| end) | |
| -- End Deferred Implementation | |
| -- Start Task Implementation | |
| -- Task class | |
| Task = Object:extend(function(Task) | |
| function Task.constructor(self, executor) | |
| self.executor = executor | |
| end | |
| function Task:run() | |
| self.executor() | |
| end | |
| end) | |
| -- End Task Implementation | |
| -- Start Channel Implementation | |
| -- Channel class | |
| Channel = Object:extend(function(Channel) | |
| function Channel.constructor(self) | |
| self.queue = DoublyLinkedList:new() | |
| self.isClosed = false | |
| self.onReceive = Callable:new(function() end) | |
| end | |
| function Channel:send(value) | |
| if self.isClosed then | |
| return | |
| end | |
| self.queue:pushBack(value) | |
| self.onReceive:call() | |
| end | |
| function Channel:receive() | |
| if self.queue:isEmpty() then | |
| return nil | |
| end | |
| return self.queue:popFront() | |
| end | |
| function Channel:close() | |
| self.isClosed = true | |
| end | |
| end) | |
| -- End Channel Implementation | |
| -- Start Event Implementation | |
| -- Event class | |
| Event = Object:extend(function(Event) | |
| function Event.constructor(self) | |
| self.listeners = {} | |
| end | |
| function Event:subscribe(listener) | |
| table.insert(self.listeners, listener) | |
| end | |
| function Event:unsubscribe(listener) | |
| for i = #self.listeners, 1, -1 do | |
| if self.listeners[i] == listener then | |
| table.remove(self.listeners, i) | |
| end | |
| end | |
| end | |
| function Event:emit(...) | |
| for _, listener in ipairs(self.listeners) do | |
| listener(...) | |
| end | |
| end | |
| end) | |
| -- End Event Implementation | |
| -- Start Worker Implementation | |
| -- Worker class | |
| Worker = Object:extend(function(Worker) | |
| function Worker.constructor(self, func) | |
| self.func = func | |
| self.channel = Channel:new() | |
| self.isRunning = false | |
| self.thread = coroutine.create(function() | |
| while true do | |
| local value = self.channel:receive() | |
| if value == nil then | |
| break | |
| end | |
| self.func(value) | |
| end | |
| end) | |
| end | |
| function Worker:run() | |
| self.isRunning = true | |
| coroutine.resume(self.thread) | |
| end | |
| function Worker:send(value) | |
| self.channel:send(value) | |
| end | |
| function Worker:close() | |
| self.channel:close() | |
| end | |
| end) | |
| -- End Worker Implementation | |
| -- Start Mutex Implementation | |
| -- Mutex class | |
| Mutex = Object:extend(function(Mutex) | |
| function Mutex.constructor(self) | |
| self.queue = DoublyLinkedList:new() | |
| self.isLocked = false | |
| end | |
| function Mutex:lock() | |
| local deferred = Deferred:new() | |
| if self.isLocked then | |
| self.queue:pushBack(deferred) | |
| else | |
| self.isLocked = true | |
| deferred.resolve() | |
| end | |
| return deferred.promise | |
| end | |
| function Mutex:unlock() | |
| if not self.isLocked then | |
| return | |
| end | |
| local deferred = self.queue:popFront() | |
| if deferred then | |
| deferred.resolve() | |
| else | |
| self.isLocked = false | |
| end | |
| end | |
| end) | |
| -- End Mutex Implementation | |
| -- Start Semaphore Implementation | |
| -- Semaphore class | |
| Semaphore = Object:extend(function(Semaphore) | |
| function Semaphore.constructor(self, count) | |
| self.count = count | |
| self.queue = DoublyLinkedList:new() | |
| end | |
| function Semaphore:wait() | |
| local deferred = Deferred:new() | |
| if self.count > 0 then | |
| self.count = self.count - 1 | |
| deferred.resolve() | |
| else | |
| self.queue:pushBack(deferred) | |
| end | |
| return deferred.promise | |
| end | |
| function Semaphore:signal() | |
| local deferred = self.queue:popFront() | |
| if deferred then | |
| deferred.resolve() | |
| else | |
| self.count = self.count + 1 | |
| end | |
| end | |
| end) | |
| -- End Semaphore Implementation | |
| -- Start Barrier Implementation | |
| -- Barrier class | |
| Barrier = Object:extend(function(Barrier) | |
| function Barrier.constructor(self, count) | |
| self.count = count | |
| self.queue = DoublyLinkedList:new() | |
| end | |
| function Barrier:wait() | |
| local deferred = Deferred:new() | |
| self.queue:pushBack(deferred) | |
| if self.queue:size() == self.count then | |
| for i = 1, self.count do | |
| self.queue:popFront():resolve() | |
| end | |
| end | |
| return deferred.promise | |
| end | |
| end) | |
| -- End Barrier Implementation | |
| -- Start ConditionVariable Implementation | |
| -- ConditionVariable class | |
| ConditionVariable = Object:extend(function(ConditionVariable) | |
| function ConditionVariable.constructor(self) | |
| self.queue = DoublyLinkedList:new() | |
| end | |
| function ConditionVariable:wait(mutex) | |
| local deferred = Deferred:new() | |
| self.queue:pushBack(deferred) | |
| mutex:unlock() | |
| return deferred.promise | |
| end | |
| function ConditionVariable:signal() | |
| local deferred = self.queue:popFront() | |
| if deferred then | |
| deferred.resolve() | |
| end | |
| end | |
| end) | |
| -- End ConditionVariable Implementation | |
| -- Start ReadWriteLock Implementation | |
| -- ReadWriteLock class | |
| ReadWriteLock = Object:extend(function(ReadWriteLock) | |
| function ReadWriteLock.constructor(self) | |
| self.mutex = Mutex:new() | |
| self.resource = Mutex:new() | |
| self.readers = 0 | |
| end | |
| function ReadWriteLock:readLock() | |
| local deferred = Deferred:new() | |
| self.mutex:lock():thenAfter(function() | |
| self.readers = self.readers + 1 | |
| if self.readers == 1 then | |
| self.resource:lock() | |
| end | |
| self.mutex:unlock() | |
| deferred.resolve() | |
| end) | |
| return deferred.promise | |
| end | |
| function ReadWriteLock:readUnlock() | |
| local deferred = Deferred:new() | |
| self.mutex:lock():thenAfter(function() | |
| self.readers = self.readers - 1 | |
| if self.readers == 0 then | |
| self.resource:unlock() | |
| end | |
| self.mutex:unlock() | |
| deferred.resolve() | |
| end) | |
| return deferred.promise | |
| end | |
| function ReadWriteLock:writeLock() | |
| return self.resource:lock() | |
| end | |
| function ReadWriteLock:writeUnlock() | |
| return self.resource:unlock() | |
| end | |
| end) | |
| -- End ReadWriteLock Implementation | |
| -- Start Latch Implementation | |
| -- Latch class | |
| Latch = Object:extend(function(Latch) | |
| function Latch.constructor(self, count) | |
| self.count = count | |
| self.queue = DoublyLinkedList:new() | |
| end | |
| function Latch:wait() | |
| local deferred = Deferred:new() | |
| self.queue:pushBack(deferred) | |
| if self.queue:size() == self.count then | |
| for i = 1, self.count do | |
| self.queue:popFront():resolve() | |
| end | |
| end | |
| return deferred.promise | |
| end | |
| end) | |
| -- End Latch Implementation | |
| -- Start CountDownLatch Implementation | |
| -- CountDownLatch class | |
| CountDownLatch = Object:extend(function(CountDownLatch) | |
| function CountDownLatch.constructor(self, count) | |
| self.count = count | |
| self.queue = DoublyLinkedList:new() | |
| end | |
| function CountDownLatch:countDown() | |
| local deferred = self.queue:popFront() | |
| if deferred then | |
| deferred.resolve() | |
| else | |
| self.count = self.count - 1 | |
| end | |
| end | |
| function CountDownLatch:await() | |
| local deferred = Deferred:new() | |
| if self.count > 0 then | |
| self.queue:pushBack(deferred) | |
| else | |
| deferred.resolve() | |
| end | |
| return deferred.promise | |
| end | |
| end) | |
| -- End CountDownLatch Implementation | |
| -- Start CyclicBarrier Implementation | |
| -- CyclicBarrier class | |
| CyclicBarrier = Object:extend(function(CyclicBarrier) | |
| function CyclicBarrier.constructor(self, count) | |
| self.count = count | |
| self.queue = DoublyLinkedList:new() | |
| end | |
| function CyclicBarrier:await() | |
| local deferred = Deferred:new() | |
| self.queue:pushBack(deferred) | |
| if self.queue:size() == self.count then | |
| for i = 1, self.count do | |
| self.queue:popFront():resolve() | |
| end | |
| end | |
| return deferred.promise | |
| end | |
| end) | |
| -- End CyclicBarrier Implementation | |
| -- Start Phaser Implementation | |
| -- Phaser class | |
| Phaser = Object:extend(function(Phaser) | |
| function Phaser.constructor(self, count) | |
| self.count = count | |
| self.queue = DoublyLinkedList:new() | |
| end | |
| function Phaser:arrive() | |
| local deferred = self.queue:popFront() | |
| if deferred then | |
| deferred.resolve() | |
| else | |
| self.count = self.count - 1 | |
| end | |
| end | |
| function Phaser:await() | |
| local deferred = Deferred:new() | |
| if self.count > 0 then | |
| self.queue:pushBack(deferred) | |
| else | |
| deferred.resolve() | |
| end | |
| return deferred.promise | |
| end | |
| end) | |
| -- End Phaser Implementation | |
| -- Start Exchanger Implementation | |
| -- Exchanger class | |
| Exchanger = Object:extend(function(Exchanger) | |
| function Exchanger.constructor(self) | |
| self.queue = DoublyLinkedList:new() | |
| end | |
| function Exchanger:exchange(value) | |
| local deferred = Deferred:new() | |
| self.queue:pushBack({ value = value, deferred = deferred }) | |
| if self.queue:size() == 2 then | |
| local first = self.queue:popFront() | |
| local second = self.queue:popFront() | |
| first.deferred.resolve(second.value) | |
| second.deferred.resolve(first.value) | |
| end | |
| return deferred.promise | |
| end | |
| end) | |
| -- End Exchanger Implementation | |
| -- Start Lock Implementation | |
| -- Lock class | |
| Lock = Object:extend(function(Lock) | |
| function Lock.constructor(self) | |
| self.mutex = Mutex:new() | |
| end | |
| function Lock:lock() | |
| return self.mutex:lock() | |
| end | |
| function Lock:unlock() | |
| return self.mutex:unlock() | |
| end | |
| end) | |
| -- End Lock Implementation | |
| -- Start ObjectPool Implementation | |
| -- ObjectPool class | |
| ObjectPool = Object:extend(function(ObjectPool) | |
| function ObjectPool.constructor(self, factory, size) | |
| self.factory = factory | |
| self.size = size | |
| self.pool = DoublyLinkedList:new() | |
| for i = 1, size do | |
| self.pool:pushBack(factory()) | |
| end | |
| end | |
| function ObjectPool:acquire() | |
| if self.pool:isEmpty() then | |
| return self.factory() | |
| end | |
| return self.pool:popFront() | |
| end | |
| function ObjectPool:release(object) | |
| if self.pool:size() < self.size then | |
| self.pool:pushBack(object) | |
| end | |
| end | |
| end) | |
| -- End ObjectPool Implementation | |
| -- Start ObjectPoolFactory Implementation | |
| -- ObjectPoolFactory class | |
| ObjectPoolFactory = Object:extend(function(ObjectPoolFactory) | |
| function ObjectPoolFactory.constructor(self, factory) | |
| self.factory = factory | |
| end | |
| function ObjectPoolFactory:create(size) | |
| return ObjectPool:new(self.factory, size) | |
| end | |
| end) | |
| -- End ObjectPoolFactory Implementation | |
| -- Start Observable Implementation | |
| -- Observable class | |
| Observable = Object:extend(function(Observable) | |
| function Observable.constructor(self) | |
| self.listeners = {} | |
| end | |
| function Observable:subscribe(listener) | |
| table.insert(self.listeners, listener) | |
| end | |
| function Observable:unsubscribe(listener) | |
| for i = #self.listeners, 1, -1 do | |
| if self.listeners[i] == listener then | |
| table.remove(self.listeners, i) | |
| end | |
| end | |
| end | |
| function Observable:notify(...) | |
| for _, listener in ipairs(self.listeners) do | |
| listener(...) | |
| end | |
| end | |
| end) | |
| -- End Observable Implementation | |
| -- Start Subject Implementation | |
| -- Subject class | |
| Subject = Object:extend(function(Subject) | |
| function Subject.constructor(self) | |
| self.observers = {} | |
| end | |
| function Subject:subscribe(observer) | |
| table.insert(self.observers, observer) | |
| end | |
| function Subject:unsubscribe(observer) | |
| for i = #self.observers, 1, -1 do | |
| if self.observers[i] == observer then | |
| table.remove(self.observers, i) | |
| end | |
| end | |
| end | |
| function Subject:notify(...) | |
| for _, observer in ipairs(self.observers) do | |
| observer:notify(...) | |
| end | |
| end | |
| end) | |
| -- End Subject Implementation | |
| -- Start Mediator Implementation | |
| -- Mediator class | |
| Mediator = Object:extend(function(Mediator) | |
| function Mediator.constructor(self) | |
| self.handlers = {} | |
| end | |
| function Mediator:subscribe(event, handler) | |
| if not self.handlers[event] then | |
| self.handlers[event] = {} | |
| end | |
| table.insert(self.handlers[event], handler) | |
| end | |
| function Mediator:unsubscribe(event, handler) | |
| if self.handlers[event] then | |
| for i = #self.handlers[event], 1, -1 do | |
| if self.handlers[event][i] == handler then | |
| table.remove(self.handlers[event], i) | |
| end | |
| end | |
| end | |
| end | |
| function Mediator:publish(event, ...) | |
| if self.handlers[event] then | |
| for _, handler in ipairs(self.handlers[event]) do | |
| handler(...) | |
| end | |
| end | |
| end | |
| end) | |
| -- End Mediator Implementation | |
| -- Start Command Implementation | |
| -- Command class | |
| Command = Object:extend(function(Command) | |
| function Command.constructor(self, func) | |
| self.func = func | |
| end | |
| function Command:execute() | |
| self.func() | |
| end | |
| end) | |
| -- End Command Implementation | |
| -- Start CommandFactory Implementation | |
| -- CommandFactory class | |
| CommandFactory = Object:extend(function(CommandFactory) | |
| function CommandFactory.constructor(self) | |
| self.commands = {} | |
| end | |
| function CommandFactory:createCommand(name, func) | |
| self.commands[name] = Command:new(func) | |
| end | |
| function CommandFactory:executeCommand(name) | |
| self.commands[name]:execute() | |
| end | |
| end) | |
| -- End CommandFactory Implementation | |
| -- Start CommandInvoker Implementation | |
| -- CommandInvoker class | |
| CommandInvoker = Object:extend(function(CommandInvoker) | |
| function CommandInvoker.constructor(self) | |
| self.commands = {} | |
| end | |
| function CommandInvoker:execute(command) | |
| table.insert(self.commands, command) | |
| command:execute() | |
| end | |
| end) | |
| -- End CommandInvoker Implementation | |
| -- Start CommandProcessor Implementation | |
| -- CommandProcessor class | |
| CommandProcessor = Object:extend(function(CommandProcessor) | |
| function CommandProcessor.constructor(self) | |
| self.invoker = CommandInvoker:new() | |
| end | |
| function CommandProcessor:process(command) | |
| self.invoker:execute(command) | |
| end | |
| end) | |
| -- End CommandProcessor Implementation | |
| -- Start Observer Implementation | |
| -- Observer class | |
| Observer = Object:extend(function(Observer) | |
| function Observer.constructor(self) | |
| self.subjects = {} | |
| end | |
| function Observer:subscribe(subject) | |
| table.insert(self.subjects, subject) | |
| end | |
| function Observer:unsubscribe(subject) | |
| for i = #self.subjects, 1, -1 do | |
| if self.subjects[i] == subject then | |
| table.remove(self.subjects, i) | |
| end | |
| end | |
| end | |
| function Observer:notify(...) | |
| for _, subject in ipairs(self.subjects) do | |
| subject:notify(...) | |
| end | |
| end | |
| end) | |
| -- End Observer Implementation | |
| -- Start Visitor Implementation | |
| -- Visitor class | |
| Visitor = Object:extend(function(Visitor) | |
| function Visitor.constructor(self) | |
| self.elements = {} | |
| end | |
| function Visitor:visit(element) | |
| table.insert(self.elements, element) | |
| end | |
| end) | |
| -- End Visitor Implementation | |
| -- Start Computed Implementation | |
| -- Computed class | |
| Computed = Object:extend(function(Computed) | |
| function Computed.constructor(self, func) | |
| self.func = func | |
| end | |
| function Computed:compute() | |
| return self.func() | |
| end | |
| end) | |
| -- End Computed Implementation | |
| end | |
| function ClassFactory.testClassFactory() | |
| -- Example class definition | |
| local MyClass = Object:extend(function (MyClass) | |
| function MyClass.constructor(self, x, y) | |
| self.x = x | |
| self.y = y | |
| end | |
| function MyClass.static.resetX(self) | |
| self.x = nil | |
| end | |
| end) | |
| -- Example mixin definition | |
| local MyMixin = Mixin:create(function (MyMixin) | |
| function MyMixin:printX() | |
| print(self.x) | |
| end | |
| end) | |
| Mixin:extend(function (Mixin) | |
| function Mixin:printY() | |
| print(self.y) | |
| end | |
| end) | |
| MyClass:implement(MyMixin) | |
| local myInstance = MyClass:new(10, 25) | |
| -- Example Object implementation | |
| local myObject = Object:new({ x = 10, y = 25 }) | |
| local myOtherObject = Object:new({ x = 10, y = 25 }) | |
| -- Example Array implementation | |
| local myArray = Array:new(1, 2, 3, 4, 5, 6) | |
| local divided = myArray:divide(function(element) return element % 2 == 0 end) | |
| local mapped = myArray:map(function(element) return element * 2 end) | |
| local filtered = myArray:filter(function(element) return element % 2 == 0 end) | |
| local reduced = myArray:reduce(function(accumulator, element) return accumulator + element end, 0) | |
| myArray:push(7) | |
| myArray:pop() | |
| myArray:length() | |
| -- Example Set implementation | |
| local mySet = Set:new({"apple", "banana", "cherry"}) | |
| local anotherSet = Set:new({"banana", "cherry", "date"}) | |
| local filteredSet = mySet:filterBySet(anotherSet) | |
| local isIncluded = mySet:isIncludedIn("I have an apple and a banana.") | |
| local unionSet = mySet:union(anotherSet) | |
| local intersectionSet = mySet:intersection(anotherSet) | |
| local differenceSet = mySet:difference(anotherSet) | |
| local size = mySet:size() | |
| mySet:clear() | |
| -- Example String implementation | |
| local myString = String:new("hello world") | |
| local myStringValue = myString:toUpperCase().value -- "HELLO WORLD" | |
| local myStringHash = myString:hashCode() -- prints the hash code of "hello world" | |
| local myStringSplit = myString:split(" ") -- {"hello", "world"} | |
| local myStringTrim = String:new(" hello world "):trim().value -- "hello world" | |
| local myStringSubstring = myString:substring(1, 5).value -- "hello" | |
| local myStringCharAt = myString:charAt(1) -- "h" | |
| local myStringLength = myString:length() -- 11 | |
| -- Example HashMap implementation | |
| local myMap = HashMap:new() | |
| myMap:set("one", 1) | |
| myMap:set("two", 2) | |
| local myMapGetOne = myMap:get("one") -- 1 | |
| local myMapHasTwo myMap:has("two") -- true | |
| myMap:delete("one") | |
| local myMapHasOne = myMap:has("one") -- false | |
| local myMapSize = myMap:size() -- 1 | |
| local myMapString = myMap:toString() -- {two: 2} | |
| local keys = myMap:keys() | |
| local values = myMap:values() | |
| local entries = myMap:entries() | |
| myMap:forEach(function(key, value) | |
| local file = key .. ": " .. value | |
| end) | |
| local clonedMap = myMap:clone() | |
| -- Example HashSet implementation | |
| local mySet = HashSet:new() | |
| mySet:add("apple") | |
| mySet:add("banana") | |
| local mySetHasApple = mySet:has("apple") -- true | |
| mySet:delete("apple") | |
| local mySetHasBanana = mySet:has("banana") -- false | |
| local mySetSize = mySet:size() -- 1 | |
| local mySetString = mySet:toString() -- {banana} | |
| local mySetKeys = mySet:keys() | |
| local mySetValues = mySet:values() | |
| local mySetEntries = mySet:entries() | |
| mySet:forEach(function(value) | |
| local file = value | |
| end) | |
| local clonedSet = mySet:clone() | |
| -- Example Stack implementation | |
| local myStack = Stack:new() | |
| myStack:push(1) | |
| myStack:push(2) | |
| local myStackPeek = myStack:peek() -- 2 | |
| local myStackPop = myStack:pop() -- 2 | |
| local myStackIsEmpty = myStack:isEmpty() -- false | |
| local myStackSize = myStack:size() -- 1 | |
| myStack:clear() | |
| -- Example Queue implementation | |
| local myQueue = Queue:new() | |
| myQueue:enqueue(1) | |
| myQueue:enqueue(2) | |
| local myQueuePeek = myQueue:peek() -- 1 | |
| local myQueueDequeue = myQueue:dequeue() -- 1 | |
| local myQueueIsEmpty = myQueue:isEmpty() -- false | |
| local myQueueSize = myQueue:size() -- 1 | |
| myQueue:clear() | |
| -- Example PriorityQueue implementation | |
| local myPriorityQueue = PriorityQueue:new() | |
| myPriorityQueue:enqueue(1) | |
| myPriorityQueue:enqueue(2) | |
| local myPriorityQueuePeek = myPriorityQueue:peek() -- 1 | |
| local myPriorityQueueDequeue = myPriorityQueue:dequeue() -- 1 | |
| local myPriorityQueueIsEmpty = myPriorityQueue:isEmpty() -- false | |
| local myPriorityQueueSize = myPriorityQueue:size() -- 1 | |
| myPriorityQueue:clear() | |
| -- Example Deque implementation | |
| local myDeque = Deque:new() | |
| myDeque:pushFront(1) | |
| myDeque:pushBack(2) | |
| local myDequePeekFront = myDeque:peekFront() -- 1 | |
| local myDequePeekBack = myDeque:peekBack() -- 2 | |
| local myDequePopFront = myDeque:popFront() -- 1 | |
| local myDequePopBack = myDeque:popBack() -- 2 | |
| local myDequeIsEmpty = myDeque:isEmpty() -- true | |
| local myDequeSize = myDeque:size() -- 0 | |
| myDeque:clear() | |
| -- Example AVLTree implementation | |
| local myAVLTree = AVLTree:new() | |
| myAVLTree:insert(1) | |
| myAVLTree:insert(2) | |
| local myAVLTreeHasOne = myAVLTree:has(1) -- true | |
| local myAVLTreeHasTwo = myAVLTree:has(2) -- true | |
| local myAVLTreeHasThree = myAVLTree:has(3) -- false | |
| local myAVLTreeHeight = myAVLTree:height() -- 1 | |
| local myAVLTreeSize = myAVLTree:size() -- 2 | |
| myAVLTree:delete(1) | |
| myAVLTree:clear() | |
| end | |
| local classDefinitions, testEnvironment = ClassFactory.defineCompiledInNamespace(ClassFactory.defineClassSystem, ClassFactory.testClassFactory) | |
| ClassFactory.cleanAutomaticallyNamespace(testEnvironment) | |
| return classDefinitions |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment