Updated to 1.10.0!

Check changes.md for what was done
This commit is contained in:
Ryan Ward 2018-05-31 22:48:14 -04:00
parent 67499cc5ba
commit a07fe49880
31 changed files with 6386 additions and 2131 deletions

File diff suppressed because one or more lines are too long

683
README.md
View File

@ -1,6 +1,6 @@
# multi Version: 1.9.2 (Yes I am alive, and some tweaks and additions have been made)
# multi Version: 1.10.0 (Changelog has its own dedicated file now, also bug fixes and a new object)
**NOTE: I have been studying a lot about threading in the past few weeks and have some awesome additions in store! They will take a while to come out though. The goal of the library is still to provide a simple and efficient way to multi task in lua**
**NOTE: I have been studying a lot about threading for the past few months and have some awesome additions in store! They will take a while to come out though. The goal of the library is still to provide a simple and efficient way to multi task in lua**
In Changes you'll find documentation for(In Order):
- Sterilizing Objects
@ -18,16 +18,20 @@ My multitasking library for lua. It is a pure lua binding if you ingore the inte
INSTALLING
----------
Note: The latest version of lualanes is required if you want to make use of system threads on lua 5.2+. I will update the dependencies for luarocks since this library should work fine on lua 5.2+
I still need to test though
To install copy the multi folder into your enviroment and you are good to go</br>
Note: The latest version of lualanes is required if you want to make use of system threads on lua 5.1+. I will update the dependencies for luarocks since this library should work fine on lua 5.1+
To install copy the multi folder into your enviroment and you are good to go</br>
If you want to use the system threads then you'll need to install lanes!
**or** use luarocks
```
luarocks install bin -- Inorder to use the new save state stuff
luarocks install multi
```
Note: In the near future you may be able to run multitasking code on multiple machines, network paralisim. This however will have to wait until I hammer out some bugs within the core of system threading itself.
See the rambling section to get an idea of how this will work.
Discord
-------
For real-time assistance with my libraries! A place where you can ask questions and get help with any of my libraries. Also you can request features and stuff there as well.</br>
@ -58,15 +62,15 @@ Planned features/TODO
Known Bugs/Issues
-----------------
In regards to integrations, thread cancellation works slightly different for love2d and lanes. Within love2d I was unable to (To lazy to...) not use the multi library within the thread. A fix for this is to call `multi:Stop()` when you are done with your threaded code! This may change however if I find a way to work around this. In love2d in order to mimic the GLOBAL table I needed the library to constantly sync tha data... You can use the sThread.waitFor(varname), or sThread.hold(func) methods to sync the globals, to get the value instead of using GLOBAL and this could work. If you want to go this route I suggest setting multi.isRunning=true to prevent the auto runner from doing its thing! This will make the multi manager no longer function, but thats the point :P
~~In regards to integrations, thread cancellation works slightly different for love2d and lanes. Within love2d I was unable to (To lazy to...) not use the multi library within the thread. A fix for this is to call `multi:Stop()` when you are done with your threaded code! This may change however if I find a way to work around this. In love2d in order to mimic the GLOBAL table I needed the library to constantly sync tha data... You can use the sThread.waitFor(varname), or sThread.hold(func) methods to sync the globals, to get the value instead of using GLOBAL and this could work. If you want to go this route I suggest setting multi.isRunning=true to prevent the auto runner from doing its thing! This will make the multi manager no longer function, but thats the point :P~~ THREAD.kill() should do the trick from within the thread. A listener could be made to detect when thread kill has been requested and sent to the running thread.
Another bug concerns the SystemThreadedJobQueue, Only 1 can be used for now... Creating more may not be a good idea.
Another bug concerns the SystemThreadedJobQueue, Only 1 can be used for now. Going to change in a future update
And systemThreadedTables only supports 1 table between the main and worker thread! They do not work when shared between 2 or more threads. If you need that much flexiblity ust the GLOBAL table that all threads have.
~~And systemThreadedTables only supports 1 table between the main and worker thread! They do not work when shared between 2 or more threads. If you need that much flexiblity ust the GLOBAL table that all threads have.~~ **FIXED**
For module creators using this library. I suggest using SystemThreadedQueues for data transfer instead of SystemThreadedTables for rapid data transfer, If you plan on having Constants that will always be the same then a table is a good idea! They support up to **n** threads and can be messed with and abused as much as you want :D
~~For module creators using this library. I suggest using SystemThreadedQueues for data transfer instead of SystemThreadedTables for rapid data transfer, If you plan on having Constants that will always be the same then a table is a good idea! They support up to **n** threads and can be messed with and abused as much as you want :D~~ FIXED Use what you want!
Love2D SystemThreadedTAbles do not send love2d userdata, use queues instead for that!
~~Love2D SystemThreadedTAbles do not send love2d userdata, use queues instead for that!~~ **FIXED**
Usage:</br>
-----
@ -811,619 +815,56 @@ Looping...</br>
Looping...</br>
We did it! 1 2 3</br>
Changes
-------
Update: 1.9.2
-------------
Added:
- (THREAD).kill() kills a thread. Note: THREAD is based on what you name it
- newTimeStamper() Part of the persistant systems... Useful for when you are running this library for a massive amount of time... like years stright!
Allows one to hook to timed events such as whenever the clock strikes midnight or when the day turns to monday. The event is only done once though. so as soon as monday is set it would trigger then not trigger again until next monday
works for seconds, minutes, days, months, year.
```lua
stamper = multi:newTimeStamper()
stamper:OnTime(int hour,int minute,int second,func) or stamper:OnTime(string time,func) time as 00:00:00
stamper:OnHour(int hour,func)
stamper:OnMinute(int minute,func)
stamper:OnSecond(int second,func)
stamper:OnDay(int day,func) or stamper:OnDay(string day,func) Mon, Tues, Wed, etc...
stamper:OnMonth(int month,func)
stamper:OnYear(int year,func)
```
Updated:
- LoadBalancing, well bettwr load balancing than existed before. This one allowd for multiple processes to have their own load reading. Calling this on the multi object will return the total load for the entire multi enviroment... loads of other processes are indeed affected by what other processes are doing. However if you combine prorioty to the mix of things then you will get differing results... these results however will most likely be higher than normal... different pirorities will have different default thresholds of performence.
Fixed:
- Thread.getName() should now work on lanes and love2d, haven't tested ut nuch with the luvit side of things...
- A bug with the lovemanager table.remove arguments were backwards haha
- The queue object in the love2d threading has been fixed! It now supports sending all objects (even functions as long as no upvalues are present!)
Changed:
- SystemThreadedJobQueues now have built in load management so they are not constantly at 100% cpu usage.
- SystemThreadedJobQueues pushJob now retunts an id of that job which will match the same one that OnJobCompleted returns
Update: 1.9.1
-------------
Added:
- Integration "multi.integration.luvitManager"
- Limited... Only the basic multi:newSystemThread(...) will work
- Not even data passing will work other than arguments... If using the bin library you can pass tables and function... Even full objects as long as inner recursion is not preasent.
Updated:
- multi:newSystemThread(name,func,...)
- It will not pass the ... to the func(). Do not know why this wasn't done in the first place :P
- Also multi:getPlatform(will now return "luvit" if using luvit... Though Idk if module creators would use the multi library when inside the luvit enviroment
Update: 1.9.0
-------------
Added:
- multiobj:ToString() -- returns a string repersenting the object
- multi:newFromString(str) -- creates an object from a string
Works on threads and regular objects. Requires the latest bin library to work!
```lua
talarm=multi:newThreadedAlarm("AlarmTest",5)
talarm:OnRing(function()
print("Ring!")
end)
bin.new(talarm:ToString()):tofile("test.dat")
-- multi:newFromString(bin.load("test.dat"))
```
-- A more seamless way to use this will be made in the form of state saving.
This is still a WIP
processes, timers, timemasters, watchers, and queuers have not been worked on yet
Update: 1.8.7
-------------
Added:
- multi.timer(func,...)
```lua
function test(a,b,c)
print("Running...")
a=0
for i=1,1000000000 do
a=a+1
end
return a,b+c
end
print(multi.timer(test,1,2,3))
print(multi.timer(test,1,2,3))
-- multi.time returns the time taken then the arguments from the function... Uses unpack so careful of nil values!
```
Update: 1.8.6
-------------
Added:
- jobQueue:doToAll(function)
- jobQueue:start() is now required Call this after all calls to registerJob()'s. Calling it afterwards will not guarantee your next push job with that job will work. Not calling this will make pushing jobs impossible!
- Fixed a bug with love2d Threaded Queue
- Fixed some bugs
- Old versions of this library! It stems back from 2012 see rambling for more info...
This will run said function in every thread.
```lua
-- Going to use love2d code this time, almost the same as last time... See ramblings
require("core.Library")
GLOBAL,sThread=require("multi.integration.loveManager").init() -- load the love2d version of the lanesManager and requires the entire multi library
require("core.GuiManager")
gui.ff.Color=Color.Black
jQueue=multi:newSystemThreadedJobQueue()
jQueue:registerJob("TEST_JOB",function(a,s)
math.randomseed(s)
TEST_JOB2()
return math.random(0,255)
end)
jQueue:registerJob("TEST_JOB2",function()
print("Test Works!")
end)
-- 1.8.6 EXAMPLE Change
jQueue:start() -- This is now needed!
--
jQueue:doToAll(function()
print("Doing this 2? times!")
end)
tableOfOrder={}
jQueue.OnJobCompleted(function(JOBID,n)
tableOfOrder[JOBID]=n
if #tableOfOrder==10 then
t.text="We got all of the pieces!"
end
end)
for i=1,10 do -- Job Name of registered function, ... varargs
jQueue:pushJob("TEST_JOB","This is a test!",math.random(1,1000000))
end
t=gui:newTextLabel("no done yet!",0,0,300,100)
t:centerX()
t:centerY()
```
Update: 1.8.5
-------------
Added:
- SystemThreadedExecute(cmd)
Allows the execution of system calls without hold up. It is possible to do the same using io.popen()! You decide which works best for you!
```lua
local GLOBAL,sThread=require("multi.integration.lanesManager").init()
cmd=multi:newSystemThreadedExecute("SystemThreadedExecuteTest.lua") -- This file is important!
cmd.OnCMDFinished(function(code) -- callback function to grab the exit code... Called when the command goes through
print("Got Code: "..code)
end)
multi:newTLoop(function()
print("...") -- lets show that we aren't being held up
end,1)
multi:mainloop()
```
Update: 1.8.4
-------------
Added:
- multi:newSystemThreadedJobQueue()
- Improved stability of the library
- Fixed a bug that made the benchmark and getload commands non-thread(coroutine) safe
- Tweaked the loveManager to help improve idle cpu usage
- Minor tweaks to the coroutine scheduling
# Using multi:newSystemThreadedJobQueue()
First you need to create the object
This works the same way as love2d as it does with lanes... It is getting increasing harder to make both work the same way with speed in mind... Anyway...
```lua
-- Creating the object using lanes manager to show case this. Examples has the file for love2d
local GLOBAL,sThread=require("multi.integration.lanesManager").init()
jQueue=multi:newSystemThreadedJobQueue(n) -- this internally creates System threads. By defualt it will use the # of processors on your system You can set this number though.
-- Only create 1 jobqueue! For now making more than 1 is buggy. You only really need one though. Just register new functions if you want 1 queue to do more. The one reason though is keeping track of jobIDs. I have an idea that I will roll out in the next update.
jQueue:registerJob("TEST_JOB",function(a,s)
math.randomseed(s)
-- We will push a random #
TEST_JOB2() -- You can call other registered functions as well!
return math.random(0,255) -- send the result to the main thread
end)
jQueue:registerJob("TEST_JOB2",function()
print("Test Works!") -- this is called from the job since it is registered on the same queue
end)
tableOfOrder={} -- This is how we will keep order of our completed jobs. There is no guarantee that the order will be correct
jQueue.OnJobCompleted(function(JOBID,n) -- whenever a job is completed you hook to the event that is called. This passes the JOBID folled by the returns of the job
-- JOBID is the completed job, starts at 1 and counts up by 1.
-- Threads finish at different times so jobids may be passed out of order! Be sure to have a way to order them
tableOfOrder[JOBID]=n -- we order ours by putting them into a table
if #tableOfOrder==10 then
print("We got all of the pieces!")
end
end)
-- Lets push the jobs now
for i=1,10 do -- Job Name of registered function, ... varargs
jQueue:pushJob("TEST_JOB","This is a test!",math.random(1,1000000))
end
print("I pushed all of the jobs :)")
multi:mainloop() -- Start the main loop :D
```
Thats it from this version!
Update: 1.8.3
-------------
Added:</br>
**New Mainloop functions** Below you can see the slight differences... Function overhead is not too bad in lua, but has a real difference. multi:mainloop() and multi:unprotectedMainloop() use the same algorithm yet the dedicated unprotected one is slightly faster due to having less function overhead.
- multi:mainloop()\* -- Bench: 16830003 Steps in 3 second(s)!
- multi:protectedMainloop() -- Bench: 16699308 Steps in 3 second(s)!
- multi:unprotectedMainloop() -- Bench: 16976627 Steps in 3 second(s)!
- multi:prioritizedMainloop1() -- Bench: 15007133 Steps in 3 second(s)!
- multi:prioritizedMainloop2() -- Bench: 15526248 Steps in 3 second(s)!
\* The OG mainloop function remains the same and old methods to achieve what we have with the new ones still exist
These new methods help by removing function overhead that is caused through the original mainloop function. The one downside is that you no longer have the flexiblity to change the processing during runtime.
However there is a work around! You can use processes to run multiobjs as well and use the other methods on them.
I may make a full comparison between each method and which is faster, but for now trust that the dedicated ones with less function overhead are infact faster. Not by much but still faster. :D
Update: 1.8.2
-------------
Added:</br>
- multi:newsystemThreadedTable(name) NOTE: Metatables are not supported in transfers. However there is a work around obj:init() that you see does this. Take a look in the multi/integration/shared/shared.lua files to see how I did it!
- Modified the GLOBAL metatable to sync before doing its tests
- multi._VERSION was multi.Version, felt it would be more consistant this way... I left the old way of getting the version just incase someone has used that way. It will eventually be gone. Also multi:getVersion() will do the job just as well and keep your code nice and update related bug free!
- Also everything that is included in the: multi/integration/shared/shared.lua (Which is loaded automatically) works in both lanes and love2d enviroments!
The threaded table is setup just like the threaded queue.</br>
It provids GLOBAL like features without having to write to GLOBAL!</br>
This is useful for module creators who want to keep their data private, but also use GLOBAL like coding.</br>
It has a few features that makes it a bit better than plain ol GLOBAL (For now...)
(ThreadedTable - TT for short)
- TT:waitFor(name)
- TT:sync()
- TT["var"]=value
- print(TT["var"])
we also have the "sync" method, this one was made for love2d because we do a syncing trick to get data in a table format. The lanes side has a sync method as well so no worries. Using indexing calls sync once and may grab your variable. This allows you to have the lanes indexing 'like' syntax when doing regular indexing in love2d side of the module. As of right now both sides work flawlessly! And this effect is now the GLOBAL as well</br>
On GLOBALS sync is a internal method for keeping the GLOBAL table in order. You can still use sThread.waitFor(name) to wait for variables that may of may not yet exist!
Time for some examples:
# Using multi:newSystemThreadedTable(name)
```lua
-- lanes Desktop lua! NOTE: this is in lanesintergratetest6.lua in the examples folder
local GLOBAL,sThread=require("multi.integration.lanesManager").init()
test=multi:newSystemThreadedTable("YO"):init()
test["test1"]="lol"
multi:newSystemThread("test",function()
tab=sThread.waitFor("YO"):init()
print(tab:has("test1"))
sThread.sleep(3)
tab["test2"]="Whats so funny?"
end)
multi:newThread("test2",function()
print(test:waitFor("test2"))
end)
multi:mainloop()
```
```lua
-- love2d gaming lua! NOTE: this is in main4.lua in the love2d examples
require("core.Library")
GLOBAL,sThread=require("multi.integration.loveManager").init() -- load the love2d version of the lanesManager and requires the entire multi library
require("core.GuiManager")
gui.ff.Color=Color.Black
test=multi:newSystemThreadedTable("YO"):init()
test["test1"]="lol"
multi:newSystemThread("test",function()
tab=sThread.waitFor("YO"):init()
print(tab["test1"])
sThread.sleep(3)
tab["test2"]="Whats so funny?"
end)
multi:newThread("test2",function()
print(test:waitFor("test2"))
t.text="DONE!"
end)
t=gui:newTextLabel("no done yet!",0,0,300,100)
t:centerX()
t:centerY()
```
Update: 1.8.1
-------------
No real change!</br>
Changed the structure of the library. Combined the coroutine based threads into the core!</br>
Only compat and integrations are not part of the core and never will be by nature.</br>
This should make the library more convient to use.</br>
I left multi/all.lua file so if anyone had libraries/projects that used that it will still work!</br>
Updated from 1.7.6 to 1.8.0</br> (How much thread could a thread thread if a thread could thread thread?)
Added:</br>
- multi:newSystemThreadedQueue()
- multi:systemThreadedBenchmark()
- More example files
- multi:canSystemThread() -- true if an integration was added false otherwise (For module creation)
- Fixed a few bugs in the loveManager
# Using multi:systemThreadedBenchmark()
```lua
package.path="?/init.lua;"..package.path
local GLOBAL,sThread=require("multi.integration.lanesManager").init()
multi:systemThreadedBenchmark(3):OnBench(function(self,count)
print("First Bench: "..count)
multi:systemThreadedBenchmark(3,"All Threads: ")
end)
multi:mainloop()
```
# Using multi:newSystemThreadedQueue()
Quick Note: queues shared across multiple objects will be pulling from the same "queue" keep this in mind when coding! ~~Also the queue respects direction a push on the thread side cannot be popped on the thread side... Same goes for the mainthread!</br>~~ Turns out i was wrong about this...
```lua
-- in love2d, this file will be in the same example folder as before, but is named main2.lua
require("core.Library")
GLOBAL,sThread=require("multi.integration.loveManager").init() -- load the love2d version of the lanesManager and requires the entire multi library
--IMPORTANT
-- Do not make the above local, this is the one difference that the lanesManager does not have
-- If these are local the functions will have the upvalues put into them that do not exist on the threaded side
-- You will need to ensure that the function does not refer to any upvalues in its code. It will print an error if it does though
-- Also each thread has a .1 second delay! This is used to generate a random values for each thread!
require("core.GuiManager")
gui.ff.Color=Color.Black
function multi:newSystemThreadedQueue(name) -- in love2d this will spawn a channel on both ends
local c={}
c.name=name
if love then
if love.thread then
function c:init()
self.chan=love.thread.getChannel(self.name)
function self:push(v)
self.chan:push(v)
end
function self:pop()
return self.chan:pop()
end
GLOBAL[self.name]=self
return self
end
return c
else
error("Make sure you required the love.thread module!")
end
else
c.linda=lanes.linda()
function c:push(v)
self.linda:send("Q",v)
end
function c:pop()
return ({self.linda:receive(0,"Q")})[2]
end
function c:init()
return self
end
GLOBAL[name]=c
end
return c
end
queue=multi:newSystemThreadedQueue("QUEUE"):init()
queue:push("This is a test")
queue:push("This is a test2")
queue:push("This is a test3")
queue:push("This is a test4")
multi:newSystemThread("test2",function()
queue=sThread.waitFor("QUEUE"):init()
data=queue:pop()
while data do
print(data)
data=queue:pop()
end
queue:push("DONE!")
end)
multi:newThread("test!",function()
thread.hold(function() return queue:pop() end)
t.text="Done!"
end)
t=gui:newTextLabel("no done yet!",0,0,300,100)
t:centerX()
t:centerY()
```
# In Lanes
```lua
-- The code is compatible with each other, I just wanted to show different things you can do in both examples
-- This file can be found in the examples folder as lanesintegrationtest4.lua
local GLOBAL,sThread=require("multi.integration.lanesManager").init()
queue=multi:newSystemThreadedQueue("QUEUE"):init()
queue:push("This is a test")
queue:push("This is a test2")
queue:push("This is a test3")
queue:push("This is a test4")
multi:newSystemThread("test2",function()
queue=sThread.waitFor("QUEUE"):init()
data=queue:pop()
while data do
print(data)
data=queue:pop()
end
queue:push("This is a test5")
queue:push("This is a test6")
queue:push("This is a test7")
queue:push("This is a test8")
end)
multi:newThread("test!",function() -- this is a lua thread
thread.sleep(.1)
data=queue:pop()
while data do
print(data)
data=queue:pop()
end
end)
multi:mainloop()
```
Update: 1.7.6
-------------
Fixed:
Typos like always
Added:</br>
multi:getPlatform() -- returns "love2d" if using the love2d platform or returns "lanes" if using lanes for threading</br>
examples files</br>
In Events added method setTask(func)</br>
The old way still works and is more convient to be honest, but I felt a method to do this was ok.</br>
Updated:
some example files to reflect changes to the core. Changes allow for less typing</br>
loveManager to require the compat if used so you don't need 2 require line to retrieve the library</br>
Update: 1.7.5
-------------
Fixed some typos in the readme... (I am sure there are more there are always more)</br>
Added more features for module support</br>
TODO:</br>
Work on performance of the library... I see 3 places where I can make this thing run quicker</br>
I'll show case some old versions of the multitasking library eventually so you can see its changes in days past!</br>
Update: 1.7.4
-------------
Added: the example folder which will be populated with more examples in the near future!</br>
The loveManager integration that mimics the lanesManager integration almost exactly to keep coding in both enviroments as close to possible. This is done mostly for library creation support!</br>
An example of the loveManager in action using almost the same code as the lanesintergreationtest2.lua</br>
NOTE: This code has only been tested to work on love2d version 1.10.2 thoough it should work version 0.9.0
```lua
require("core.Library") -- Didn't add this to a repo yet! Will do eventually... Allows for injections and other cool things
require("multi.compat.love2d") -- allows for multitasking and binds my libraies to the love2d engine that i am using
GLOBAL,sThread=require("multi.integration.loveManager").init() -- load the love2d version of the lanesManager
--IMPORTANT
-- Do not make the above local, this is the one difference that the lanesManager does not have
-- If these are local the functions will have the upvalues put into them that do not exist on the threaded side
-- You will need to ensure that the function does not refer to any upvalues in its code. It will print an error if it does though
-- Also each thread has a .1 second delay! This is used to generate a random values for each thread!
require("core.GuiManager") -- allows the use of graphics in the program.
gui.ff.Color=Color.Black
function comma_value(amount)
local formatted = amount
while true do
formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2')
if (k==0) then
break
end
end
return formatted
end
multi:newSystemThread("test1",function() -- Another difference is that the multi library is already loaded in the threaded enviroment as well as a call to multi:mainloop()
multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 1"):OnBench(function(self,c) GLOBAL["T1"]=c multi:Stop() end)
end)
multi:newSystemThread("test2",function() -- spawns a thread in another lua process
multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 2"):OnBench(function(self,c) GLOBAL["T2"]=c multi:Stop() end)
end)
multi:newSystemThread("test3",function() -- spawns a thread in another lua process
multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 3"):OnBench(function(self,c) GLOBAL["T3"]=c multi:Stop() end)
end)
multi:newSystemThread("test4",function() -- spawns a thread in another lua process
multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 4"):OnBench(function(self,c) GLOBAL["T4"]=c multi:Stop() end)
end)
multi:newSystemThread("test5",function() -- spawns a thread in another lua process
multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 5"):OnBench(function(self,c) GLOBAL["T5"]=c multi:Stop() end)
end)
multi:newSystemThread("test6",function() -- spawns a thread in another lua process
multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 6"):OnBench(function(self,c) GLOBAL["T6"]=c multi:Stop() end)
end)
multi:newSystemThread("Combiner",function() -- spawns a thread in another lua process
function comma_value(amount)
local formatted = amount
while true do
formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2')
if (k==0) then
break
end
end
return formatted
end
local b=comma_value(tostring(sThread.waitFor("T1")+sThread.waitFor("T2")+sThread.waitFor("T3")+sThread.waitFor("T4")+sThread.waitFor("T5")+sThread.waitFor("T6")))
GLOBAL["DONE"]=b
end)
multi:newThread("test0",function()
-- sThread.waitFor("DONE") -- lets hold the main thread completely so we don't eat up cpu
-- os.exit()
-- when the main thread is holding there is a chance that error handling on the system threads may not work!
-- instead we can do this
while true do
thread.skip(1) -- allow error handling to take place... Otherwise lets keep the main thread running on the low
-- Before we held just because we could... But this is a game and we need to have logic continue
--sThreadM.sleep(.001) -- Sleeping for .001 is a greeat way to keep cpu usage down. Make sure if you aren't doing work to rest. Abuse the hell out of GLOBAL if you need to :P
if GLOBAL["DONE"] then
t.text="Bench: "..GLOBAL["DONE"]
end
end
end)
GLOBAL["Bench"]=3
t=gui:newTextLabel("no done yet!",0,0,300,100)
t:centerX()
t:centerY()
```
Update: 1.7.3
-------------
Changed how requiring the library works!
`require("multi.all")` Will still work as expected; however, with the exception of threading, compat, and integrations everything else has been moved into the core of the library.
```lua
-- This means that these are no longer required and will cause an error if done so
require("multi.loop")
require("multi.alarm")
require("multi.updater")
require("multi.tloop")
require("multi.watcher")
require("multi.tstep")
require("multi.step")
require("multi.task")
-- ^ they are all part of the core now
```
Update: 1.7.2
-------------
Moved updaters, loops, and alarms into the init.lua file. I consider them core features and they are referenced in the init.lua file so they need to exist there. Threaded versions are still separate though. Added another example file
Update: 1.7.1 Bug Fixes Only
-------------
Update: 1.7.0
-------------
Modified: multi.integration.lanesManager.lua
It is now in a stable and simple state Works with the latest lanes version! Tested with version 3.11 I cannot promise that everything will work with eariler versions. Future versions are good though.</br>
Example Usage:</br>
sThread is a handle to a global interface for system threads to interact with themself</br>
thread is the interface for multithreads as seen in the threading section</br>
GLOBAL a table that can be used throughout each and every thread
sThreads have a few methods</br>
sThread.set(name,val) -- you can use the GLOBAL table instead modifies the same table anyway</br>
sThread.get(name) -- you can use the GLOBAL table instead modifies the same table anyway</br>
sThread.waitFor(name) -- waits until a value exists, if it does it returns it</br>
sThread.getCores() -- returns the number of cores on your cpu</br>
sThread.sleep(n) -- sleeps for a bit stopping the entire thread from running</br>
sThread.hold(n) -- sleeps until a condition is met</br>
```lua
local GLOBAL,sThread=require("multi.integration.lanesManager").init()
require("multi.all")
multi:newAlarm(2):OnRing(function(self)
GLOBAL["NumOfCores"]=sThread.getCores()
end)
multi:newAlarm(7):OnRing(function(self)
GLOBAL["AnotherTest"]=true
end)
multi:newAlarm(13):OnRing(function(self)
GLOBAL["FinalTest"]=true
end)
multi:newSystemThread("test",function() -- spawns a thread in another lua process
require("multi.all") -- now you can do all of your coding with the multi library! You could even spawn more threads from here with the integration. You would need to require the interaction again though
print("Waiting for variable: NumOfCores")
print("Got it: ",sThread.waitFor("NumOfCores"))
sThread.hold(function()
return GLOBAL["AnotherTest"] -- note this would hold the entire systemthread. Spawn a coroutine thread using multi:newThread() or multi:newThreaded...
end)
print("Holding works!")
multi:newThread("tests",function()
thread.hold(function()
return GLOBAL["FinalTest"] -- note this will not hold the entire systemthread. As seen with the TLoop constantly going!
end)
print("Final test works!")
os.exit()
end)
local a=0
multi:newTLoop(function()
a=a+1
print(a)
end,.5)
multi:mainloop()
end)
multi:mainloop()
```
Update: 1.6.0
-------------
Changed: steps and loops
```lua
-- Was
step:OnStep(function(pos,self) -- same goes for tsteps as well
print(pos)
end)
multi:newLoop(function(dt,self)
print(dt)
end)
-- Is now
step:OnStep(function(self,pos) -- same goes for tsteps as well
print(pos)
end)
multi:newLoop(function(self,dt)
print(dt)
end)
```
Reasoning I wanted to keep objects consistant, but a lot of my older libraries use the old way of doing things. Therefore I added a backwards module
```lua
require("multi.all")
require("multi.compat.backwards[1,5,0]") -- allows for the use of features that were scrapped/changed in 1.6.0+
```
Update: 1.5.0
-------------
Added:
- An easy way to manage timeouts
- Small bug fixes
Update: 1.4.1 - First Public release of the library
-------------
IMPORTANT:</br>
Every update I make aims to make things simpler more efficent and just better, but a lot of old code, which can be really big, uses a lot of older features. I know the pain of having to rewrite everything. My promise to my library users is that I will always have backwards support for older features! New ways may exist that are quicker and eaiser, but the old features/methods will be supported.</br>
Rambling
--------
5/23/18:
When it comes to running code across different systems we run into a problem. It takes time to send objects from one maching to another. In the beginning only local networks will be supported. I may add support to send commands to another network to do computing. Like having your own lus cloud. userdata will never be allowed to run on other machines. It is not possible unless the library you are using allows userdata to be turned into a string and back into an object. With this feature you want to send a command that will take time or needs tons of them done millions+, reason being networks are not that "fast" and only simple objects can be sent. If you mirror your enviroment then you can do some cool things.
The planned structure will be something like this:
multi-Single Threaded Multitasking
multi-Threads
multi-System Threads
multi-Network threads
where netThreads can contain systemThreads which can intern contain both Threads and single threaded multitasking
Nothing has been built yet, but the system will work something like this:
#host:
```lua
sGLOBAL, nGlobal,sThread=require("multi.integration.networkManager").init() -- This will determine if one is using lanes,love2d, or luvit
multi:Host("MainSystem") -- tell the network that this is the main system. Uses broadcast so that nodes know how to find the host!
nThread = multi:newNetworkThread("NetThread_1",function(...)
-- basic usage
nGLOBAL["RemoteVaraible"] = true -- will sync data to all nodes and the host
sGLOBAL["LocalMachineVaraible"] = true -- will sync data to all system threads on the local machine
return "Hello Network!" -- send "Hello Network" back to the host node
end)
multi:mainloop()
```
#node
```lua
GLOBAL,sThread=require("multi.integration.networkManager").init() -- This will determine if one is using lanes,love2d, or luvit
node = multi:newNode("NodeName","MainSystem") -- Search the network for the host, connect to it and be ready for requests!
-- On the main thread, a simple multi:newNetworkThread thread and also non system threads, you can access global data without an issue. When dealing with system threads is when you have a problem.
node:setLog{
maxLines = 10000,
cleanOnInterval = true,
cleanInterval = "day", -- every day Supports(day, week, month, year)
noLog = false -- default is false, make true if you do not need a log
}
node:settings{
maxJobs = 100, -- Job queues will respect this as well as the host when it is figuting out which node is under the least load. Default: 0 or infinite
sendLoadInterval = 60 -- every 60 seconds update the host of the nodes load
sendLoad = true -- default is true, tells the server how stressed the system is
}
multi:mainloop()
-- Note: the node will contain a log of all the commands that it gets. A file called "NodeName.log" will contain the info. You can set the limit by lines or file size. Also you can set it to clear the log every interval of time if an error does not exist. All errors are both logged and sent to the host as well. You can have more than one host and more than one node(duh :P).
```
The goal of the node is to set up a simple and easy way to run commands on a remote machine.
There are 2 main ways you can use this feature. 1. One node per machine with system threads being able to use the full processing power of the machine. 2. Multiple nodes on one machine where each node is acting like its own thread. And of course a mix of the two is indeed possible.
Love2d Sleeping reduces the cpu time making my load detection think the system is under more load, thus preventing it from sleeping... I will look into other means. As of right now it will not eat all of your cpu if threads are active. For now I suggest killing threads that aren't needed anymore. On lanes threads at idle use 0% cpu and it is amazing. A state machine may solve what I need though. One state being idle state that sleeps and only goes into the active state if a job request or data is sent to it... after some time of not being under load it wil switch back into the idle state... We'll see what happens.
Love2d doesn't like to send functions through channels. By defualt it does not support this. I achieve this by dumping the function and loadstring it on the thread. This however is slow. For the System Threaded Job Queue I had to change my original idea of sending functions as jobs. The current way you do it now is register a job functions once and then call that job across the thread through a queue. Each worker thread pops from the queue and returns the job. The Job ID is automatically updated and allows you to keep track of the order that the data comes in. A table with # indexes can be used to originze the data...

868
changes.html Normal file

File diff suppressed because one or more lines are too long

693
changes.md Normal file
View File

@ -0,0 +1,693 @@
Changes
-------
Update: 1.10.0
-------------
**Note:** The library is now considered to be stable!
**Upcoming:** Network parallelism is on the way. It is in the works and should be released soon
Added:
------
- isMainThread true/nil
- multi:newSystemThreadedConnection(name,protect) -- Works like normal connections, but are able to trigger events across threads
Example of threaded connections
```lua
package.path="?/init.lua;?.lua;"..package.path
local GLOBAL,THREAD=require("multi.integration.lanesManager").init()
multi:newSystemThread("Test_Thread_1",function()
connOut = THREAD.waitFor("ConnectionNAMEHERE"):init()
connOut(function(arg)
print(THREAD.getName(),arg)
end)
multi:mainloop()
end)
multi:newSystemThread("Test_Thread_2",function()
connOut = THREAD.waitFor("ConnectionNAMEHERE"):init()
connOut(function(arg)
print(THREAD.getName(),arg)
end)
multi:mainloop()
end)
connOut = multi:newSystemThreadedConnection("ConnectionNAMEHERE"):init()
a=0
connOut(function(arg)
print("Main",arg)
end)
multi:newTLoop(function()
a=a+1
connOut:Fire("Test From Main Thread: "..a.."\n")
end,1)
```
Fixed:
------
**loveManager** and **shared threading objects**
- sThread.waitFor()
- sThread.hold()
- some typos
- SystemThreadedTables (They now work on both lanes and love2d as expected)
Example of threaded tables
```lua
package.path="?/init.lua;?.lua;"..package.path
local GLOBAL,sThread=require("multi.integration.lanesManager").init()
multi:newSystemThread("Test_Thread_1",function()
require("multi")
test = sThread.waitFor("testthing"):init()
multi:newTLoop(function()
print("------")
for i,v in pairs(test.tab) do
print("T1",i,v)
end
end,1)
multi:mainloop()
end)
multi:newSystemThread("Test_Thread_1",function()
require("multi")
test = sThread.waitFor("testthing"):init()
multi:newTLoop(function()
print("------")
for i,v in pairs(test.tab) do
print("T2",i,v)
end
end,1)
multi:mainloop()
end)
test = multi:newSystemThreadedTable("testthing"):init()
multi:newTLoop(function()
local a,b = multi.randomString(8),multi.randomString(4)
print(">",a,b)
test[a]=b
end,1)
multi:mainloop()
```
Update: 1.9.2
-------------
Added:
- (THREAD).kill() kills a thread. Note: THREAD is based on what you name it
- newTimeStamper() Part of the persistant systems... Useful for when you are running this library for a massive amount of time... like years stright!
Allows one to hook to timed events such as whenever the clock strikes midnight or when the day turns to monday. The event is only done once though. so as soon as monday is set it would trigger then not trigger again until next monday
works for seconds, minutes, days, months, year.
```lua
stamper = multi:newTimeStamper()
stamper:OnTime(int hour,int minute,int second,func) or stamper:OnTime(string time,func) time as 00:00:00
stamper:OnHour(int hour,func)
stamper:OnMinute(int minute,func)
stamper:OnSecond(int second,func)
stamper:OnDay(int day,func) or stamper:OnDay(string day,func) Mon, Tues, Wed, etc...
stamper:OnMonth(int month,func)
stamper:OnYear(int year,func)
```
Updated:
- LoadBalancing, well bettwr load balancing than existed before. This one allowd for multiple processes to have their own load reading. Calling this on the multi object will return the total load for the entire multi enviroment... loads of other processes are indeed affected by what other processes are doing. However if you combine prorioty to the mix of things then you will get differing results... these results however will most likely be higher than normal... different pirorities will have different default thresholds of performence.
Fixed:
- Thread.getName() should now work on lanes and love2d, haven't tested ut nuch with the luvit side of things...
- A bug with the lovemanager table.remove arguments were backwards haha
- The queue object in the love2d threading has been fixed! It now supports sending all objects (even functions as long as no upvalues are present!)
Changed:
- SystemThreadedJobQueues now have built in load management so they are not constantly at 100% cpu usage.
- SystemThreadedJobQueues pushJob now retunts an id of that job which will match the same one that OnJobCompleted returns
Update: 1.9.1
-------------
Added:
- Integration "multi.integration.luvitManager"
- Limited... Only the basic multi:newSystemThread(...) will work
- Not even data passing will work other than arguments... If using the bin library you can pass tables and function... Even full objects as long as inner recursion is not preasent.
Updated:
- multi:newSystemThread(name,func,...)
- It will not pass the ... to the func(). Do not know why this wasn't done in the first place :P
- Also multi:getPlatform(will now return "luvit" if using luvit... Though Idk if module creators would use the multi library when inside the luvit enviroment
Update: 1.9.0
-------------
Added:
- multiobj:ToString() -- returns a string repersenting the object
- multi:newFromString(str) -- creates an object from a string
Works on threads and regular objects. Requires the latest bin library to work!
```lua
talarm=multi:newThreadedAlarm("AlarmTest",5)
talarm:OnRing(function()
print("Ring!")
end)
bin.new(talarm:ToString()):tofile("test.dat")
-- multi:newFromString(bin.load("test.dat"))
```
-- A more seamless way to use this will be made in the form of state saving.
This is still a WIP
processes, timers, timemasters, watchers, and queuers have not been worked on yet
Update: 1.8.7
-------------
Added:
- multi.timer(func,...)
```lua
function test(a,b,c)
print("Running...")
a=0
for i=1,1000000000 do
a=a+1
end
return a,b+c
end
print(multi.timer(test,1,2,3))
print(multi.timer(test,1,2,3))
-- multi.timer returns the time taken then the arguments from the function... Uses unpack so careful of nil values!
```
Update: 1.8.6
-------------
Added:
- jobQueue:doToAll(function)
- jobQueue:start() is now required Call this after all calls to registerJob()'s. Calling it afterwards will not guarantee your next push job with that job will work. Not calling this will make pushing jobs impossible!
- Fixed a bug with love2d Threaded Queue
- Fixed some bugs
- Old versions of this library! It stems back from 2012 see rambling for more info...
This will run said function in every thread.
```lua
-- Going to use love2d code this time, almost the same as last time... See ramblings
require("core.Library")
GLOBAL,sThread=require("multi.integration.loveManager").init() -- load the love2d version of the lanesManager and requires the entire multi library
require("core.GuiManager")
gui.ff.Color=Color.Black
jQueue=multi:newSystemThreadedJobQueue()
jQueue:registerJob("TEST_JOB",function(a,s)
math.randomseed(s)
TEST_JOB2()
return math.random(0,255)
end)
jQueue:registerJob("TEST_JOB2",function()
print("Test Works!")
end)
-- 1.8.6 EXAMPLE Change
jQueue:start() -- This is now needed!
--
jQueue:doToAll(function()
print("Doing this 2? times!")
end)
tableOfOrder={}
jQueue.OnJobCompleted(function(JOBID,n)
tableOfOrder[JOBID]=n
if #tableOfOrder==10 then
t.text="We got all of the pieces!"
end
end)
for i=1,10 do -- Job Name of registered function, ... varargs
jQueue:pushJob("TEST_JOB","This is a test!",math.random(1,1000000))
end
t=gui:newTextLabel("no done yet!",0,0,300,100)
t:centerX()
t:centerY()
```
Update: 1.8.5
-------------
Added:
- SystemThreadedExecute(cmd)
Allows the execution of system calls without hold up. It is possible to do the same using io.popen()! You decide which works best for you!
```lua
local GLOBAL,sThread=require("multi.integration.lanesManager").init()
cmd=multi:newSystemThreadedExecute("SystemThreadedExecuteTest.lua") -- This file is important!
cmd.OnCMDFinished(function(code) -- callback function to grab the exit code... Called when the command goes through
print("Got Code: "..code)
end)
multi:newTLoop(function()
print("...") -- lets show that we aren't being held up
end,1)
multi:mainloop()
```
Update: 1.8.4
-------------
Added:
- multi:newSystemThreadedJobQueue()
- Improved stability of the library
- Fixed a bug that made the benchmark and getload commands non-thread(coroutine) safe
- Tweaked the loveManager to help improve idle cpu usage
- Minor tweaks to the coroutine scheduling
# Using multi:newSystemThreadedJobQueue()
First you need to create the object
This works the same way as love2d as it does with lanes... It is getting increasing harder to make both work the same way with speed in mind... Anyway...
```lua
-- Creating the object using lanes manager to show case this. Examples has the file for love2d
local GLOBAL,sThread=require("multi.integration.lanesManager").init()
jQueue=multi:newSystemThreadedJobQueue(n) -- this internally creates System threads. By defualt it will use the # of processors on your system You can set this number though.
-- Only create 1 jobqueue! For now making more than 1 is buggy. You only really need one though. Just register new functions if you want 1 queue to do more. The one reason though is keeping track of jobIDs. I have an idea that I will roll out in the next update.
jQueue:registerJob("TEST_JOB",function(a,s)
math.randomseed(s)
-- We will push a random #
TEST_JOB2() -- You can call other registered functions as well!
return math.random(0,255) -- send the result to the main thread
end)
jQueue:registerJob("TEST_JOB2",function()
print("Test Works!") -- this is called from the job since it is registered on the same queue
end)
tableOfOrder={} -- This is how we will keep order of our completed jobs. There is no guarantee that the order will be correct
jQueue.OnJobCompleted(function(JOBID,n) -- whenever a job is completed you hook to the event that is called. This passes the JOBID folled by the returns of the job
-- JOBID is the completed job, starts at 1 and counts up by 1.
-- Threads finish at different times so jobids may be passed out of order! Be sure to have a way to order them
tableOfOrder[JOBID]=n -- we order ours by putting them into a table
if #tableOfOrder==10 then
print("We got all of the pieces!")
end
end)
-- Lets push the jobs now
for i=1,10 do -- Job Name of registered function, ... varargs
jQueue:pushJob("TEST_JOB","This is a test!",math.random(1,1000000))
end
print("I pushed all of the jobs :)")
multi:mainloop() -- Start the main loop :D
```
Thats it from this version!
Update: 1.8.3
-------------
Added:</br>
**New Mainloop functions** Below you can see the slight differences... Function overhead is not too bad in lua, but has a real difference. multi:mainloop() and multi:unprotectedMainloop() use the same algorithm yet the dedicated unprotected one is slightly faster due to having less function overhead.
- multi:mainloop()\* -- Bench: 16830003 Steps in 3 second(s)!
- multi:protectedMainloop() -- Bench: 16699308 Steps in 3 second(s)!
- multi:unprotectedMainloop() -- Bench: 16976627 Steps in 3 second(s)!
- multi:prioritizedMainloop1() -- Bench: 15007133 Steps in 3 second(s)!
- multi:prioritizedMainloop2() -- Bench: 15526248 Steps in 3 second(s)!
\* The OG mainloop function remains the same and old methods to achieve what we have with the new ones still exist
These new methods help by removing function overhead that is caused through the original mainloop function. The one downside is that you no longer have the flexiblity to change the processing during runtime.
However there is a work around! You can use processes to run multiobjs as well and use the other methods on them.
I may make a full comparison between each method and which is faster, but for now trust that the dedicated ones with less function overhead are infact faster. Not by much but still faster. :D
Update: 1.8.2
-------------
Added:</br>
- multi:newsystemThreadedTable(name) NOTE: Metatables are not supported in transfers. However there is a work around obj:init() that you see does this. Take a look in the multi/integration/shared/shared.lua files to see how I did it!
- Modified the GLOBAL metatable to sync before doing its tests
- multi._VERSION was multi.Version, felt it would be more consistant this way... I left the old way of getting the version just incase someone has used that way. It will eventually be gone. Also multi:getVersion() will do the job just as well and keep your code nice and update related bug free!
- Also everything that is included in the: multi/integration/shared/shared.lua (Which is loaded automatically) works in both lanes and love2d enviroments!
The threaded table is setup just like the threaded queue.</br>
It provids GLOBAL like features without having to write to GLOBAL!</br>
This is useful for module creators who want to keep their data private, but also use GLOBAL like coding.</br>
It has a few features that makes it a bit better than plain ol GLOBAL (For now...)
(ThreadedTable - TT for short)
- TT:waitFor(name)
- TT:sync()
- TT["var"]=value
- print(TT["var"])
we also have the "sync" method, this one was made for love2d because we do a syncing trick to get data in a table format. The lanes side has a sync method as well so no worries. Using indexing calls sync once and may grab your variable. This allows you to have the lanes indexing 'like' syntax when doing regular indexing in love2d side of the module. As of right now both sides work flawlessly! And this effect is now the GLOBAL as well</br>
On GLOBALS sync is a internal method for keeping the GLOBAL table in order. You can still use sThread.waitFor(name) to wait for variables that may of may not yet exist!
Time for some examples:
# Using multi:newSystemThreadedTable(name)
```lua
-- lanes Desktop lua! NOTE: this is in lanesintergratetest6.lua in the examples folder
local GLOBAL,sThread=require("multi.integration.lanesManager").init()
test=multi:newSystemThreadedTable("YO"):init()
test["test1"]="lol"
multi:newSystemThread("test",function()
tab=sThread.waitFor("YO"):init()
print(tab:has("test1"))
sThread.sleep(3)
tab["test2"]="Whats so funny?"
end)
multi:newThread("test2",function()
print(test:waitFor("test2"))
end)
multi:mainloop()
```
```lua
-- love2d gaming lua! NOTE: this is in main4.lua in the love2d examples
require("core.Library")
GLOBAL,sThread=require("multi.integration.loveManager").init() -- load the love2d version of the lanesManager and requires the entire multi library
require("core.GuiManager")
gui.ff.Color=Color.Black
test=multi:newSystemThreadedTable("YO"):init()
test["test1"]="lol"
multi:newSystemThread("test",function()
tab=sThread.waitFor("YO"):init()
print(tab["test1"])
sThread.sleep(3)
tab["test2"]="Whats so funny?"
end)
multi:newThread("test2",function()
print(test:waitFor("test2"))
t.text="DONE!"
end)
t=gui:newTextLabel("no done yet!",0,0,300,100)
t:centerX()
t:centerY()
```
Update: 1.8.1
-------------
No real change!</br>
Changed the structure of the library. Combined the coroutine based threads into the core!</br>
Only compat and integrations are not part of the core and never will be by nature.</br>
This should make the library more convient to use.</br>
I left multi/all.lua file so if anyone had libraries/projects that used that it will still work!</br>
Updated from 1.7.6 to 1.8.0</br> (How much thread could a thread thread if a thread could thread thread?)
Added:</br>
- multi:newSystemThreadedQueue()
- multi:systemThreadedBenchmark()
- More example files
- multi:canSystemThread() -- true if an integration was added false otherwise (For module creation)
- Fixed a few bugs in the loveManager
# Using multi:systemThreadedBenchmark()
```lua
package.path="?/init.lua;"..package.path
local GLOBAL,sThread=require("multi.integration.lanesManager").init()
multi:systemThreadedBenchmark(3):OnBench(function(self,count)
print("First Bench: "..count)
multi:systemThreadedBenchmark(3,"All Threads: ")
end)
multi:mainloop()
```
# Using multi:newSystemThreadedQueue()
Quick Note: queues shared across multiple objects will be pulling from the same "queue" keep this in mind when coding! ~~Also the queue respects direction a push on the thread side cannot be popped on the thread side... Same goes for the mainthread!</br>~~ Turns out i was wrong about this...
```lua
-- in love2d, this file will be in the same example folder as before, but is named main2.lua
require("core.Library")
GLOBAL,sThread=require("multi.integration.loveManager").init() -- load the love2d version of the lanesManager and requires the entire multi library
--IMPORTANT
-- Do not make the above local, this is the one difference that the lanesManager does not have
-- If these are local the functions will have the upvalues put into them that do not exist on the threaded side
-- You will need to ensure that the function does not refer to any upvalues in its code. It will print an error if it does though
-- Also each thread has a .1 second delay! This is used to generate a random values for each thread!
require("core.GuiManager")
gui.ff.Color=Color.Black
function multi:newSystemThreadedQueue(name) -- in love2d this will spawn a channel on both ends
local c={}
c.name=name
if love then
if love.thread then
function c:init()
self.chan=love.thread.getChannel(self.name)
function self:push(v)
self.chan:push(v)
end
function self:pop()
return self.chan:pop()
end
GLOBAL[self.name]=self
return self
end
return c
else
error("Make sure you required the love.thread module!")
end
else
c.linda=lanes.linda()
function c:push(v)
self.linda:send("Q",v)
end
function c:pop()
return ({self.linda:receive(0,"Q")})[2]
end
function c:init()
return self
end
GLOBAL[name]=c
end
return c
end
queue=multi:newSystemThreadedQueue("QUEUE"):init()
queue:push("This is a test")
queue:push("This is a test2")
queue:push("This is a test3")
queue:push("This is a test4")
multi:newSystemThread("test2",function()
queue=sThread.waitFor("QUEUE"):init()
data=queue:pop()
while data do
print(data)
data=queue:pop()
end
queue:push("DONE!")
end)
multi:newThread("test!",function()
thread.hold(function() return queue:pop() end)
t.text="Done!"
end)
t=gui:newTextLabel("no done yet!",0,0,300,100)
t:centerX()
t:centerY()
```
# In Lanes
```lua
-- The code is compatible with each other, I just wanted to show different things you can do in both examples
-- This file can be found in the examples folder as lanesintegrationtest4.lua
local GLOBAL,sThread=require("multi.integration.lanesManager").init()
queue=multi:newSystemThreadedQueue("QUEUE"):init()
queue:push("This is a test")
queue:push("This is a test2")
queue:push("This is a test3")
queue:push("This is a test4")
multi:newSystemThread("test2",function()
queue=sThread.waitFor("QUEUE"):init()
data=queue:pop()
while data do
print(data)
data=queue:pop()
end
queue:push("This is a test5")
queue:push("This is a test6")
queue:push("This is a test7")
queue:push("This is a test8")
end)
multi:newThread("test!",function() -- this is a lua thread
thread.sleep(.1)
data=queue:pop()
while data do
print(data)
data=queue:pop()
end
end)
multi:mainloop()
```
Update: 1.7.6
-------------
Fixed:
Typos like always
Added:</br>
multi:getPlatform() -- returns "love2d" if using the love2d platform or returns "lanes" if using lanes for threading</br>
examples files</br>
In Events added method setTask(func)</br>
The old way still works and is more convient to be honest, but I felt a method to do this was ok.</br>
Updated:
some example files to reflect changes to the core. Changes allow for less typing</br>
loveManager to require the compat if used so you don't need 2 require line to retrieve the library</br>
Update: 1.7.5
-------------
Fixed some typos in the readme... (I am sure there are more there are always more)</br>
Added more features for module support</br>
TODO:</br>
Work on performance of the library... I see 3 places where I can make this thing run quicker</br>
I'll show case some old versions of the multitasking library eventually so you can see its changes in days past!</br>
Update: 1.7.4
-------------
Added: the example folder which will be populated with more examples in the near future!</br>
The loveManager integration that mimics the lanesManager integration almost exactly to keep coding in both enviroments as close to possible. This is done mostly for library creation support!</br>
An example of the loveManager in action using almost the same code as the lanesintergreationtest2.lua</br>
NOTE: This code has only been tested to work on love2d version 1.10.2 thoough it should work version 0.9.0
```lua
require("core.Library") -- Didn't add this to a repo yet! Will do eventually... Allows for injections and other cool things
require("multi.compat.love2d") -- allows for multitasking and binds my libraies to the love2d engine that i am using
GLOBAL,sThread=require("multi.integration.loveManager").init() -- load the love2d version of the lanesManager
--IMPORTANT
-- Do not make the above local, this is the one difference that the lanesManager does not have
-- If these are local the functions will have the upvalues put into them that do not exist on the threaded side
-- You will need to ensure that the function does not refer to any upvalues in its code. It will print an error if it does though
-- Also each thread has a .1 second delay! This is used to generate a random values for each thread!
require("core.GuiManager") -- allows the use of graphics in the program.
gui.ff.Color=Color.Black
function comma_value(amount)
local formatted = amount
while true do
formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2')
if (k==0) then
break
end
end
return formatted
end
multi:newSystemThread("test1",function() -- Another difference is that the multi library is already loaded in the threaded enviroment as well as a call to multi:mainloop()
multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 1"):OnBench(function(self,c) GLOBAL["T1"]=c multi:Stop() end)
end)
multi:newSystemThread("test2",function() -- spawns a thread in another lua process
multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 2"):OnBench(function(self,c) GLOBAL["T2"]=c multi:Stop() end)
end)
multi:newSystemThread("test3",function() -- spawns a thread in another lua process
multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 3"):OnBench(function(self,c) GLOBAL["T3"]=c multi:Stop() end)
end)
multi:newSystemThread("test4",function() -- spawns a thread in another lua process
multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 4"):OnBench(function(self,c) GLOBAL["T4"]=c multi:Stop() end)
end)
multi:newSystemThread("test5",function() -- spawns a thread in another lua process
multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 5"):OnBench(function(self,c) GLOBAL["T5"]=c multi:Stop() end)
end)
multi:newSystemThread("test6",function() -- spawns a thread in another lua process
multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 6"):OnBench(function(self,c) GLOBAL["T6"]=c multi:Stop() end)
end)
multi:newSystemThread("Combiner",function() -- spawns a thread in another lua process
function comma_value(amount)
local formatted = amount
while true do
formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2')
if (k==0) then
break
end
end
return formatted
end
local b=comma_value(tostring(sThread.waitFor("T1")+sThread.waitFor("T2")+sThread.waitFor("T3")+sThread.waitFor("T4")+sThread.waitFor("T5")+sThread.waitFor("T6")))
GLOBAL["DONE"]=b
end)
multi:newThread("test0",function()
-- sThread.waitFor("DONE") -- lets hold the main thread completely so we don't eat up cpu
-- os.exit()
-- when the main thread is holding there is a chance that error handling on the system threads may not work!
-- instead we can do this
while true do
thread.skip(1) -- allow error handling to take place... Otherwise lets keep the main thread running on the low
-- Before we held just because we could... But this is a game and we need to have logic continue
--sThreadM.sleep(.001) -- Sleeping for .001 is a greeat way to keep cpu usage down. Make sure if you aren't doing work to rest. Abuse the hell out of GLOBAL if you need to :P
if GLOBAL["DONE"] then
t.text="Bench: "..GLOBAL["DONE"]
end
end
end)
GLOBAL["Bench"]=3
t=gui:newTextLabel("no done yet!",0,0,300,100)
t:centerX()
t:centerY()
```
Update: 1.7.3
-------------
Changed how requiring the library works!
`require("multi.all")` Will still work as expected; however, with the exception of threading, compat, and integrations everything else has been moved into the core of the library.
```lua
-- This means that these are no longer required and will cause an error if done so
require("multi.loop")
require("multi.alarm")
require("multi.updater")
require("multi.tloop")
require("multi.watcher")
require("multi.tstep")
require("multi.step")
require("multi.task")
-- ^ they are all part of the core now
```
Update: 1.7.2
-------------
Moved updaters, loops, and alarms into the init.lua file. I consider them core features and they are referenced in the init.lua file so they need to exist there. Threaded versions are still separate though. Added another example file
Update: 1.7.1 Bug Fixes Only
-------------
Update: 1.7.0
-------------
Modified: multi.integration.lanesManager.lua
It is now in a stable and simple state Works with the latest lanes version! Tested with version 3.11 I cannot promise that everything will work with eariler versions. Future versions are good though.</br>
Example Usage:</br>
sThread is a handle to a global interface for system threads to interact with themself</br>
thread is the interface for multithreads as seen in the threading section</br>
GLOBAL a table that can be used throughout each and every thread
sThreads have a few methods</br>
sThread.set(name,val) -- you can use the GLOBAL table instead modifies the same table anyway</br>
sThread.get(name) -- you can use the GLOBAL table instead modifies the same table anyway</br>
sThread.waitFor(name) -- waits until a value exists, if it does it returns it</br>
sThread.getCores() -- returns the number of cores on your cpu</br>
sThread.sleep(n) -- sleeps for a bit stopping the entire thread from running</br>
sThread.hold(n) -- sleeps until a condition is met</br>
```lua
local GLOBAL,sThread=require("multi.integration.lanesManager").init()
require("multi.all")
multi:newAlarm(2):OnRing(function(self)
GLOBAL["NumOfCores"]=sThread.getCores()
end)
multi:newAlarm(7):OnRing(function(self)
GLOBAL["AnotherTest"]=true
end)
multi:newAlarm(13):OnRing(function(self)
GLOBAL["FinalTest"]=true
end)
multi:newSystemThread("test",function() -- spawns a thread in another lua process
require("multi.all") -- now you can do all of your coding with the multi library! You could even spawn more threads from here with the integration. You would need to require the interaction again though
print("Waiting for variable: NumOfCores")
print("Got it: ",sThread.waitFor("NumOfCores"))
sThread.hold(function()
return GLOBAL["AnotherTest"] -- note this would hold the entire systemthread. Spawn a coroutine thread using multi:newThread() or multi:newThreaded...
end)
print("Holding works!")
multi:newThread("tests",function()
thread.hold(function()
return GLOBAL["FinalTest"] -- note this will not hold the entire systemthread. As seen with the TLoop constantly going!
end)
print("Final test works!")
os.exit()
end)
local a=0
multi:newTLoop(function()
a=a+1
print(a)
end,.5)
multi:mainloop()
end)
multi:mainloop()
```
Update: 1.6.0
-------------
Changed: steps and loops
```lua
-- Was
step:OnStep(function(pos,self) -- same goes for tsteps as well
print(pos)
end)
multi:newLoop(function(dt,self)
print(dt)
end)
-- Is now
step:OnStep(function(self,pos) -- same goes for tsteps as well
print(pos)
end)
multi:newLoop(function(self,dt)
print(dt)
end)
```
Reasoning I wanted to keep objects consistant, but a lot of my older libraries use the old way of doing things. Therefore I added a backwards module
```lua
require("multi.all")
require("multi.compat.backwards[1,5,0]") -- allows for the use of features that were scrapped/changed in 1.6.0+
```
Update: 1.5.0
-------------
Added:
- An easy way to manage timeouts
- Small bug fixes
Update: 1.4.1 - First Public release of the library
-------------
**IMPORTANT:**
Every update I make aims to make things simpler more efficent and just better, but a lot of old code, which can be really big, uses a lot of older features. I know the pain of having to rewrite everything. My promise to my library users is that I will always have backwards support for older features! New ways may exist that are quicker and eaiser, but the old features/methods will be supported.</br>

View File

@ -0,0 +1,73 @@
--[[
LZW String Compression demo for Gideros
This code is MIT licensed, see http://www.opensource.org/licenses/mit-license.php
(C) 2013 - Guava7
]]
CLZWCompression = {}
function CLZWCompression:InitDictionary(isEncode)
self.mDictionary = {}
-- local s = " !#$%&'\"()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
local s={}
for i=1,255 do
s[#s+1]=string.char(i)
end
s=table.concat(s)
local len = #s
for i = 1, len do
if isEncode then
self.mDictionary[s:sub(i, i)] = i
else
self.mDictionary[i] = s:sub(i, i)
end
end
self.mDictionaryLen = len
end
function CLZWCompression:Encode(sInput)
self:InitDictionary(true)
local s = ""
local ch
local len = #sInput
local result = {}
local dic = self.mDictionary
local temp
for i = 1, len do
ch = sInput:sub(i, i)
temp = s..ch
if dic[temp] then
s = temp
else
result[#result + 1] = dic[s]
self.mDictionaryLen = self.mDictionaryLen + 1
dic[temp] = self.mDictionaryLen
s = ch
end
end
result[#result + 1] = dic[s]
return result
end
function CLZWCompression:Decode(data)
self:InitDictionary(false)
local dic = self.mDictionary
local entry
local ch
local prevCode, currCode
local result = {}
prevCode = data[1]
result[#result + 1] = dic[prevCode]
for i = 2, #data do
currCode = data[i]
entry = dic[currCode]
if entry then
ch = entry:sub(1, 1)
result[#result + 1] = entry
else
ch = dic[prevCode]:sub(1, 1)
result[#result + 1] = dic[prevCode]..ch
end
dic[#dic + 1] = dic[prevCode]..ch
prevCode = currCode
end
return table.concat(result)
end
return CLZWCompression

View File

@ -0,0 +1,30 @@
local base64={}
local bs = { [0] =
'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/',
}
local bsd=table.flip(bs)
local char=string.char
function base64.encode(s)
local byte, rep, pad = string.byte, string.rep, 2 - ((#s-1) % 3)
s = (s..rep('\0', pad)):gsub("...", function(cs)
local a, b, c = byte(cs, 1, 3)
return bs[bit.rshift(a,2)] .. bs[bit.bor(bit.lshift(bit.band(a,3),4),bit.rshift(b,4))] .. bs[bit.bor(bit.lshift(bit.band(b,15),2),bit.rshift(c,6))] .. bs[bit.band(c,63)]
end)
return s:sub(1, #s-pad) .. rep('=', pad)
end
function base64.decode(s)
local s=s:match("["..s.."=]+")
local p,cc=s:gsub("=","A")
local r=""
local n=0
s=s:sub(1,#s-#p)..p
for c = 1,#s,4 do
n = bit.lshift(bsd[s:sub(c, c)], 18) + bit.lshift(bsd[s:sub(c+1, c+1)], 12) + bit.lshift(bsd[s:sub(c + 2, c + 2)], 6) + bsd[s:sub(c + 3, c + 3)]
r = r .. char(bit.band(bit.arshift(n, 16), 0xFF)) .. char(bit.band(bit.arshift(n, 8), 0xFF)) .. char(bit.band(n, 0xFF))
end
return r:sub(1,-(cc+1))
end
return base64

View File

@ -0,0 +1,72 @@
local base91={}
local b91enc={[0]=
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '!', '#', '$',
'%', '&', '(', ')', '*', '+', ',', '.', '/', ':', ';', '<', '=',
'>', '?', '@', '[', ']', '^', '_', '`', '{', '|', '}', '~', '"'
}
local b91dec=table.flip(b91enc)
function base91.decode(d)
local l,v,o,b,n = #d,-1,"",0,0
for i in d:gmatch(".") do
local c=b91dec[i]
if not(c) then
-- Continue
else
if v < 0 then
v = c
else
v = v+c*91
b = bit.bor(b, bit.lshift(v,n))
if bit.band(v,8191) then
n = n + 13
else
n = n + 14
end
while true do
o=o..string.char(bit.band(b,255))
b=bit.rshift(b,8)
n=n-8
if not (n>7) then
break
end
end
v=-1
end
end
end
if v + 1>0 then
o=o..string.char(bit.band(bit.bor(b,bit.lshift(v,n)),255))
end
return o
end
function base91.encode(d)
local b,n,o,l=0,0,"",#d
for i in d:gmatch(".") do
b=bit.bor(b,bit.lshift(string.byte(i),n))
n=n+8
if n>13 then
v=bit.band(b,8191)
if v>88 then
b=bit.rshift(b,13)
n=n-13
else
v=bit.band(b,16383)
b=bit.rshift(b,14)
n=n-14
end
o=o..b91enc[v % 91] .. b91enc[math.floor(v / 91)]
end
end
if n>0 then
o=o..b91enc[b % 91]
if n>7 or b>90 then
o=o .. b91enc[math.floor(b / 91)]
end
end
return o
end
return base91

View File

@ -0,0 +1,377 @@
local md5 = {
_VERSION = "md5.lua 1.1.0",
_DESCRIPTION = "MD5 computation in Lua (5.1-3, LuaJIT)",
_URL = "https://github.com/kikito/md5.lua",
_LICENSE = [[
MIT LICENSE
Copyright (c) 2013 Enrique García Cota + Adam Baldwin + hanzao + Equi 4 Software
]]
}
-- bit lib implementions
local char, byte, format, rep, sub =
string.char, string.byte, string.format, string.rep, string.sub
local bit_or, bit_and, bit_not, bit_xor, bit_rshift, bit_lshift
local ok, bit = pcall(require, 'bit')
if ok then
bit_or, bit_and, bit_not, bit_xor, bit_rshift, bit_lshift = bit.bor, bit.band, bit.bnot, bit.bxor, bit.rshift, bit.lshift
else
ok, bit = pcall(require, 'bit32')
if ok then
bit_not = bit.bnot
local tobit = function(n)
return n <= 0x7fffffff and n or -(bit_not(n) + 1)
end
local normalize = function(f)
return function(a,b) return tobit(f(tobit(a), tobit(b))) end
end
bit_or, bit_and, bit_xor = normalize(bit.bor), normalize(bit.band), normalize(bit.bxor)
bit_rshift, bit_lshift = normalize(bit.rshift), normalize(bit.lshift)
else
local function tbl2number(tbl)
local result = 0
local power = 1
for i = 1, #tbl do
result = result + tbl[i] * power
power = power * 2
end
return result
end
local function expand(t1, t2)
local big, small = t1, t2
if(#big < #small) then
big, small = small, big
end
-- expand small
for i = #small + 1, #big do
small[i] = 0
end
end
local to_bits -- needs to be declared before bit_not
bit_not = function(n)
local tbl = to_bits(n)
local size = math.max(#tbl, 32)
for i = 1, size do
if(tbl[i] == 1) then
tbl[i] = 0
else
tbl[i] = 1
end
end
return tbl2number(tbl)
end
-- defined as local above
to_bits = function (n)
if(n < 0) then
-- negative
return to_bits(bit_not(math.abs(n)) + 1)
end
-- to bits table
local tbl = {}
local cnt = 1
local last
while n > 0 do
last = n % 2
tbl[cnt] = last
n = (n-last)/2
cnt = cnt + 1
end
return tbl
end
bit_or = function(m, n)
local tbl_m = to_bits(m)
local tbl_n = to_bits(n)
expand(tbl_m, tbl_n)
local tbl = {}
for i = 1, #tbl_m do
if(tbl_m[i]== 0 and tbl_n[i] == 0) then
tbl[i] = 0
else
tbl[i] = 1
end
end
return tbl2number(tbl)
end
bit_and = function(m, n)
local tbl_m = to_bits(m)
local tbl_n = to_bits(n)
expand(tbl_m, tbl_n)
local tbl = {}
for i = 1, #tbl_m do
if(tbl_m[i]== 0 or tbl_n[i] == 0) then
tbl[i] = 0
else
tbl[i] = 1
end
end
return tbl2number(tbl)
end
bit_xor = function(m, n)
local tbl_m = to_bits(m)
local tbl_n = to_bits(n)
expand(tbl_m, tbl_n)
local tbl = {}
for i = 1, #tbl_m do
if(tbl_m[i] ~= tbl_n[i]) then
tbl[i] = 1
else
tbl[i] = 0
end
end
return tbl2number(tbl)
end
bit_rshift = function(n, bits)
local high_bit = 0
if(n < 0) then
-- negative
n = bit_not(math.abs(n)) + 1
high_bit = 0x80000000
end
local floor = math.floor
for i=1, bits do
n = n/2
n = bit_or(floor(n), high_bit)
end
return floor(n)
end
bit_lshift = function(n, bits)
if(n < 0) then
-- negative
n = bit_not(math.abs(n)) + 1
end
for i=1, bits do
n = n*2
end
return bit_and(n, 0xFFFFFFFF)
end
end
end
-- convert little-endian 32-bit int to a 4-char string
local function lei2str(i)
local f=function (s) return char( bit_and( bit_rshift(i, s), 255)) end
return f(0)..f(8)..f(16)..f(24)
end
-- convert raw string to big-endian int
local function str2bei(s)
local v=0
for i=1, #s do
v = v * 256 + byte(s, i)
end
return v
end
-- convert raw string to little-endian int
local function str2lei(s)
local v=0
for i = #s,1,-1 do
v = v*256 + byte(s, i)
end
return v
end
-- cut up a string in little-endian ints of given size
local function cut_le_str(s,...)
local o, r = 1, {}
local args = {...}
for i=1, #args do
table.insert(r, str2lei(sub(s, o, o + args[i] - 1)))
o = o + args[i]
end
return r
end
local swap = function (w) return str2bei(lei2str(w)) end
-- An MD5 mplementation in Lua, requires bitlib (hacked to use LuaBit from above, ugh)
-- 10/02/2001 jcw@equi4.com
local CONSTS = {
0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391,
0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476
}
local f=function (x,y,z) return bit_or(bit_and(x,y),bit_and(-x-1,z)) end
local g=function (x,y,z) return bit_or(bit_and(x,z),bit_and(y,-z-1)) end
local h=function (x,y,z) return bit_xor(x,bit_xor(y,z)) end
local i=function (x,y,z) return bit_xor(y,bit_or(x,-z-1)) end
local z=function (ff,a,b,c,d,x,s,ac)
a=bit_and(a+ff(b,c,d)+x+ac,0xFFFFFFFF)
-- be *very* careful that left shift does not cause rounding!
return bit_or(bit_lshift(bit_and(a,bit_rshift(0xFFFFFFFF,s)),s),bit_rshift(a,32-s))+b
end
local function transform(A,B,C,D,X)
local a,b,c,d=A,B,C,D
local t=CONSTS
a=z(f,a,b,c,d,X[ 0], 7,t[ 1])
d=z(f,d,a,b,c,X[ 1],12,t[ 2])
c=z(f,c,d,a,b,X[ 2],17,t[ 3])
b=z(f,b,c,d,a,X[ 3],22,t[ 4])
a=z(f,a,b,c,d,X[ 4], 7,t[ 5])
d=z(f,d,a,b,c,X[ 5],12,t[ 6])
c=z(f,c,d,a,b,X[ 6],17,t[ 7])
b=z(f,b,c,d,a,X[ 7],22,t[ 8])
a=z(f,a,b,c,d,X[ 8], 7,t[ 9])
d=z(f,d,a,b,c,X[ 9],12,t[10])
c=z(f,c,d,a,b,X[10],17,t[11])
b=z(f,b,c,d,a,X[11],22,t[12])
a=z(f,a,b,c,d,X[12], 7,t[13])
d=z(f,d,a,b,c,X[13],12,t[14])
c=z(f,c,d,a,b,X[14],17,t[15])
b=z(f,b,c,d,a,X[15],22,t[16])
a=z(g,a,b,c,d,X[ 1], 5,t[17])
d=z(g,d,a,b,c,X[ 6], 9,t[18])
c=z(g,c,d,a,b,X[11],14,t[19])
b=z(g,b,c,d,a,X[ 0],20,t[20])
a=z(g,a,b,c,d,X[ 5], 5,t[21])
d=z(g,d,a,b,c,X[10], 9,t[22])
c=z(g,c,d,a,b,X[15],14,t[23])
b=z(g,b,c,d,a,X[ 4],20,t[24])
a=z(g,a,b,c,d,X[ 9], 5,t[25])
d=z(g,d,a,b,c,X[14], 9,t[26])
c=z(g,c,d,a,b,X[ 3],14,t[27])
b=z(g,b,c,d,a,X[ 8],20,t[28])
a=z(g,a,b,c,d,X[13], 5,t[29])
d=z(g,d,a,b,c,X[ 2], 9,t[30])
c=z(g,c,d,a,b,X[ 7],14,t[31])
b=z(g,b,c,d,a,X[12],20,t[32])
a=z(h,a,b,c,d,X[ 5], 4,t[33])
d=z(h,d,a,b,c,X[ 8],11,t[34])
c=z(h,c,d,a,b,X[11],16,t[35])
b=z(h,b,c,d,a,X[14],23,t[36])
a=z(h,a,b,c,d,X[ 1], 4,t[37])
d=z(h,d,a,b,c,X[ 4],11,t[38])
c=z(h,c,d,a,b,X[ 7],16,t[39])
b=z(h,b,c,d,a,X[10],23,t[40])
a=z(h,a,b,c,d,X[13], 4,t[41])
d=z(h,d,a,b,c,X[ 0],11,t[42])
c=z(h,c,d,a,b,X[ 3],16,t[43])
b=z(h,b,c,d,a,X[ 6],23,t[44])
a=z(h,a,b,c,d,X[ 9], 4,t[45])
d=z(h,d,a,b,c,X[12],11,t[46])
c=z(h,c,d,a,b,X[15],16,t[47])
b=z(h,b,c,d,a,X[ 2],23,t[48])
a=z(i,a,b,c,d,X[ 0], 6,t[49])
d=z(i,d,a,b,c,X[ 7],10,t[50])
c=z(i,c,d,a,b,X[14],15,t[51])
b=z(i,b,c,d,a,X[ 5],21,t[52])
a=z(i,a,b,c,d,X[12], 6,t[53])
d=z(i,d,a,b,c,X[ 3],10,t[54])
c=z(i,c,d,a,b,X[10],15,t[55])
b=z(i,b,c,d,a,X[ 1],21,t[56])
a=z(i,a,b,c,d,X[ 8], 6,t[57])
d=z(i,d,a,b,c,X[15],10,t[58])
c=z(i,c,d,a,b,X[ 6],15,t[59])
b=z(i,b,c,d,a,X[13],21,t[60])
a=z(i,a,b,c,d,X[ 4], 6,t[61])
d=z(i,d,a,b,c,X[11],10,t[62])
c=z(i,c,d,a,b,X[ 2],15,t[63])
b=z(i,b,c,d,a,X[ 9],21,t[64])
return bit_and(A+a,0xFFFFFFFF),bit_and(B+b,0xFFFFFFFF),
bit_and(C+c,0xFFFFFFFF),bit_and(D+d,0xFFFFFFFF)
end
----------------------------------------------------------------
local function md5_update(self, s)
self.pos = self.pos + #s
s = self.buf .. s
for ii = 1, #s - 63, 64 do
local X = cut_le_str(sub(s,ii,ii+63),4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4)
assert(#X == 16)
X[0] = table.remove(X,1) -- zero based!
self.a,self.b,self.c,self.d = transform(self.a,self.b,self.c,self.d,X)
end
self.buf = sub(s, math.floor(#s/64)*64 + 1, #s)
return self
end
local function md5_finish(self)
local msgLen = self.pos
local padLen = 56 - msgLen % 64
if msgLen % 64 > 56 then padLen = padLen + 64 end
if padLen == 0 then padLen = 64 end
local s = char(128) .. rep(char(0),padLen-1) .. lei2str(bit_and(8*msgLen, 0xFFFFFFFF)) .. lei2str(math.floor(msgLen/0x20000000))
md5_update(self, s)
assert(self.pos % 64 == 0)
return lei2str(self.a) .. lei2str(self.b) .. lei2str(self.c) .. lei2str(self.d)
end
----------------------------------------------------------------
function md5.new()
return { a = CONSTS[65], b = CONSTS[66], c = CONSTS[67], d = CONSTS[68],
pos = 0,
buf = '',
update = md5_update,
finish = md5_finish }
end
function md5.tohex(s)
return format("%08x%08x%08x%08x", str2bei(sub(s, 1, 4)), str2bei(sub(s, 5, 8)), str2bei(sub(s, 9, 12)), str2bei(sub(s, 13, 16)))
end
function md5.sum(s)
return md5.new():update(s):finish()
end
function md5.sumhexa(s)
return md5.tohex(md5.sum(s))
end
return md5

View File

@ -0,0 +1,739 @@
bin={}
bin.Version={5,1,0}
bin.stage='stable'
bin.data=''
bin.t='bin'
bin.__index = bin
bin.__tostring=function(self) return self:getData() end
bin.__len=function(self) return self:getlength() end
bin.lastBlockSize=0
bin.streams={}
-- Helpers
function bin.getVersion()
return bin.Version[1]..'.'..bin.Version[2]..'.'..bin.Version[3]
end
require("bin.support.utils")
if jit then
bit=require("bit")
elseif bit32 then
bit=bit32
else
bit=require("bin.numbers.no_jit_bit")
end
base64=require("bin.converters.base64")
base91=require("bin.converters.base91")
bin.lzw=require("bin.compressors.lzw") -- A WIP
bits=require("bin.numbers.bits")
infinabits=require("bin.numbers.infinabits") -- like the bits library but works past 32 bits for 32bit lua and 64 bits for 64 bit lua.
bin.md5=require("bin.hashes.md5")
randomGen=require("bin.numbers.random")
function bin.setBitsInterface(int)
bin.defualtBit=int or bits
end
bin.setBitsInterface()
function bin.normalizeData(data) -- unified function to allow for all types to string
if type(data)=="string" then return data end
if type(data)=="table" then
if data.Type=="bin" or data.Type=="streamable" or data.Type=="buffer" then
return data:getData()
elseif data.Type=="bits" or data.Type=="infinabits" then
return data:toSbytes()
elseif data.Type=="sink" then
-- LATER
else
return ""
end
elseif type(data)=="userdata" then
if tostring(data):sub(1,4)=="file" then
local cur=data:seek("cur")
data:seek("set",0)
local dat=data:read("*a")
data:seek("set",cur)
return dat
else
error("File handles are the only userdata that can be used!")
end
end
end
function bin.resolveType(tab) -- used in getblock for auto object creation. Internal method
if tab.Type then
if tab.Type=="bin" then
return bin.new(tab.data)
elseif tab.Type=="streamable" then
if bin.fileExist(tab.file) then return nil,"Cannot load the stream file, source file does not exist!" end
return bin.stream(tab.file,tab.lock)
elseif tab.Type=="buffer" then
local buf=bin.newDataBuffer(tab.size)
buf[1]=tab:getData()
return buf
elseif tab.Type=="bits" then
local b=bits.new("")
b.data=tab.data
return b
elseif tab.Type=="infinabits" then
local b=infinabits.new("")
b.data=tab.data
return b
elseif tab.Type=="sink" then
return bin.newSync(tab.data)
else -- maybe a type from another library
return tab
end
else return tab end
end
function bin.fileExist(path)
g=io.open(path or '','r')
if path =='' then
p='empty path'
return nil
end
if g~=nil and true or false then
p=(g~=nil and true or false)
end
if g~=nil then
io.close(g)
else
return false
end
return p
end
function bin.toHex(str)
local str=bin.normalizeData(str)
return (str:gsub('.', function (c)
return string.format('%02X', string.byte(c))
end))
end
function bin.fromHex(str)
return (str:gsub('..', function (cc)
return string.char(tonumber(cc, 16))
end))
end
function bin.toBase64(s)
return base64.encode(s)
end
function bin.fromBase64(s)
return base64.decode(s)
end
function bin.toBase91(s)
return base91.encode(s)
end
function bin.fromBase91(s)
return base91.decode(s)
end
-- Constructors
function bin.new(data)
data=bin.normalizeData(data)
local c = {}
setmetatable(c, bin)
c.data=data
c.Type="bin"
c.t="bin"
c.pos=1
c.stream=false
return c
end
function bin.newFromBase64(data)
return bin.new(bin.fromBase64(data))
end
function bin.newFromBase91(data)
return bin.new(bin.fromBase91(data))
end
function bin.newFromHex(data)
return bin.new(bin.fromHex(data))
end
function bin.load(path)
if type(path) ~= "string" then error("Path must be a string!") end
local f = io.open(path, 'rb')
local content = f:read('*a')
f:close()
return bin.new(content)
end
function bin.stream(file,l)
if not(l==false) then l=true end
local c=bin.new()
c.Type="streamable"
c.t="streamable"
if bin.streams[file]~=nil then
c.file=file
c.lock = l
c.workingfile=bin.streams[file][1].workingfile
bin.streams[file][2]=bin.streams[file][2]+1
c.stream=true
return c
end
if bin.fileExist(file) then
c.file=file
c.lock = l
c.workingfile=io.open(file,'rb+')
else
c.file=file
c.lock = l
c.workingfile=io.open(file,'w')
io.close(c.workingfile)
c.workingfile=io.open(file,'rb+')
end
c.stream=true
bin.streams[file]={c,1}
return c
end
function bin.newTempFile()
local c=bin.new()
c.file=file
c.lock = false
c.workingfile=io.tmpfile()
c.stream=true
return c
end
function bin.freshStream(file)
bin.new():tofile(file)
return bin.stream(file,false)
end
function bin.newStreamFileObject(file)
local c=bin.new()
c.Type="streamable"
c.t="streamable"
c.file="FILE_OBJECT"
c.lock = false
c.workingfile=file
c.stream=true
return c
end
-- Core Methods
function bin:canStreamWrite()
return (self.stream and not(self.lock))
end
function bin:getSeek()
if self.stream then
return self.workingfile:seek("cur")+1
else
return self.pos
end
end
function bin:setSeek(n)
if self.stream then
self.workingfile:seek("set",n-1)
else
self.pos=n
end
end
function bin:seek(n)
if self.stream then
if not n then return self.workingfile:seek("cur") end
local cur=self.workingfile:seek("cur")
self.workingfile:seek("set",cur+n)
else
if not n then return self.pos end
if #self.data-(self.pos-1)<n then
print(n-((#self.data)-(self.pos-1)))
self:write(string.rep("\0",n-((#self.data)-(self.pos-1))))
return
end
self.pos=self.pos+n
end
end
function bin:read(n)
if self.stream then
return self.workingfile:read(n)
else
local data=self.data:sub(self.pos,self.pos+n-1)
self.pos=self.pos+n
if data=="" then return end
return data
end
end
function bin:write(data,size)
local data=bin.normalizeData(data)
local dsize=#data
local size=tonumber(size or dsize)
if dsize>size then
data = data:sub(1,size)
elseif dsize<size then
data=data..string.rep("\0",size-dsize)
end
if self:canStreamWrite() then
self.workingfile:write(data)
elseif self.Type=="bin" then
local tab={}
if self.pos==1 then
tab={data,self.data:sub(self.pos+size)}
else
tab={self.data:sub(1,self.pos-1),data,self.data:sub(self.pos+size)}
end
self.pos=self.pos+size
self.data=table.concat(tab)
else
error("Attempted to write to a locked file!")
end
end
function bin:sub(a,b)
local data=""
if self.stream then
local cur=self.workingfile:seek("cur")
self.workingfile:seek("set",a-1)
data=self.workingfile:read(b-(a-1))
self.workingfile:seek("set",cur)
else
data=self.data:sub(a,b)
end
return data
end
function bin:slide(n)
local s=self:getSize()
local buf=bin.newDataBuffer(s)
buf:fillBuffer(1,self:getData())
for i=1,s do
nn=buf[i]+n
if nn>255 then
nn=nn%256
elseif nn<0 then
nn=256-math.abs(nn)
end
buf[i]=nn
end
self:setSeek(1)
self:write(buf:getData())
end
function bin:getData(a,b,fmt)
local data=""
if a or b then
data=self:sub(a,b)
else
if self.stream then
local cur=self.workingfile:seek("cur")
self.workingfile:seek("set",0)
data=self.workingfile:read("*a")
self.workingfile:seek("set",cur)
else
data=self.data
end
end
if fmt=="%x" or fmt=="hex" then
return bin.toHex(data):lower()
elseif fmt=="%X" or fmt=="HEX" then
return bin.toHex(data)
elseif fmt=="%b" or fmt=="b64" then
return bin.toB64(data)
elseif fmt then
return bin.new(data):getBlock(fmt,#data)
end
return data
end
function bin:getSize(fmt)
local len=0
if self.stream then
local cur=self.workingfile:seek("cur")
len=self.workingfile:seek("end")
self.workingfile:seek("set",cur)
else
len=#self.data
end
if fmt=="%b" then
return bin.toB64()
elseif fmt then
return string.format(fmt, len)
else
return len
end
end
function bin:tackE(data,size,h)
local data=bin.normalizeData(data)
local cur=self:getSize()
self:setSeek(self:getSize()+1)
self:write(data,size)
if h then
self:setSeek(cur+1)
end
end
function bin:tonumber(a,b)
local temp={}
if a then
temp.data=self:sub(a,b)
else
temp=self
end
local l,r=0,0
local g=#temp.data
for i=1,g do
r=r+(256^(g-i))*string.byte(string.sub(temp.data,i,i))
l=l+(256^(i-1))*string.byte(string.sub(temp.data,i,i))
end
return r,l
end
function bin.endianflop(data)
return string.reverse(data)
end
function bin:tofile(name)
if self.stream then return end
if not name then error("Must include a filename to save as!") end
file = io.open(name, "wb")
file:write(self.data)
file:close()
end
function bin:close()
if self.stream then
if bin.streams[self.file][2]==1 then
bin.streams[self.file]=nil
self.workingfile:close()
else
bin.streams[self.file][2]=bin.streams[self.file][2]-1
self.workingfile=io.tmpfile()
self.workingfile:close()
end
end
end
function bin:getBlock(t,n)
local data=""
if not n then
if bin.registerBlocks[t] then
return bin.registerBlocks[t][1](nil,self)
else
error("Unknown format! Cannot read from file: "..tostring(t))
end
else
if t=="n" or t=="%e" or t=="%E" then
data=self:read(n)
local numB=bin.defualtBit.new(data)
local numL=bin.defualtBit.new(string.reverse(data))
local little=numL:tonumber(0)
local big=numB:tonumber(0)
if t=="%E" then
return big
elseif t=="%X" then
return bin.toHex(data):upper()
elseif t=="%x" then
return bin.toHex(data):lower()
elseif t=="%b" then
return bin.toB64(data)
elseif t=="%e" then
return little
end
return big,little
elseif t=="s" then
return self:read(n)
elseif bin.registerBlocks[t] then
return bin.registerBlocks[t][1](n,self)
else
error("Unknown format! Cannot read from file: "..tostring(t))
end
end
end
function bin:addBlock(d,fit,fmt)
if not fmt then fmt=type(d):sub(1,1) end
if bin.registerBlocks[fmt] then
self:tackE(bin.registerBlocks[fmt][2](d,fit,fmt,self,bin.registerBlocks[fmt][2]))
elseif type(d)=="number" then
local data=bin.defualtBit.numToBytes(d,fit or 4,fmt,function()
error("Overflow! Space allotted for number is smaller than the number takes up. Increase the fit!")
end)
self:tackE(data)
elseif type(d)=="string" then
local data=d:sub(1,fit or -1)
if #data<(fit or #data) then
data=data..string.rep("\0",fit-#data)
end
self:tackE(data)
end
end
bin.registerBlocks={}
function bin.registerBlock(t,funcG,funcA)
bin.registerBlocks[t]={funcG,funcA}
end
function bin.newDataBuffer(size,fill) -- fills with \0 or nul or with what you enter
local c={}
local fill=fill or "\0"
c.data={self=c}
c.Type="buffer"
c.size=size or 0 -- 0 means an infinite buffer, sometimes useful
for i=1,c.size do
c.data[i]=fill
end
local mt={
__index=function(t,k)
if type(k)=="number" then
local data=t.data[k]
if data then
return string.byte(data)
else
error("Index out of range!")
end
elseif type(k)=="string" then
local num=tonumber(k)
if num then
local data=t.data[num]
if data then
return data
else
error("Index out of range!")
end
else
error("Only number-strings and numbers can be indexed!")
end
else
error("Only number-strings and numbers can be indexed!")
end
end,
__newindex=function(t,k,v)
if type(k)~="number" then error("Can only set a buffers data with a numeric index!") end
local data=""
if type(v)=="string" then
data=v
elseif type(v)=="number" then
data=string.char(v)
else
-- try to normalize the data of type v
data=bin.normalizeData(v)
end
t:fillBuffer(k,data)
end,
__tostring=function(t)
return t:getData()
end,
}
function c:fillBuffer(a,data)
local len=#data
if len==1 then
self.data[a]=data
else
local i=a-1
for d in data:gmatch(".") do
i=i+1
if i>c.size then
return #data-i+a
end
self.data[i]=d
end
return #data-i+(a-1)
end
end
function c:getData(a,b,fmt) -- LATER
local dat=bin.new(table.concat(self.data,"",a,b))
local n=dat:getSize()
return dat:getBlock(fmt or "s",n)
end
function c:getSize()
return #self:getData()
end
setmetatable(c,mt)
return c
end
function bin:newDataBufferFromStream(pos,size,fill) -- fills with \0 or nul or with what you enter IF the nothing exists inside the bin file.
local s=self:getSize()
if not self.stream then error("Can only created a streamed buffer on a streamable file!") end
if s==0 then
self:write(string.rep("\0",pos+size))
end
self:setSeek(1)
local c=bin.newDataBuffer(size,fill)
rawset(c,"pos",pos)
rawset(c,"size",size)
rawset(c,"fill",fill)
rawset(c,"bin",self)
rawset(c,"sync",function(self)
local cur=self.bin:getSeek()
self.bin:setSeek(self.pos)
self.bin:write(self:getData(),size)
self.bin:setSeek(cur)
end)
c:fillBuffer(1,self:sub(pos,pos+size))
function c:fillBuffer(a,data)
local len=#data
if len==1 then
self.data[a]=data
self:sync()
else
local i=a-1
for d in data:gmatch(".") do
i=i+1
if i>c.size then
self:sync()
return #data-i+a
end
self.data[i]=d
end
self:sync()
return #data-i+(a-1)
end
end
return c
end
function bin:toDataBuffer()
local s=self:getSize()
-- if self:canStreamWrite() then
-- return self:newDataBufferFromStream(0,s)
-- end
local buf=bin.newDataBuffer(s)
local data=self:read(512)
local i=1
while data~=nil do
buf[i]=data
data=self:read(512)
i=i+512
end
return buf
end
function bin:getMD5Hash()
self:setSeek(1)
local len=self:getSize()
local md5=bin.md5.new()
local SIZE=2048
if len>SIZE then
local dat=self:read(SIZE)
while dat~=nil do
md5:update(dat)
dat=self:read(SIZE)
end
return bin.md5.tohex(md5:finish()):upper()
else
return bin.md5.sumhexa(self:getData()):upper()
end
end
function bin:getHash()
if self:getSize()==0 then
return "NaN"
end
n=32
local rand = randomGen:newND(1,self:getSize(),self:getSize())
local h,g={},0
for i=1,n do
g=rand:nextInt()
table.insert(h,bin.toHex(self:sub(g,g)))
end
return table.concat(h,'')
end
function bin:flipbits()
if self:canStreamWrite() then
self:setSeek(1)
for i=1,self:getSize() do
self:write(string.char(255-string.byte(self:sub(i,i))))
end
else
local temp={}
for i=1,#self.data do
table.insert(temp,string.char(255-string.byte(string.sub(self.data,i,i))))
end
self.data=table.concat(temp,'')
end
end
function bin:encrypt()
self:flipbits()
end
function bin:decrypt()
self:flipbits()
end
-- Use with small files!
function bin:gsub(...)
local data=self:getData()
local pos=self:getSeek()
self:setSeek(1)
self:write((data:gsub(...)) or data)
self:setSeek(loc)
end
function bin:gmatch(pat)
return self:getData():gmatch(pat)
end
function bin:match(pat)
return self:getData():match(pat)
end
function bin:trim()
local data=self:getData()
local pos=self:getSeek()
self:setSeek(1)
self:write(data:match'^()%s*$' and '' or data:match'^%s*(.*%S)')
self:setSeek(loc)
end
function bin:lines()
local t = {}
local function helper(line) table.insert(t, line) return '' end
helper((self:getData():gsub('(.-)\r?\n', helper)))
return t
end
function bin._lines(str)
local t = {}
local function helper(line) table.insert(t, line) return '' end
helper((str:gsub('(.-)\r?\n', helper)))
return t
end
function bin:wipe()
if self:canStreamWrite() then
self:close()
local c=bin.freshStream(self.file)
self.workingfile=c.workingfile
else
self.data=""
end
self:setSeek(1)
end
function bin:fullTrim(empty)
local t=self:lines()
for i=#t,1,-1 do
t[i]=bin._trim(t[i])
if empty then
if t[i]=="" then
table.remove(t,i)
end
end
end
self:wipe()
self:write(table.concat(t,"\n"))
end
require("bin.support.extraBlocks") -- registered blocks that you can use
if love then
function bin.load(file,s,r)
content, size = love.filesystem.read(file)
local temp=bin.new(content)
temp.filepath=file
return temp
end
function bin:tofile(filename)
if not(filename) or self.Stream then return nil end
love.filesystem.write(filename,self.data)
end
function bin.stream(file)
return bin.newStreamFileObject(love.filesystem.newFile(file))
end
function bin:getSize(fmt)
local len=0
if self.stream then
local len=self.workingfile:getSize()
else
len=#self.data
end
if fmt=="%b" then
return bin.toB64()
elseif fmt then
return string.format(fmt, len)
else
return len
end
end
function bin:getSeek()
if self.stream then
return self.workingfile:tell()+1
else
return self.pos
end
end
function bin:setSeek(n)
if self.stream then
self.workingfile:seek(n-1)
else
self.pos=n
end
end
function bin:seek(n)
if self.stream then
self.workingfile:seek(n)
else
if not n then return self.pos end
if #self.data-(self.pos-1)<n then
print(n-((#self.data)-(self.pos-1)))
self:write(string.rep("\0",n-((#self.data)-(self.pos-1))))
return
end
self.pos=self.pos+n
end
end
function bin:close()
self.workingfile:close()
end
end

View File

@ -0,0 +1,683 @@
RADIX = 10^7 ;
RADIX_LEN = math.floor( math.log10 ( RADIX ) ) ;
BigNum = {} ;
BigNum.mt = {} ;
function BigNum.new( num ) --{{{2
local bignum = {} ;
setmetatable( bignum , BigNum.mt ) ;
BigNum.change( bignum , num ) ;
return bignum ;
end
function BigNum.mt.sub( num1 , num2 )
local temp = BigNum.new() ;
local bnum1 = BigNum.new( num1 ) ;
local bnum2 = BigNum.new( num2 ) ;
BigNum.sub( bnum1 , bnum2 , temp ) ;
return temp ;
end
function BigNum.mt.mod( num1 , num2 )
return BigNum.new(num1 - (num1/num2)*num2)
end
function BigNum.mt.add( num1 , num2 )
local temp = BigNum.new() ;
local bnum1 = BigNum.new( num1 ) ;
local bnum2 = BigNum.new( num2 ) ;
BigNum.add( bnum1 , bnum2 , temp ) ;
return temp ;
end
function BigNum.mt.mul( num1 , num2 )
local temp = BigNum.new() ;
local bnum1 = BigNum.new( num1 ) ;
local bnum2 = BigNum.new( num2 ) ;
BigNum.mul( bnum1 , bnum2 , temp ) ;
return temp ;
end
function BigNum.mt.div( num1 , num2 )
local bnum1 = {} ;
local bnum2 = {} ;
local bnum3 = BigNum.new() ;
local bnum4 = BigNum.new() ;
bnum1 = BigNum.new( num1 ) ;
bnum2 = BigNum.new( num2 ) ;
BigNum.div( bnum1 , bnum2 , bnum3 , bnum4 ) ;
return bnum3 , bnum4 ;
end
function BigNum.mt.tostring( bnum )
local i = 0 ;
local j = 0 ;
local str = "" ;
local temp = "" ;
if bnum == nil then
return "nil" ;
elseif bnum.len > 0 then
for i = bnum.len - 2 , 0 , -1 do
for j = 0 , RADIX_LEN - string.len( bnum[i] ) - 1 do
temp = temp .. '0' ;
end
temp = temp .. bnum[i] ;
end
if bnum[bnum.len - 1]==nil then
return "nil"
end
temp = bnum[bnum.len - 1] .. temp ;
if bnum.signal == '-' then
temp = bnum.signal .. temp ;
end
return temp ;
else
return "" ;
end
end
function BigNum.mt.pow( num1 , num2 )
local bnum1 = BigNum.new( num1 ) ;
local bnum2 = BigNum.new( num2 ) ;
return BigNum.pow( bnum1 , bnum2 ) ;
end
function BigNum.mt.eq( num1 , num2 )
local bnum1 = BigNum.new( num1 ) ;
local bnum2 = BigNum.new( num2 ) ;
return BigNum.eq( bnum1 , bnum2 ) ;
end
function BigNum.mt.lt( num1 , num2 )
local bnum1 = BigNum.new( num1 ) ;
local bnum2 = BigNum.new( num2 ) ;
return BigNum.lt( bnum1 , bnum2 ) ;
end
function BigNum.mt.le( num1 , num2 )
local bnum1 = BigNum.new( num1 ) ;
local bnum2 = BigNum.new( num2 ) ;
return BigNum.le( bnum1 , bnum2 ) ;
end
function BigNum.mt.unm( num )
local ret = BigNum.new( num )
if ret.signal == '+' then
ret.signal = '-'
else
ret.signal = '+'
end
return ret
end
BigNum.mt.__metatable = "hidden"
BigNum.mt.__tostring = BigNum.mt.tostring ;
BigNum.mt.__add = BigNum.mt.add ;
BigNum.mt.__sub = BigNum.mt.sub ;
BigNum.mt.__mul = BigNum.mt.mul ;
BigNum.mt.__div = BigNum.mt.div ;
BigNum.mt.__pow = BigNum.mt.pow ;
BigNum.mt.__unm = BigNum.mt.unm ;
BigNum.mt.__mod = BigNum.mt.mod ;
BigNum.mt.__eq = BigNum.mt.eq ;
BigNum.mt.__le = BigNum.mt.le ;
BigNum.mt.__lt = BigNum.mt.lt ;
setmetatable( BigNum.mt, { __index = "inexistent field", __newindex = "not available", __metatable="hidden" } ) ;
function BigNum.add( bnum1 , bnum2 , bnum3 )
local maxlen = 0 ;
local i = 0 ;
local carry = 0 ;
local signal = '+' ;
local old_len = 0 ;
--Handle the signals
if bnum1 == nil or bnum2 == nil or bnum3 == nil then
error("Function BigNum.add: parameter nil") ;
elseif bnum1.signal == '-' and bnum2.signal == '+' then
bnum1.signal = '+' ;
BigNum.sub( bnum2 , bnum1 , bnum3 ) ;
if not rawequal(bnum1, bnum3) then
bnum1.signal = '-' ;
end
return 0 ;
elseif bnum1.signal == '+' and bnum2.signal == '-' then
bnum2.signal = '+' ;
BigNum.sub( bnum1 , bnum2 , bnum3 ) ;
if not rawequal(bnum2, bnum3) then
bnum2.signal = '-' ;
end
return 0 ;
elseif bnum1.signal == '-' and bnum2.signal == '-' then
signal = '-' ;
end
--
old_len = bnum3.len ;
if bnum1.len > bnum2.len then
maxlen = bnum1.len ;
else
maxlen = bnum2.len ;
bnum1 , bnum2 = bnum2 , bnum1 ;
end
--School grade sum
for i = 0 , maxlen - 1 do
if bnum2[i] ~= nil then
bnum3[i] = bnum1[i] + bnum2[i] + carry ;
else
bnum3[i] = bnum1[i] + carry ;
end
if bnum3[i] >= RADIX then
bnum3[i] = bnum3[i] - RADIX ;
carry = 1 ;
else
carry = 0 ;
end
end
--Update the answer's size
if carry == 1 then
bnum3[maxlen] = 1 ;
end
bnum3.len = maxlen + carry ;
bnum3.signal = signal ;
for i = bnum3.len, old_len do
bnum3[i] = nil ;
end
return 0 ;
end
function BigNum.sub( bnum1 , bnum2 , bnum3 )
local maxlen = 0 ;
local i = 0 ;
local carry = 0 ;
local old_len = 0 ;
if bnum1 == nil or bnum2 == nil or bnum3 == nil then
error("Function BigNum.sub: parameter nil") ;
elseif bnum1.signal == '-' and bnum2.signal == '+' then
bnum1.signal = '+' ;
BigNum.add( bnum1 , bnum2 , bnum3 ) ;
bnum3.signal = '-' ;
if not rawequal(bnum1, bnum3) then
bnum1.signal = '-' ;
end
return 0 ;
elseif bnum1.signal == '-' and bnum2.signal == '-' then
bnum1.signal = '+' ;
bnum2.signal = '+' ;
BigNum.sub( bnum2, bnum1 , bnum3 ) ;
if not rawequal(bnum1, bnum3) then
bnum1.signal = '-' ;
end
if not rawequal(bnum2, bnum3) then
bnum2.signal = '-' ;
end
return 0 ;
elseif bnum1.signal == '+' and bnum2.signal == '-' then
bnum2.signal = '+' ;
BigNum.add( bnum1 , bnum2 , bnum3 ) ;
if not rawequal(bnum2, bnum3) then
bnum2.signal = '-' ;
end
return 0 ;
end
--Tests if bnum2 > bnum1
if BigNum.compareAbs( bnum1 , bnum2 ) == 2 then
BigNum.sub( bnum2 , bnum1 , bnum3 ) ;
bnum3.signal = '-' ;
return 0 ;
else
maxlen = bnum1.len ;
end
old_len = bnum3.len ;
bnum3.len = 0 ;
--School grade subtraction
for i = 0 , maxlen - 1 do
if bnum2[i] ~= nil then
bnum3[i] = bnum1[i] - bnum2[i] - carry ;
else
bnum3[i] = bnum1[i] - carry ;
end
if bnum3[i] < 0 then
bnum3[i] = RADIX + bnum3[i] ;
carry = 1 ;
else
carry = 0 ;
end
if bnum3[i] ~= 0 then
bnum3.len = i + 1 ;
end
end
bnum3.signal = '+' ;
--Check if answer's size if zero
if bnum3.len == 0 then
bnum3.len = 1 ;
bnum3[0] = 0 ;
end
if carry == 1 then
error( "Error in function sub" ) ;
end
for i = bnum3.len , max( old_len , maxlen - 1 ) do
bnum3[i] = nil ;
end
return 0 ;
end
function BigNum.mul( bnum1 , bnum2 , bnum3 )
local i = 0 ; j = 0 ;
local temp = BigNum.new( ) ;
local temp2 = 0 ;
local carry = 0 ;
local oldLen = bnum3.len ;
if bnum1 == nil or bnum2 == nil or bnum3 == nil then
error("Function BigNum.mul: parameter nil") ;
--Handle the signals
elseif bnum1.signal ~= bnum2.signal then
BigNum.mul( bnum1 , -bnum2 , bnum3 ) ;
bnum3.signal = '-' ;
return 0 ;
end
bnum3.len = ( bnum1.len ) + ( bnum2.len ) ;
--Fill with zeros
for i = 1 , bnum3.len do
bnum3[i - 1] = 0 ;
end
--Places nil where passes through this
for i = bnum3.len , oldLen do
bnum3[i] = nil ;
end
--School grade multiplication
for i = 0 , bnum1.len - 1 do
for j = 0 , bnum2.len - 1 do
carry = ( bnum1[i] * bnum2[j] + carry ) ;
carry = carry + bnum3[i + j] ;
bnum3[i + j] = ( carry % RADIX ) ;
temp2 = bnum3[i + j] ;
carry = math.floor ( carry / RADIX ) ;
end
if carry ~= 0 then
bnum3[i + bnum2.len] = carry ;
end
carry = 0 ;
end
--Update the answer's size
for i = bnum3.len - 1 , 1 , -1 do
if bnum3[i] ~= nil and bnum3[i] ~= 0 then
break ;
else
bnum3[i] = nil ;
end
bnum3.len = bnum3.len - 1 ;
end
return 0 ;
end
function BigNum.div( bnum1 , bnum2 , bnum3 , bnum4 )
local temp = BigNum.new() ;
local temp2 = BigNum.new() ;
local one = BigNum.new( "1" ) ;
local zero = BigNum.new( "0" ) ;
--Check division by zero
if BigNum.compareAbs( bnum2 , zero ) == 0 then
error( "Function BigNum.div: Division by zero" ) ;
end
--Handle the signals
if bnum1 == nil or bnum2 == nil or bnum3 == nil or bnum4 == nil then
error( "Function BigNum.div: parameter nil" ) ;
elseif bnum1.signal == "+" and bnum2.signal == "-" then
bnum2.signal = "+" ;
BigNum.div( bnum1 , bnum2 , bnum3 , bnum4 ) ;
bnum2.signal = "-" ;
bnum3.signal = "-" ;
return 0 ;
elseif bnum1.signal == "-" and bnum2.signal == "+" then
bnum1.signal = "+" ;
BigNum.div( bnum1 , bnum2 , bnum3 , bnum4 ) ;
bnum1.signal = "-" ;
if bnum4 < zero then --Check if remainder is negative
BigNum.add( bnum3 , one , bnum3 ) ;
BigNum.sub( bnum2 , bnum4 , bnum4 ) ;
end
bnum3.signal = "-" ;
return 0 ;
elseif bnum1.signal == "-" and bnum2.signal == "-" then
bnum1.signal = "+" ;
bnum2.signal = "+" ;
BigNum.div( bnum1 , bnum2 , bnum3 , bnum4 ) ;
bnum1.signal = "-" ;
if bnum4 < zero then --Check if remainder is negative
BigNum.add( bnum3 , one , bnum3 ) ;
BigNum.sub( bnum2 , bnum4 , bnum4 ) ;
end
bnum2.signal = "-" ;
return 0 ;
end
temp.len = bnum1.len - bnum2.len - 1 ;
--Reset variables
BigNum.change( bnum3 , "0" ) ;
BigNum.change( bnum4 , "0" ) ;
BigNum.copy( bnum1 , bnum4 ) ;
--Check if can continue dividing
while( BigNum.compareAbs( bnum4 , bnum2 ) ~= 2 ) do
if bnum4[bnum4.len - 1] >= bnum2[bnum2.len - 1] then
BigNum.put( temp , math.floor( bnum4[bnum4.len - 1] / bnum2[bnum2.len - 1] ) , bnum4.len - bnum2.len ) ;
temp.len = bnum4.len - bnum2.len + 1 ;
else
BigNum.put( temp , math.floor( ( bnum4[bnum4.len - 1] * RADIX + bnum4[bnum4.len - 2] ) / bnum2[bnum2.len -1] ) , bnum4.len - bnum2.len - 1 ) ;
temp.len = bnum4.len - bnum2.len ;
end
if bnum4.signal ~= bnum2.signal then
temp.signal = "-";
else
temp.signal = "+";
end
BigNum.add( temp , bnum3 , bnum3 ) ;
temp = temp * bnum2 ;
BigNum.sub( bnum4 , temp , bnum4 ) ;
end
--Update if the remainder is negative
if bnum4.signal == '-' then
decr( bnum3 ) ;
BigNum.add( bnum2 , bnum4 , bnum4 ) ;
end
return 0 ;
end
function BigNum.pow( bnum1 , bnum2 )
local n = BigNum.new( bnum2 ) ;
local y = BigNum.new( 1 ) ;
local z = BigNum.new( bnum1 ) ;
local zero = BigNum.new( "0" ) ;
if bnum2 < zero then
error( "Function BigNum.exp: domain error" ) ;
elseif bnum2 == zero then
return y ;
end
while 1 do
if ( n[0] % 2 ) == 0 then
n = n / 2 ;
else
n = n / 2 ;
y = z * y ;
if n == zero then
return y ;
end
end
z = z * z ;
end
end
-- Português :
BigNum.exp = BigNum.pow
function BigNum.gcd( bnum1 , bnum2 )
local a = {} ;
local b = {} ;
local c = {} ;
local d = {} ;
local zero = {} ;
zero = BigNum.new( "0" ) ;
if bnum1 == zero or bnum2 == zero then
return BigNum.new( "1" ) ;
end
a = BigNum.new( bnum1 ) ;
b = BigNum.new( bnum2 ) ;
a.signal = '+' ;
b.signal = '+' ;
c = BigNum.new() ;
d = BigNum.new() ;
while b > zero do
BigNum.div( a , b , c , d ) ;
a , b , d = b , d , a ;
end
return a ;
end
-- Português:
BigNum.mmc = BigNum.gcd
function BigNum.eq( bnum1 , bnum2 )
if BigNum.compare( bnum1 , bnum2 ) == 0 then
return true ;
else
return false ;
end
end
function BigNum.lt( bnum1 , bnum2 )
if BigNum.compare( bnum1 , bnum2 ) == 2 then
return true ;
else
return false ;
end
end
function BigNum.le( bnum1 , bnum2 )
local temp = -1 ;
temp = BigNum.compare( bnum1 , bnum2 )
if temp == 0 or temp == 2 then
return true ;
else
return false ;
end
end
function BigNum.compareAbs( bnum1 , bnum2 )
if bnum1 == nil or bnum2 == nil then
error("Function compare: parameter nil") ;
elseif bnum1.len > bnum2.len then
return 1 ;
elseif bnum1.len < bnum2.len then
return 2 ;
else
local i ;
for i = bnum1.len - 1 , 0 , -1 do
if bnum1[i] > bnum2[i] then
return 1 ;
elseif bnum1[i] < bnum2[i] then
return 2 ;
end
end
end
return 0 ;
end
function BigNum.compare( bnum1 , bnum2 )
local signal = 0 ;
if bnum1 == nil or bnum2 == nil then
error("Funtion BigNum.compare: parameter nil") ;
elseif bnum1.signal == '+' and bnum2.signal == '-' then
return 1 ;
elseif bnum1.signal == '-' and bnum2.signal == '+' then
return 2 ;
elseif bnum1.signal == '-' and bnum2.signal == '-' then
signal = 1 ;
end
if bnum1.len > bnum2.len then
return 1 + signal ;
elseif bnum1.len < bnum2.len then
return 2 - signal ;
else
local i ;
for i = bnum1.len - 1 , 0 , -1 do
if bnum1[i] > bnum2[i] then
return 1 + signal ;
elseif bnum1[i] < bnum2[i] then
return 2 - signal ;
end
end
end
return 0 ;
end
function BigNum.copy( bnum1 , bnum2 )
if bnum1 ~= nil and bnum2 ~= nil then
local i ;
for i = 0 , bnum1.len - 1 do
bnum2[i] = bnum1[i] ;
end
bnum2.len = bnum1.len ;
else
error("Function BigNum.copy: parameter nil") ;
end
end
function BigNum.change( bnum1 , num )
local j = 0 ;
local len = 0 ;
local num = num ;
local l ;
local oldLen = 0 ;
if bnum1 == nil then
error( "BigNum.change: parameter nil" ) ;
elseif type( bnum1 ) ~= "table" then
error( "BigNum.change: parameter error, type unexpected" ) ;
elseif num == nil then
bnum1.len = 1 ;
bnum1[0] = 0 ;
bnum1.signal = "+";
elseif type( num ) == "table" and num.len ~= nil then --check if num is a big number
--copy given table to the new one
for i = 0 , num.len do
bnum1[i] = num[i] ;
end
if num.signal ~= '-' and num.signal ~= '+' then
bnum1.signal = '+' ;
else
bnum1.signal = num.signal ;
end
oldLen = bnum1.len ;
bnum1.len = num.len ;
elseif type( num ) == "string" or type( num ) == "number" then
if string.sub( num , 1 , 1 ) == '+' or string.sub( num , 1 , 1 ) == '-' then
bnum1.signal = string.sub( num , 1 , 1 ) ;
num = string.sub(num, 2) ;
else
bnum1.signal = '+' ;
end
num = string.gsub( num , " " , "" ) ;
local sf = string.find( num , "e" ) ;
--Handles if the number is in exp notation
if sf ~= nil then
num = string.gsub( num , "%." , "" ) ;
local e = string.sub( num , sf + 1 ) ;
e = tonumber(e) ;
if e ~= nil and e > 0 then
e = tonumber(e) ;
else
error( "Function BigNum.change: string is not a valid number" ) ;
end
num = string.sub( num , 1 , sf - 2 ) ;
for i = string.len( num ) , e do
num = num .. "0" ;
end
else
sf = string.find( num , "%." ) ;
if sf ~= nil then
num = string.sub( num , 1 , sf - 1 ) ;
end
end
l = string.len( num ) ;
oldLen = bnum1.len ;
if (l > RADIX_LEN) then
local mod = l-( math.floor( l / RADIX_LEN ) * RADIX_LEN ) ;
for i = 1 , l-mod, RADIX_LEN do
bnum1[j] = tonumber( string.sub( num, -( i + RADIX_LEN - 1 ) , -i ) );
--Check if string dosn't represents a number
if bnum1[j] == nil then
error( "Function BigNum.change: string is not a valid number" ) ;
bnum1.len = 0 ;
return 1 ;
end
j = j + 1 ;
len = len + 1 ;
end
if (mod ~= 0) then
bnum1[j] = tonumber( string.sub( num , 1 , mod ) ) ;
bnum1.len = len + 1 ;
else
bnum1.len = len ;
end
--Eliminate trailing zeros
for i = bnum1.len - 1 , 1 , -1 do
if bnum1[i] == 0 then
bnum1[i] = nil ;
bnum1.len = bnum1.len - 1 ;
else
break ;
end
end
else
-- string.len(num) <= RADIX_LEN
bnum1[j] = tonumber( num ) ;
bnum1.len = 1 ;
end
else
error( "Function BigNum.change: parameter error, type unexpected" ) ;
end
--eliminates the deprecated higher order 'algarisms'
if oldLen ~= nil then
for i = bnum1.len , oldLen do
bnum1[i] = nil ;
end
end
return 0 ;
end
function BigNum.put( bnum , int , pos )
if bnum == nil then
error("Function BigNum.put: parameter nil") ;
end
local i = 0 ;
for i = 0 , pos - 1 do
bnum[i] = 0 ;
end
bnum[pos] = int ;
for i = pos + 1 , bnum.len do
bnum[i] = nil ;
end
bnum.len = pos ;
return 0 ;
end
--printraw{{{2
function printraw( bnum )
local i = 0 ;
if bnum == nil then
error( "Function printraw: parameter nil" ) ;
end
while 1 == 1 do
if bnum[i] == nil then
io.write( ' len '..bnum.len ) ;
if i ~= bnum.len then
io.write( ' ERRO!!!!!!!!' ) ;
end
io.write( "\n" ) ;
return 0 ;
end
io.write( 'r'..bnum[i] ) ;
i = i + 1 ;
end
end
--max{{{2
function max( int1 , int2 )
if int1 > int2 then
return int1 ;
else
return int2 ;
end
end
--decr{{{2
function decr( bnum1 )
local temp = {} ;
temp = BigNum.new( "1" ) ;
BigNum.sub( bnum1 , temp , bnum1 ) ;
return 0 ;
end

View File

@ -0,0 +1,227 @@
require( "bin.numbers.BigNum" ) ;
BigRat = {} ;
BigRat.mt = {} ;
function BigRat.new( num1 , num2 ) --{{{2
local bigrat = {} ;
local f ;
setmetatable(bigrat, BigRat.mt) ;
if type( num1 ) == "table" then
if num1.num ~= nil and num1.den ~= nil then
bigrat.num = BigNum.new( num1.num ) ;
bigrat.den = BigNum.new( num1.den ) ;
else
bigrat.num = BigNum.new( num1 ) ;
bigrat.den = BigNum.new( "1" ) ;
end
elseif num1 ~= nil then
if num2 == nil then
bigrat.den = BigNum.new( "1" ) ;
else
bigrat.den = BigNum.new( num2 ) ;
end
bigrat.num = BigNum.new( num1 ) ;
else
bigrat.den = BigNum.new( ) ;
bigrat.num = BigNum.new( ) ;
end
--Update the signals
if bigrat.den.signal == "-" then
if bigrat.num.signal == "-" then
bigrat.num.signal = "+" ;
else
bigrat.num.signal = "-" ;
end
bigrat.den.signal = "+" ;
end
return bigrat ;
end
function BigRat.mt.sub( num1 , num2 )
local temp = BigRat.new() ;
local brat1 = BigRat.new( num1 ) ;
local brat2 = BigRat.new( num2 ) ;
BigRat.sub( brat1 , brat2 , temp ) ;
return temp ;
end
function BigRat.mt.add( num1 , num2 )
local temp = BigRat.new() ;
local brat1 = BigRat.new( num1 ) ;
local brat2 = BigRat.new( num2 ) ;
BigRat.add( brat1 , brat2 , temp ) ;
return temp ;
end
function BigRat.mt.mul( num1 , num2 )
local temp = BigRat.new() ;
local brat1 = BigRat.new( num1 ) ;
local brat2 = BigRat.new( num2 ) ;
BigRat.mul( brat1 , brat2 , temp ) ;
return temp ;
end
function BigRat.mt.div( num1 , num2 )
local brat1 = BigRat.new( num1 ) ;
local brat2 = BigRat.new( num2 ) ;
local brat3 = BigRat.new() ;
local brat4 = BigRat.new() ;
BigRat.div( brat1 , brat2 , brat3 , brat4 ) ;
return brat3 , brat4 ;
end
function BigRat.mt.tostring( brat )
BigRat.simplify( brat ) ;
return BigNum.mt.tostring( brat.num ) .. " / " .. BigNum.mt.tostring( brat.den ) ;
end
function BigRat.mt.pow ( num1 , num2 )
local brat1 = BigRat.new( num1 ) ;
local brat2 = BigRat.new( num2 ) ;
return BigRat.pow( brat1 , brat2 )
end
function BigRat.mt.eq ( num1 , num2 )
return BigRat.eq( num1 , num2 )
end
function BigRat.mt.lt ( num1 , num2 )
return BigRat.lt( num1 , num2 )
end
function BigRat.mt.le ( num1 , num2 )
return BigRat.le( num1 , num2 )
end
function BigRat.mt.unm ( num )
local ret = BigRat.new( num )
if ret.num.signal == '-' then
ret.num.signal = '+'
else
ret.num.signal = '-'
end
return ret
end
BigRat.mt.__metatable = "hidden"
BigRat.mt.__tostring = BigRat.mt.tostring
BigRat.mt.__add = BigRat.mt.add
BigRat.mt.__sub = BigRat.mt.sub
BigRat.mt.__mul = BigRat.mt.mul
BigRat.mt.__div = BigRat.mt.div
BigRat.mt.__pow = BigRat.mt.pow
BigRat.mt.__unm = BigRat.mt.unm
BigRat.mt.__eq = BigRat.mt.eq
BigRat.mt.__le = BigRat.mt.le
BigRat.mt.__lt = BigRat.mt.lt
setmetatable( BigRat.mt, { __index = "inexistent field", __newindex = "not available", __metatable="hidden" } ) ;
function BigRat.add( brat1 , brat2 , brat3 )
brat3.den = brat1.den * brat2.den ;
brat3.num = ( brat1.num * brat2.den ) + ( brat2.num * brat1.den ) ;
return brat3 ;
end
function BigRat.sub( brat1 , brat2 , brat3 )
brat3.den = brat1.den * brat2.den ;
brat3.num = ( brat1.num * brat2.den ) - ( brat2.num * brat1.den ) ;
return brat3 ;
end
function BigRat.mul( brat1 , brat2 , brat3 )
brat3.num = brat1.num * brat2.num ;
brat3.den = brat1.den * brat2.den ;
return 0 ;
end
function BigRat.div( brat1 , brat2 , brat3 )
brat3.num = brat1.num * brat2.den ;
brat3.den = brat1.den * brat2.num ;
return brat3 ;
end
function BigRat.pow( bnum1 , bnum2 )
if bnum1 == nil or bnum2 == nil then
error( "Function BigRat.pow: parameter nil" ) ;
end
local x = BigRat.new( "8" ) ;
local n = BigRat.new( bnum2.den ) ;
local n2 ;
local y = BigRat.new( ) ;
local i ;
local temp = BigRat.new( ) ;
BigRat.simplify( bnum2 ) ;
temp.num = BigNum.exp( bnum1.num , bnum2.num ) ;
temp.den = BigNum.exp( bnum1.den , bnum2.num ) ;
n2 = n - 1 ;
for i = 0 , 4 do
y.num = x.num ^ n2.num ;
y.den = x.den ^ n2.num ;
x = (( temp / y ) + ( n2 * x )) / n ;
end
return x ;
end
function BigRat.simplify( brat )
if brat == nil then
error( "Function BigRat.simplify: parameter nil" ) ;
end
local gcd = BigNum.new( ) ;
local temp = BigRat.new( brat ) ;
local devnull = BigNum.new( ) ;
local zero = BigNum.new( "0" ) ;
--Check if numerator is zero
if BigNum.compareAbs( brat.num , zero ) == 0 then
brat.den = BigNum.new( "1" ) ;
return 0 ;
end
gcd = BigNum.gcd( brat.num , brat.den ) ;
BigNum.div( temp.num , gcd , brat.num , devnull ) ;
BigNum.div( temp.den , gcd , brat.den , devnull ) ;
--Update the signal
if brat.num.signal == '-' and brat.den.signal == '-' then
brat.num.signal = '+' ;
brat.den.signal = '+' ;
end
return 0 ;
end
function BigRat.eq( brat1 , brat2 )
if BigRat.compare( brat1 , brat2 ) == 0 then
return true ;
else
return false ;
end
end
function BigRat.lt( brat1 , brat2 )
if BigRat.compare( brat1 , brat2 ) == 2 then
return true ;
else
return false ;
end
end
function BigRat.le( brat1 , brat2 )
local temp = -1 ;
temp = BigRat.compare( brat1 , brat2 )
if temp == 0 or temp == 2 then
return true ;
else
return false ;
end
end
function BigRat.compare( bnum1 , bnum2 )
local temp ;
temp = bnum1 - bnum2 ;
if temp.num[0] == 0 and temp.num.len == 1 then --Check if is zero
return 0 ;
elseif temp.num.signal == "-" then
return 2 ;
else
return 1 ;
end
end

View File

@ -0,0 +1,191 @@
local bits={}
bits.data=''
bits.t='bits'
bits.Type='bits'
bits.__index = bits
bits.__tostring=function(self) return self.data end
bits.__len=function(self) return (#self.data)/8 end
local floor,insert = math.floor, table.insert
function bits.newBitBuffer(n)
--
end
function bits.newConverter(bitsIn,bitsOut)
local c={}
--
end
function basen(n,b)
if not b or b == 10 then return tostring(n) end
local digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
local t = {}
local sign = ""
if n < 0 then
sign = "-"
n = -n
end
repeat
local d = n % b + 1
n = n / b
insert(t, 1, digits:sub(d,d))
until n == 0
return sign .. table.concat(t,"")
end
bits.ref={}
function bits.newByte(d)
local c={}
if type(d)=="string" then
if #d>1 or #d<1 then
error("A byte must be one character!")
else
c.data=string.byte(d)
end
elseif type(d)=="number" then
if d>255 or d<0 then
error("A byte must be between 0 and 255!")
else
c.data=d
end
else
error("cannot use type "..type(d).." as an argument! Takes only strings or numbers!")
end
c.__index=function(self,k)
if k>=0 and k<9 then
if self.data==0 then
return 0
elseif self.data==255 then
return 1
else
return bits.ref[self.data][k]
end
end
end
c.__tostring=function(self)
return bits.ref[tostring(self.data)]
end
setmetatable(c,c)
return c
end
function bits.newByteArray(s)
local c={}
if type(s)~="string" then
error("Must be a string type or bin/buffer type")
elseif type(s)=="table" then
if s.t=="sink" or s.t=="buffer" or s.t=="bin" then
local data=s:getData()
for i=1,#data do
c[#c+1]=bits.newByte(data:sub(i,i))
end
else
error("Must be a string type or bin/buffer type")
end
else
for i=1,#s do
c[#c+1]=bits.newByte(s:sub(i,i))
end
end
return c
end
function bits.new(n,binary)
local temp={}
temp.t="bits"
temp.Type="bits"
if type(n)=="string" then
if binary then
temp.data=n:match("[10]+")
else
local t={}
for i=#n,1,-1 do
table.insert(t,bits:conv(string.byte(n,i)))
end
temp.data=table.concat(t)
end
elseif type(n)=="number" or type(n)=="table" then
temp.data=basen(n,2)
end
if #temp.data%8~=0 then
temp.data=string.rep('0',8-#temp.data%8)..temp.data
end
setmetatable(temp, bits)
return temp
end
for i=0,255 do
local d=bits.new(i).data
bits.ref[i]={d:match("(%d)(%d)(%d)(%d)(%d)(%d)(%d)(%d)")}
bits.ref[tostring(i)]=d
bits.ref[d]=i
bits.ref["\255"..string.char(i)]=d
end
function bits.numToBytes(n,fit,func)
local num=string.reverse(bits.new(n):toSbytes())
local ref={["num"]=num,["fit"]=fit}
if fit then
if fit<#num then
if func then
print("Warning: attempting to store a number that takes up more space than allotted! Using provided method!")
func(ref)
else
print("Warning: attempting to store a number that takes up more space than allotted!")
end
return ref.num:sub(1,ref.fit)
elseif fit==#num then
return string.reverse(num)
else
return string.reverse(string.rep("\0",fit-#num)..num)
end
else
return string.reverse(num)
end
end
function bits:conv(n)
local tab={}
while n>=1 do
table.insert(tab,n%2)
n=math.floor(n/2)
end
local str=string.reverse(table.concat(tab))
if #str%8~=0 or #str==0 then
str=string.rep('0',8-#str%8)..str
end
return str
end
function bits:tonumber(s,e)
if s==0 then
return tonumber(self.data,2)
end
s=s or 1
return tonumber(string.sub(self.data,(8*(s-1))+1,8*s),2) or error('Bounds!')
end
function bits:isover()
return #self.data>8
end
function bits:flipbits()
tab={}
for i=1,#self.data do
if string.sub(self.data,i,i)=='1' then
table.insert(tab,'0')
else
table.insert(tab,'1')
end
end
self.data=table.concat(tab)
end
function bits:tobytes()
local tab={}
for i=self:getbytes(),1,-1 do
table.insert(tab,string.char(self:tonumber(i)))
end
return bin.new(table.concat(tab))
end
function bits:toSbytes()
local tab={}
for i=self:getbytes(),1,-1 do
table.insert(tab,string.char(self:tonumber(i)))
end
return table.concat(tab)
end
function bits:getBin()
return self.data
end
function bits:getbytes()
return #self.data/8
end
return bits

View File

@ -0,0 +1,244 @@
local binNum=require("bin.numbers.BigNum")
local infinabits={}
infinabits.data=''
infinabits.t='infinabits'
infinabits.Type='infinabits'
infinabits.__index = infinabits
infinabits.__tostring=function(self) return self.data end
infinabits.__len=function(self) return (#self.data)/8 end
local floor,insert = math.floor, table.insert
function basen(n,b)
n=BigNum.new(n)
if not b or b == 10 then return tostring(n) end
local digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
local t = {}
local sign = ""
if n < BigNum.new(0) then
sign = "-"
n = -n
end
repeat
local d = tonumber(tostring(n % b)) + 1
n = n / b
insert(t, 1, digits:sub(d,d))
until n == BigNum.new(0)
return sign .. table.concat(t,"")
end
function base2to10(num)
local n=BigNum.new(0)
for i = #num-1,0,-1 do
nn=BigNum.new(num:sub(i+1,i+1))*(BigNum.new(2)^((#num-i)-1))
n=n+nn
end
return n
end
function infinabits.newBitBuffer(n)
-- WIP
end
function infinabits.newConverter(bitsIn,bitsOut)
local c={}
-- WIP
end
infinabits.ref={}
function infinabits.newByte(d)-- WIP
local c={}
if type(d)=="string" then
if #d>1 or #d<1 then
error("A byte must be one character!")
else
c.data=string.byte(d)
end
elseif type(d)=="number" then
if d>255 or d<0 then
error("A byte must be between 0 and 255!")
else
c.data=d
end
else
error("cannot use type "..type(d).." as an argument! Takes only strings or numbers!")
end
c.__index=function(self,k)
if k>=0 and k<9 then
if self.data==0 then
return 0
elseif self.data==255 then
return 1
else
return infinabits.ref[self.data][k]
end
end
end
c.__tostring=function(self)
return infinabits.ref[tostring(self.data)]
end
setmetatable(c,c)
return c
end
function infinabits.newByteArray(s)-- WIP
local c={}
if type(s)~="string" then
error("Must be a string type or bin/buffer type")
elseif type(s)=="table" then
if s.t=="sink" or s.t=="buffer" or s.t=="bin" then
local data=s:getData()
for i=1,#data do
c[#c+1]=infinabits.newByte(data:sub(i,i))
end
else
error("Must be a string type or bin/buffer type")
end
else
for i=1,#s do
c[#c+1]=infinabits.newByte(s:sub(i,i))
end
end
return c
end
function infinabits.new(n,binary)
local temp={}
temp.t="infinabits"
temp.Type="infinabits"
if type(n)=="string" then
if binary then
temp.data=n:match("[10]+")
else
local t={}
for i=#n,1,-1 do
table.insert(t,infinabits:conv(string.byte(n,i)))
end
temp.data=table.concat(t)
end
elseif type(n)=="number" or type(n)=="table" then
temp.data=basen(tostring(n),2)
end
if #temp.data%8~=0 then
temp.data=string.rep('0',8-#temp.data%8)..temp.data
end
setmetatable(temp, infinabits)
return temp
end
for i=0,255 do
local d=infinabits.new(i).data
infinabits.ref[i]={d:match("(%d)(%d)(%d)(%d)(%d)(%d)(%d)(%d)")}
infinabits.ref[tostring(i)]=d
infinabits.ref[d]=i
infinabits.ref["\255"..string.char(i)]=d
end
function infinabits.numToBytes(n,fit,func)
local num=string.reverse(infinabits.new(BigNum.new(n)):toSbytes())
local ref={["num"]=num,["fit"]=fit}
if fit then
if fit<#num then
if func then
print("Warning: attempting to store a number that takes up more space than allotted! Using provided method!")
func(ref)
else
print("Warning: attempting to store a number that takes up more space than allotted!")
end
return ref.num:sub(1,ref.fit)
elseif fit==#num then
return string.reverse(num)
else
return string.reverse(string.rep("\0",fit-#num)..num)
end
else
return string.reverse(num)
end
end
function infinabits.numToBytes(n,fit,fmt,func)
if fmt=="%e" then
local num=string.reverse(infinabits.new(BigNum.new(n)):toSbytes())
local ref={["num"]=num,["fit"]=fit}
if fit then
if fit<#num then
if func then
print("Warning: attempting to store a number that takes up more space than allotted! Using provided method!")
func(ref)
else
print("Warning: attempting to store a number that takes up more space than allotted!")
end
return ref.num:sub(1,ref.fit)
elseif fit==#num then
return num
else
return string.rep("\0",fit-#num)..num
end
else
return num
end
else
local num=string.reverse(infinabits.new(BigNum.new(n)):toSbytes())
local ref={["num"]=num,["fit"]=fit}
if fit then
if fit<#num then
if func then
print("Warning: attempting to store a number that takes up more space than allotted! Using provided method!")
func(ref)
else
print("Warning: attempting to store a number that takes up more space than allotted!")
end
return ref.num:sub(1,ref.fit)
elseif fit==#num then
return string.reverse(num)
else
return string.reverse(string.rep("\0",fit-#num)..num)
end
else
return string.reverse(num)
end
end
end
function infinabits:conv(n)
local tab={}
local one=BigNum.new(1)
local n=BigNum.new(n)
while n>=one do
table.insert(tab,tonumber(tostring(n%2)))
n=n/2
end
local str=string.reverse(table.concat(tab))
if #str%8~=0 or #str==0 then
str=string.rep('0',8-#str%8)..str
end
return str
end
function infinabits:tonumber(s)
if s==0 then
return tonumber(self.data,2)
end
s=s or 1
return tonumber(tostring(base2to10(string.sub(self.data,(8*(s-1))+1,8*s)))) or error('Bounds!')
end
function infinabits:isover()
return #self.data>8
end
function infinabits:flipbits()
tab={}
local s=self.data
s=s:gsub("1","_")
s=s:gsub("0","1")
s=s:gsub("_","0")
self.data=s
end
function infinabits:tobytes()
local tab={}
for i=self:getbytes(),1,-1 do
table.insert(tab,string.char(self:tonumber(i)))
end
return bin.new(table.concat(tab))
end
function infinabits:toSbytes()
local tab={}
for i=self:getbytes(),1,-1 do
table.insert(tab,string.char(self:tonumber(i)))
end
return table.concat(tab)
end
function infinabits:getBin()
return self.data
end
function infinabits:getbytes()
return #self.data/8
end
return infinabits

View File

@ -0,0 +1,333 @@
--[[
LICENSE
(c) 2008-2011 David Manura. Licensed under the same terms as Lua (MIT).
--]]
local M = {_TYPE='module', _NAME='bit.numberlua', _VERSION='0.3.1.20120131'}
local floor = math.floor
local MOD = 2^32
local MODM = MOD-1
local function memoize(f)
local mt = {}
local t = setmetatable({}, mt)
function mt:__index(k)
local v = f(k); t[k] = v
return v
end
return t
end
local function make_bitop_uncached(t, m)
local function bitop(a, b)
local res,p = 0,1
while a ~= 0 and b ~= 0 do
local am, bm = a%m, b%m
res = res + t[am][bm]*p
a = (a - am) / m
b = (b - bm) / m
p = p*m
end
res = res + (a+b)*p
return res
end
return bitop
end
local function make_bitop(t)
local op1 = make_bitop_uncached(t,2^1)
local op2 = memoize(function(a)
return memoize(function(b)
return op1(a, b)
end)
end)
return make_bitop_uncached(op2, 2^(t.n or 1))
end
-- ok? probably not if running on a 32-bit int Lua number type platform
function M.tobit(x)
return x % 2^32
end
M.bxor = make_bitop {[0]={[0]=0,[1]=1},[1]={[0]=1,[1]=0}, n=4}
local bxor = M.bxor
function M.bnot(a) return MODM - a end
local bnot = M.bnot
function M.band(a,b) return ((a+b) - bxor(a,b))/2 end
local band = M.band
function M.bor(a,b) return MODM - band(MODM - a, MODM - b) end
local bor = M.bor
local lshift, rshift -- forward declare
function M.rshift(a,disp) -- Lua5.2 insipred
if disp < 0 then return lshift(a,-disp) end
return floor(a % 2^32 / 2^disp)
end
rshift = M.rshift
function M.lshift(a,disp) -- Lua5.2 inspired
if disp < 0 then return rshift(a,-disp) end
return (a * 2^disp) % 2^32
end
lshift = M.lshift
function M.tohex(x, n) -- BitOp style
n = n or 8
local up
if n <= 0 then
if n == 0 then return '' end
up = true
n = - n
end
x = band(x, 16^n-1)
return ('%0'..n..(up and 'X' or 'x')):format(x)
end
local tohex = M.tohex
function M.extract(n, field, width) -- Lua5.2 inspired
width = width or 1
return band(rshift(n, field), 2^width-1)
end
local extract = M.extract
function M.replace(n, v, field, width) -- Lua5.2 inspired
width = width or 1
local mask1 = 2^width-1
v = band(v, mask1) -- required by spec?
local mask = bnot(lshift(mask1, field))
return band(n, mask) + lshift(v, field)
end
local replace = M.replace
function M.bswap(x) -- BitOp style
local a = band(x, 0xff); x = rshift(x, 8)
local b = band(x, 0xff); x = rshift(x, 8)
local c = band(x, 0xff); x = rshift(x, 8)
local d = band(x, 0xff)
return lshift(lshift(lshift(a, 8) + b, 8) + c, 8) + d
end
local bswap = M.bswap
function M.rrotate(x, disp) -- Lua5.2 inspired
disp = disp % 32
local low = band(x, 2^disp-1)
return rshift(x, disp) + lshift(low, 32-disp)
end
local rrotate = M.rrotate
function M.lrotate(x, disp) -- Lua5.2 inspired
return rrotate(x, -disp)
end
local lrotate = M.lrotate
M.rol = M.lrotate -- LuaOp inspired
M.ror = M.rrotate -- LuaOp insipred
function M.arshift(x, disp) -- Lua5.2 inspired
local z = rshift(x, disp)
if x >= 0x80000000 then z = z + lshift(2^disp-1, 32-disp) end
return z
end
local arshift = M.arshift
function M.btest(x, y) -- Lua5.2 inspired
return band(x, y) ~= 0
end
M.bit32 = {} -- Lua 5.2 'bit32' compatibility
local function bit32_bnot(x)
return (-1 - x) % MOD
end
M.bit32.bnot = bit32_bnot
local function bit32_bxor(a, b, c, ...)
local z
if b then
a = a % MOD
b = b % MOD
z = bxor(a, b)
if c then
z = bit32_bxor(z, c, ...)
end
return z
elseif a then
return a % MOD
else
return 0
end
end
M.bit32.bxor = bit32_bxor
local function bit32_band(a, b, c, ...)
local z
if b then
a = a % MOD
b = b % MOD
z = ((a+b) - bxor(a,b)) / 2
if c then
z = bit32_band(z, c, ...)
end
return z
elseif a then
return a % MOD
else
return MODM
end
end
M.bit32.band = bit32_band
local function bit32_bor(a, b, c, ...)
local z
if b then
a = a % MOD
b = b % MOD
z = MODM - band(MODM - a, MODM - b)
if c then
z = bit32_bor(z, c, ...)
end
return z
elseif a then
return a % MOD
else
return 0
end
end
M.bit32.bor = bit32_bor
function M.bit32.btest(...)
return bit32_band(...) ~= 0
end
function M.bit32.lrotate(x, disp)
return lrotate(x % MOD, disp)
end
function M.bit32.rrotate(x, disp)
return rrotate(x % MOD, disp)
end
function M.bit32.lshift(x,disp)
if disp > 31 or disp < -31 then return 0 end
return lshift(x % MOD, disp)
end
function M.bit32.rshift(x,disp)
if disp > 31 or disp < -31 then return 0 end
return rshift(x % MOD, disp)
end
function M.bit32.arshift(x,disp)
x = x % MOD
if disp >= 0 then
if disp > 31 then
return (x >= 0x80000000) and MODM or 0
else
local z = rshift(x, disp)
if x >= 0x80000000 then z = z + lshift(2^disp-1, 32-disp) end
return z
end
else
return lshift(x, -disp)
end
end
function M.bit32.extract(x, field, ...)
local width = ... or 1
if field < 0 or field > 31 or width < 0 or field+width > 32 then error 'out of range' end
x = x % MOD
return extract(x, field, ...)
end
function M.bit32.replace(x, v, field, ...)
local width = ... or 1
if field < 0 or field > 31 or width < 0 or field+width > 32 then error 'out of range' end
x = x % MOD
v = v % MOD
return replace(x, v, field, ...)
end
M.bit = {} -- LuaBitOp "bit" compatibility
function M.bit.tobit(x)
x = x % MOD
if x >= 0x80000000 then x = x - MOD end
return x
end
local bit_tobit = M.bit.tobit
function M.bit.tohex(x, ...)
return tohex(x % MOD, ...)
end
function M.bit.bnot(x)
return bit_tobit(bnot(x % MOD))
end
local function bit_bor(a, b, c, ...)
if c then
return bit_bor(bit_bor(a, b), c, ...)
elseif b then
return bit_tobit(bor(a % MOD, b % MOD))
else
return bit_tobit(a)
end
end
M.bit.bor = bit_bor
local function bit_band(a, b, c, ...)
if c then
return bit_band(bit_band(a, b), c, ...)
elseif b then
return bit_tobit(band(a % MOD, b % MOD))
else
return bit_tobit(a)
end
end
M.bit.band = bit_band
local function bit_bxor(a, b, c, ...)
if c then
return bit_bxor(bit_bxor(a, b), c, ...)
elseif b then
return bit_tobit(bxor(a % MOD, b % MOD))
else
return bit_tobit(a)
end
end
M.bit.bxor = bit_bxor
function M.bit.lshift(x, n)
return bit_tobit(lshift(x % MOD, n % 32))
end
function M.bit.rshift(x, n)
return bit_tobit(rshift(x % MOD, n % 32))
end
function M.bit.arshift(x, n)
return bit_tobit(arshift(x % MOD, n % 32))
end
function M.bit.rol(x, n)
return bit_tobit(lrotate(x % MOD, n % 32))
end
function M.bit.ror(x, n)
return bit_tobit(rrotate(x % MOD, n % 32))
end
function M.bit.bswap(x)
return bit_tobit(bswap(x % MOD))
end
return M

View File

@ -0,0 +1,232 @@
--[[----------------------------------------
Random
Not all of this is mine
------------------------------------------]]
--[[------------------------------------
RandomLua v0.3.1
Pure Lua Pseudo-Random Numbers Generator
Under the MIT license.
copyright(c) 2011 linux-man
--]]------------------------------------
local math_floor = math.floor
local function normalize(n)
return n % 0x80000000
end
local function bit_and(a, b)
local r = 0
local m = 0
for m = 0, 31 do
if (a % 2 == 1) and (b % 2 == 1) then r = r + 2^m end
if a % 2 ~= 0 then a = a - 1 end
if b % 2 ~= 0 then b = b - 1 end
a = a / 2 b = b / 2
end
return normalize(r)
end
local function bit_or(a, b)
local r = 0
local m = 0
for m = 0, 31 do
if (a % 2 == 1) or (b % 2 == 1) then r = r + 2^m end
if a % 2 ~= 0 then a = a - 1 end
if b % 2 ~= 0 then b = b - 1 end
a = a / 2 b = b / 2
end
return normalize(r)
end
local function bit_xor(a, b)
local r = 0
local m = 0
for m = 0, 31 do
if a % 2 ~= b % 2 then r = r + 2^m end
if a % 2 ~= 0 then a = a - 1 end
if b % 2 ~= 0 then b = b - 1 end
a = a / 2 b = b / 2
end
return normalize(r)
end
local function seed()
return normalize(os.time())
end
--Mersenne twister
local mersenne_twister = {}
mersenne_twister.__index = mersenne_twister
function mersenne_twister:randomseed(s)
if not s then s = seed() end
self.mt[0] = normalize(s)
for i = 1, 623 do
self.mt[i] = normalize(0x6c078965 * bit_xor(self.mt[i-1], math_floor(self.mt[i-1] / 0x40000000)) + i)
end
end
function mersenne_twister:random(a, b)
local y
if self.index == 0 then
for i = 0, 623 do
y = self.mt[(i + 1) % 624] % 0x80000000
self.mt[i] = bit_xor(self.mt[(i + 397) % 624], math_floor(y / 2))
if y % 2 ~= 0 then self.mt[i] = bit_xor(self.mt[i], 0x9908b0df) end
end
end
y = self.mt[self.index]
y = bit_xor(y, math_floor(y / 0x800))
y = bit_xor(y, bit_and(normalize(y * 0x80), 0x9d2c5680))
y = bit_xor(y, bit_and(normalize(y * 0x8000), 0xefc60000))
y = bit_xor(y, math_floor(y / 0x40000))
self.index = (self.index + 1) % 624
if not a then return y / 0x80000000
elseif not b then
if a == 0 then return y
else return 1 + (y % a)
end
else
return a + (y % (b - a + 1))
end
end
local function twister(s)
local temp = {}
setmetatable(temp, mersenne_twister)
temp.mt = {}
temp.index = 0
temp:randomseed(s)
return temp
end
--Linear Congruential Generator
local linear_congruential_generator = {}
linear_congruential_generator.__index = linear_congruential_generator
function linear_congruential_generator:random(a, b)
local y = (self.a * self.x + self.c) % self.m
self.x = y
if not a then return y / 0x10000
elseif not b then
if a == 0 then return y
else return 1 + (y % a) end
else
return a + (y % (b - a + 1))
end
end
function linear_congruential_generator:randomseed(s)
if not s then s = seed() end
self.x = normalize(s)
end
local function lcg(s, r)
local temp = {}
setmetatable(temp, linear_congruential_generator)
temp.a, temp.c, temp.m = 1103515245, 12345, 0x10000 --from Ansi C
if r then
if r == 'nr' then temp.a, temp.c, temp.m = 1664525, 1013904223, 0x10000 --from Numerical Recipes.
elseif r == 'mvc' then temp.a, temp.c, temp.m = 214013, 2531011, 0x10000 end--from MVC
end
temp:randomseed(s)
return temp
end
-- Multiply-with-carry
local multiply_with_carry = {}
multiply_with_carry.__index = multiply_with_carry
function multiply_with_carry:random(a, b)
local m = self.m
local t = self.a * self.x + self.c
local y = t % m
self.x = y
self.c = math_floor(t / m)
if not a then return y / 0x10000
elseif not b then
if a == 0 then return y
else return 1 + (y % a) end
else
return a + (y % (b - a + 1))
end
end
function multiply_with_carry:randomseed(s)
if not s then s = seed() end
self.c = self.ic
self.x = normalize(s)
end
local function mwc(s, r)
local temp = {}
setmetatable(temp, multiply_with_carry)
temp.a, temp.c, temp.m = 1103515245, 12345, 0x10000 --from Ansi C
if r then
if r == 'nr' then temp.a, temp.c, temp.m = 1664525, 1013904223, 0x10000 --from Numerical Recipes.
elseif r == 'mvc' then temp.a, temp.c, temp.m = 214013, 2531011, 0x10000 end--from MVC
end
temp.ic = temp.c
temp:randomseed(s)
return temp
end
-- Little bind for the methods: My code starts
local randomGen={}
randomGen.__index=randomGen
function randomGen:new(s)
local temp={}
setmetatable(temp,randomGen)
temp[1]=twister()
temp[2]=lcg()
temp[3]=mwc()
temp.pos=1
for i=1,3 do
temp[i]:randomseed(s)
end
return temp
end
function randomGen:randomseed(s)
self.pos=1
self[1]:randomseed(s)
self[2]:randomseed(s)
self[3]:randomseed(s)
end
function randomGen:randomInt(a,b)
local t=self[self.pos]:random(a,b)
self.pos=self.pos+1
if self.pos>3 then
self.pos=1
end
return t
end
function randomGen:newND(a,b,s)
if not(a) or not(b) then error('You must include a range!') end
local temp=randomGen:new(s)
temp.a=a
temp.b=b
temp.range=b-a+1
temp.dups={no=0}
function temp:nextInt()
local t=self:randomInt(self.a,self.b)
if self.dups[t]==nil then
self.dups[t]=true
self.dups.no=self.dups.no+1
else
return self:nextInt()
end
if self.dups.no==self.range then
function self:nextInt()
return 1,true
end
return t
else
return t
end
end
function temp:nextIInt()
return function() return self:nextInt() end
end
return temp
end
return randomGen

View File

@ -0,0 +1,146 @@
local __CURRENTVERSION=2
bin.registerBlock("t",function(SIZE_OR_NIL,ref)
local header=ref:read(3)
if not header:match("(LT.)") then error("Not a valid table struct!") end
if bin.defualtBit.new(header:sub(3,3)):tonumber(1)>__CURRENTVERSION then error("Incompatible Version of LuaTable!") end
local len=ref:getBlock("n",4) -- hehe lets make life easier
local tab={}
local ind
local n=0
while true do
local _dat=ref:read(2)
if _dat==nil then break end
local it,dt=_dat:match("(.)(.)")
n=n+2
if it=="N" then -- get the index stuff out of the way first
ind=ref:getBlock("n",4)
n=n+4
else
indL=ref:getBlock("n",1)
n=n+1+indL
ind=ref:read(indL)
end
if dt=="N" then
tab[ind]=ref:getBlock("d")
n=n+8
elseif dt=="I" then
tab[ind]=math.huge
ref:getBlock("n",4)
n=n+4
elseif dt=="i" then
tab[ind]=-math.huge
ref:getBlock("n",4)
n=n+4
elseif dt=="S" then
local nn=ref:getBlock("n",4)
tab[ind]=ref:read(nn)
n=n+4+nn
elseif dt=="B" then
tab[ind]=({["\255"]=true,["\0"]=false})[ref:read(1)]
n=n+1
elseif dt=="F" then
local nn=ref:getBlock("n",4)
tab[ind]=loadstring(ref:read(nn))
n=n+4+nn
elseif dt=="T" then
local cur=ref:getSeek()
local size=ref:getBlock("n",4)
ref:setSeek(cur)
ref:read(4)
if size==7 then
tab[ind]={}
ref:read(7)
n=n+11
else
local data=bin.new(ref:read(size))
local dat=data:getBlock("t")
if dat.__RECURSIVE then
tab[ind]=tab
else
tab[ind]=dat
end
n=n+data:getSize()+4
end
end
if n==len then break end
end
return bin.resolveType(tab)
end,function(d,fit,fmt,self,rec,tabsaw)
-- INGORE FIT WE ARE CREATING A STRUCT!!!
-- fmt will apply to all numbers
local __rem=nil
if not tabsaw then rem=true end
local tabsaw=tabsaw or {}
if rem then
table.insert(tabsaw,d)
end
local bData={}
for i,v in pairs(d) do -- this is for tables, all but userdata is fine. Depending on where you are using lua functions may or may not work
local tp=type(v):sub(1,1):upper() -- uppercase of datatype
if type(i)=="number" then -- Lets handle indexies
if v==math.huge then
tp="I"
v=0
elseif v==-math.huge then
tp="i"
v=0
end
table.insert(bData,"N"..tp..bin.defualtBit.numToBytes(i,4)) -- number index?
elseif type(i)=="string" then
if #i>255 then error("A string index cannot be larger than 255 bytes!") end
table.insert(bData,"S"..tp..bin.defualtBit.numToBytes(#i,1)..i) -- string index?
else
error("Only numbers and strings can be a table index!") -- throw error?
end
if type(v)=="number" then
-- How do we handle number data
local temp=bin.new()
temp:addBlock(v,nil,"d")
table.insert(bData,temp.data)
elseif type(v)=="string" then
-- Lets work on strings
table.insert(bData,bin.defualtBit.numToBytes(#v,4)) -- add length of string
table.insert(bData,v) -- add string
elseif type(v)=="boolean" then -- bools are easy :D
table.insert(bData,({[true]="\255",[false]="\0"})[v])
elseif type(v)=="function" then -- should we allow this? why not...
local dump=string.dump(v)
table.insert(bData,bin.defualtBit.numToBytes(#dump,4)) -- add length of dumped string
table.insert(bData,dump) -- add it
elseif type(v)=="table" then -- tables...
if tabsaw[1]==v then
v={__RECURSIVE=i}
else
tabsaw[i]=v
end
local data=rec(v,nil,"t",self,rec,tabsaw)
table.insert(bData,bin.defualtBit.numToBytes(#data,4)) -- add length of string
table.insert(bData,data) -- add string
end
end
local data=table.concat(bData)
return "LT"..string.char(__CURRENTVERSION)..bin.defualtBit.numToBytes(#data,4)..data
end)
bin.registerBlock("b",function(SIZE_OR_NIL,ref)
return ({["\255"]=true,["\0"]=false})[ref:read(1)]
end,function(d)
return ({[true]="\255",[false]="\0"})[d]
end)
bin.registerBlock("f",function(SIZE_OR_NIL,ref)
local nn=ref:getBlock("n",4)
return loadstring(ref:read(nn))
end,function(d)
local dump=string.dump(d)
return bin.defualtBit.numToBytes(#dump,4)..dump
end)
bin.registerBlock("d",function(SIZE_OR_NIL,ref)
local w,p=ref:getBlock("n",4),ref:getBlock("n",4)
p=tonumber("0."..tostring(p))
return w+p
end,function(d,fit,fmt,self,rec,tabsaw)
local w,p = toFraction(d)
local temp=bin.new()
temp:addBlock(w,4)
temp:addBlock(p,4)
return temp.data
end)

View File

@ -0,0 +1,127 @@
function table.print(tbl, indent)
if not indent then indent = 0 end
for k, v in pairs(tbl) do
formatting = string.rep(" ", indent) .. k .. ": "
if type(v) == "table" then
print(formatting)
table.print(v, indent+1)
elseif type(v) == 'boolean' then
print(formatting .. tostring(v))
else
print(formatting .. tostring(v))
end
end
end
function table.flip(t)
local tt={}
for i,v in pairs(t) do
tt[v]=i
end
return tt
end
function toFraction(n)
local w,p=math.modf(n)
if p~=0 then
p=tonumber(tostring(p):sub(3))
end
return w,p
end
function io.cleanName(name)
name=name:gsub("\\","")
name=name:gsub("/","")
name=name:gsub(":","")
name=name:gsub("*","")
name=name:gsub("%?","")
name=name:gsub("\"","''")
name=name:gsub("<","")
name=name:gsub(">","")
name=name:gsub("|","")
return name
end
function math.numfix(n,x)
local str=tostring(n)
if #str<x then
str=('0'):rep(x-#str)..str
end
return str
end
function bin.stripFileName(path)
path=path:gsub("\\","/")
local npath=path:reverse()
a=npath:find("/",1,true)
npath=npath:sub(a)
npath=npath:reverse()
return npath
end
function bin._trim(str)
return str:match'^()%s*$' and '' or str:match'^%s*(.*%S)'
end
function io.dirExists(strFolderName)
strFolderName = strFolderName or io.getDir()
local fileHandle, strError = io.open(strFolderName..'\\*.*','r')
if fileHandle ~= nil then
io.close(fileHandle)
return true
else
if string.match(strError,'No such file or directory') then
return false
else
return true
end
end
end
function bin.fileExists(name)
local f=io.open(name,"r")
if f~=nil then io.close(f) return true else return false end
end
function bin.randomName(n,ext)
n=n or math.random(7,15)
if ext then
a,b=ext:find('.',1,true)
if a and b then
ext=ext:sub(2)
end
end
local str,h = '',0
strings = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','1','2','3','4','5','6','7','8','9','0','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'}
for i=1,n do
h = math.random(1,#strings)
str = str..''..strings[h]
end
return str..'.'..(ext or 'tmp')
end
function bin.trimNul(str)
return str:match("(.-)[%z]*$")
end
function io.mkDir(dirname)
os.execute('mkdir "' .. dirname..'"')
end
function string.lines(str)
local t = {}
local function helper(line) table.insert(t, line) return '' end
helper((str:gsub('(.-)\r?\n', helper)))
return t
end
function log(data,name,fmt)
if name then
name=io.cleanName(name)
end
if not bin.logger then
bin.logger = bin.stream(name or 'lua.log',false)
elseif bin.logger and name then
bin.logger:close()
bin.logger = bin.stream(name or 'lua.log',false)
end
local d=os.date('*t',os.time())
bin.logger:tackE((fmt or '['..math.numfix(d.month,2)..'-'..math.numfix(d.day,2)..'-'..d.year..'|'..math.numfix(d.hour,2)..':'..math.numfix(d.min,2)..':'..math.numfix(d.sec,2)..']\t')..data..'\r\n')
end
function table.max(t)
if #t == 0 then return end
local value = t[1]
for i = 2, #t do
if (value < t[i]) then
value = t[i]
end
end
return value
end

View File

@ -1,69 +1,37 @@
require("core.Library")
GLOBAL,sThread=require("multi.integration.loveManager").init() -- load the love2d version of the lanesManager and requires the entire multi library
GLOBAL,sThread=require("multi.integration.loveManager").init{threadNamespace="THREAD"} -- load the love2d version of the lanesManager and requires the entire multi library
--IMPORTANT
-- Do not make the above local, this is the one difference that the lanesManager does not have
-- If these are local the functions will have the upvalues put into them that do not exist on the threaded side
-- You will need to ensure that the function does not refer to any upvalues in its code. It will print an error if it does though
-- If these are local the functions will have the up-values put into them that do not exist on the threaded side
-- You will need to ensure that the function does not refer to any up-values in its code. It will print an error if it does though
-- Also each thread has a .1 second delay! This is used to generate a random values for each thread!
require("core.GuiManager") -- allows the use of graphics in the program.
gui.ff.Color=Color.Black
function comma_value(amount)
local formatted = amount
while true do
formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2')
if (k==0) then
break
multi:newSystemThread("Test_Thread_1",function()
require("multi")
test = sThread.waitFor("testthing"):init()
multi:newTLoop(function()
print("------")
for i,v in pairs(test.tab) do
print("T1",i,v)
end
end,1)
multi:mainloop()
end)
multi:newSystemThread("Test_Thread_1",function()
require("multi")
test = sThread.waitFor("testthing"):init()
multi:newTLoop(function()
print("------")
for i,v in pairs(test.tab) do
print("T2",i,v)
end
return formatted
end
multi:newSystemThread("test1",function() -- Another difference is that the multi library is already loaded in the threaded enviroment as well as a call to multi:mainloop()
multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 1"):OnBench(function(self,c) GLOBAL["T1"]=c multi:Stop() end)
end,1)
multi:mainloop()
end)
multi:newSystemThread("test2",function() -- spawns a thread in another lua process
multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 2"):OnBench(function(self,c) GLOBAL["T2"]=c multi:Stop() end)
end)
multi:newSystemThread("test3",function() -- spawns a thread in another lua process
multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 3"):OnBench(function(self,c) GLOBAL["T3"]=c multi:Stop() end)
end)
multi:newSystemThread("test4",function() -- spawns a thread in another lua process
multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 4"):OnBench(function(self,c) GLOBAL["T4"]=c multi:Stop() end)
end)
multi:newSystemThread("test5",function() -- spawns a thread in another lua process
multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 5"):OnBench(function(self,c) GLOBAL["T5"]=c multi:Stop() end)
end)
multi:newSystemThread("test6",function() -- spawns a thread in another lua process
multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 6"):OnBench(function(self,c) GLOBAL["T6"]=c multi:Stop() end)
end)
multi:newSystemThread("Combiner",function() -- spawns a thread in another lua process
function comma_value(amount)
local formatted = amount
while true do
formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2')
if (k==0) then
break
end
end
return formatted
end
local b=comma_value(tostring(sThread.waitFor("T1")+sThread.waitFor("T2")+sThread.waitFor("T3")+sThread.waitFor("T4")+sThread.waitFor("T5")+sThread.waitFor("T6")))
GLOBAL["DONE"]=b
end)
multi:newThread("test0",function()
-- sThread.waitFor("DONE") -- lets hold the main thread completely so we don't eat up cpu
-- os.exit()
-- when the main thread is holding there is a chance that error handling on the system threads may not work!
-- instead we can do this
while true do
thread.skip(1) -- allow error handling to take place... Otherwise lets keep the main thread running on the low
-- Before we held just because we could... But this is a game and we need to have logic continue
--sThreadM.sleep(.001) -- Sleeping for .001 is a greeat way to keep cpu usage down. Make sure if you aren't doing work to rest. Abuse the hell out of GLOBAL if you need to :P
if GLOBAL["DONE"] then
t.text="Bench: "..GLOBAL["DONE"]
end
end
end)
GLOBAL["Bench"]=3
t=gui:newTextLabel("no done yet!",0,0,300,100)
t:centerX()
t:centerY()
test = multi:newSystemThreadedTable("testthing"):init()
multi:newTLoop(function()
local a,b = multi.randomString(8),multi.randomString(4)
print(">",a,b)
test[a]=b
end,1)

View File

@ -0,0 +1,69 @@
require("core.Library")
GLOBAL,sThread=require("multi.integration.loveManager").init() -- load the love2d version of the lanesManager and requires the entire multi library
--IMPORTANT
-- Do not make the above local, this is the one difference that the lanesManager does not have
-- If these are local the functions will have the upvalues put into them that do not exist on the threaded side
-- You will need to ensure that the function does not refer to any upvalues in its code. It will print an error if it does though
-- Also each thread has a .1 second delay! This is used to generate a random values for each thread!
require("core.GuiManager") -- allows the use of graphics in the program.
gui.ff.Color=Color.Black
function comma_value(amount)
local formatted = amount
while true do
formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2')
if (k==0) then
break
end
end
return formatted
end
multi:newSystemThread("test1",function() -- Another difference is that the multi library is already loaded in the threaded enviroment as well as a call to multi:mainloop()
multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 1"):OnBench(function(self,c) GLOBAL["T1"]=c multi:Stop() end)
end)
multi:newSystemThread("test2",function() -- spawns a thread in another lua process
multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 2"):OnBench(function(self,c) GLOBAL["T2"]=c multi:Stop() end)
end)
multi:newSystemThread("test3",function() -- spawns a thread in another lua process
multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 3"):OnBench(function(self,c) GLOBAL["T3"]=c multi:Stop() end)
end)
multi:newSystemThread("test4",function() -- spawns a thread in another lua process
multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 4"):OnBench(function(self,c) GLOBAL["T4"]=c multi:Stop() end)
end)
multi:newSystemThread("test5",function() -- spawns a thread in another lua process
multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 5"):OnBench(function(self,c) GLOBAL["T5"]=c multi:Stop() end)
end)
multi:newSystemThread("test6",function() -- spawns a thread in another lua process
multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 6"):OnBench(function(self,c) GLOBAL["T6"]=c multi:Stop() end)
end)
multi:newSystemThread("Combiner",function() -- spawns a thread in another lua process
function comma_value(amount)
local formatted = amount
while true do
formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2')
if (k==0) then
break
end
end
return formatted
end
local b=comma_value(tostring(sThread.waitFor("T1")+sThread.waitFor("T2")+sThread.waitFor("T3")+sThread.waitFor("T4")+sThread.waitFor("T5")+sThread.waitFor("T6")))
GLOBAL["DONE"]=b
end)
multi:newThread("test0",function()
-- sThread.waitFor("DONE") -- lets hold the main thread completely so we don't eat up cpu
-- os.exit()
-- when the main thread is holding there is a chance that error handling on the system threads may not work!
-- instead we can do this
while true do
thread.skip(1) -- allow error handling to take place... Otherwise lets keep the main thread running on the low
-- Before we held just because we could... But this is a game and we need to have logic continue
--sThreadM.sleep(.001) -- Sleeping for .001 is a greeat way to keep cpu usage down. Make sure if you aren't doing work to rest. Abuse the hell out of GLOBAL if you need to :P
if GLOBAL["DONE"] then
t.text="Bench: "..GLOBAL["DONE"]
end
end
end)
GLOBAL["Bench"]=3
t=gui:newTextLabel("no done yet!",0,0,300,100)
t:centerX()
t:centerY()

View File

@ -33,20 +33,21 @@ function love.run()
if love.load then love.load(arg) end
if love.timer then love.timer.step() end
local dt = 0
local breakme=false
multi:newThread("MAIN-RUN",function()
while true do
-- Process events.
if love.event then
love.event.pump()
for e,a,b,c,d in love.event.poll() do
if e == "quit" then
for name, a,b,c,d,e,f in love.event.poll() do
if name == "quit" then
if not love.quit or not love.quit() then
if love.audio then
love.audio.stop()
end
return
breakme=true
thread.kill()
break
end
end
love.handlers[e](a,b,c,d)
love.handlers[name](a,b,c,d,e,f)
end
end
if love.timer then
@ -54,12 +55,6 @@ function love.run()
dt = love.timer.getDelta()
end
if love.update then love.update(dt) end
if multi.boost then
for i=1,multi.boost-1 do
multi:uManager(dt)
end
end
multi:uManager(dt)
if love.window and love.graphics and love.window.isCreated() then
love.graphics.clear()
love.graphics.origin()
@ -69,7 +64,19 @@ function love.run()
if multi.draw then multi.draw() end
love.graphics.present()
end
thread.sleep()
end
end)
while not breakme do
love.timer.sleep(.005)
multi:uManager(dt)
if multi.boost then
for i=1,multi.boost-1 do
multi:uManager(dt)
end
end
end
return
end
multi.drawF={}
function multi:dManager()
@ -81,34 +88,34 @@ function multi:onDraw(func,i)
i=i or 1
table.insert(self.drawF,i,func)
end
function multi:lManager()
if love.event then
love.event.pump()
for e,a,b,c,d in love.event.poll() do
if e == "quit" then
if not love.quit or not love.quit() then
if love.audio then
love.audio.stop()
end
return nil
end
end
love.handlers[e](a,b,c,d)
end
end
if love.timer then
love.timer.step()
dt = love.timer.getDelta()
end
if love.update then love.update(dt) end
multi:uManager(dt)
if love.window and love.graphics and love.window.isCreated() then
love.graphics.clear()
love.graphics.origin()
if love.draw then love.draw() end
multi.dManager()
love.graphics.setColor(255,255,255,255)
if multi.draw then multi.draw() end
love.graphics.present()
end
end
--~ function multi:lManager()
--~ if love.event then
--~ love.event.pump()
--~ for e,a,b,c,d in love.event.poll() do
--~ if e == "quit" then
--~ if not love.quit or not love.quit() then
--~ if love.audio then
--~ love.audio.stop()
--~ end
--~ return nil
--~ end
--~ end
--~ love.handlers[e](a,b,c,d)
--~ end
--~ end
--~ if love.timer then
--~ love.timer.step()
--~ dt = love.timer.getDelta()
--~ end
--~ if love.update then love.update(dt) end
--~ multi:uManager(dt)
--~ if love.window and love.graphics and love.window.isCreated() then
--~ love.graphics.clear()
--~ love.graphics.origin()
--~ if love.draw then love.draw() end
--~ multi.dManager()
--~ love.graphics.setColor(255,255,255,255)
--~ if multi.draw then multi.draw() end
--~ love.graphics.present()
--~ end
--~ end

File diff suppressed because it is too large Load Diff

View File

@ -21,6 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
]]
package.path="?/init.lua;?.lua;"..package.path
function os.getOS()
if package.config:sub(1,1)=='\\' then
return 'windows'
@ -32,9 +33,13 @@ end
lanes=require("lanes").configure()
--~ package.path="lua/?/init.lua;lua/?.lua;"..package.path
require("multi") -- get it all and have it on all lanes
isMainThread=true
function multi:canSystemThread()
return true
end
function multi:getPlatform()
return "lanes"
end
local multi=multi
-- Step 2 set up the linda objects
local __GlobalLinda = lanes.linda() -- handles global stuff
@ -85,7 +90,10 @@ else
THREAD.__CORES=tonumber(io.popen("nproc --all"):read("*n"))
end
function THREAD.kill() -- trigger the lane destruction
-- coroutine.yield({"_kill_",":)"})
error("Thread was killed!")
end
function THREAD.getName()
return THREAD_NAME
end
--[[ Step 4 We need to get sleeping working to handle timing... We want idle wait, not busy wait
Idle wait keeps the CPU running better where busy wait wastes CPU cycles... Lanes does not have a sleep method
@ -102,12 +110,17 @@ function THREAD.hold(n)
repeat wait() until n()
end
-- Step 5 Basic Threads!
function multi:newSystemThread(name,func)
function multi:newSystemThread(name,func,...)
local c={}
local __self=c
c.name=name
c.Type="sthread"
c.thread=lanes.gen("*", func)()
local THREAD_NAME=name
local function func2(...)
_G["THREAD_NAME"]=THREAD_NAME
func()
end
c.thread=lanes.gen("*", func2)(...)
function c:kill()
--self.status:Destroy()
self.thread:cancel()

View File

@ -2,17 +2,22 @@ require("multi.compat.love2d")
function multi:canSystemThread()
return true
end
function multi:getPlatform()
return "love2d"
end
multi.integration={}
multi.integration.love2d={}
multi.integration.love2d.ThreadBase=[[
tab={...}
__THREADNAME__=tab[2]
__THREADID__=tab[1]
__THREADID__=table.remove(tab,1)
__THREADNAME__=table.remove(tab,1)
require("love.filesystem")
require("love.system")
require("love.timer")
require("love.image")
require("multi")
GLOBAL={}
isMainThread=false
setmetatable(GLOBAL,{
__index=function(t,k)
__sync__()
@ -31,7 +36,7 @@ setmetatable(GLOBAL,{
function __sync__()
local data=__mythread__:pop()
while data do
love.timer.sleep(.001)
love.timer.sleep(.01)
if type(data)=="string" then
local cmd,tp,name,d=data:match("(%S-) (%S-) (%S-) (.+)")
if name=="__DIEPLZ"..__THREADID__.."__" then
@ -136,6 +141,12 @@ function dump(func)
end
return table.concat(code)
end
function sThread.getName()
return __THREADNAME__
end
function sThread.kill()
error("Thread was killed!")
end
function sThread.set(name,val)
GLOBAL[name]=val
end
@ -155,17 +166,9 @@ end
function sThread.hold(n)
repeat __sync__() until n()
end
multi:newLoop(function(self)
self:Pause()
local ld=multi:getLoad()
self:Resume()
if ld<80 then
love.timer.sleep(.01)
end
end)
updater=multi:newUpdater()
updater:OnUpdate(__sync__)
func=loadDump([=[INSERT_USER_CODE]=])()
func=loadDump([=[INSERT_USER_CODE]=])(unpack(tab))
multi:mainloop()
]]
GLOBAL={} -- Allow main thread to interact with these objects as well
@ -187,6 +190,10 @@ setmetatable(GLOBAL,{
})
THREAD={} -- Allow main thread to interact with these objects as well
multi.integration.love2d.mainChannel=love.thread.getChannel("__MainChan__")
isMainThread=true
function THREAD.getName()
return __THREADNAME__
end
function ToStr(val, name, skipnewlines, depth)
skipnewlines = skipnewlines or false
depth = depth or 0
@ -265,12 +272,12 @@ local function randomString(n)
end
return str
end
function multi:newSystemThread(name,func) -- the main method
function multi:newSystemThread(name,func,...) -- the main method
local c={}
c.name=name
c.ID=c.name.."<ID|"..randomString(8)..">"
c.thread=love.thread.newThread(multi.integration.love2d.ThreadBase:gsub("INSERT_USER_CODE",dump(func)))
c.thread:start(c.ID,c.name)
c.thread:start(c.ID,c.name,...)
function c:kill()
multi.integration.GLOBAL["__DIEPLZ"..self.ID.."__"]="__DIEPLZ"..self.ID.."__"
end
@ -288,9 +295,7 @@ function THREAD.get(name)
return GLOBAL[name]
end
function THREAD.waitFor(name)
multi.OBJ_REF:Pause()
repeat multi:lManager() until GLOBAL[name]
multi.OBJ_REF:Resume()
repeat multi:uManager() until GLOBAL[name]
return GLOBAL[name]
end
function THREAD.getCores()
@ -300,9 +305,7 @@ function THREAD.sleep(n)
love.timer.sleep(n)
end
function THREAD.hold(n)
multi.OBJ_REF:Pause()
repeat multi:lManager() until n()
multi.OBJ_REF:Resume()
repeat multi:uManager() until n()
end
__channels__={}
multi.integration.GLOBAL=GLOBAL
@ -311,7 +314,6 @@ updater=multi:newUpdater()
updater:OnUpdate(function(self)
local data=multi.integration.love2d.mainChannel:pop()
while data do
--print("MAIN:",data)
if type(data)=="string" then
local cmd,tp,name,d=data:match("(%S-) (%S-) (%S-) (.+)")
if cmd=="SYNC" then

View File

@ -0,0 +1,127 @@
--[[
MIT License
Copyright (c) 2017 Ryan Ward
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
]]
-- I DEMAND USAGE FOR LUVIT
-- Cannot use discordia without my multitasking library (Which I love more that the luvit platform... then again i'm partial :P)
package.path="?/init.lua;?.lua;"..package.path
local function _INIT(luvitThread,timer)
-- lots of this stuff should be able to stay the same
function os.getOS()
if package.config:sub(1,1)=='\\' then
return 'windows'
else
return 'unix'
end
end
-- Step 1 get setup threads on luvit... Sigh how do i even...
require("multi")
isMainThread=true
function multi:canSystemThread()
return true
end
function multi:getPlatform()
return "luvit"
end
local multi=multi
-- Step 2 set up the Global table... is this possible?
local GLOBAL={}
setmetatable(GLOBAL,{
__index=function(t,k)
--print("No Global table when using luvit integration!")
return nil
end,
__newindex=function(t,k,v)
--print("No Global table when using luvit integration!")
end,
})
local THREAD={}
function THREAD.set(name,val)
--print("No Global table when using luvit integration!")
end
function THREAD.get(name)
--print("No Global table when using luvit integration!")
end
local function randomString(n)
local str = ''
local strings = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','1','2','3','4','5','6','7','8','9','0','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'}
for i=1,n do
str = str..''..strings[math.random(1,#strings)]
end
return str
end
function THREAD.waitFor(name)
--print("No Global table when using luvit integration!")
end
function THREAD.testFor(name,val,sym)
--print("No Global table when using luvit integration!")
end
function THREAD.getCores()
return THREAD.__CORES
end
if os.getOS()=="windows" then
THREAD.__CORES=tonumber(os.getenv("NUMBER_OF_PROCESSORS"))
else
THREAD.__CORES=tonumber(io.popen("nproc --all"):read("*n"))
end
function THREAD.kill() -- trigger the thread destruction
error("Thread was Killed!")
end
-- hmmm if im cleaver I can get this to work... but since data passing isn't going to be a thing its probably not important
function THREAD.sleep(n)
--print("No Global table when using luvit integration!")
end
function THREAD.hold(n)
--print("No Global table when using luvit integration!")
end
-- Step 5 Basic Threads!
local function entry(path,name,func,...)
local timer = require'timer'
local luvitThread = require'thread'
package.path=path
loadstring(func)(...)
end
function multi:newSystemThread(name,func,...)
local c={}
local __self=c
c.name=name
c.Type="sthread"
c.thread={}
c.func=string.dump(func)
function c:kill()
-- print("No Global table when using luvit integration!")
end
luvitThread.start(entry,package.path,name,c.func,...)
return c
end
print("Integrated Luvit!")
multi.integration={} -- for module creators
multi.integration.GLOBAL=GLOBAL
multi.integration.THREAD=THREAD
require("multi.integration.shared")
-- Start the main mainloop... This allows you to process your multi objects, but the engine on the main thread will be limited to .001 or 1 millisecond sigh...
local interval = timer.setInterval(1, function ()
multi:uManager()
end)
end
return {init=function(threadHandle,timerHandle) _INIT(threadHandle,timerHandle) return GLOBAL,THREAD end}

View File

@ -21,35 +21,70 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
]]
function multi.randomString(n)
local str = ''
local strings = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','1','2','3','4','5','6','7','8','9','0','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'}
for i=1,n do
str = str..''..strings[math.random(1,#strings)]
end
return str
end
function multi:newSystemThreadedQueue(name) -- in love2d this will spawn a channel on both ends
local c={} -- where we will store our object
c.name=name -- set the name this is important for the love2d side
if love then -- check love
if love.thread then -- make sure we can use the threading module
function c:init() -- create an init function so we can mimic on bith love2d and lanes
function c:init() -- create an init function so we can mimic on both love2d and lanes
self.chan=love.thread.getChannel(self.name) -- create channel by the name self.name
function self:push(v) -- push to the channel
self.chan:push({type(v),resolveData(v)})
local tab
if type(v)=="table" then
tab = {}
for i,c in pairs(v) do
if type(c)=="function" then
tab[i]="\1"..string.dump(c)
else
tab[i]=c
end
end
self.chan:push(tab)
else
self.chan:push(c)
end
end
function self:pop() -- pop from the channel
local tab=self.chan:pop()
--print(tab)
if not tab then return end
return resolveType(tab[1],tab[2])
local v=self.chan:pop()
if not v then return end
if type(v)=="table" then
tab = {}
for i,c in pairs(v) do
if type(c)=="string" then
if c:sub(1,1)=="\1" then
tab[i]=loadstring(c:sub(2,-1))
else
tab[i]=c
end
else
tab[i]=c
end
end
return tab
else
return self.chan:pop()
end
end
function self:peek()
local tab=self.chan:peek()
--print(tab)
if not tab then return end
return resolveType(tab[1],tab[2])
local v=self.chan:peek()
if not v then return end
if type(v)=="table" then
tab = {}
for i,c in pairs(v) do
if type(c)=="string" then
if c:sub(1,1)=="\1" then
tab[i]=loadstring(c:sub(2,-1))
else
tab[i]=c
end
else
tab[i]=c
end
end
return tab
else
return self.chan:pop()
end
end
GLOBAL[self.name]=self -- send the object to the thread through the global interface
return self -- return the object
@ -59,7 +94,7 @@ function multi:newSystemThreadedQueue(name) -- in love2d this will spawn a chann
error("Make sure you required the love.thread module!") -- tell the user if he/she didn't require said module
end
else
c.linda=lanes.linda() -- lanes is a bit eaiser, create the linda on the main thread
c.linda=lanes.linda() -- lanes is a bit easier, create the linda on the main thread
function c:push(v) -- push to the queue
self.linda:send("Q",v)
end
@ -76,6 +111,100 @@ function multi:newSystemThreadedQueue(name) -- in love2d this will spawn a chann
end
return c
end
function multi:newSystemThreadedConnection(name,protect)
local c={}
c.name = name
c.protect=protect
local sThread=multi.integration.THREAD
local GLOBAL=multi.integration.GLOBAL
function c:init()
require("multi")
if multi:getPlatform()=="love2d" then
GLOBAL=_G.GLOBAL
sThread=_G.sThread
end
local conn = {}
conn.name = self.name
conn.count = 0
if isMainThread then
if GLOBAL[self.name.."THREADED_CONNQ"] then -- if this thing exists then lets grab it, we are doing something different here. instead of cleaning things up, we will gave a dedicated queue to manage things
conn.queueCall = sThread.waitFor(self.name.."THREADED_CALLQ"):init()
else
conn.queueCall = multi:newSystemThreadedQueue(self.name.."THREADED_CALLQ"):init()
end
else
require("multi") -- so things don't break, but also allows bi-directional connections to work
conn.queueCall = sThread.waitFor(self.name.."THREADED_CALLQ"):init()
end
setmetatable(conn,{__call=function(self,...) return self:connect(...) end})
conn.obj=multi:newConnection(self.protect)
function conn:connect(func)
return self.obj(func)
end
function conn:fConnect(func)
return self.obj:fConnect(func)
end
function conn:holdUT(n)
self.obj:holdUT(n)
end
function conn:Bind(t)
self.obj:Bind(t)
end
function conn:Remove()
self.obj:Remove()
end
function conn:getConnection(name,ingore)
return self.obj:getConnection(name,ingore)
end
function conn:Fire(...)
local args = {...}
table.insert(args,1,multi.randomString(8))
table.insert(args,1,self.name)
table.insert(args,1,"F")
self.queueCall:push(args)
if self.trigger_self then
self.obj:Fire(...)
end
end
self.cleanup = .01
function conn:SetCleanUpRate(n)
self.cleanup=n or .01
end
conn.lastid=""
conn.looper = multi:newLoop(function(self)
local con = self.link
local data = con.queueCall:peek()
if not data then return end
local id = data[3]
if data[1]=="F" and data[2]==con.name and con.lastid~=id then
con.lastid=id
table.remove(data,1)-- Remove the first 3 elements
table.remove(data,1)-- Remove the first 3 elements
table.remove(data,1)-- Remove the first 3 elements
con.obj:Fire(unpack(data))
multi:newThread("Clean_UP",function()
thread.sleep(con.cleanup)
local dat = con.queueCall:peek()
if not dat then return end
table.remove(data,1)-- Remove the first 3 elements
table.remove(data,1)-- Remove the first 3 elements
table.remove(data,1)-- Remove the first 3 elements
if dat[3]==id then
con.queueCall:pop()
end
end)
end
end)
conn.HoldUT=conn.holdUT
conn.looper.link=conn
conn.Connect=conn.connect
conn.FConnect=conn.fConnect
conn.GetConnection=conn.getConnection
return conn
end
GLOBAL[name]=c
return c
end
function multi:systemThreadedBenchmark(n,p)
n=n or 1
local cores=multi.integration.THREAD.getCores()
@ -120,88 +249,43 @@ function multi:systemThreadedBenchmark(n,p)
end)
return c
end
function multi:newSystemThreadedTable(name,n)
local c={} -- where we will store our object
c.name=name -- set the name this is important for the love2d side
c.cores=n
c.hasT={}
if love then -- check love
if love.thread then -- make sure we can use the threading module
function c:init() -- create an init function so we can mimic on bith love2d and lanes
self.tab={}
self.chan=love.thread.getChannel(self.name) -- create channel by the name self.name
function self:waitFor(name) -- pop from the channel
repeat self:sync() until self[name]
return self[name]
end
function self:sync()
local data=self.chan:peek()
if data then
local cmd,tp,name,d=data:match("(%S-) (%S-) (%S-) (.+)")
if not self.hasT[name] then
if type(data)=="string" then
if cmd=="SYNC" then
self.tab[name]=resolveType(tp,d) -- this is defined in the loveManager.lua file
self.hasT[name]=true
function multi:newSystemThreadedTable(name)
local c={}
c.name=name -- set the name this is important for identifying what is what
local sThread=multi.integration.THREAD
local GLOBAL=multi.integration.GLOBAL
function c:init() -- create an init function so we can mimic on both love2d and lanes
if multi:getPlatform()=="love2d" then
GLOBAL=_G.GLOBAL
sThread=_G.sThread
end
local cc={}
cc.tab={}
if isMainThread then
cc.conn = multi:newSystemThreadedConnection(self.name.."_Tabled_Connection"):init()
else
self.tab[name]=data
cc.conn = sThread.waitFor(self.name.."_Tabled_Connection"):init()
end
self.chan:pop()
function cc:waitFor(name)
repeat multi:uManager() until tab[name]~=nil
return tab[name]
end
end
end
function self:reset(name)
self.hasT[core]=nil
end
setmetatable(self,{
local link = cc
cc.conn(function(k,v)
link.tab[k]=v
end)
setmetatable(cc,{
__index=function(t,k)
self:sync()
return self.tab[k]
return t.tab[k]
end,
__newindex=function(t,k,v)
self:sync()
self.tab[k]=v
if type(v)=="userdata" then
self.chan:push(v)
else
for i=1,self.cores do
self.chan:push("SYNC "..type(v).." "..k.." "..resolveData(v)) -- this is defined in the loveManager.lua file
end
end
t.tab[k]=v
t.conn:Fire(k,v)
end,
})
GLOBAL[self.name]=self -- send the object to the thread through the global interface
return self -- return the object
end
return c
else
error("Make sure you required the love.thread module!") -- tell the user if he/she didn't require said module
end
else
c.linda=lanes.linda() -- lanes is a bit eaiser, create the linda on the main thread
function c:waitFor(name)
while self[name]==nil do
-- Waiting
end
return self[name]
end
function c:sync()
return -- just so we match the love2d side
end
function c:init() -- set the metatable
setmetatable(self,{
__index=function(t,k)
return self.linda:get(k)
end,
__newindex=function(t,k,v)
self.linda:set(k,v)
end,
})
return self
end
multi.integration.GLOBAL[name]=c -- send the object to the thread through the global interface
return cc
end
GLOBAL[c.name]=c
return c
end
function multi:newSystemThreadedJobQueue(numOfCores)
@ -212,8 +296,7 @@ function multi:newSystemThreadedJobQueue(numOfCores)
c.queueOUT=multi:newSystemThreadedQueue("THREADED_JQO"):init()
c.queueALL=multi:newSystemThreadedQueue("THREADED_QALL"):init()
c.REG=multi:newSystemThreadedQueue("THREADED_JQ_F_REG"):init()
-- registerJob(name,func)
-- pushJob(...)
c.OnReady=multi:newConnection()
function c:registerJob(name,func)
for i=1,self.cores do
self.REG:push({name,func})
@ -222,9 +305,10 @@ function multi:newSystemThreadedJobQueue(numOfCores)
function c:pushJob(name,...)
self.queueOUT:push({self.jobnum,name,...})
self.jobnum=self.jobnum+1
return self.jobnum-1
end
local GLOBAL=multi.integration.GLOBAL -- set up locals incase we are using lanes
local sThread=multi.integration.THREAD -- set up locals incase we are using lanes
local GLOBAL=multi.integration.GLOBAL -- set up locals in case we are using lanes
local sThread=multi.integration.THREAD -- set up locals in case we are using lanes
function c:doToAll(func)
local TaskName=multi.randomString(16)
for i=1,self.cores do
@ -232,17 +316,26 @@ function multi:newSystemThreadedJobQueue(numOfCores)
end
end
function c:start()
multi:newEvent(function()
return self.ThreadsLoaded==true
end):OnEvent(function(evnt)
GLOBAL["THREADED_JQ"]=nil -- remove it
GLOBAL["THREADED_JQO"]=nil -- remove it
GLOBAL["THREADED_JQ_F_REG"]=nil -- remove it
self:doToAll(function()
_G["__started__"]=true
SFunc()
end)
evnt:Destroy()
end)
end
GLOBAL["__JQ_COUNT__"]=c.cores
for i=1,c.cores do
multi:newSystemThread("System Threaded Job Queue Worker Thread #"..i,function()
multi:newSystemThread("System Threaded Job Queue Worker Thread #"..i,function(name,ind)
require("multi")
ThreadName=name
__sleep__=.001
if love then -- lets make sure we don't reference upvalues if using love2d
if love then -- lets make sure we don't reference up-values if using love2d
GLOBAL=_G.GLOBAL
sThread=_G.sThread
__sleep__=.1
@ -251,10 +344,6 @@ function multi:newSystemThreadedJobQueue(numOfCores)
JQO=sThread.waitFor("THREADED_JQ"):init() -- Grab it
REG=sThread.waitFor("THREADED_JQ_F_REG"):init() -- Grab it
QALL=sThread.waitFor("THREADED_QALL"):init() -- Grab it
sThread.sleep(.1) -- lets wait for things to work out
GLOBAL["THREADED_JQ"]=nil -- remove it
GLOBAL["THREADED_JQO"]=nil -- remove it
GLOBAL["THREADED_JQ_F_REG"]=nil -- remove it
QALLT={}
FUNCS={}
SFunc=multi:newFunction(function(self)
@ -287,10 +376,12 @@ function multi:newSystemThreadedJobQueue(numOfCores)
return FUNCS[k]
end
})
lastjob=os.clock()
MainLoop=multi:newLoop(function(self)
if __started__ then
local job=JQI:pop()
if job then
lastjob=os.clock()
local d=QALL:peek()
if d then
if not QALLT[d[1]] then
@ -311,18 +402,37 @@ function multi:newSystemThreadedJobQueue(numOfCores)
end
end
end)
multi:newThread("Idler",function()
while true do
if os.clock()-lastjob>1 then
sThread.sleep(.1)
end
thread.sleep(.001)
end
end)
JQO:push({"_THREADINIT_"})
if not love then
multi:mainloop()
end
end)
end,"Thread<"..i..">",i)
end
c.OnJobCompleted=multi:newConnection()
c.threadsResponded = 0
c.updater=multi:newLoop(function(self)
local data=self.link.queueIN:pop()
while data do
if data then
local a=unpack(data)
if a=="_THREADINIT_" then
self.link.threadsResponded=self.link.threadsResponded+1
if self.link.threadsResponded==self.link.cores then
self.link.ThreadsLoaded=true
self.link.OnReady:Fire()
end
else
self.link.OnJobCompleted:Fire(unpack(data))
end
end
data=self.link.queueIN:pop()
end
end)

View File

@ -23,9 +23,9 @@ SOFTWARE.
]]
require("bin")
multi = {}
multi.Version = "1.9.2"
multi._VERSION = "1.9.2"
multi.stage = "mostly-stable"
multi.Version = "1.10.0"
multi._VERSION = "1.10.0"
multi.stage = "stable"
multi.__index = multi
multi.Mainloop = {}
multi.Tasks = {}
@ -162,6 +162,14 @@ else
os.execute('sleep ' .. tonumber(n))
end
end
function multi.randomString(n)
local str = ''
local strings = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','1','2','3','4','5','6','7','8','9','0','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'}
for i=1,n do
str = str..''..strings[math.random(1,#strings)]
end
return str
end
function multi:getParentProcess()
return self.Mainloop[self.CID]
end
@ -798,25 +806,32 @@ function multi:newConnection(protect)
c.connections={}
c.fconnections={}
c.FC=0
function c:holdUT()
function c:holdUT(n)
local n=n or 0
self.waiting=true
local count=0
local id=self:connect(function()
count = count + 1
if n<=count then
self.waiting=false
end
end)
repeat
self.Parent:uManager()
until self.waiting==false
id:Destroy()
end
c.HoldUT=c.holdUT
function c:fConnect(func)
local temp=self:connect(func)
table.insert(self.fconnections,temp)
self.FC=self.FC+1
end
c.FConnect=c.fConnect
function c:getConnection(name,ingore)
if ingore then
return self.connections[name] or {
Fire=function() end -- if the connection doesn't exist lets call all of them or silently ingore
Fire=function() end -- if the connection doesn't exist lets call all of them or silently ignore
}
else
return self.connections[name] or self
@ -889,6 +904,7 @@ function multi:newConnection(protect)
return temp
end
c.Connect=c.connect
c.GetConnection=c.getConnection
function c:tofile(path)
local m=bin.new()
m:addBlock(self.Type)

View File

@ -33,6 +33,7 @@ end
lanes=require("lanes").configure()
--~ package.path="lua/?/init.lua;lua/?.lua;"..package.path
require("multi") -- get it all and have it on all lanes
isMainThread=true
function multi:canSystemThread()
return true
end

View File

@ -17,6 +17,7 @@ require("love.timer")
require("love.image")
require("multi")
GLOBAL={}
isMainThread=false
setmetatable(GLOBAL,{
__index=function(t,k)
__sync__()
@ -189,6 +190,7 @@ setmetatable(GLOBAL,{
})
THREAD={} -- Allow main thread to interact with these objects as well
multi.integration.love2d.mainChannel=love.thread.getChannel("__MainChan__")
isMainThread=true
function THREAD.getName()
return __THREADNAME__
end
@ -293,9 +295,7 @@ function THREAD.get(name)
return GLOBAL[name]
end
function THREAD.waitFor(name)
multi.OBJ_REF:Pause()
repeat multi:lManager() until GLOBAL[name]
multi.OBJ_REF:Resume()
repeat multi:uManager() until GLOBAL[name]
return GLOBAL[name]
end
function THREAD.getCores()
@ -305,9 +305,7 @@ function THREAD.sleep(n)
love.timer.sleep(n)
end
function THREAD.hold(n)
multi.OBJ_REF:Pause()
repeat multi:lManager() until n()
multi.OBJ_REF:Resume()
repeat multi:uManager() until n()
end
__channels__={}
multi.integration.GLOBAL=GLOBAL
@ -316,7 +314,6 @@ updater=multi:newUpdater()
updater:OnUpdate(function(self)
local data=multi.integration.love2d.mainChannel:pop()
while data do
--print("MAIN:",data)
if type(data)=="string" then
local cmd,tp,name,d=data:match("(%S-) (%S-) (%S-) (.+)")
if cmd=="SYNC" then

View File

@ -36,6 +36,7 @@ local function _INIT(luvitThread,timer)
end
-- Step 1 get setup threads on luvit... Sigh how do i even...
require("multi")
isMainThread=true
function multi:canSystemThread()
return true
end

View File

@ -21,20 +21,12 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
]]
function multi.randomString(n)
local str = ''
local strings = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','1','2','3','4','5','6','7','8','9','0','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'}
for i=1,n do
str = str..''..strings[math.random(1,#strings)]
end
return str
end
function multi:newSystemThreadedQueue(name) -- in love2d this will spawn a channel on both ends
local c={} -- where we will store our object
c.name=name -- set the name this is important for the love2d side
if love then -- check love
if love.thread then -- make sure we can use the threading module
function c:init() -- create an init function so we can mimic on bith love2d and lanes
function c:init() -- create an init function so we can mimic on both love2d and lanes
self.chan=love.thread.getChannel(self.name) -- create channel by the name self.name
function self:push(v) -- push to the channel
local tab
@ -102,7 +94,7 @@ function multi:newSystemThreadedQueue(name) -- in love2d this will spawn a chann
error("Make sure you required the love.thread module!") -- tell the user if he/she didn't require said module
end
else
c.linda=lanes.linda() -- lanes is a bit eaiser, create the linda on the main thread
c.linda=lanes.linda() -- lanes is a bit easier, create the linda on the main thread
function c:push(v) -- push to the queue
self.linda:send("Q",v)
end
@ -119,6 +111,100 @@ function multi:newSystemThreadedQueue(name) -- in love2d this will spawn a chann
end
return c
end
function multi:newSystemThreadedConnection(name,protect)
local c={}
c.name = name
c.protect=protect
local sThread=multi.integration.THREAD
local GLOBAL=multi.integration.GLOBAL
function c:init()
require("multi")
if multi:getPlatform()=="love2d" then
GLOBAL=_G.GLOBAL
sThread=_G.sThread
end
local conn = {}
conn.name = self.name
conn.count = 0
if isMainThread then
if GLOBAL[self.name.."THREADED_CONNQ"] then -- if this thing exists then lets grab it, we are doing something different here. instead of cleaning things up, we will gave a dedicated queue to manage things
conn.queueCall = sThread.waitFor(self.name.."THREADED_CALLQ"):init()
else
conn.queueCall = multi:newSystemThreadedQueue(self.name.."THREADED_CALLQ"):init()
end
else
require("multi") -- so things don't break, but also allows bi-directional connections to work
conn.queueCall = sThread.waitFor(self.name.."THREADED_CALLQ"):init()
end
setmetatable(conn,{__call=function(self,...) return self:connect(...) end})
conn.obj=multi:newConnection(self.protect)
function conn:connect(func)
return self.obj(func)
end
function conn:fConnect(func)
return self.obj:fConnect(func)
end
function conn:holdUT(n)
self.obj:holdUT(n)
end
function conn:Bind(t)
self.obj:Bind(t)
end
function conn:Remove()
self.obj:Remove()
end
function conn:getConnection(name,ingore)
return self.obj:getConnection(name,ingore)
end
function conn:Fire(...)
local args = {...}
table.insert(args,1,multi.randomString(8))
table.insert(args,1,self.name)
table.insert(args,1,"F")
self.queueCall:push(args)
if self.trigger_self then
self.obj:Fire(...)
end
end
self.cleanup = .01
function conn:SetCleanUpRate(n)
self.cleanup=n or .01
end
conn.lastid=""
conn.looper = multi:newLoop(function(self)
local con = self.link
local data = con.queueCall:peek()
if not data then return end
local id = data[3]
if data[1]=="F" and data[2]==con.name and con.lastid~=id then
con.lastid=id
table.remove(data,1)-- Remove the first 3 elements
table.remove(data,1)-- Remove the first 3 elements
table.remove(data,1)-- Remove the first 3 elements
con.obj:Fire(unpack(data))
multi:newThread("Clean_UP",function()
thread.sleep(con.cleanup)
local dat = con.queueCall:peek()
if not dat then return end
table.remove(data,1)-- Remove the first 3 elements
table.remove(data,1)-- Remove the first 3 elements
table.remove(data,1)-- Remove the first 3 elements
if dat[3]==id then
con.queueCall:pop()
end
end)
end
end)
conn.HoldUT=conn.holdUT
conn.looper.link=conn
conn.Connect=conn.connect
conn.FConnect=conn.fConnect
conn.GetConnection=conn.getConnection
return conn
end
GLOBAL[name]=c
return c
end
function multi:systemThreadedBenchmark(n,p)
n=n or 1
local cores=multi.integration.THREAD.getCores()
@ -163,88 +249,43 @@ function multi:systemThreadedBenchmark(n,p)
end)
return c
end
function multi:newSystemThreadedTable(name,n) -- NEDS FIXING SING SO MUCH WORK!!!
local c={} -- where we will store our object
c.name=name -- set the name this is important for the love2d side
c.cores=n
c.hasT={}
if love then -- check love
if love.thread then -- make sure we can use the threading module
function c:init() -- create an init function so we can mimic on bith love2d and lanes
self.tab={}
self.chan=love.thread.getChannel(self.name) -- create channel by the name self.name
function self:waitFor(name) -- pop from the channel
repeat self:sync() until self[name]
return self[name]
end
function self:sync()
local data=self.chan:peek()
if data then
local cmd,tp,name,d=data:match("(%S-) (%S-) (%S-) (.+)")
if not self.hasT[name] then
if type(data)=="string" then
if cmd=="SYNC" then
self.tab[name]=resolveType(tp,d) -- this is defined in the loveManager.lua file
self.hasT[name]=true
function multi:newSystemThreadedTable(name)
local c={}
c.name=name -- set the name this is important for identifying what is what
local sThread=multi.integration.THREAD
local GLOBAL=multi.integration.GLOBAL
function c:init() -- create an init function so we can mimic on both love2d and lanes
if multi:getPlatform()=="love2d" then
GLOBAL=_G.GLOBAL
sThread=_G.sThread
end
local cc={}
cc.tab={}
if isMainThread then
cc.conn = multi:newSystemThreadedConnection(self.name.."_Tabled_Connection"):init()
else
self.tab[name]=data
cc.conn = sThread.waitFor(self.name.."_Tabled_Connection"):init()
end
self.chan:pop()
function cc:waitFor(name)
repeat multi:uManager() until tab[name]~=nil
return tab[name]
end
end
end
function self:reset(name)
self.hasT[core]=nil
end
setmetatable(self,{
local link = cc
cc.conn(function(k,v)
link.tab[k]=v
end)
setmetatable(cc,{
__index=function(t,k)
self:sync()
return self.tab[k]
return t.tab[k]
end,
__newindex=function(t,k,v)
self:sync()
self.tab[k]=v
if type(v)=="userdata" then
self.chan:push(v)
else
for i=1,self.cores do
self.chan:push("SYNC "..type(v).." "..k.." "..resolveData(v)) -- this is defined in the loveManager.lua file
end
end
t.tab[k]=v
t.conn:Fire(k,v)
end,
})
GLOBAL[self.name]=self -- send the object to the thread through the global interface
return self -- return the object
end
return c
else
error("Make sure you required the love.thread module!") -- tell the user if he/she didn't require said module
end
else
c.linda=lanes.linda() -- lanes is a bit eaiser, create the linda on the main thread
function c:waitFor(name)
while self[name]==nil do
-- Waiting
end
return self[name]
end
function c:sync()
return -- just so we match the love2d side
end
function c:init() -- set the metatable
setmetatable(self,{
__index=function(t,k)
return self.linda:get(k)
end,
__newindex=function(t,k,v)
self.linda:set(k,v)
end,
})
return self
end
multi.integration.GLOBAL[name]=c -- send the object to the thread through the global interface
return cc
end
GLOBAL[c.name]=c
return c
end
function multi:newSystemThreadedJobQueue(numOfCores)
@ -266,8 +307,8 @@ function multi:newSystemThreadedJobQueue(numOfCores)
self.jobnum=self.jobnum+1
return self.jobnum-1
end
local GLOBAL=multi.integration.GLOBAL -- set up locals incase we are using lanes
local sThread=multi.integration.THREAD -- set up locals incase we are using lanes
local GLOBAL=multi.integration.GLOBAL -- set up locals in case we are using lanes
local sThread=multi.integration.THREAD -- set up locals in case we are using lanes
function c:doToAll(func)
local TaskName=multi.randomString(16)
for i=1,self.cores do
@ -294,7 +335,7 @@ function multi:newSystemThreadedJobQueue(numOfCores)
require("multi")
ThreadName=name
__sleep__=.001
if love then -- lets make sure we don't reference upvalues if using love2d
if love then -- lets make sure we don't reference up-values if using love2d
GLOBAL=_G.GLOBAL
sThread=_G.sThread
__sleep__=.1
@ -369,20 +410,22 @@ function multi:newSystemThreadedJobQueue(numOfCores)
thread.sleep(.001)
end
end)
JQO:push({"_THREADINIT_",ind})
JQO:push({"_THREADINIT_"})
if not love then
multi:mainloop()
end
end,"Thread<"..i..">",i)
end
c.OnJobCompleted=multi:newConnection()
c.threadsResponded = 0
c.updater=multi:newLoop(function(self)
local data=self.link.queueIN:pop()
while data do
if data then
local a,b=unpack(data)
local a=unpack(data)
if a=="_THREADINIT_" then
if b==self.link.cores then
self.link.threadsResponded=self.link.threadsResponded+1
if self.link.threadsResponded==self.link.cores then
self.link.ThreadsLoaded=true
self.link.OnReady:Fire()
end