From 871db53cbc03458a88c7365afc6c8de951a20496 Mon Sep 17 00:00:00 2001 From: Edd Barrett Date: Tue, 30 Jul 2024 10:56:43 +0100 Subject: [PATCH] Add initial benchmarks. This adds the Lua benchmarks from awfy and a CI task that just runs them quickly using regular Lua (not yklua). We do no data processing at this time. Future PRs will add Yk support and add support for processing results. --- .buildbot.sh | 8 + .buildbot_dockerfile_default | 11 + .github/workflows/sdci.yml | 11 + .gitignore | 1 + LICENSE-awfy | 5 + README.md | 15 + awfy/Lua/benchmark.lua | 44 ++ awfy/Lua/bounce.lua | 99 ++++ awfy/Lua/cd.lua | 933 +++++++++++++++++++++++++++++++++ awfy/Lua/deltablue.lua | 751 ++++++++++++++++++++++++++ awfy/Lua/harness.lua | 98 ++++ awfy/Lua/hashindextable-53.lua | 42 ++ awfy/Lua/hashindextable.lua | 45 ++ awfy/Lua/havlak.lua | 647 +++++++++++++++++++++++ awfy/Lua/json.lua | 587 +++++++++++++++++++++ awfy/Lua/list.lua | 88 ++++ awfy/Lua/mandelbrot-fn-53.lua | 71 +++ awfy/Lua/mandelbrot-fn.lua | 74 +++ awfy/Lua/mandelbrot.lua | 75 +++ awfy/Lua/nbody.lua | 189 +++++++ awfy/Lua/permute.lua | 58 ++ awfy/Lua/queens.lua | 77 +++ awfy/Lua/richards.lua | 517 ++++++++++++++++++ awfy/Lua/sieve.lua | 55 ++ awfy/Lua/som.lua | 621 ++++++++++++++++++++++ awfy/Lua/storage.lua | 56 ++ awfy/Lua/towers.lua | 83 +++ rebench.conf | 51 ++ 28 files changed, 5312 insertions(+) create mode 100644 .buildbot.sh create mode 100644 .buildbot_dockerfile_default create mode 100644 .github/workflows/sdci.yml create mode 100644 .gitignore create mode 100644 LICENSE-awfy create mode 100644 awfy/Lua/benchmark.lua create mode 100644 awfy/Lua/bounce.lua create mode 100644 awfy/Lua/cd.lua create mode 100644 awfy/Lua/deltablue.lua create mode 100755 awfy/Lua/harness.lua create mode 100644 awfy/Lua/hashindextable-53.lua create mode 100644 awfy/Lua/hashindextable.lua create mode 100644 awfy/Lua/havlak.lua create mode 100644 awfy/Lua/json.lua create mode 100644 awfy/Lua/list.lua create mode 100644 awfy/Lua/mandelbrot-fn-53.lua create mode 100644 awfy/Lua/mandelbrot-fn.lua create mode 100644 awfy/Lua/mandelbrot.lua create mode 100644 awfy/Lua/nbody.lua create mode 100644 awfy/Lua/permute.lua create mode 100644 awfy/Lua/queens.lua create mode 100644 awfy/Lua/richards.lua create mode 100644 awfy/Lua/sieve.lua create mode 100644 awfy/Lua/som.lua create mode 100644 awfy/Lua/storage.lua create mode 100644 awfy/Lua/towers.lua create mode 100644 rebench.conf diff --git a/.buildbot.sh b/.buildbot.sh new file mode 100644 index 0000000..05c6bc5 --- /dev/null +++ b/.buildbot.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +set -eu + +pipx install rebench + +# Do a "quick" run as a smoke-test. +~/.local/bin/rebench --quick --no-denoise -c rebench.conf diff --git a/.buildbot_dockerfile_default b/.buildbot_dockerfile_default new file mode 100644 index 0000000..6c375ad --- /dev/null +++ b/.buildbot_dockerfile_default @@ -0,0 +1,11 @@ +FROM debian:latest +WORKDIR /ci +RUN --mount=target=/var/lib/apt/lists,type=cache,sharing=locked \ + --mount=target=/var/cache/apt,type=cache,sharing=locked \ + rm -f /etc/apt/apt.conf.d/docker-clean && \ + apt update && apt install -y pipx lua5.3 git +ARG CI_UID +RUN useradd -m -u ${CI_UID} ci && chown ${CI_UID}:${CI_UID} . +RUN chown ${CI_UID}:${CI_UID} . +COPY --chown=${CI_UID}:${CI_UID} . . +CMD sh -x .buildbot.sh diff --git a/.github/workflows/sdci.yml b/.github/workflows/sdci.yml new file mode 100644 index 0000000..f20da18 --- /dev/null +++ b/.github/workflows/sdci.yml @@ -0,0 +1,11 @@ +on: + pull_request: + merge_group: + +# This is required to silence emails about the workflow having no jobs. +# We simply define a dummy job that does nothing much. +jobs: + dummy: + runs-on: ubuntu-latest + steps: + - run: /usr/bin/true diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a2a76bb --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +benchmark.data diff --git a/LICENSE-awfy b/LICENSE-awfy new file mode 100644 index 0000000..71930bf --- /dev/null +++ b/LICENSE-awfy @@ -0,0 +1,5 @@ +See the headers in individual benchmarks source files in the `awfy` directory +for their licenses. + +Upstream lists licensing information here: +https://github.com/smarr/are-we-fast-yet/blob/master/LICENSE.md diff --git a/README.md b/README.md index b2f7fdb..a36de68 100644 --- a/README.md +++ b/README.md @@ -1 +1,16 @@ # Yk Benchmarks + +This is a repository of benchmarks for evaluating the performance of the [yk +meta-tracer](https://github.com/ykjit/yk/). + +## Suites + +At present the following benchmark suites are here: + +| **Suite** | **Languages** | +|--------------------------------------------------------------|---------------| +| [are-we-fast-yet](https://github.com/smarr/are-we-fast-yet/) | Lua | + +## Licenses + +See the `LICENSE-` files for information on software licenses. diff --git a/awfy/Lua/benchmark.lua b/awfy/Lua/benchmark.lua new file mode 100644 index 0000000..9650486 --- /dev/null +++ b/awfy/Lua/benchmark.lua @@ -0,0 +1,44 @@ +-- This code is derived from the SOM benchmarks, see AUTHORS.md file. +-- +-- Copyright (c) 2016 Francois Perrad +-- +-- 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. + +local benchmark = {} do + +function benchmark:inner_benchmark_loop (inner_iterations) + for _ = 1, inner_iterations do + if not self:verify_result(self:benchmark()) then + return false + end + end + return true +end + +function benchmark:benchmark () + error 'subclass_responsibility' +end + +function benchmark:verify_result () + error 'subclass_responsibility' +end + +end + +return benchmark diff --git a/awfy/Lua/bounce.lua b/awfy/Lua/bounce.lua new file mode 100644 index 0000000..2817432 --- /dev/null +++ b/awfy/Lua/bounce.lua @@ -0,0 +1,99 @@ +-- This code is derived from the SOM benchmarks, see AUTHORS.md file. +-- +-- Copyright (c) 2016 Francois Perrad +-- +-- 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. + +local Random = require'som'.Random + +local Ball = {_CLASS = 'Ball'} do + +local abs = math.abs + +function Ball.new (random) + local obj = { + x = random:next() % 500, + y = random:next() % 500, + x_vel = (random:next() % 300) - 150, + y_vel = (random:next() % 300) - 150, + } + return setmetatable(obj, {__index = Ball}) +end + +function Ball:bounce () + local x_limit, y_limit = 500, 500 + local bounced = false + self.x = self.x + self.x_vel + self.y = self.y + self.y_vel + if self.x > x_limit then + self.x = x_limit + self.x_vel = 0 - abs(self.x_vel) + bounced = true + end + if self.x < 0 then + self.x = 0 + self.x_vel = abs(self.x_vel) + bounced = true + end + if self.y > y_limit then + self.y = y_limit + self.y_vel = 0 - abs(self.y_vel) + bounced = true + end + if self.y < 0 then + self.y = 0 + self.y_vel = abs(self.y_vel) + bounced = true + end + return bounced +end + +end -- class Ball + +local bounce = {} do +setmetatable(bounce, {__index = require'benchmark'}) + +function bounce:benchmark () + local random = Random.new() + local ball_count = 100 + local bounces = 0 + local balls = {} + + for i = 1, ball_count do + balls[i] = Ball.new(random) + end + + for _ = 1, 50 do + for i = 1, #balls do + local ball = balls[i] + if ball:bounce() then + bounces = bounces + 1 + end + end + end + return bounces +end + +function bounce:verify_result (result) + return 1331 == result +end + +end -- object bounce + +return bounce diff --git a/awfy/Lua/cd.lua b/awfy/Lua/cd.lua new file mode 100644 index 0000000..dfad5ae --- /dev/null +++ b/awfy/Lua/cd.lua @@ -0,0 +1,933 @@ +-- This code is derived from the SOM benchmarks, see AUTHORS.md file. +-- +-- Copyright (c) 2016 Francois Perrad +-- +-- 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. + +local Vector = require'som'.Vector +local MIN_X = 0.0 +local MIN_Y = 0.0 +local MAX_X = 1000.0 +local MAX_Y = 1000.0 +local MIN_Z = 0.0 +local MAX_Z = 10.0 +local PROXIMITY_RADIUS = 1.0 +local GOOD_VOXEL_SIZE = PROXIMITY_RADIUS * 2.0 + +local Vector2D = {_CLASS = 'Vector2D'} do + +function Vector2D.new (x, y) + local obj = {x = x, y = y} + return setmetatable(obj, {__index = Vector2D}) +end + +function Vector2D:plus (other) + return Vector2D.new(self.x + other.x, self.y + other.y) +end + +function Vector2D:minus (other) + return Vector2D.new(self.x - other.x, self.y - other.y) +end + +local function compare_numbers (a, b) + if a == b then + return 0 + elseif a < b then + return -1 + elseif a > b then + return 1 + -- We say that NaN is smaller than non-NaN. + elseif a == a then + return 1 + else + return -1 + end +end + +function Vector2D:compare_to (other) + local result = compare_numbers(self.x, other.x) + if result ~= 0 then + return result + else + return compare_numbers(self.y, other.y) + end +end + +end -- class Vector2D + +local Vector3D = {_CLASS = 'Vector3D'} do + +local sqrt = math.sqrt + +function Vector3D.new (x, y, z) + local obj = {x = x, y = y, z = z} + return setmetatable(obj, {__index = Vector3D}) +end + +function Vector3D:plus (other) + return Vector3D.new(self.x + other.x, self.y + other.y, self.z + other.z) +end + +function Vector3D:minus (other) + return Vector3D.new(self.x - other.x, self.y - other.y, self.z - other.z) +end + +function Vector3D:dot (other) + return self.x * other.x + self.y * other.y + self.z * other.z +end + +function Vector3D:squared_magnitude () + return self:dot(self) +end + +function Vector3D:magnitude () + return sqrt(self:squared_magnitude()) +end + +function Vector3D:times (amount) + return Vector3D.new(self.x * amount, self.y * amount, self.z * amount) +end + +end -- class Vector3D + +local function tree_minimum (x) + local current = x + while current.left do + current = current.left + end + return current +end + +local Node = {_CLASS = 'Node'} do + +function Node.new (key, value) + local obj = { + key = key, + value = value, + left = nil, + right = nil, + parent = nil, + color = 'red', + } + return setmetatable(obj, {__index = Node}) +end + +function Node:successor () + local x = self + if x.right then + return tree_minimum(x.right) + end + + local y = x.parent + while y and x == y.right do + x = y + y = y.parent + end + return y +end + +end -- class Node + +local RbtEntry = {_CLASS = 'RbtEntry'} do + +function RbtEntry.new(key, value) + local obj = { + key = key, + value = value + } + return setmetatable(obj, {__index = RbtEntry}) +end + +end -- class RbtEntry + +local InsertResult = {_CLASS = 'InsertResult'} do + +function InsertResult.new(is_new_entry, new_node, old_value) + local obj = { + is_new_entry = is_new_entry, + new_node = new_node, + old_value = old_value, + } + return setmetatable(obj, {__index = InsertResult}) +end + +end -- class InsertResult + +local RedBlackTree = {_CLASS = 'RedBlackTree'} do + +function RedBlackTree.new () + local obj = {root = nil} + return setmetatable(obj, {__index = RedBlackTree}) +end + +function RedBlackTree:put (key, value) + local insertion_result = self:tree_insert(key, value) + if not insertion_result.is_new_entry then + return insertion_result.old_value + end + + local x = insertion_result.new_node + + while x ~= self.root and x.parent.color == 'red' do + if x.parent == x.parent.parent.left then + local y = x.parent.parent.right + if y and y.color == 'red' then + -- Case 1 + x.parent.color = 'black' + y.color = 'black' + x.parent.parent.color = 'red' + x = x.parent.parent + else + if x == x.parent.right then + -- Case 2 + x = x.parent + self:left_rotate(x) + end + + -- Case 3 + x.parent.color = 'black' + x.parent.parent.color = 'red' + self:right_rotate(x.parent.parent) + end + else + -- Same as "then" clause with "right" and "left" exchanged. + local y = x.parent.parent.left + if y and y.color == 'red' then + -- Case 1 + x.parent.color = 'black' + y.color = 'black' + x.parent.parent.color = 'red' + x = x.parent.parent + else + if x == x.parent.left then + -- Case 2 + x = x.parent + self:right_rotate(x) + end + + -- Case 3 + x.parent.color = 'black' + x.parent.parent.color = 'red' + self:left_rotate(x.parent.parent) + end + end + end + + self.root.color = 'black' + return nil +end + +function RedBlackTree:remove (key) + local z = self:find_node(key) + if not z then + return nil + end + + -- Y is the node to be unlinked from the tree. + local y + if not z.left or not z.right then + y = z + else + y = z:successor() + end + + -- Y is guaranteed to be non-null at this point. + local x + if y.left then + x = y.left + else + x = y.right + end + + -- X is the child of y which might potentially replace y + -- in the tree. X might be null at this point. + local x_parent + if x then + x.parent = y.parent + x_parent = x.parent + else + x_parent = y.parent + end + + if not y.parent then + self.root = x + else + if y == y.parent.left then + y.parent.left = x + else + y.parent.right = x + end + end + + if y ~= z then + if y.color == 'black' then + self:remove_fixup(x, x_parent) + end + + y.parent = z.parent + y.color = z.color + y.left = z.left + y.right = z.right + + if z.left then + z.left.parent = y + end + if z.right then + z.right.parent = y + end + if z.parent then + if z.parent.left == z then + z.parent.left = y + else + z.parent.right = y + end + else + self.root = y + end + elseif y.color == 'black' then + self:remove_fixup(x, x_parent) + end + + return z.value +end + +function RedBlackTree:get (key) + local node = self:find_node(key) + if node then + return node.value + end + return nil +end + +function RedBlackTree:for_each (fn) + if not self.root then + return + end + local current = tree_minimum(self.root) + while current do + fn(RbtEntry.new(current.key, current.value)) + current = current:successor() + end +end + +function RedBlackTree:find_node (key) + local current = self.root + while current do + local comparison_result = key:compare_to(current.key) + if comparison_result == 0 then + return current + elseif comparison_result < 0 then + current = current.left + else + current = current.right + end + end + return nil +end + +function RedBlackTree:tree_insert (key, value) + local y = nil + local x = self.root + + while x do + y = x + local comparison_result = key:compare_to(x.key) + if comparison_result < 0 then + x = x.left + elseif comparison_result > 0 then + x = x.right + else + local old_value = x.value + x.value = value + return InsertResult.new(false, nil, old_value) + end + end + + local z = Node.new(key, value) + z.parent = y + + if not y then + self.root = z + else + if key:compare_to(y.key) < 0 then + y.left = z + else + y.right = z + end + end + return InsertResult.new(true, z, nil) +end + +function RedBlackTree:left_rotate (x) + local y = x.right + + -- Turn y's left subtree into x's right subtree. + x.right = y.left + if y.left then + y.left.parent = x + end + + -- Link x's parent to y. + y.parent = x.parent + if not x.parent then + self.root = y + else + if x == x.parent.left then + x.parent.left = y + else + x.parent.right = y + end + end + + -- Put x on y's left. + y.left = x + x.parent = y + + return y +end + +function RedBlackTree:right_rotate (y) + local x = y.left + + -- Turn x's right subtree into y's left subtree. + y.left = x.right + if x.right then + x.right.parent = y + end + + -- Link y's parent to x. + x.parent = y.parent + if not y.parent then + self.root = x + else + if y == y.parent.left then + y.parent.left = x + else + y.parent.right = x + end + end + + x.right = y + y.parent = x + + return x +end + +function RedBlackTree:remove_fixup (x, x_parent) + while x ~= self.root and (not x or x.color == 'black') do + if x == x_parent.left then + -- Note: the text points out that w cannot be null. + -- The reason is not obvious from simply looking at the code; + -- it comes about from the properties of the red-black tree. + local w = x_parent.right + if w.color == 'red' then + -- Case 1 + w.color = 'black' + x_parent.color = 'red' + self:left_rotate(x_parent) + w = x_parent.right + end + if (not w.left or w.left.color == 'black') and + (not w.right or w.right.color == 'black') then + -- Case 2 + w.color = 'red' + x = x_parent + x_parent = x.parent + else + if not w.right or w.right.color == 'black' then + -- Case 3 + w.left.color = 'black' + w.color = 'red' + self:right_rotate(w) + w = x_parent.right + end + -- Case 4 + w.color = x_parent.color + x_parent.color = 'black' + if w.right then + w.right.color = 'black' + end + self:left_rotate(x_parent) + x = self.root + x_parent = x.parent + end + else + -- Same as "then" clause with "right" and "left" exchanged. + local w = x_parent.left + if w.color == 'red' then + -- Case 1 + w.color = 'black' + x_parent.color = 'red' + self:right_rotate(x_parent) + w = x_parent.left + end + if (not w.right or w.right.color == 'black') and + (not w.left or w.left.color == 'black') then + -- Case 2 + w.color = 'red' + x = x_parent + x_parent = x.parent + else + if not w.left or w.left.color == 'black' then + -- Case 3 + w.right.color = 'black' + w.color = 'red' + self:left_rotate(w) + w = x_parent.left + end + -- Case 4 + w.color = x_parent.color + x_parent.color = 'black' + if w.left then + w.left.color = 'black' + end + self:right_rotate(x_parent) + x = self.root + x_parent = x.parent + end + end + end + if x then + x.color = 'black' + end +end + +end -- class RedBlackTree + +local CallSign = {_CLASS = 'CallSign'} do + +function CallSign.new (value) + local obj = {value = value} + return setmetatable(obj, {__index = CallSign}) +end + +function CallSign:compare_to (other) + return (self.value == other.value) and 0 or ((self.value < other.value) and -1 or 1) +end + +end -- class CallSign + +local Collision = {_CLASS = 'Collision'} do + +function Collision.new (aircraft_a, aircraft_b, position) + local obj = { + aircraft_a = aircraft_a, + aircraft_b = aircraft_b, + position = position + } + return setmetatable(obj, {__index = Collision}) +end + +end -- class Collision + +local Motion = {_CLASS = 'Motion'} do + +local sqrt = math.sqrt + +function Motion.new (callsign, pos_one, pos_two) + local obj = { + callsign = callsign, + pos_one = pos_one, + pos_two = pos_two, + } + return setmetatable(obj, {__index = Motion}) +end + +function Motion:delta () + return self.pos_two:minus(self.pos_one) +end + +function Motion:find_intersection (other) + local init1 = self.pos_one + local init2 = other.pos_one + local vec1 = self:delta() + local vec2 = other:delta() + local radius = PROXIMITY_RADIUS + + -- this test is not geometrical 3-d intersection test, + -- it takes the fact that the aircraft move + -- into account; so it is more like a 4d test + -- (it assumes that both of the aircraft have a constant speed + -- over the tested interval) + + -- we thus have two points, + -- each of them moving on its line segment at constant speed; + -- we are looking for times when the distance between + -- these two points is smaller than r + + -- vec1 is vector of aircraft 1 + -- vec2 is vector of aircraft 2 + + -- a = (V2 - V1)^T * (V2 - V1) + local a = vec2:minus(vec1):squared_magnitude() + + if a ~= 0.0 then + -- we are first looking for instances + -- of time when the planes are exactly r from each other + -- at least one plane is moving; + -- if the planes are moving in parallel, they do not have constant speed + + -- if the planes are moving in parallel, then + -- if the faster starts behind the slower, + -- we can have 2, 1, or 0 solutions + -- if the faster plane starts in front of the slower, + -- we can have 0 or 1 solutions + + -- if the planes are not moving in parallel, then + + -- point P1 = I1 + vV1 + -- point P2 = I2 + vV2 + -- - looking for v, such that dist(P1,P2) = || P1 - P2 || = r + + -- it follows that || P1 - P2 || = sqrt( < P1-P2, P1-P2 > ) + -- 0 = -r^2 + < P1 - P2, P1 - P2 > + -- from properties of dot product + -- 0 = -r^2 + + v * 2 + v^2 * + -- so we calculate a, b, c - and solve the quadratic equation + -- 0 = c + bv + av^2 + + -- b = 2 * + local b = 2.0 * init1:minus(init2):dot(vec1:minus(vec2)) + + -- c = -r^2 + (I2 - I1)^T * (I2 - I1) + local c = -radius * radius + init2:minus(init1):squared_magnitude() + + local discr = b * b - 4.0 * a * c + if discr < 0.0 then + return nil + end + + local v1 = (-b - sqrt(discr)) / (2.0 * a) + local v2 = (-b + sqrt(discr)) / (2.0 * a) + + if v1 <= v2 and ((v1 <= 1.0 and 1.0 <= v2) or + (v1 <= 0.0 and 0.0 <= v2) or + (0.0 <= v1 and v2 <= 1.0)) then + -- Pick a good "time" at which to report the collision. + local v + if v1 <= 0.0 then + -- The collision started before this frame. + -- Report it at the start of the frame. + v = 0.0 + else + -- The collision started during this frame. Report it at that moment. + v = v1 + end + + local result1 = init1:plus(vec1:times(v)) + local result2 = init2:plus(vec2:times(v)) + + local result = result1:plus(result2):times(0.5) + if result.x >= MIN_X and + result.x <= MAX_X and + result.y >= MIN_Y and + result.y <= MAX_Y and + result.z >= MIN_Z and + result.z <= MAX_Z then + return result + end + end + + return nil + end + + -- the planes have the same speeds and are moving in parallel + -- (or they are not moving at all) + -- they thus have the same distance all the time; + -- we calculate it from the initial point + + -- dist = || i2 - i1 || = sqrt( ( i2 - i1 )^T * ( i2 - i1 ) ) + local dist = init2:minus(init1):magnitude() + if dist <= radius then + return init1:plus(init2):times(0.5) + end + return nil +end + +end -- class Motion + +local CollisionDetector = {_CLASS = 'CollisionDetector'} do + +local floor = math.floor +local HORIZONTAL = Vector2D.new(GOOD_VOXEL_SIZE, 0.0) +local VERTICAL = Vector2D.new(0.0, GOOD_VOXEL_SIZE) + +function CollisionDetector.new () + local obj = {state = RedBlackTree.new()} + return setmetatable(obj, {__index = CollisionDetector}) +end + +function CollisionDetector:handle_new_frame (frame) + local motions = Vector.new() + local seen = RedBlackTree.new() + + frame:each(function (aircraft) + local old_position = self.state:put(aircraft.callsign, aircraft.position) + local new_position = aircraft.position + seen:put(aircraft.callsign, true) + + if not old_position then + -- Treat newly introduced aircraft as if they were stationary. + old_position = new_position + end + + motions:append(Motion.new(aircraft.callsign, old_position, new_position)) + end) + + -- Remove aircraft that are no longer present. + local to_remove = Vector.new() + self.state:for_each(function (e) + if not seen:get(e.key) then + to_remove:append(e.key) + end + end) + + to_remove:each(function (e) + self.state:remove(e) + end) + + local all_reduced = self:reduce_collision_set(motions) + local collisions = Vector.new() + all_reduced:each(function (reduced) + for i = 1, reduced:size() do + local motion1 = reduced:at(i) + for j = i + 1, reduced:size() do + local motion2 = reduced:at(j) + local collision = motion1:find_intersection(motion2) + if collision then + collisions:append(Collision.new(motion1.callsign, + motion2.callsign, + collision)) + end + end + end + end) + + return collisions +end + +function CollisionDetector:is_in_voxel (voxel, motion) + if voxel.x > MAX_X or + voxel.x < MIN_X or + voxel.y > MAX_Y or + voxel.y < MIN_Y then + return false + end + + local init = motion.pos_one + local fin = motion.pos_two + + local v_s = GOOD_VOXEL_SIZE + local r = PROXIMITY_RADIUS / 2.0 + + local v_x = voxel.x + local x0 = init.x + local xv = fin.x - init.x + + local v_y = voxel.y + local y0 = init.y + local yv = fin.y - init.y + + local low_x = (v_x - r - x0) / xv + local high_x = (v_x + v_s + r - x0) / xv + + if xv < 0.0 then + local tmp = low_x + low_x = high_x + high_x = tmp + end + + local low_y = (v_y - r - y0) / yv + local high_y = (v_y + v_s + r - y0) / yv + + if yv < 0.0 then + local tmp = low_y + low_y = high_y + high_y = tmp + end + + return (((xv == 0.0 and v_x <= x0 + r and x0 - r <= v_x + v_s) or -- no motion in x + (low_x <= 1.0 and 1.0 <= high_x) or (low_x <= 0.0 and 0.0 <= high_x) or + (0.0 <= low_x and high_x <= 1.0)) and + ((yv == 0.0 and v_y <= y0 + r and y0 - r <= v_y + v_s) or -- no motion in y + ((low_y <= 1.0 and 1.0 <= high_y) or (low_y <= 0.0 and 0.0 <= high_y) or + (0.0 <= low_y and high_y <= 1.0))) and + (xv == 0.0 or yv == 0.0 or -- no motion in x or y or both + (low_y <= high_x and high_x <= high_y) or + (low_y <= low_x and low_x <= high_y) or + (low_x <= low_y and high_y <= high_x))) +end + +function CollisionDetector:put_into_map (voxel_map, voxel, motion) + local array = voxel_map:get(voxel) + if not array then + array = Vector.new() + voxel_map:put(voxel, array) + end + array:append(motion) +end + +function CollisionDetector:recurse (voxel_map, seen, next_voxel, motion) + if not self:is_in_voxel(next_voxel, motion) then + return + end + if seen:put(next_voxel, true) then + return + end + + self:put_into_map(voxel_map, next_voxel, motion) + + self:recurse(voxel_map, seen, next_voxel:minus(HORIZONTAL), motion) + self:recurse(voxel_map, seen, next_voxel:plus(HORIZONTAL), motion) + self:recurse(voxel_map, seen, next_voxel:minus(VERTICAL), motion) + self:recurse(voxel_map, seen, next_voxel:plus(VERTICAL), motion) + self:recurse(voxel_map, seen, next_voxel:minus(HORIZONTAL):minus(VERTICAL), motion) + self:recurse(voxel_map, seen, next_voxel:minus(HORIZONTAL):plus(VERTICAL), motion) + self:recurse(voxel_map, seen, next_voxel:plus(HORIZONTAL):minus(VERTICAL), motion) + self:recurse(voxel_map, seen, next_voxel:plus(HORIZONTAL):plus(VERTICAL), motion) +end + +function CollisionDetector:reduce_collision_set (motions) + local voxel_map = RedBlackTree.new() + motions:each(function (motion) + self:draw_motion_on_voxel_map(voxel_map, motion) + end) + + local result = Vector.new() + voxel_map:for_each(function (e) + if e.value:size() > 1 then + result:append(e.value) + end + end) + return result +end + +function CollisionDetector:voxel_hash (position) + local x_div = floor(position.x / GOOD_VOXEL_SIZE) + local y_div = floor(position.y / GOOD_VOXEL_SIZE) + + local x = GOOD_VOXEL_SIZE * x_div + local y = GOOD_VOXEL_SIZE * y_div + + if position.x < 0 then + x = x - GOOD_VOXEL_SIZE + end + if position.y < 0 then + y = y - GOOD_VOXEL_SIZE + end + + return Vector2D.new(x, y) +end + +function CollisionDetector:draw_motion_on_voxel_map (voxel_map, motion) + local seen = RedBlackTree.new() + return self:recurse(voxel_map, seen, self:voxel_hash(motion.pos_one), motion) +end + +end -- class CollisionDetector + +local Aircraft = {_CLASS = 'Aircraft'} do + +function Aircraft.new (callsign, position) + local obj = { + callsign = callsign, + position = position + } + return setmetatable(obj, {__index = Aircraft}) +end + +end -- class Collision + +local Simulator = {_CLASS = 'Simulator'} do + +local cos = math.cos +local sin = math.sin + +function Simulator.new (num_aircrafts) + local aircraft = Vector.new() + for i = 1, num_aircrafts do + aircraft:append(CallSign.new(i)) + end + local obj = {aircraft = aircraft} + return setmetatable(obj, {__index = Simulator}) +end + +function Simulator:simulate (time) + local frame = Vector.new() + for i = 1, self.aircraft:size() - 1, 2 do + frame:append(Aircraft.new(self.aircraft:at(i), + Vector3D.new(time, + cos(time) * 2 + (i - 1) * 3, + 10.0))) + frame:append(Aircraft.new(self.aircraft:at(i + 1), + Vector3D.new(time, + sin(time) * 2 + (i - 1) * 3, + 10.0))) + end + return frame +end + +end -- class Simulator + +local cd = {} do +setmetatable(cd, {__index = require'benchmark'}) + +function cd:benchmark (num_aircrafts) + local num_frames = 200 + local simulator = Simulator.new(num_aircrafts) + local detector = CollisionDetector.new() + + local actual_collisions = 0 + for i = 0, num_frames - 1 do + local time = i / 10.0 + local collisions = detector:handle_new_frame(simulator:simulate(time)) + actual_collisions = actual_collisions + collisions:size() + end + return actual_collisions +end + +function cd:inner_benchmark_loop (inner_iterations) + return self:verify_result(self:benchmark(inner_iterations), inner_iterations) +end + +function cd:verify_result (actual_collisions, num_aircrafts) + if num_aircrafts == 1000 then + return actual_collisions == 14484 + elseif num_aircrafts == 500 then + return actual_collisions == 14484 + elseif num_aircrafts == 250 then + return actual_collisions == 10830 + elseif num_aircrafts == 200 then + return actual_collisions == 8655 + elseif num_aircrafts == 100 then + return actual_collisions == 4305 + elseif num_aircrafts == 10 then + return actual_collisions == 390 + elseif num_aircrafts == 2 then + return actual_collisions == 42 + else + print(('No verification result for %d found'):format(num_aircrafts)) + print(('Result is: %d'):format(actual_collisions)) + return false + end +end + +end -- object cd + +return cd diff --git a/awfy/Lua/deltablue.lua b/awfy/Lua/deltablue.lua new file mode 100644 index 0000000..7cc214c --- /dev/null +++ b/awfy/Lua/deltablue.lua @@ -0,0 +1,751 @@ +-- This code is derived from the SOM benchmarks, see AUTHORS.md file. +-- +-- Copyright (c) 2016 Francois Perrad +-- +-- 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. + +local IdentityDictionary = require'som'.IdentityDictionary +local Vector = require'som'.Vector + +local Sym = {_CLASS = 'Sym'} do + +function Sym.new (hash) + local obj = {hash = hash} + return setmetatable(obj, {__index = Sym}) +end + +function Sym:custom_hash () + return self.hash +end + +end -- class Sym + +local ABSOLUTE_STRONGEST = Sym.new(0) +local REQUIRED = Sym.new(1) +local STRONG_PREFERRED = Sym.new(2) +local PREFERRED = Sym.new(3) +local STRONG_DEFAULT = Sym.new(4) +local DEFAULT = Sym.new(5) +local WEAK_DEFAULT = Sym.new(6) +local ABSOLUTE_WEAKEST = Sym.new(7) + +local Strength = {_CLASS = 'Strength'} do + +local function create_strength_table () + local dict = IdentityDictionary.new() + dict:at_put(ABSOLUTE_STRONGEST, -10000) + dict:at_put(REQUIRED, -800) + dict:at_put(STRONG_PREFERRED, -600) + dict:at_put(PREFERRED, -400) + dict:at_put(STRONG_DEFAULT, -200) + dict:at_put(DEFAULT, 0) + dict:at_put(WEAK_DEFAULT, 500) + dict:at_put(ABSOLUTE_WEAKEST, 10000) + return dict +end + +local STRENGHT_TABLE = create_strength_table() + +function Strength.new (strength_sym) + local obj = { + symbolic_value = strength_sym, + arithmetic_value = STRENGHT_TABLE:at(strength_sym), + } + return setmetatable(obj, {__index = Strength}) +end + +local function create_strength_constants () + local dict = IdentityDictionary.new() + STRENGHT_TABLE:keys():each(function (key) + dict:at_put(key, Strength.new(key)) + end) + return dict +end + +local STRENGHT_CONSTANTS = create_strength_constants() + +function Strength.of (sym) + return STRENGHT_CONSTANTS:at(sym) +end + +Strength.ABSOLUTE_STRONGEST = Strength.of(ABSOLUTE_STRONGEST) +Strength.ABSOLUTE_WEAKEST = Strength.of(ABSOLUTE_WEAKEST) +Strength.REQUIRED = Strength.of(REQUIRED) + +function Strength:same_as (strength) + return self.arithmetic_value == strength.arithmetic_value +end + +function Strength:stronger (strength) + return self.arithmetic_value < strength.arithmetic_value +end + +function Strength:weaker (strength) + return self.arithmetic_value > strength.arithmetic_value +end + +function Strength:strongest (strength) + if strength:stronger(self) then + return strength + else + return self + end +end + +function Strength:weakest (strength) + if strength:weaker(self) then + return strength + else + return self + end +end + +end -- class Strength + +local AbstractConstraint = {_CLASS = 'AbstractConstraint'} do + +function AbstractConstraint:build (strength_sym) + self.strength = Strength.of(strength_sym) +end + +function AbstractConstraint:is_input () + return false +end + +function AbstractConstraint:add_constraint (planner) + self:add_to_graph() + planner:incremental_add(self) +end + +function AbstractConstraint:destroy_constraint (planner) + if self:is_satisfied() then + planner:incremental_remove(self) + end + self:remove_from_graph() +end + +function AbstractConstraint:inputs_known (mark) + return not self:inputs_has_one(function (v) + return not ((v.mark == mark) or v.stay or (v.determined_by == nil)) + end) +end + +function AbstractConstraint:satisfy (mark, planner) + local overridden + self:choose_method(mark) + + if self:is_satisfied() then + -- constraint can be satisfied + -- mark inputs to allow cycle detection in addPropagate + self:inputs_do(function (i) + i.mark = mark + end) + + local out = self:output() + overridden = out.determined_by + if overridden then + overridden:mark_unsatisfied() + end + out.determined_by = self + assert(planner:add_propagate(self, mark), + 'Cycle encountered adding: Constraint removed') + out.mark = mark + else + overridden = nil + assert(not self.strength:same_as(Strength.REQUIRED), + 'Failed to satisfy a required constraint') + end + return overridden +end + +end -- abstract class AbstractConstraint + +local BinaryConstraint = {_CLASS = 'BinaryConstraint'} do +setmetatable(BinaryConstraint, {__index = AbstractConstraint}) + +function BinaryConstraint:build (v1, v2, strength) + AbstractConstraint.build(self, strength) + self.v1 = v1 + self.v2 = v2 + self.direction = nil +end + +function BinaryConstraint:is_satisfied () + return self.direction ~= nil +end + +function BinaryConstraint:add_to_graph () + self.v1:add_constraint(self) + self.v2:add_constraint(self) + self.direction = nil +end + +function BinaryConstraint:remove_from_graph () + if self.v1 then + self.v1:remove_constraint(self) + end + if self.v2 then + self.v2:remove_constraint(self) + end + self.direction = nil +end + +function BinaryConstraint:choose_method (mark) + if self.v1.mark == mark then + if (self.v2.mark ~= mark) and self.strength:stronger(self.v2.walk_strength) then + self.direction = 'forward' + return self.direction + else + self.direction = nil + return nil + end + end + + if self.v2.mark == mark then + if (self.v1.mark ~= mark) and self.strength:stronger(self.v1.walk_strength) then + self.direction = 'backward' + return self.direction + else + self.direction = nil + return nil + end + end + + -- If we get here, neither variable is marked, so we have a choice. + if self.v1.walk_strength:weaker(self.v2.walk_strength) then + if self.strength:stronger(self.v1.walk_strength) then + self.direction = 'backward' + return self.direction + else + self.direction = nil + return nil + end + else + if self.strength:stronger(self.v2.walk_strength) then + self.direction = 'forward' + return self.direction + else + self.direction = nil + return nil + end + end +end + +function BinaryConstraint:inputs_do (fn) + if self.direction == 'forward' then + fn(self.v1) + else + fn(self.v2) + end +end + +function BinaryConstraint:inputs_has_one (fn) + if self.direction == 'forward' then + return fn(self.v1) + else + return fn(self.v2) + end +end + +function BinaryConstraint:mark_unsatisfied () + self.direction = nil +end + +function BinaryConstraint:output () + return self.direction == 'forward' and self.v2 or self.v1 +end + +function BinaryConstraint:recalculate () + local ihn, out + if self.direction == 'forward' then + ihn = self.v1 + out = self.v2 + else + ihn = self.v2 + out = self.v1 + end + out.walk_strength = self.strength:weakest(ihn.walk_strength) + out.stay = ihn.stay + if out.stay then + self:execute() + end +end + +end -- abstract class BinaryConstraint + +local UnaryConstraint = {_CLASS = 'UnaryConstraint'} do +setmetatable(UnaryConstraint, {__index = AbstractConstraint}) + +function UnaryConstraint:build (v, strength, planner) + AbstractConstraint.build(self, strength) + self.output_ = v + self.satisfied = false + self:add_constraint(planner) +end + +function UnaryConstraint:is_satisfied () + return self.satisfied +end + +function UnaryConstraint:add_to_graph () + self.output_:add_constraint(self) + self.satisfied = false +end + +function UnaryConstraint:remove_from_graph () + if self.output_ then + self.output_:remove_constraint(self) + end + self.satisfied = false +end + +function UnaryConstraint:choose_method (mark) + self.satisfied = (self.output_.mark ~= mark) and + self.strength:stronger(self.output_.walk_strength) + return nil +end + +function UnaryConstraint:inputs_do () + -- No-op. I have no input variable. +end + +function UnaryConstraint:inputs_has_one () + return false +end + +function UnaryConstraint:mark_unsatisfied () + self.satisfied = false +end + +function UnaryConstraint:output () + return self.output_ +end + +function UnaryConstraint:recalculate () + self.output_.walk_strength = self.strength + self.output_.stay = not self.is_input() + if self.output_.stay then + self:execute() -- stay optimization + end +end + +end -- abstract class UnaryConstraint + +local EditConstraint = {_CLASS = 'EditConstraint'} do +setmetatable(EditConstraint, {__index = UnaryConstraint}) + +function EditConstraint.new (v, strength, planner) + local obj = setmetatable({}, {__index = EditConstraint}) + UnaryConstraint.build(obj, v, strength, planner) + return obj +end + +function EditConstraint:is_input () + return true +end + +function EditConstraint:execute () + -- Edit constraints does nothing. +end + +end -- class EditConstraint + +local EqualityConstraint = {_CLASS = 'EqualityConstraint'} do +setmetatable(EqualityConstraint, {__index = BinaryConstraint}) + +function EqualityConstraint.new (var1, var2, strength, planner) + local obj = setmetatable({}, {__index = EqualityConstraint}) + BinaryConstraint.build(obj, var1, var2, strength) + obj:add_constraint(planner) + return obj +end + +function EqualityConstraint:execute () + if self.direction == 'forward' then + self.v2.value = self.v1.value + else + self.v1.value = self.v2.value + end +end + +end -- class EqualityConstraint + +local ScaleConstraint = {_CLASS = 'ScaleConstraint'} do +setmetatable(ScaleConstraint, {__index = BinaryConstraint}) + +function ScaleConstraint.new (src, scale, offset, dest, strength, planner) + local obj = { + scale = scale, + offset = offset, + } + setmetatable(obj, {__index = ScaleConstraint}) + BinaryConstraint.build(obj, src, dest, strength) + obj:add_constraint(planner) + return obj +end + +function ScaleConstraint:add_to_graph () + self.v1:add_constraint(self) + self.v2:add_constraint(self) + self.scale:add_constraint(self) + self.offset:add_constraint(self) + self.direction = nil +end + +function ScaleConstraint:remove_from_graph () + if self.v1 then + self.v1:remove_constraint(self) + end + if self.v2 then + self.v2:remove_constraint(self) + end + if self.scale then + self.scale:remove_constraint(self) + end + if self.offset then + self.offset:remove_constraint(self) + end + self.direction = nil +end + +function ScaleConstraint:execute () + if self.direction == 'forward' then + self.v2.value = self.v1.value * self.scale.value + self.offset.value + else + self.v1.value = (self.v2.value - self.offset.value) / self.scale.value + end +end + +function ScaleConstraint:inputs_do (fn) + if self.direction == 'forward' then + fn(self.v1) + fn(self.scale) + fn(self.offset) + else + fn(self.v2) + fn(self.scale) + fn(self.offset) + end +end + +function ScaleConstraint:recalculate () + local ihn, out + if self.direction == 'forward' then + ihn = self.v1 + out = self.v2 + else + out = self.v1 + ihn = self.v2 + end + out.walk_strength = self.strength:weakest(ihn.walk_strength) + out.stay = ihn.stay and self.scale.stay and self.offset.stay + if out.stay then + self:execute() -- stay optimization + end +end + +end -- class ScaleConstraint + +local StayConstraint = {_CLASS = 'StayConstraint'} do +setmetatable(StayConstraint, {__index = UnaryConstraint}) + +function StayConstraint.new (v, strength, planner) + local obj = setmetatable({}, {__index = StayConstraint}) + UnaryConstraint.build(obj, v, strength, planner) + return obj +end + +function StayConstraint:execute () + -- Stay Constraints do nothing +end + +end -- class StayConstraint + +local Variable = {_CLASS = 'Variable'} do + +function Variable.new (initial_value) + local obj = { + value = initial_value or 0, + constraints = Vector.new(2), + determined_by = nil, + walk_strength = Strength.ABSOLUTE_WEAKEST, + stay = true, + mark = 0, + } + return setmetatable(obj, {__index = Variable}) +end + +function Variable:add_constraint (constraint) + self.constraints:append(constraint) +end + +function Variable:remove_constraint (constraint) + self.constraints:remove(constraint) + if self.determined_by == constraint then + self.determined_by = nil + end +end + +end -- class Variable + +local Plan = {_CLASS = 'Plan'} do +setmetatable(Plan, {__index = Vector}) + +function Plan.new () + local obj = Vector.new(15) + return setmetatable(obj, {__index = Plan}) +end + +function Plan:execute () + self:each(function (c) + c:execute() + end) +end + +end -- class Plan + +local Planner = {_CLASS = 'Planner'} do + +function Planner.new () + local obj = {current_mark = 1} + return setmetatable(obj, {__index = Planner}) +end + +function Planner:incremental_add (constraint) + local mark = self:new_mark() + local overridden = constraint:satisfy(mark, self) + while overridden do + overridden = overridden:satisfy(mark, self) + end +end + +function Planner:incremental_remove (constraint) + local out = constraint:output() + constraint:mark_unsatisfied() + constraint:remove_from_graph() + local unsatisfied = self:remove_propagate_from(out) + unsatisfied:each(function (u) + self:incremental_add(u) + end) +end + +function Planner:extract_plan_from_constraints (constraints) + local sources = Vector.new() + constraints:each(function (c) + if c:is_input() and c:is_satisfied() then + sources:append(c) + end + end) + return self:make_plan(sources) +end + +function Planner:make_plan (sources) + local mark = self:new_mark() + local plan = Plan.new() + local todo = sources + while not todo:is_empty () do + local c = todo:remove_first() + if (c:output().mark ~= mark) and c:inputs_known(mark) then + -- not in plan already and eligible for inclusion + plan:append(c) + c:output().mark = mark + self:add_constraints_consuming_to(c:output(), todo) + end + end + return plan +end + +function Planner:propagate_from (v) + local todo = Vector.new() + self:add_constraints_consuming_to(v, todo) + while not todo:is_empty() do + local c = todo:remove_first() + c:execute() + self:add_constraints_consuming_to(c:output(), todo) + end +end + +function Planner:add_constraints_consuming_to (v, coll) + local determining_c = v.determined_by + v.constraints:each(function (c) + if (c ~= determining_c) and c:is_satisfied() then + coll:append(c) + end + end) +end + +function Planner:add_propagate (c, mark) + local todo = Vector.with(c) + while not todo:is_empty() do + local d = todo:remove_first() + if d:output().mark == mark then + self:incremental_remove(c) + return false + end + d:recalculate() + self:add_constraints_consuming_to(d:output(), todo) + end + return true +end + +function Planner:change_var (var, val) + local edit_constraint = EditConstraint.new(var, PREFERRED, self) + local plan = self:extract_plan_from_constraints(Vector.with(edit_constraint)) + for _ = 1, 10 do + var.value = val + plan:execute() + end + edit_constraint:destroy_constraint(self) +end + +function Planner:constraints_consuming (v, fn) + local determining_c = v.determined_by + v.constraints:each(function (c) + if (c ~= determining_c) and c:is_satisfied() then + fn(c) + end + end) +end + +function Planner:new_mark () + local current_mark = self.current_mark + self.current_mark = current_mark + 1 + return current_mark +end + +function Planner:remove_propagate_from (out) + local unsatisfied = Vector.new() + + out.determined_by = nil + out.walk_strength = Strength.ABSOLUTE_WEAKEST + out.stay = true + + local todo = Vector.with(out) + while not todo:is_empty() do + local v = todo:remove_first() + + v.constraints:each(function (c) + if not c:is_satisfied() then + unsatisfied:append(c) + end + end) + + self:constraints_consuming(v, function (c) + c:recalculate() + todo:append(c:output()) + end) + end + + unsatisfied:sort(function (c1, c2) + return c1.strength:stronger(c2.strength) + end) + return unsatisfied +end + +function Planner.chain_test (n) + -- This is the standard DeltaBlue benchmark. A long chain of equality + -- constraints is constructed with a stay constraint on one end. An + -- edit constraint is then added to the opposite end and the time is + -- measured for adding and removing this constraint, and extracting + -- and executing a constraint satisfaction plan. There are two cases. + -- In case 1, the added constraint is stronger than the stay + -- constraint and values must propagate down the entire length of the + -- chain. In case 2, the added constraint is weaker than the stay + -- constraint so it cannot be accomodated. The cost in this case is, + -- of course, very low. Typical situations lie somewhere between these + -- two extremes. + + local planner = Planner.new() + local vars = {} + for i = 1, n + 1 do + vars[i] = Variable.new() + end + + -- thread a chain of equality constraints through the variables + for i = 1, n do + local v1, v2 = vars[i], vars[i + 1] + EqualityConstraint.new(v1, v2, REQUIRED, planner) + end + + StayConstraint.new(vars[n + 1], STRONG_DEFAULT, planner) + local edit = EditConstraint.new(vars[1], PREFERRED, planner) + local plan = planner:extract_plan_from_constraints(Vector.with(edit)) + + for v = 1, 100 do + vars[1].value = v + plan:execute() + assert(vars[n + 1].value == v, 'Chain test failed!') + end + + edit:destroy_constraint(planner) +end + +function Planner.projection_test (n) + -- This test constructs a two sets of variables related to each + -- other by a simple linear transformation (scale and offset). The + -- time is measured to change a variable on either side of the + -- mapping and to change the scale and offset factors. + + local planner = Planner.new() + local dests = Vector.new() + local scale = Variable.new(10) + local offset = Variable.new(1000) + + local src = nil + local dst = nil + + for i = 1, n do + src = Variable.new(i) + dst = Variable.new(i) + dests:append(dst) + StayConstraint.new(src, DEFAULT, planner) + ScaleConstraint.new(src, scale, offset, dst, REQUIRED, planner) + end + + planner:change_var(src, 17) + assert(dst.value == 1170, 'Projection 1 failed') + + planner:change_var(dst, 1050) + assert(src.value == 5, 'Projection 2 failed') + + planner:change_var(scale, 5) + for i = 1, n - 1 do + assert(dests:at(i).value == i * 5 + 1000, 'Projection 3 failed') + end + + planner:change_var(offset, 2000) + for i = 1, n - 1 do + assert(dests:at(i).value == i * 5 + 2000, 'Projection 4 failed') + end +end + +end -- class Planner + +local deltablue = {} do +setmetatable(deltablue, {__index = require'benchmark'}) + +function deltablue:inner_benchmark_loop (inner_iterations) + Planner.chain_test(inner_iterations) + Planner.projection_test(inner_iterations) + return true +end + +end -- object deltablue + +return deltablue diff --git a/awfy/Lua/harness.lua b/awfy/Lua/harness.lua new file mode 100755 index 0000000..5260114 --- /dev/null +++ b/awfy/Lua/harness.lua @@ -0,0 +1,98 @@ +#!/usr/bin/env lua +-- This code is derived from the SOM benchmarks, see AUTHORS.md file. +-- +-- Copyright (c) 2016 Francois Perrad +-- +-- 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. + +--[[ + os.clock() wraps the C clock() / CLOCKS_PER_SEC + socket.gettime() wraps the POSIX gettimeofday() with a microsecond resolution +]] +local ok, socket = pcall(require, 'socket') +local gettime = ok and socket.gettime or os.clock + +local run = {} do + +function run:init (name, num_iterations, inner_iterations) + self.name = name + self.benchmark = require(name:lower()) + self.total = 0 + self.num_iterations = tonumber(num_iterations) + self.inner_iterations = tonumber(inner_iterations) +end + +function run:run_benchmark () + print(("Starting %s benchmark ..."):format(self.name)) + self:do_runs() + self:report_benchmark() +end + +function run:measure () + local start_time = gettime() + assert(self.benchmark:inner_benchmark_loop(self.inner_iterations), + 'Benchmark failed with incorrect result') + local end_time = gettime() + + local run_time = (end_time - start_time) * 1.0e6 + self:print_result(run_time) + self.total = self.total + run_time +end + +function run:do_runs () + for _ = 1, self.num_iterations do + self:measure() + end +end + +function run:report_benchmark () + print(("%s: iterations=%d average: %.0fus total: %.0fus\n"):format( + self.name, self.num_iterations, self.total / self.num_iterations, self.total)) +end + +function run:print_result (run_time) + print(("%s: iterations=1 runtime: %.0fus"):format(self.name, run_time)) + +end + +function run:print_total () + print(("Total Runtime: %.0fus"):format(self.total)) +end + +end -- object run + +local function print_usage () + print [==[ +./harness.lua benchmark [num-iterations [inner-iter]] + + benchmark - benchmark class name + num-iterations - number of times to execute benchmark, default: 1 + inner-iter - number of times the benchmark is executed in an inner loop, + which is measured in total, default: 1 +]==] +end + +if #arg < 1 then + print_usage() + os.exit(1) +end + +run:init(arg[1], arg[2] or 1, arg[3] or 1) +run:run_benchmark() +run:print_total() diff --git a/awfy/Lua/hashindextable-53.lua b/awfy/Lua/hashindextable-53.lua new file mode 100644 index 0000000..ec48f2e --- /dev/null +++ b/awfy/Lua/hashindextable-53.lua @@ -0,0 +1,42 @@ +local HashIndexTable = {_CLASS = 'HashIndexTable'} do + +function HashIndexTable.new () + local obj = { + hash_table = {length = 32; + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0}, + } + return setmetatable(obj, {__index = HashIndexTable}) +end + +function HashIndexTable:add (name, index) + local slot = self:hash_slot_for(name) + if index < 255 then + -- increment by 1, 0 stands for empty + self.hash_table[slot] = (index + 1) & 0xFF + else + self.hash_table[slot] = 0 + end +end + +function HashIndexTable:get (name) + local slot = self:hash_slot_for(name) + -- subtract 1, 0 stands for empty + return (self.hash_table[slot] & 0xFF) - 1 +end + +function HashIndexTable:string_hash (s) + -- this is not a proper hash, but sufficient for the benchmark, + -- and very portable! + return #s * 1402589 +end + +function HashIndexTable:hash_slot_for (element) + return (self:string_hash(element) & self.hash_table.length - 1) + 1 +end + +end -- class HashIndexTable + +return HashIndexTable diff --git a/awfy/Lua/hashindextable.lua b/awfy/Lua/hashindextable.lua new file mode 100644 index 0000000..fa0afe6 --- /dev/null +++ b/awfy/Lua/hashindextable.lua @@ -0,0 +1,45 @@ +local HashIndexTable = {_CLASS = 'HashIndexTable'} do + +local bit = bit32 or require'bit' +local band = bit.band + +function HashIndexTable.new () + local obj = { + hash_table = {length = 32; + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0}, + } + return setmetatable(obj, {__index = HashIndexTable}) +end + +function HashIndexTable:add (name, index) + local slot = self:hash_slot_for(name) + if index < 255 then + -- increment by 1, 0 stands for empty + self.hash_table[slot] = band(index + 1, 0xFF) + else + self.hash_table[slot] = 0 + end +end + +function HashIndexTable:get (name) + local slot = self:hash_slot_for(name) + -- subtract 1, 0 stands for empty + return band(self.hash_table[slot], 0xFF) - 1 +end + +function HashIndexTable:string_hash (s) + -- this is not a proper hash, but sufficient for the benchmark, + -- and very portable! + return #s * 1402589 +end + +function HashIndexTable:hash_slot_for (element) + return band(self:string_hash(element), self.hash_table.length - 1) + 1 +end + +end -- class HashIndexTable + +return HashIndexTable diff --git a/awfy/Lua/havlak.lua b/awfy/Lua/havlak.lua new file mode 100644 index 0000000..906fd60 --- /dev/null +++ b/awfy/Lua/havlak.lua @@ -0,0 +1,647 @@ +-- Adapted based on SOM benchmark. +-- Ported on Lua by Francois Perrad +-- +-- Copyright 2011 Google Inc. +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. + +local Vector = require'som'.Vector +local Set = require'som'.Set +local IdentitySet = require'som'.IdentitySet +local IdentityDictionary = require'som'.IdentityDictionary + +local BasicBlock = {_CLASS = 'BasicBlock'} do + +function BasicBlock.new (name) + local obj = { + name = name, + in_edges = Vector.new(2), + out_edges = Vector.new(2), + } + return setmetatable(obj, {__index = BasicBlock}) +end + +function BasicBlock:num_pred () + return self.in_edges:size() +end + +function BasicBlock:add_out_edge (to) + self.out_edges:append(to) +end + +function BasicBlock:add_in_edge (from) + self.in_edges:append(from) +end + +function BasicBlock:custom_hash () + return self.name +end + +end -- class BasicBlock + +local BasicBlockEdge = {_CLASS = 'BasicBlockEdge'} do + +function BasicBlockEdge.new (cfg, from_name, to_name) + local from = cfg:create_node(from_name) + local to = cfg:create_node(to_name) + + from:add_out_edge(to) + to:add_in_edge(from) + local obj = { + from = from, + to = to, + } + setmetatable(obj, {__index = BasicBlockEdge}) + + cfg:add_edge(obj) + return obj +end + +end -- class BasicBlockEdge + +local ControlFlowGraph = {_CLASS = 'ControlFlowGraph'} do + +function ControlFlowGraph.new () + local obj = { + start_node = nil, + basic_block_map = Vector.new(), + edge_list = Vector.new(), + } + return setmetatable(obj, {__index = ControlFlowGraph}) +end + +function ControlFlowGraph:create_node (name) + local node + if self.basic_block_map:at(name) then + node = self.basic_block_map:at(name) + else + node = BasicBlock.new(name) + self.basic_block_map:at_put(name, node) + end + if self:num_nodes() == 1 then + self.start_node = node + end + return node +end + +function ControlFlowGraph:add_edge (edge) + self.edge_list:append(edge) +end + +function ControlFlowGraph:num_nodes () + return self.basic_block_map:size() +end + +function ControlFlowGraph:get_start_basic_block () + return self.start_node +end + +function ControlFlowGraph:get_basic_blocks () + return self.basic_block_map +end + +end -- class ControlFlowGraph + +local SimpleLoop = {_CLASS = 'SimpleLoop'} do + +function SimpleLoop.new (bb, is_reducible) + local obj = { + header = bb, + is_reducible = is_reducible, + parent = nil, + is_root = false, + nesting_level = 0, + depth_level = 0, + counter = 0, + basic_blocks = IdentitySet.new(), + children = IdentitySet.new(), + } + if bb then + obj.basic_blocks:add(bb) + end + return setmetatable(obj, {__index = SimpleLoop}) +end + +function SimpleLoop:add_node (bb) + self.basic_blocks:add(bb) +end + +function SimpleLoop:add_child_loop (loop) + self.children:add(loop) +end + +function SimpleLoop:set_parent (parent) + self.parent = parent + self.parent:add_child_loop(self) +end + +function SimpleLoop:set_is_root () + self.is_root = true +end + +function SimpleLoop:set_nesting_level (level) + self.nesting_level = level + if level == 0 then + self:set_is_root() + end +end + +end -- class SimpleLoop + +local LoopStructureGraph = {_CLASS = 'LoopStructureGraph'} do + +function LoopStructureGraph.new () + local loops = Vector.new() + local root = SimpleLoop.new(nil, true) + local obj = { + loop_counter = 0, + loops = loops, + root = root, + } + root:set_nesting_level(0) + root.counter = obj.loop_counter + obj.loop_counter = obj.loop_counter + 1 + loops:append(root) + return setmetatable(obj, {__index = LoopStructureGraph}) +end + +function LoopStructureGraph:create_new_loop (bb, is_reducible) + local loop = SimpleLoop.new(bb, is_reducible) + loop.counter = self.loop_counter + self.loop_counter = self.loop_counter + 1 + self.loops:append(loop) + return loop +end + +function LoopStructureGraph:calculate_nesting_level () + -- link up all 1st level loops to artificial root node. + self.loops:each(function (it) + if not it.is_root then + if not it.parent then + it:set_parent(self.root) + end + end + end) + -- recursively traverse the tree and assign levels. + self:calculate_nesting_level_rec(self.root, 0) +end + +function LoopStructureGraph:calculate_nesting_level_rec (loop, depth) + loop.depth_level = depth + loop.children:each(function (it) + self:calculate_nesting_level_rec(it, depth + 1) + loop:set_nesting_level(math.max(loop.nesting_level, 1 + it.nesting_level)) + end) +end + +function LoopStructureGraph:num_loops () + return self.loops:size() +end + +end -- class LoopStructureGraph + +local UnionFindNode = {_CLASS = 'UnionFindNode'} do + +function UnionFindNode.new () + local obj = { + dfs_number = 0, + parent = nil, + bb = nil, + loop = nil, + } + return setmetatable(obj, {__index = UnionFindNode}) +end + +function UnionFindNode:init_node (bb, dfs_number) + self.parent = self + self.bb = bb + self.dfs_number = dfs_number + self.loop = nil +end + +function UnionFindNode:find_set () + local node_list = Vector.new() + + local node = self + while node ~= node.parent do + if node.parent ~= node.parent.parent then + node_list:append(node) + end + node = node.parent + end + + -- Path Compression, all nodes' parents point to the 1st level parent. + node_list:each(function (it) + it:union(self.parent) + end) + return node +end + +function UnionFindNode:union (basic_block) + self.parent = basic_block +end + +end -- class UnionFindNode + +local HavlakLoopFinder = {_CLASS = 'HavlakLoopFinder'} do + +local UNVISITED = 2147483647 -- Marker for uninitialized nodes. +local MAXNONBACKPREDS = 32 * 1024 -- Safeguard against pathological algorithm behavior. + +function HavlakLoopFinder.new (cfg, lsg) + local obj = { + cfg = cfg, + lsg = lsg, + non_back_preds = Vector.new(), + back_preds = Vector.new(), + number = IdentityDictionary.new(), + max_size = 0, + header = nil, + type = nil, + last = nil, + nodes = nil, + } + return setmetatable(obj, {__index = HavlakLoopFinder}) +end + +-- As described in the paper, determine whether a node 'w' is a +-- "true" ancestor for node 'v'. +-- +-- Dominance can be tested quickly using a pre-order trick +-- for depth-first spanning trees. This is why DFS is the first +-- thing we run below. +function HavlakLoopFinder:is_ancestor (w, v) + return (w <= v) and (v <= self.last[w]) +end + +-- DFS - Depth-First-Search +-- +-- DESCRIPTION: +-- Simple depth first traversal along out edges with node numbering. +function HavlakLoopFinder:do_dfs (current_node, current) + self.nodes[current]:init_node(current_node, current) + self.number:at_put(current_node, current) + + local last_id = current + local outer_blocks = current_node.out_edges + + outer_blocks:each(function (target) + if self.number:at(target) == UNVISITED then + last_id = self:do_dfs(target, last_id + 1) + end + end) + + self.last[current] = last_id + return last_id +end + +function HavlakLoopFinder:init_all_nodes () + -- Step a: + -- - initialize all nodes as unvisited. + -- - depth-first traversal and numbering. + -- - unreached BB's are marked as dead. + self.cfg:get_basic_blocks():each(function (bb) + self.number:at_put(bb, UNVISITED) + end) + self:do_dfs(self.cfg:get_start_basic_block(), 1) +end + +function HavlakLoopFinder:identify_edges (size) + -- Step b: + -- - iterate over all nodes. + -- + -- A backedge comes from a descendant in the DFS tree, and non-backedges + -- from non-descendants (following Tarjan). + -- + -- - check incoming edges 'v' and add them to either + -- - the list of backedges (backPreds) or + -- - the list of non-backedges (nonBackPreds) + for w = 1, size do + self.header[w] = 1 + self.type[w] = 'BB_NONHEADER' + + local node_w = self.nodes[w].bb + if not node_w then + self.type[w] = 'BB_DEAD' + else + self:process_edges(node_w, w) + end + end +end + +function HavlakLoopFinder:process_edges (node_w, w) + local number = self.number + if node_w:num_pred() > 0 then + node_w.in_edges:each(function (node_v) + local v = number:at(node_v) + if v ~= UNVISITED then + if self:is_ancestor(w, v) then + self.back_preds:at(w):append(v) + else + self.non_back_preds:at(w):add(v) + end + end + end) + end +end + +-- Find loops and build loop forest using Havlak's algorithm, which +-- is derived from Tarjan. Variable names and step numbering has +-- been chosen to be identical to the nomenclature in Havlak's +-- paper (which, in turn, is similar to the one used by Tarjan). +function HavlakLoopFinder:find_loops () + if not self.cfg:get_start_basic_block() then + return + end + + local size = self.cfg:num_nodes() + self.non_back_preds:remove_all() + self.back_preds:remove_all() + self.number:remove_all() + + if size > self.max_size then + self.header = {} + self.type = {} + self.last = {} + self.nodes = {} + self.max_size = size + end + + for i = 1, size do + self.non_back_preds:append(Set.new()) + self.back_preds:append(Vector.new()) + self.nodes[i] = UnionFindNode.new() + end + + self:init_all_nodes() + self:identify_edges(size) + + -- Start node is root of all other loops. + self.header[0] = 0 + + -- Step c: + -- + -- The outer loop, unchanged from Tarjan. It does nothing except + -- for those nodes which are the destinations of backedges. + -- For a header node w, we chase backward from the sources of the + -- backedges adding nodes to the set P, representing the body of + -- the loop headed by w. + -- + -- By running through the nodes in reverse of the DFST preorder, + -- we ensure that inner loop headers will be processed before the + -- headers for surrounding loops. + for w = size, 1, -1 do + -- this is 'P' in Havlak's paper + local node_pool = Vector.new() + local node_w = self.nodes[w].bb + if node_w then + self:step_d(w, node_pool) + + -- Copy nodePool to workList. + local work_list = Vector.new() + node_pool:each(function (it) + work_list:append(it) + end) + + if node_pool:size() ~= 0 then + self.type[w] = 'BB_REDUCIBLE' + end + + -- work the list... + while not work_list:is_empty() do + local x = work_list:remove_first() + + -- Step e: + -- + -- Step e represents the main difference from Tarjan's method. + -- Chasing upwards from the sources of a node w's backedges. If + -- there is a node y' that is not a descendant of w, w is marked + -- the header of an irreducible loop, there is another entry + -- into this loop that avoids w. + + -- The algorithm has degenerated. Break and + -- return in this case. + local non_back_size = self.non_back_preds:at(x.dfs_number):size() + if non_back_size > MAXNONBACKPREDS then + return + end + self:step_e_process_non_back_preds(w, node_pool, work_list, x) + end + end + + -- Collapse/Unionize nodes in a SCC to a single node + -- For every SCC found, create a loop descriptor and link it in. + if (node_pool:size() > 0) or (self.type[w] == 'BB_SELF') then + local loop = self.lsg:create_new_loop(node_w, self.type[w] ~= 'BB_IRREDUCIBLE') + self:set_loop_attributes(w, node_pool, loop) + end + end +end + +function HavlakLoopFinder:step_e_process_non_back_preds (w, node_pool, work_list, x) + self.non_back_preds:at(x.dfs_number):each(function (it) + local y = self.nodes[it] + local ydash = y:find_set() + + if not self:is_ancestor(w, ydash.dfs_number) then + self.type[w] = 'BB_IRREDUCIBLE' + self.non_back_preds:at(w):add(ydash.dfs_number) + else + if ydash.dfs_number ~= w then + if not node_pool:has_some(function (e) return e == ydash end) then + work_list:append(ydash) + node_pool:append(ydash) + end + end + end + end) +end + +function HavlakLoopFinder:set_loop_attributes (w, node_pool, loop) + -- At this point, one can set attributes to the loop, such as: + -- + -- the bottom node: + -- iter = backPreds[w].begin(); + -- loop bottom is: nodes[iter].node); + -- + -- the number of backedges: + -- backPreds[w].size() + -- + -- whether this loop is reducible: + -- type[w] != BasicBlockClass.BB_IRREDUCIBLE + self.nodes[w].loop = loop + node_pool:each(function (node) + -- Add nodes to loop descriptor. + self.header[node.dfs_number] = w + node:union(self.nodes[w]) + + -- Nested loops are not added, but linked together. + if node.loop then + node.loop:set_parent(loop) + else + loop:add_node(node.bb) + end + end) +end + +function HavlakLoopFinder:step_d (w, node_pool) + self.back_preds:at(w):each(function (v) + if v ~= w then + node_pool:append(self.nodes[v]:find_set()) + else + self.type[w] = 'BB_SELF' + end + end) +end + +end -- class HavlakLoopFinder + +local LoopTesterApp = {_CLASS = 'LoopTesterApp'} do + +function LoopTesterApp.new () + local cfg = ControlFlowGraph.new() + local lsg = LoopStructureGraph.new() + local obj = { + cfg = cfg, + lsg = lsg, + } + cfg:create_node(1) + return setmetatable(obj, {__index = LoopTesterApp}) +end + +-- Create 4 basic blocks, corresponding to and if/then/else clause +-- with a CFG that looks like a diamond +function LoopTesterApp:build_diamond (start) + local bb0 = start + BasicBlockEdge.new(self.cfg, bb0, bb0 + 1) + BasicBlockEdge.new(self.cfg, bb0, bb0 + 2) + BasicBlockEdge.new(self.cfg, bb0 + 1, bb0 + 3) + BasicBlockEdge.new(self.cfg, bb0 + 2, bb0 + 3) + return bb0 + 3 +end + +-- Connect two existing nodes +function LoopTesterApp:build_connect (start, end_) + BasicBlockEdge.new(self.cfg, start, end_) +end + +-- Form a straight connected sequence of n basic blocks +function LoopTesterApp:build_straight (start, n) + for i = 1, n do + self:build_connect(start + i - 1, start + i) + end + return start + n +end + +-- Construct a simple loop with two diamonds in it +function LoopTesterApp:build_base_loop (from) + local header = self:build_straight(from, 1) + local diamond1 = self:build_diamond(header) + local d11 = self:build_straight(diamond1, 1) + local diamond2 = self:build_diamond(d11) + local footer = self:build_straight(diamond2, 1) + self:build_connect(diamond2, d11) + self:build_connect(diamond1, header) + self:build_connect(footer, from) + return self:build_straight(footer, 1) +end + +function LoopTesterApp:main (num_dummy_loops, find_loop_iterations, par_loops, + ppar_loops, pppar_loops) + self:construct_simple_cfg() + self:add_dummy_loops(num_dummy_loops) + self:construct_cfg(par_loops, ppar_loops, pppar_loops) + + -- Performing Loop Recognition, 1 Iteration, then findLoopIteration + self:find_loops(self.lsg) + + for _ = 0, find_loop_iterations do + self:find_loops(LoopStructureGraph.new()) + end + + self.lsg:calculate_nesting_level() + return {self.lsg:num_loops(), self.cfg:num_nodes()} +end + +function LoopTesterApp:construct_cfg (par_loops, ppar_loops, pppar_loops) + local n = 3 + + for _ = 1, par_loops do + self.cfg:create_node(n + 1) + self:build_connect(3, n + 1) + n = n + 1 + + for _ = 1, ppar_loops do + local top = n + n = self:build_straight(n, 1) + for _ = 1, pppar_loops do + n = self:build_base_loop(n) + end + local bottom = self:build_straight(n, 1) + self:build_connect(n, top) + n = bottom + end + self:build_connect(n, 1) + end +end + +function LoopTesterApp:add_dummy_loops (num_dummy_loops) + for _ = 1, num_dummy_loops do + self:find_loops(self.lsg) + end +end + +function LoopTesterApp:find_loops (loop_structure) + local finder = HavlakLoopFinder.new(self.cfg, loop_structure) + finder:find_loops() +end + +function LoopTesterApp:construct_simple_cfg () + self.cfg:create_node(1) + self:build_base_loop(1) + self.cfg:create_node(2) + BasicBlockEdge.new(self.cfg, 1, 3) +end + +end -- class LoopTesterApp + + +local havlak = {} do +setmetatable(havlak, {__index = require'benchmark'}) + +function havlak:inner_benchmark_loop (inner_iterations) + local result = LoopTesterApp.new():main(inner_iterations, 50, 10, 10, 5) + return self:verify_result(result, inner_iterations) +end + +function havlak:verify_result (result, inner_iterations) + if inner_iterations == 15000 then + return result[1] == 46602 and result[2] == 5213 + elseif inner_iterations == 1500 then + return result[1] == 6102 and result[2] == 5213 + elseif inner_iterations == 150 then + return result[1] == 2052 and result[2] == 5213 + elseif inner_iterations == 15 then + return result[1] == 1647 and result[2] == 5213 + elseif inner_iterations == 1 then + return result[1] == 1605 and result[2] == 5213 + else + print(('No verification result for %d found'):format(inner_iterations)) + print(('Result is: %d, %d'):format(result[0], result[1])) + return false + end +end + +end -- object havlak + +return havlak diff --git a/awfy/Lua/json.lua b/awfy/Lua/json.lua new file mode 100644 index 0000000..11b7d75 --- /dev/null +++ b/awfy/Lua/json.lua @@ -0,0 +1,587 @@ +-- This code is derived from the SOM benchmarks, see AUTHORS.md file. +-- This benchmark is based on the minimal-json Java library maintained at: +-- https://github.com/ralfstx/minimal-json +-- +-- Copyright (c) 2016 Francois Perrad +-- +-- 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. + +local Vector = require'som'.Vector + +local JsonValue = {_CLASS = 'JsonValue'} do + +function JsonValue:is_object () + return false +end + +function JsonValue:is_array () + return false +end + +function JsonValue:is_number () + return false +end + +function JsonValue:is_string () + return false +end + +function JsonValue:is_boolean () + return false +end + +function JsonValue:is_true () + return false +end + +function JsonValue:is_false () + return false +end + +function JsonValue:is_null () + return false +end + +function JsonValue:as_object () + error('Unsupported operation, not an object: ' .. self:as_string()) +end + +function JsonValue:as_array () + error('Unsupported operation, not an array: ' .. self:as_string()) +end + +end -- abstract JsonValue + +local JsonArray = {_CLASS = 'JsonArray'} do +setmetatable(JsonArray, {__index = JsonValue}) + +function JsonArray.new () + local obj = {values = Vector.new()} + return setmetatable(obj, {__index = JsonArray}) +end + +function JsonArray:add (value) + assert(value, 'value is null') + self.values:append(value) + return self +end + +function JsonArray:size () + return self.values:size() +end + +function JsonArray:get (index) + return self.values:at(index) +end + +function JsonArray:is_array () + return true +end + +function JsonArray:as_array () + return self +end + +end -- class JsonArray + +local JsonLiteral = {_CLASS = 'JsonLiteral'} do +setmetatable(JsonLiteral, {__index = JsonValue}) + +function JsonLiteral.new (value) + local obj = { + value = value, + is_null = 'null' == value, + is_true = 'true' == value, + is_false = 'false' == value, + } + return setmetatable(obj, {__index = JsonLiteral}) +end + +function JsonLiteral:as_string () + return self.value +end + +function JsonLiteral:is_boolean () + return self.is_true or self.is_false +end + +JsonLiteral.NULL = JsonLiteral.new('null') +JsonLiteral.TRUE = JsonLiteral.new('true') +JsonLiteral.FALSE = JsonLiteral.new('false') + +end -- class JsonLiteral + +local JsonNumber = {_CLASS = 'JsonNumber'} do +setmetatable(JsonNumber, {__index = JsonValue}) + +function JsonNumber.new (string) + assert(string, 'string is null') + local obj = {string = string} + return setmetatable(obj, {__index = JsonNumber}) +end + +function JsonNumber:as_string () + return self.string +end + +function JsonNumber:is_number () + return true +end + +end -- class JsonNumber + +local JsonString = {_CLASS = 'JsonString'} do +setmetatable(JsonString, {__index = JsonValue}) + +function JsonString.new (string) + assert(string, 'string is null') + local obj = {string = string} + return setmetatable(obj, {__index = JsonString}) +end + +function JsonString:is_string () + return true +end + +function JsonString:as_string () + return self.string +end + +end -- class JsonString + +local HashIndexTable +if _VERSION < 'Lua 5.3' then + HashIndexTable = require'hashindextable' +else + HashIndexTable = require'hashindextable-53' +end + +local JsonObject = {_CLASS = 'JsonObject'} do +setmetatable(JsonObject, {__index = JsonValue}) + +function JsonObject.new () + local obj = { + names = Vector.new(), + values = Vector.new(), + table = HashIndexTable.new(), + } + return setmetatable(obj, {__index = JsonObject}) +end + +function JsonObject:add (name, value) + assert(name, 'name is null') + assert(value, 'value is null') + + self.names:append(name) + self.values:append(value) + self.table:add(name, self.names:size()) + return self +end + +function JsonObject:get (name) + assert(name, 'name is null') + local index = self:index_of(name) + if index == -1 then + return nil + else + return self.values:at(index) + end +end + +function JsonObject:size () + return self.names:size() +end + +function JsonObject:is_empty () + return self.names:is_empty() +end + +function JsonObject:is_object () + return true +end + +function JsonObject:as_object () + return self +end + +function JsonObject:index_of (name) + local index = self.table:get(name) + if index ~= -1 and name == self.names:at(index) then + return index + end + error('NotImplemented') +end + +end -- class JsonObject + +local Parser = {_CLASS = 'Parser'} do + +local function ParseException (message, offset, line, column) + return ('JSON:%d:%d (%d): %s'):format(line, column, offset, message) +end + +function Parser.new (str) + local obj = { + input = str, + index = 0, + line = 1, + capture_start = -1, + column = 0, + current = nil, + capture_buffer = '', + } + return setmetatable(obj, {__index = Parser}) +end + +function Parser:parse () + self:read() + self:skip_white_space() + local result = self:read_value() + self:skip_white_space() + assert(self:is_end_of_text(), self:error('Unexpected character')) + return result +end + +function Parser:read_value () + local current = self.current + if current == 'n' then + return self:read_null() + elseif current == 't' then + return self:read_true() + elseif current == 'f' then + return self:read_false() + elseif current == '"' then + return self:read_string() + elseif current == '[' then + return self:read_array() + elseif current == '{' then + return self:read_object() + elseif current == '-' or + current == '0' or + current == '1' or + current == '2' or + current == '3' or + current == '4' or + current == '5' or + current == '6' or + current == '7' or + current == '8' or + current == '9' then + return self:read_number() + else + error(self:expected('value')) + end +end + +function Parser:read_array () + self:read() + local array = JsonArray.new() + self:skip_white_space() + if self:read_char(']') then + return array + end + + repeat + self:skip_white_space() + array:add(self:read_value()) + self:skip_white_space() + until not self:read_char(',') + + if not self:read_char(']') then + error(self:expected("',' or ']'")) + end + return array +end + +function Parser:read_object () + self:read() + local object = JsonObject.new() + self:skip_white_space() + if self:read_char('}') then + return object + end + + repeat + self:skip_white_space() + local name = self:read_name() + self:skip_white_space() + if not self:read_char(':') then + error(self:expected("':'")) + end + + self:skip_white_space() + object:add(name, self:read_value()) + self:skip_white_space() + until not self:read_char(',') + + if not self:read_char('}') then + error(self:expected("',' or '}'")) + end + return object +end + +function Parser:read_name () + if self.current ~= '"' then + error(self:expected('name')) + end + return self:read_string_internal() +end + +function Parser:read_null () + self:read() + self:read_required_char('u') + self:read_required_char('l') + self:read_required_char('l') + return JsonLiteral.NULL +end + +function Parser:read_true () + self:read() + self:read_required_char('r') + self:read_required_char('u') + self:read_required_char('e') + return JsonLiteral.TRUE +end + +function Parser:read_false () + self:read() + self:read_required_char('a') + self:read_required_char('l') + self:read_required_char('s') + self:read_required_char('e') + return JsonLiteral.FALSE +end + +function Parser:read_required_char (ch) + if not self:read_char(ch) then + error(self:expected("'" .. ch .. "'")) + end +end + +function Parser:read_string () + return JsonString.new(self:read_string_internal()) +end + +function Parser:read_string_internal () + self:read() + self:start_capture() + while self.current ~= '"' do + if self.current == '\\' then + self:pause_capture() + self:read_escape() + self:start_capture() + else + self:read() + end + end + local str = self:end_capture() + self:read() + return str +end + +function Parser:read_escape () + self:read() + local current = self.current + if current == '"' or + current == '/' or + current == '\\' then + self.capture_buffer = self.capture_buffer .. current + elseif current == 'b' then + self.capture_buffer = self.capture_buffer .. "\b" + elseif current == 'f' then + self.capture_buffer = self.capture_buffer .. "\f" + elseif current == 'n' then + self.capture_buffer = self.capture_buffer .. "\n" + elseif current == 'r' then + self.capture_buffer = self.capture_buffer .. "\r" + elseif current == 't' then + self.capture_buffer = self.capture_buffer .. "\t" + else + error(self:expected('valid escape sequence')) + end + self:read() +end + +function Parser:read_number () + self:start_capture() + self:read_char('-') + local first_digit = self.current + if not self:read_digit() then + error(self:expected('digit')) + end + + if first_digit ~= '0' then + while self:read_digit() do + end + end + self:read_fraction() + self:read_exponent() + return JsonNumber.new(self:end_capture()) +end + +function Parser:read_fraction () + if not self:read_char('.') then + return false + end + if not self:read_digit() then + error(self:expected('digit')) + end + + while self:read_digit() do + end + return true +end + +function Parser:read_exponent () + if not self:read_char('e') and not self:read_char('E') then + return false + end + + if not self:read_char('+') then + self:read_char('-') + end + + if not self:read_digit() then + error(self:expected('digit')) + end + + while self:read_digit() do + end + + return true +end + +function Parser:read_char (ch) + if self.current ~= ch then + return false + end + self:read() + return true +end + +function Parser:read_digit () + if not self:is_digit() then + return false + end + self:read() + return true +end + +function Parser:skip_white_space () + while self:is_white_space() do + self:read() + end +end + +function Parser:read () + if '\n' == self.current then + self.line = self.line + 1 + self.column = 0 + end + + self.index = self.index + 1 + + if self.index <= #self.input then + self.current = self.input:sub(self.index, self.index) + else + self.current = nil + end +end + +function Parser:start_capture () + self.capture_start = self.index +end + +function Parser:pause_capture () + local end_ = not self.current and self.index or (self.index - 1) + self.capture_buffer = self.capture_buffer .. self.input:sub(self.capture_start, end_) + self.capture_start = -1 +end + +function Parser:end_capture () + local end_ = not self.current and self.index or (self.index - 1) + + local captured + if '' == self.capture_buffer then + captured = self.input:sub(self.capture_start, end_) + else + self.capture_buffer = self.capture_buffer .. self.input:sub(self.capture_start, end_) + captured = self.capture_buffer + self.capture_buffer = '' + end + self.capture_start = -1 + return captured +end + +function Parser:expected (expected) + if self:is_end_of_text() then + return self:error('Unexpected end of input') + else + return self:error('Expected ' .. expected) + end +end + +function Parser:error (message) + return ParseException(message, self.index, self.line, self.column - 1) +end + +function Parser:is_white_space () + local current = self.current + return ' ' == current or "\t" == current or "\n" == current or "\r" == current +end + +function Parser:is_digit () + local current = self.current + return '0' == current or '1' == current or '2' == current or '3' == current or + '4' == current or '5' == current or '6' == current or + '7' == current or '8' == current or '9' == current +end + +function Parser:is_end_of_text () + return self.current == nil +end + +end -- class Parser + +local json = {} do +setmetatable(json, {__index = require'benchmark'}) + +local RAP_BENCHMARK_MINIFIED = "{\"head\":{\"requestCounter\":4},\"operations\":[[\"destroy\",\"w54\"],[\"set\",\"w2\",{\"activeControl\":\"w99\"}],[\"set\",\"w21\",{\"customVariant\":\"variant_navigation\"}],[\"set\",\"w28\",{\"customVariant\":\"variant_selected\"}],[\"set\",\"w53\",{\"children\":[\"w95\"]}],[\"create\",\"w95\",\"rwt.widgets.Composite\",{\"parent\":\"w53\",\"style\":[\"NONE\"],\"bounds\":[0,0,1008,586],\"children\":[\"w96\",\"w97\"],\"tabIndex\":-1,\"clientArea\":[0,0,1008,586]}],[\"create\",\"w96\",\"rwt.widgets.Label\",{\"parent\":\"w95\",\"style\":[\"NONE\"],\"bounds\":[10,30,112,26],\"tabIndex\":-1,\"customVariant\":\"variant_pageHeadline\",\"text\":\"TableViewer\"}],[\"create\",\"w97\",\"rwt.widgets.Composite\",{\"parent\":\"w95\",\"style\":[\"NONE\"],\"bounds\":[0,61,1008,525],\"children\":[\"w98\",\"w99\",\"w226\",\"w228\"],\"tabIndex\":-1,\"clientArea\":[0,0,1008,525]}],[\"create\",\"w98\",\"rwt.widgets.Text\",{\"parent\":\"w97\",\"style\":[\"LEFT\",\"SINGLE\",\"BORDER\"],\"bounds\":[10,10,988,32],\"tabIndex\":22,\"activeKeys\":[\"#13\",\"#27\",\"#40\"]}],[\"listen\",\"w98\",{\"KeyDown\":true,\"Modify\":true}],[\"create\",\"w99\",\"rwt.widgets.Grid\",{\"parent\":\"w97\",\"style\":[\"SINGLE\",\"BORDER\"],\"appearance\":\"table\",\"indentionWidth\":0,\"treeColumn\":-1,\"markupEnabled\":false}],[\"create\",\"w100\",\"rwt.widgets.ScrollBar\",{\"parent\":\"w99\",\"style\":[\"HORIZONTAL\"]}],[\"create\",\"w101\",\"rwt.widgets.ScrollBar\",{\"parent\":\"w99\",\"style\":[\"VERTICAL\"]}],[\"set\",\"w99\",{\"bounds\":[10,52,988,402],\"children\":[],\"tabIndex\":23,\"activeKeys\":[\"CTRL+#70\",\"CTRL+#78\",\"CTRL+#82\",\"CTRL+#89\",\"CTRL+#83\",\"CTRL+#71\",\"CTRL+#69\"],\"cancelKeys\":[\"CTRL+#70\",\"CTRL+#78\",\"CTRL+#82\",\"CTRL+#89\",\"CTRL+#83\",\"CTRL+#71\",\"CTRL+#69\"]}],[\"listen\",\"w99\",{\"MouseDown\":true,\"MouseUp\":true,\"MouseDoubleClick\":true,\"KeyDown\":true}],[\"set\",\"w99\",{\"itemCount\":118,\"itemHeight\":28,\"itemMetrics\":[[0,0,50,3,0,3,44],[1,50,50,53,0,53,44],[2,100,140,103,0,103,134],[3,240,180,243,0,243,174],[4,420,50,423,0,423,44],[5,470,50,473,0,473,44]],\"columnCount\":6,\"headerHeight\":35,\"headerVisible\":true,\"linesVisible\":true,\"focusItem\":\"w108\",\"selection\":[\"w108\"]}],[\"listen\",\"w99\",{\"Selection\":true,\"DefaultSelection\":true}],[\"set\",\"w99\",{\"enableCellToolTip\":true}],[\"listen\",\"w100\",{\"Selection\":true}],[\"set\",\"w101\",{\"visibility\":true}],[\"listen\",\"w101\",{\"Selection\":true}],[\"create\",\"w102\",\"rwt.widgets.GridColumn\",{\"parent\":\"w99\",\"text\":\"Nr.\",\"width\":50,\"moveable\":true}],[\"listen\",\"w102\",{\"Selection\":true}],[\"create\",\"w103\",\"rwt.widgets.GridColumn\",{\"parent\":\"w99\",\"text\":\"Sym.\",\"index\":1,\"left\":50,\"width\":50,\"moveable\":true}],[\"listen\",\"w103\",{\"Selection\":true}],[\"create\",\"w104\",\"rwt.widgets.GridColumn\",{\"parent\":\"w99\",\"text\":\"Name\",\"index\":2,\"left\":100,\"width\":140,\"moveable\":true}],[\"listen\",\"w104\",{\"Selection\":true}],[\"create\",\"w105\",\"rwt.widgets.GridColumn\",{\"parent\":\"w99\",\"text\":\"Series\",\"index\":3,\"left\":240,\"width\":180,\"moveable\":true}],[\"listen\",\"w105\",{\"Selection\":true}],[\"create\",\"w106\",\"rwt.widgets.GridColumn\",{\"parent\":\"w99\",\"text\":\"Group\",\"index\":4,\"left\":420,\"width\":50,\"moveable\":true}],[\"listen\",\"w106\",{\"Selection\":true}],[\"create\",\"w107\",\"rwt.widgets.GridColumn\",{\"parent\":\"w99\",\"text\":\"Period\",\"index\":5,\"left\":470,\"width\":50,\"moveable\":true}],[\"listen\",\"w107\",{\"Selection\":true}],[\"create\",\"w108\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":0,\"texts\":[\"1\",\"H\",\"Hydrogen\",\"Nonmetal\",\"1\",\"1\"],\"cellBackgrounds\":[null,null,null,[138,226,52,255],null,null]}],[\"create\",\"w109\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":1,\"texts\":[\"2\",\"He\",\"Helium\",\"Noble gas\",\"18\",\"1\"],\"cellBackgrounds\":[null,null,null,[114,159,207,255],null,null]}],[\"create\",\"w110\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":2,\"texts\":[\"3\",\"Li\",\"Lithium\",\"Alkali metal\",\"1\",\"2\"],\"cellBackgrounds\":[null,null,null,[239,41,41,255],null,null]}],[\"create\",\"w111\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":3,\"texts\":[\"4\",\"Be\",\"Beryllium\",\"Alkaline earth metal\",\"2\",\"2\"],\"cellBackgrounds\":[null,null,null,[233,185,110,255],null,null]}],[\"create\",\"w112\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":4,\"texts\":[\"5\",\"B\",\"Boron\",\"Metalloid\",\"13\",\"2\"],\"cellBackgrounds\":[null,null,null,[156,159,153,255],null,null]}],[\"create\",\"w113\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":5,\"texts\":[\"6\",\"C\",\"Carbon\",\"Nonmetal\",\"14\",\"2\"],\"cellBackgrounds\":[null,null,null,[138,226,52,255],null,null]}],[\"create\",\"w114\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":6,\"texts\":[\"7\",\"N\",\"Nitrogen\",\"Nonmetal\",\"15\",\"2\"],\"cellBackgrounds\":[null,null,null,[138,226,52,255],null,null]}],[\"create\",\"w115\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":7,\"texts\":[\"8\",\"O\",\"Oxygen\",\"Nonmetal\",\"16\",\"2\"],\"cellBackgrounds\":[null,null,null,[138,226,52,255],null,null]}],[\"create\",\"w116\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":8,\"texts\":[\"9\",\"F\",\"Fluorine\",\"Halogen\",\"17\",\"2\"],\"cellBackgrounds\":[null,null,null,[252,233,79,255],null,null]}],[\"create\",\"w117\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":9,\"texts\":[\"10\",\"Ne\",\"Neon\",\"Noble gas\",\"18\",\"2\"],\"cellBackgrounds\":[null,null,null,[114,159,207,255],null,null]}],[\"create\",\"w118\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":10,\"texts\":[\"11\",\"Na\",\"Sodium\",\"Alkali metal\",\"1\",\"3\"],\"cellBackgrounds\":[null,null,null,[239,41,41,255],null,null]}],[\"create\",\"w119\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":11,\"texts\":[\"12\",\"Mg\",\"Magnesium\",\"Alkaline earth metal\",\"2\",\"3\"],\"cellBackgrounds\":[null,null,null,[233,185,110,255],null,null]}],[\"create\",\"w120\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":12,\"texts\":[\"13\",\"Al\",\"Aluminium\",\"Poor metal\",\"13\",\"3\"],\"cellBackgrounds\":[null,null,null,[238,238,236,255],null,null]}],[\"create\",\"w121\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":13,\"texts\":[\"14\",\"Si\",\"Silicon\",\"Metalloid\",\"14\",\"3\"],\"cellBackgrounds\":[null,null,null,[156,159,153,255],null,null]}],[\"create\",\"w122\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":14,\"texts\":[\"15\",\"P\",\"Phosphorus\",\"Nonmetal\",\"15\",\"3\"],\"cellBackgrounds\":[null,null,null,[138,226,52,255],null,null]}],[\"create\",\"w123\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":15,\"texts\":[\"16\",\"S\",\"Sulfur\",\"Nonmetal\",\"16\",\"3\"],\"cellBackgrounds\":[null,null,null,[138,226,52,255],null,null]}],[\"create\",\"w124\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":16,\"texts\":[\"17\",\"Cl\",\"Chlorine\",\"Halogen\",\"17\",\"3\"],\"cellBackgrounds\":[null,null,null,[252,233,79,255],null,null]}],[\"create\",\"w125\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":17,\"texts\":[\"18\",\"Ar\",\"Argon\",\"Noble gas\",\"18\",\"3\"],\"cellBackgrounds\":[null,null,null,[114,159,207,255],null,null]}],[\"create\",\"w126\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":18,\"texts\":[\"19\",\"K\",\"Potassium\",\"Alkali metal\",\"1\",\"4\"],\"cellBackgrounds\":[null,null,null,[239,41,41,255],null,null]}],[\"create\",\"w127\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":19,\"texts\":[\"20\",\"Ca\",\"Calcium\",\"Alkaline earth metal\",\"2\",\"4\"],\"cellBackgrounds\":[null,null,null,[233,185,110,255],null,null]}],[\"create\",\"w128\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":20,\"texts\":[\"21\",\"Sc\",\"Scandium\",\"Transition metal\",\"3\",\"4\"],\"cellBackgrounds\":[null,null,null,[252,175,62,255],null,null]}],[\"create\",\"w129\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":21,\"texts\":[\"22\",\"Ti\",\"Titanium\",\"Transition metal\",\"4\",\"4\"],\"cellBackgrounds\":[null,null,null,[252,175,62,255],null,null]}],[\"create\",\"w130\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":22,\"texts\":[\"23\",\"V\",\"Vanadium\",\"Transition metal\",\"5\",\"4\"],\"cellBackgrounds\":[null,null,null,[252,175,62,255],null,null]}],[\"create\",\"w131\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":23,\"texts\":[\"24\",\"Cr\",\"Chromium\",\"Transition metal\",\"6\",\"4\"],\"cellBackgrounds\":[null,null,null,[252,175,62,255],null,null]}],[\"create\",\"w132\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":24,\"texts\":[\"25\",\"Mn\",\"Manganese\",\"Transition metal\",\"7\",\"4\"],\"cellBackgrounds\":[null,null,null,[252,175,62,255],null,null]}],[\"create\",\"w133\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":25,\"texts\":[\"26\",\"Fe\",\"Iron\",\"Transition metal\",\"8\",\"4\"],\"cellBackgrounds\":[null,null,null,[252,175,62,255],null,null]}],[\"create\",\"w134\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":26,\"texts\":[\"27\",\"Co\",\"Cobalt\",\"Transition metal\",\"9\",\"4\"],\"cellBackgrounds\":[null,null,null,[252,175,62,255],null,null]}],[\"create\",\"w135\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":27,\"texts\":[\"28\",\"Ni\",\"Nickel\",\"Transition metal\",\"10\",\"4\"],\"cellBackgrounds\":[null,null,null,[252,175,62,255],null,null]}],[\"create\",\"w136\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":28,\"texts\":[\"29\",\"Cu\",\"Copper\",\"Transition metal\",\"11\",\"4\"],\"cellBackgrounds\":[null,null,null,[252,175,62,255],null,null]}],[\"create\",\"w137\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":29,\"texts\":[\"30\",\"Zn\",\"Zinc\",\"Transition metal\",\"12\",\"4\"],\"cellBackgrounds\":[null,null,null,[252,175,62,255],null,null]}],[\"create\",\"w138\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":30,\"texts\":[\"31\",\"Ga\",\"Gallium\",\"Poor metal\",\"13\",\"4\"],\"cellBackgrounds\":[null,null,null,[238,238,236,255],null,null]}],[\"create\",\"w139\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":31,\"texts\":[\"32\",\"Ge\",\"Germanium\",\"Metalloid\",\"14\",\"4\"],\"cellBackgrounds\":[null,null,null,[156,159,153,255],null,null]}],[\"create\",\"w140\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":32,\"texts\":[\"33\",\"As\",\"Arsenic\",\"Metalloid\",\"15\",\"4\"],\"cellBackgrounds\":[null,null,null,[156,159,153,255],null,null]}],[\"create\",\"w141\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":33,\"texts\":[\"34\",\"Se\",\"Selenium\",\"Nonmetal\",\"16\",\"4\"],\"cellBackgrounds\":[null,null,null,[138,226,52,255],null,null]}],[\"create\",\"w142\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":34,\"texts\":[\"35\",\"Br\",\"Bromine\",\"Halogen\",\"17\",\"4\"],\"cellBackgrounds\":[null,null,null,[252,233,79,255],null,null]}],[\"create\",\"w143\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":35,\"texts\":[\"36\",\"Kr\",\"Krypton\",\"Noble gas\",\"18\",\"4\"],\"cellBackgrounds\":[null,null,null,[114,159,207,255],null,null]}],[\"create\",\"w144\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":36,\"texts\":[\"37\",\"Rb\",\"Rubidium\",\"Alkali metal\",\"1\",\"5\"],\"cellBackgrounds\":[null,null,null,[239,41,41,255],null,null]}],[\"create\",\"w145\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":37,\"texts\":[\"38\",\"Sr\",\"Strontium\",\"Alkaline earth metal\",\"2\",\"5\"],\"cellBackgrounds\":[null,null,null,[233,185,110,255],null,null]}],[\"create\",\"w146\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":38,\"texts\":[\"39\",\"Y\",\"Yttrium\",\"Transition metal\",\"3\",\"5\"],\"cellBackgrounds\":[null,null,null,[252,175,62,255],null,null]}],[\"create\",\"w147\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":39,\"texts\":[\"40\",\"Zr\",\"Zirconium\",\"Transition metal\",\"4\",\"5\"],\"cellBackgrounds\":[null,null,null,[252,175,62,255],null,null]}],[\"create\",\"w148\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":40,\"texts\":[\"41\",\"Nb\",\"Niobium\",\"Transition metal\",\"5\",\"5\"],\"cellBackgrounds\":[null,null,null,[252,175,62,255],null,null]}],[\"create\",\"w149\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":41,\"texts\":[\"42\",\"Mo\",\"Molybdenum\",\"Transition metal\",\"6\",\"5\"],\"cellBackgrounds\":[null,null,null,[252,175,62,255],null,null]}],[\"create\",\"w150\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":42,\"texts\":[\"43\",\"Tc\",\"Technetium\",\"Transition metal\",\"7\",\"5\"],\"cellBackgrounds\":[null,null,null,[252,175,62,255],null,null]}],[\"create\",\"w151\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":43,\"texts\":[\"44\",\"Ru\",\"Ruthenium\",\"Transition metal\",\"8\",\"5\"],\"cellBackgrounds\":[null,null,null,[252,175,62,255],null,null]}],[\"create\",\"w152\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":44,\"texts\":[\"45\",\"Rh\",\"Rhodium\",\"Transition metal\",\"9\",\"5\"],\"cellBackgrounds\":[null,null,null,[252,175,62,255],null,null]}],[\"create\",\"w153\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":45,\"texts\":[\"46\",\"Pd\",\"Palladium\",\"Transition metal\",\"10\",\"5\"],\"cellBackgrounds\":[null,null,null,[252,175,62,255],null,null]}],[\"create\",\"w154\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":46,\"texts\":[\"47\",\"Ag\",\"Silver\",\"Transition metal\",\"11\",\"5\"],\"cellBackgrounds\":[null,null,null,[252,175,62,255],null,null]}],[\"create\",\"w155\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":47,\"texts\":[\"48\",\"Cd\",\"Cadmium\",\"Transition metal\",\"12\",\"5\"],\"cellBackgrounds\":[null,null,null,[252,175,62,255],null,null]}],[\"create\",\"w156\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":48,\"texts\":[\"49\",\"In\",\"Indium\",\"Poor metal\",\"13\",\"5\"],\"cellBackgrounds\":[null,null,null,[238,238,236,255],null,null]}],[\"create\",\"w157\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":49,\"texts\":[\"50\",\"Sn\",\"Tin\",\"Poor metal\",\"14\",\"5\"],\"cellBackgrounds\":[null,null,null,[238,238,236,255],null,null]}],[\"create\",\"w158\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":50,\"texts\":[\"51\",\"Sb\",\"Antimony\",\"Metalloid\",\"15\",\"5\"],\"cellBackgrounds\":[null,null,null,[156,159,153,255],null,null]}],[\"create\",\"w159\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":51,\"texts\":[\"52\",\"Te\",\"Tellurium\",\"Metalloid\",\"16\",\"5\"],\"cellBackgrounds\":[null,null,null,[156,159,153,255],null,null]}],[\"create\",\"w160\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":52,\"texts\":[\"53\",\"I\",\"Iodine\",\"Halogen\",\"17\",\"5\"],\"cellBackgrounds\":[null,null,null,[252,233,79,255],null,null]}],[\"create\",\"w161\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":53,\"texts\":[\"54\",\"Xe\",\"Xenon\",\"Noble gas\",\"18\",\"5\"],\"cellBackgrounds\":[null,null,null,[114,159,207,255],null,null]}],[\"create\",\"w162\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":54,\"texts\":[\"55\",\"Cs\",\"Caesium\",\"Alkali metal\",\"1\",\"6\"],\"cellBackgrounds\":[null,null,null,[239,41,41,255],null,null]}],[\"create\",\"w163\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":55,\"texts\":[\"56\",\"Ba\",\"Barium\",\"Alkaline earth metal\",\"2\",\"6\"],\"cellBackgrounds\":[null,null,null,[233,185,110,255],null,null]}],[\"create\",\"w164\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":56,\"texts\":[\"57\",\"La\",\"Lanthanum\",\"Lanthanide\",\"3\",\"6\"],\"cellBackgrounds\":[null,null,null,[173,127,168,255],null,null]}],[\"create\",\"w165\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":57,\"texts\":[\"58\",\"Ce\",\"Cerium\",\"Lanthanide\",\"3\",\"6\"],\"cellBackgrounds\":[null,null,null,[173,127,168,255],null,null]}],[\"create\",\"w166\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":58,\"texts\":[\"59\",\"Pr\",\"Praseodymium\",\"Lanthanide\",\"3\",\"6\"],\"cellBackgrounds\":[null,null,null,[173,127,168,255],null,null]}],[\"create\",\"w167\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":59,\"texts\":[\"60\",\"Nd\",\"Neodymium\",\"Lanthanide\",\"3\",\"6\"],\"cellBackgrounds\":[null,null,null,[173,127,168,255],null,null]}],[\"create\",\"w168\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":60,\"texts\":[\"61\",\"Pm\",\"Promethium\",\"Lanthanide\",\"3\",\"6\"],\"cellBackgrounds\":[null,null,null,[173,127,168,255],null,null]}],[\"create\",\"w169\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":61,\"texts\":[\"62\",\"Sm\",\"Samarium\",\"Lanthanide\",\"3\",\"6\"],\"cellBackgrounds\":[null,null,null,[173,127,168,255],null,null]}],[\"create\",\"w170\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":62,\"texts\":[\"63\",\"Eu\",\"Europium\",\"Lanthanide\",\"3\",\"6\"],\"cellBackgrounds\":[null,null,null,[173,127,168,255],null,null]}],[\"create\",\"w171\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":63,\"texts\":[\"64\",\"Gd\",\"Gadolinium\",\"Lanthanide\",\"3\",\"6\"],\"cellBackgrounds\":[null,null,null,[173,127,168,255],null,null]}],[\"create\",\"w172\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":64,\"texts\":[\"65\",\"Tb\",\"Terbium\",\"Lanthanide\",\"3\",\"6\"],\"cellBackgrounds\":[null,null,null,[173,127,168,255],null,null]}],[\"create\",\"w173\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":65,\"texts\":[\"66\",\"Dy\",\"Dysprosium\",\"Lanthanide\",\"3\",\"6\"],\"cellBackgrounds\":[null,null,null,[173,127,168,255],null,null]}],[\"create\",\"w174\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":66,\"texts\":[\"67\",\"Ho\",\"Holmium\",\"Lanthanide\",\"3\",\"6\"],\"cellBackgrounds\":[null,null,null,[173,127,168,255],null,null]}],[\"create\",\"w175\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":67,\"texts\":[\"68\",\"Er\",\"Erbium\",\"Lanthanide\",\"3\",\"6\"],\"cellBackgrounds\":[null,null,null,[173,127,168,255],null,null]}],[\"create\",\"w176\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":68,\"texts\":[\"69\",\"Tm\",\"Thulium\",\"Lanthanide\",\"3\",\"6\"],\"cellBackgrounds\":[null,null,null,[173,127,168,255],null,null]}],[\"create\",\"w177\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":69,\"texts\":[\"70\",\"Yb\",\"Ytterbium\",\"Lanthanide\",\"3\",\"6\"],\"cellBackgrounds\":[null,null,null,[173,127,168,255],null,null]}],[\"create\",\"w178\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":70,\"texts\":[\"71\",\"Lu\",\"Lutetium\",\"Lanthanide\",\"3\",\"6\"],\"cellBackgrounds\":[null,null,null,[173,127,168,255],null,null]}],[\"create\",\"w179\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":71,\"texts\":[\"72\",\"Hf\",\"Hafnium\",\"Transition metal\",\"4\",\"6\"],\"cellBackgrounds\":[null,null,null,[252,175,62,255],null,null]}],[\"create\",\"w180\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":72,\"texts\":[\"73\",\"Ta\",\"Tantalum\",\"Transition metal\",\"5\",\"6\"],\"cellBackgrounds\":[null,null,null,[252,175,62,255],null,null]}],[\"create\",\"w181\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":73,\"texts\":[\"74\",\"W\",\"Tungsten\",\"Transition metal\",\"6\",\"6\"],\"cellBackgrounds\":[null,null,null,[252,175,62,255],null,null]}],[\"create\",\"w182\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":74,\"texts\":[\"75\",\"Re\",\"Rhenium\",\"Transition metal\",\"7\",\"6\"],\"cellBackgrounds\":[null,null,null,[252,175,62,255],null,null]}],[\"create\",\"w183\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":75,\"texts\":[\"76\",\"Os\",\"Osmium\",\"Transition metal\",\"8\",\"6\"],\"cellBackgrounds\":[null,null,null,[252,175,62,255],null,null]}],[\"create\",\"w184\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":76,\"texts\":[\"77\",\"Ir\",\"Iridium\",\"Transition metal\",\"9\",\"6\"],\"cellBackgrounds\":[null,null,null,[252,175,62,255],null,null]}],[\"create\",\"w185\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":77,\"texts\":[\"78\",\"Pt\",\"Platinum\",\"Transition metal\",\"10\",\"6\"],\"cellBackgrounds\":[null,null,null,[252,175,62,255],null,null]}],[\"create\",\"w186\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":78,\"texts\":[\"79\",\"Au\",\"Gold\",\"Transition metal\",\"11\",\"6\"],\"cellBackgrounds\":[null,null,null,[252,175,62,255],null,null]}],[\"create\",\"w187\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":79,\"texts\":[\"80\",\"Hg\",\"Mercury\",\"Transition metal\",\"12\",\"6\"],\"cellBackgrounds\":[null,null,null,[252,175,62,255],null,null]}],[\"create\",\"w188\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":80,\"texts\":[\"81\",\"Tl\",\"Thallium\",\"Poor metal\",\"13\",\"6\"],\"cellBackgrounds\":[null,null,null,[238,238,236,255],null,null]}],[\"create\",\"w189\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":81,\"texts\":[\"82\",\"Pb\",\"Lead\",\"Poor metal\",\"14\",\"6\"],\"cellBackgrounds\":[null,null,null,[238,238,236,255],null,null]}],[\"create\",\"w190\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":82,\"texts\":[\"83\",\"Bi\",\"Bismuth\",\"Poor metal\",\"15\",\"6\"],\"cellBackgrounds\":[null,null,null,[238,238,236,255],null,null]}],[\"create\",\"w191\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":83,\"texts\":[\"84\",\"Po\",\"Polonium\",\"Metalloid\",\"16\",\"6\"],\"cellBackgrounds\":[null,null,null,[156,159,153,255],null,null]}],[\"create\",\"w192\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":84,\"texts\":[\"85\",\"At\",\"Astatine\",\"Halogen\",\"17\",\"6\"],\"cellBackgrounds\":[null,null,null,[252,233,79,255],null,null]}],[\"create\",\"w193\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":85,\"texts\":[\"86\",\"Rn\",\"Radon\",\"Noble gas\",\"18\",\"6\"],\"cellBackgrounds\":[null,null,null,[114,159,207,255],null,null]}],[\"create\",\"w194\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":86,\"texts\":[\"87\",\"Fr\",\"Francium\",\"Alkali metal\",\"1\",\"7\"],\"cellBackgrounds\":[null,null,null,[239,41,41,255],null,null]}],[\"create\",\"w195\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":87,\"texts\":[\"88\",\"Ra\",\"Radium\",\"Alkaline earth metal\",\"2\",\"7\"],\"cellBackgrounds\":[null,null,null,[233,185,110,255],null,null]}],[\"create\",\"w196\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":88,\"texts\":[\"89\",\"Ac\",\"Actinium\",\"Actinide\",\"3\",\"7\"],\"cellBackgrounds\":[null,null,null,[173,127,168,255],null,null]}],[\"create\",\"w197\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":89,\"texts\":[\"90\",\"Th\",\"Thorium\",\"Actinide\",\"3\",\"7\"],\"cellBackgrounds\":[null,null,null,[173,127,168,255],null,null]}],[\"create\",\"w198\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":90,\"texts\":[\"91\",\"Pa\",\"Protactinium\",\"Actinide\",\"3\",\"7\"],\"cellBackgrounds\":[null,null,null,[173,127,168,255],null,null]}],[\"create\",\"w199\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":91,\"texts\":[\"92\",\"U\",\"Uranium\",\"Actinide\",\"3\",\"7\"],\"cellBackgrounds\":[null,null,null,[173,127,168,255],null,null]}],[\"create\",\"w200\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":92,\"texts\":[\"93\",\"Np\",\"Neptunium\",\"Actinide\",\"3\",\"7\"],\"cellBackgrounds\":[null,null,null,[173,127,168,255],null,null]}],[\"create\",\"w201\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":93,\"texts\":[\"94\",\"Pu\",\"Plutonium\",\"Actinide\",\"3\",\"7\"],\"cellBackgrounds\":[null,null,null,[173,127,168,255],null,null]}],[\"create\",\"w202\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":94,\"texts\":[\"95\",\"Am\",\"Americium\",\"Actinide\",\"3\",\"7\"],\"cellBackgrounds\":[null,null,null,[173,127,168,255],null,null]}],[\"create\",\"w203\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":95,\"texts\":[\"96\",\"Cm\",\"Curium\",\"Actinide\",\"3\",\"7\"],\"cellBackgrounds\":[null,null,null,[173,127,168,255],null,null]}],[\"create\",\"w204\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":96,\"texts\":[\"97\",\"Bk\",\"Berkelium\",\"Actinide\",\"3\",\"7\"],\"cellBackgrounds\":[null,null,null,[173,127,168,255],null,null]}],[\"create\",\"w205\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":97,\"texts\":[\"98\",\"Cf\",\"Californium\",\"Actinide\",\"3\",\"7\"],\"cellBackgrounds\":[null,null,null,[173,127,168,255],null,null]}],[\"create\",\"w206\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":98,\"texts\":[\"99\",\"Es\",\"Einsteinium\",\"Actinide\",\"3\",\"7\"],\"cellBackgrounds\":[null,null,null,[173,127,168,255],null,null]}],[\"create\",\"w207\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":99,\"texts\":[\"100\",\"Fm\",\"Fermium\",\"Actinide\",\"3\",\"7\"],\"cellBackgrounds\":[null,null,null,[173,127,168,255],null,null]}],[\"create\",\"w208\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":100,\"texts\":[\"101\",\"Md\",\"Mendelevium\",\"Actinide\",\"3\",\"7\"],\"cellBackgrounds\":[null,null,null,[173,127,168,255],null,null]}],[\"create\",\"w209\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":101,\"texts\":[\"102\",\"No\",\"Nobelium\",\"Actinide\",\"3\",\"7\"],\"cellBackgrounds\":[null,null,null,[173,127,168,255],null,null]}],[\"create\",\"w210\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":102,\"texts\":[\"103\",\"Lr\",\"Lawrencium\",\"Actinide\",\"3\",\"7\"],\"cellBackgrounds\":[null,null,null,[173,127,168,255],null,null]}],[\"create\",\"w211\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":103,\"texts\":[\"104\",\"Rf\",\"Rutherfordium\",\"Transition metal\",\"4\",\"7\"],\"cellBackgrounds\":[null,null,null,[252,175,62,255],null,null]}],[\"create\",\"w212\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":104,\"texts\":[\"105\",\"Db\",\"Dubnium\",\"Transition metal\",\"5\",\"7\"],\"cellBackgrounds\":[null,null,null,[252,175,62,255],null,null]}],[\"create\",\"w213\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":105,\"texts\":[\"106\",\"Sg\",\"Seaborgium\",\"Transition metal\",\"6\",\"7\"],\"cellBackgrounds\":[null,null,null,[252,175,62,255],null,null]}],[\"create\",\"w214\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":106,\"texts\":[\"107\",\"Bh\",\"Bohrium\",\"Transition metal\",\"7\",\"7\"],\"cellBackgrounds\":[null,null,null,[252,175,62,255],null,null]}],[\"create\",\"w215\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":107,\"texts\":[\"108\",\"Hs\",\"Hassium\",\"Transition metal\",\"8\",\"7\"],\"cellBackgrounds\":[null,null,null,[252,175,62,255],null,null]}],[\"create\",\"w216\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":108,\"texts\":[\"109\",\"Mt\",\"Meitnerium\",\"Transition metal\",\"9\",\"7\"],\"cellBackgrounds\":[null,null,null,[252,175,62,255],null,null]}],[\"create\",\"w217\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":109,\"texts\":[\"110\",\"Ds\",\"Darmstadtium\",\"Transition metal\",\"10\",\"7\"],\"cellBackgrounds\":[null,null,null,[252,175,62,255],null,null]}],[\"create\",\"w218\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":110,\"texts\":[\"111\",\"Rg\",\"Roentgenium\",\"Transition metal\",\"11\",\"7\"],\"cellBackgrounds\":[null,null,null,[252,175,62,255],null,null]}],[\"create\",\"w219\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":111,\"texts\":[\"112\",\"Uub\",\"Ununbium\",\"Transition metal\",\"12\",\"7\"],\"cellBackgrounds\":[null,null,null,[252,175,62,255],null,null]}],[\"create\",\"w220\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":112,\"texts\":[\"113\",\"Uut\",\"Ununtrium\",\"Poor metal\",\"13\",\"7\"],\"cellBackgrounds\":[null,null,null,[238,238,236,255],null,null]}],[\"create\",\"w221\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":113,\"texts\":[\"114\",\"Uuq\",\"Ununquadium\",\"Poor metal\",\"14\",\"7\"],\"cellBackgrounds\":[null,null,null,[238,238,236,255],null,null]}],[\"create\",\"w222\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":114,\"texts\":[\"115\",\"Uup\",\"Ununpentium\",\"Poor metal\",\"15\",\"7\"],\"cellBackgrounds\":[null,null,null,[238,238,236,255],null,null]}],[\"create\",\"w223\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":115,\"texts\":[\"116\",\"Uuh\",\"Ununhexium\",\"Poor metal\",\"16\",\"7\"],\"cellBackgrounds\":[null,null,null,[238,238,236,255],null,null]}],[\"create\",\"w224\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":116,\"texts\":[\"117\",\"Uus\",\"Ununseptium\",\"Halogen\",\"17\",\"7\"],\"cellBackgrounds\":[null,null,null,[252,233,79,255],null,null]}],[\"create\",\"w225\",\"rwt.widgets.GridItem\",{\"parent\":\"w99\",\"index\":117,\"texts\":[\"118\",\"Uuo\",\"Ununoctium\",\"Noble gas\",\"18\",\"7\"],\"cellBackgrounds\":[null,null,null,[114,159,207,255],null,null]}],[\"create\",\"w226\",\"rwt.widgets.Composite\",{\"parent\":\"w97\",\"style\":[\"BORDER\"],\"bounds\":[10,464,988,25],\"children\":[\"w227\"],\"tabIndex\":-1,\"clientArea\":[0,0,986,23]}],[\"create\",\"w227\",\"rwt.widgets.Label\",{\"parent\":\"w226\",\"style\":[\"NONE\"],\"bounds\":[10,10,966,3],\"tabIndex\":-1,\"text\":\"Hydrogen (H)\"}],[\"create\",\"w228\",\"rwt.widgets.Label\",{\"parent\":\"w97\",\"style\":[\"WRAP\"],\"bounds\":[10,499,988,16],\"tabIndex\":-1,\"foreground\":[150,150,150,255],\"font\":[[\"Verdana\",\"Lucida Sans\",\"Arial\",\"Helvetica\",\"sans-serif\"],10,false,false],\"text\":\"Shortcuts: [CTRL+F] - Filter | Sort by: [CTRL+R] - Number, [CTRL+Y] - Symbol, [CTRL+N] - Name, [CTRL+S] - Series, [CTRL+G] - Group, [CTRL+E] - Period\"}],[\"set\",\"w1\",{\"focusControl\":\"w99\"}],[\"call\",\"rwt.client.BrowserNavigation\",\"addToHistory\",{\"entries\":[[\"tableviewer\",\"TableViewer\"]]}]]}" + +function json:benchmark () + return Parser.new(RAP_BENCHMARK_MINIFIED):parse() +end + +function json:verify_result (result) + if not result:is_object() then + return false + end + if not result:as_object():get('head'):is_object() then + return false + end + if not result:as_object():get('operations'):is_array() then + return false + end + return result:as_object():get('operations'):as_array():size() == 156 +end + +end -- object json + +return json + diff --git a/awfy/Lua/list.lua b/awfy/Lua/list.lua new file mode 100644 index 0000000..6bdf192 --- /dev/null +++ b/awfy/Lua/list.lua @@ -0,0 +1,88 @@ +-- This code is derived from the SOM benchmarks, see AUTHORS.md file. +-- +-- Copyright (c) 2016 Francois Perrad +-- +-- 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. + +local Element = {_CLASS = 'Element'} do + +function Element.new (v) + local obj = {val = v, next = nil} + return setmetatable(obj, {__index = Element}) +end + +function Element:length () + if not self.next then + return 1 + else + return 1 + self.next:length() + end +end + +end -- class Element + +local list = {} do +setmetatable(list, {__index = require'benchmark'}) + +function list:benchmark () + local result = self:tail(self:make_list(15), + self:make_list(10), + self:make_list(6)) + return result:length() +end + +function list:make_list (length) + if length == 0 then + return nil + else + local e = Element.new(length) + e.next = self:make_list(length - 1) + return e + end +end + +function list:is_shorter_than (x, y) + local x_tail, y_tail = x, y + while y_tail do + if not x_tail then + return true + end + x_tail = x_tail.next + y_tail = y_tail.next + end + return false +end + +function list:tail (x, y, z) + if self:is_shorter_than(y, x) then + return self:tail(self:tail(x.next, y, z), + self:tail(y.next, z, x), + self:tail(z.next, x, y)) + else + return z + end +end + +function list:verify_result (result) + return 10 == result +end + +end -- object list + +return list diff --git a/awfy/Lua/mandelbrot-fn-53.lua b/awfy/Lua/mandelbrot-fn-53.lua new file mode 100644 index 0000000..4c058c4 --- /dev/null +++ b/awfy/Lua/mandelbrot-fn-53.lua @@ -0,0 +1,71 @@ +--[[ + The module 'bit' is available with: + * LuaJIT + * LuaBitOp extension which is available for: + * Lua 5.1 + * Lua 5.2 + The module 'bit32' is available with: + * Lua 5.2 + * Lua 5.3 when compiled with LUA_COMPAT_5_2 + The bitwise operators are added to Lua 5.3 as new lexemes (there causes + lexical error in older version) +--]] + +-- Lua 5.3 variant with bitwise operators +local function mandelbrot (size) + local sum = 0 + local byte_acc = 0 + local bit_num = 0 + + local y = 0 + while y < size do + local ci = (2.0 * y / size) - 1.0 + local x = 0 + + while x < size do + local zrzr = 0.0 + local zizi, zi = 0.0, 0.0 + local cr = (2.0 * x / size) - 1.5 + + local z = 0 + local not_done = true + local escape = 0 + while not_done and z < 50 do + local zr = zrzr - zizi + cr + zi = 2.0 * zr * zi + ci + + -- preserve recalculation + zrzr = zr * zr + zizi = zi * zi + if zrzr + zizi > 4.0 then + not_done = false + escape = 1 + end + z = z + 1 + end + + byte_acc = (byte_acc << 1) + escape + bit_num = bit_num + 1 + + -- Code is very similar for these cases, but using separate blocks + -- ensures we skip the shifting when it's unnecessary, + -- which is most cases. + if bit_num == 8 then + sum = sum ~ byte_acc + byte_acc = 0 + bit_num = 0 + elseif x == size - 1 then + byte_acc = byte_acc << (8 - bit_num) + sum = sum ~ byte_acc + byte_acc = 0 + bit_num = 0 + end + x = x + 1 + end + y = y + 1 + end + + return sum +end + +return mandelbrot diff --git a/awfy/Lua/mandelbrot-fn.lua b/awfy/Lua/mandelbrot-fn.lua new file mode 100644 index 0000000..16c562a --- /dev/null +++ b/awfy/Lua/mandelbrot-fn.lua @@ -0,0 +1,74 @@ +--[[ + The module 'bit' is available with: + * LuaJIT + * LuaBitOp extension which is available for: + * Lua 5.1 + * Lua 5.2 + The module 'bit32' is available with: + * Lua 5.2 + * Lua 5.3 when compiled with LUA_COMPAT_5_2 + The bitwise operators are added to Lua 5.3 as new lexemes (there causes + lexical error in older version) +--]] + +local bit = bit32 or require'bit' +local bxor = bit.bxor +local lshift = bit.lshift + +local function mandelbrot (size) + local sum = 0 + local byte_acc = 0 + local bit_num = 0 + + local y = 0 + while y < size do + local ci = (2.0 * y / size) - 1.0 + local x = 0 + + while x < size do + local zrzr = 0.0 + local zizi, zi = 0.0, 0.0 + local cr = (2.0 * x / size) - 1.5 + + local z = 0 + local not_done = true + local escape = 0 + while not_done and z < 50 do + local zr = zrzr - zizi + cr + zi = 2.0 * zr * zi + ci + + -- preserve recalculation + zrzr = zr * zr + zizi = zi * zi + if zrzr + zizi > 4.0 then + not_done = false + escape = 1 + end + z = z + 1 + end + + byte_acc = (byte_acc * 2) + escape + bit_num = bit_num + 1 + + -- Code is very similar for these cases, but using separate blocks + -- ensures we skip the shifting when it's unnecessary, + -- which is most cases. + if bit_num == 8 then + sum = bxor(sum, byte_acc) + byte_acc = 0 + bit_num = 0 + elseif x == size - 1 then + byte_acc = lshift(byte_acc, (8 - bit_num)) + sum = bxor(sum,byte_acc) + byte_acc = 0 + bit_num = 0 + end + x = x + 1 + end + y = y + 1 + end + + return sum +end + +return mandelbrot diff --git a/awfy/Lua/mandelbrot.lua b/awfy/Lua/mandelbrot.lua new file mode 100644 index 0000000..908fd0a --- /dev/null +++ b/awfy/Lua/mandelbrot.lua @@ -0,0 +1,75 @@ +-- This benchmark is adapted to match the SOM version. +-- Ported on Lua by Francois Perrad +-- +-- Copyright © 2004-2013 Brent Fulgham +-- +-- All rights reserved. +-- +-- Redistribution and use in source and binary forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- +-- * Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- +-- * Redistributions in binary form must reproduce the above copyright notice, +-- this list of conditions and the following disclaimer in the documentation +-- and/or other materials provided with the distribution. +-- +-- * Neither the name of "The Computer Language Benchmarks Game" nor the name +-- of "The Computer Language Shootout Benchmarks" nor the names of its +-- contributors may be used to endorse or promote products derived from this +-- software without specific prior written permission. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +-- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +-- SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +-- CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +-- OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +-- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +-- The Computer Language Benchmarks Game +-- http:--benchmarksgame.alioth.debian.org +-- +-- contributed by Karl von Laudermann +-- modified by Jeremy Echols +-- modified by Detlef Reichl +-- modified by Joseph LaFata +-- modified by Peter Zotov + +-- http:--benchmarksgame.alioth.debian.org/u64q/program.php?test=mandelbrot&lang=yarv&id=3 + +local mandelbrot = {} do +setmetatable(mandelbrot, {__index = require'benchmark'}) + +local _mandelbrot +if _VERSION < 'Lua 5.3' then + _mandelbrot = require 'mandelbrot-fn' +else + _mandelbrot = require 'mandelbrot-fn-53' +end + +function mandelbrot:inner_benchmark_loop (inner_iterations) + return self:verify_result(_mandelbrot(inner_iterations), inner_iterations) +end + +function mandelbrot:verify_result (result, inner_iterations) + if inner_iterations == 500 then + return result == 191 + elseif inner_iterations == 750 then + return result == 50 + elseif inner_iterations == 1 then + return result == 128 + else + print(('No verification result for %d found'):format(inner_iterations)) + print(('Result is: %d'):format(result)) + return false + end +end + +end -- object mandelbrot + +return mandelbrot diff --git a/awfy/Lua/nbody.lua b/awfy/Lua/nbody.lua new file mode 100644 index 0000000..a0de1a5 --- /dev/null +++ b/awfy/Lua/nbody.lua @@ -0,0 +1,189 @@ +-- The Computer Language Benchmarks Game +-- http:--shootout.alioth.debian.org/ +-- +-- contributed by Mark C. Lewis +-- modified slightly by Chad Whipkey +-- +-- Based on nbody.java ported to SOM, and then Lua by Francois Perrad. + +local Body = {_CLASS = 'Body'} do + +local PI = 3.141592653589793 +local SOLAR_MASS = 4.0 * PI * PI +local DAYS_PER_YEAR = 365.24 + +function Body.new (x, y, z, vx, vy, vz, mass) + local obj = { + x = x, + y = y, + z = z, + vx = vx * DAYS_PER_YEAR, + vy = vy * DAYS_PER_YEAR, + vz = vz * DAYS_PER_YEAR, + mass = mass * SOLAR_MASS, + } + return setmetatable(obj, {__index = Body}) +end + +function Body:offset_momentum (px, py, pz) + self.vx = 0.0 - (px / SOLAR_MASS) + self.vy = 0.0 - (py / SOLAR_MASS) + self.vz = 0.0 - (pz / SOLAR_MASS) +end + +function Body.jupiter () + return Body.new( 4.8414314424647209, + -1.16032004402742839, + -0.103622044471123109, + 0.00166007664274403694, + 0.00769901118419740425, + -0.0000690460016972063023, + 0.000954791938424326609) +end + +function Body.saturn () + return Body.new( 8.34336671824457987, + 4.12479856412430479, + -0.403523417114321381, + -0.00276742510726862411, + 0.00499852801234917238, + 0.0000230417297573763929, + 0.000285885980666130812) +end + +function Body.uranus () + return Body.new( 12.894369562139131, + -15.1111514016986312, + -0.223307578892655734, + 0.00296460137564761618, + 0.0023784717395948095, + -0.0000296589568540237556, + 0.0000436624404335156298) +end + +function Body.neptune () + return Body.new( 15.3796971148509165, + -25.9193146099879641, + 0.179258772950371181, + 0.00268067772490389322, + 0.00162824170038242295, + -0.000095159225451971587, + 0.0000515138902046611451) +end + +function Body.sun () + return Body.new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0) +end + +end -- class Body + +local NBodySystem = {_CLASS = 'NBodySystem'} do + +local sqrt = math.sqrt + +local function create_bodies () + local bodies = {Body.sun(), + Body.jupiter(), + Body.saturn(), + Body.uranus(), + Body.neptune()} + local px, py, pz = 0.0, 0.0, 0.0 + for i = 1, #bodies do + local b = bodies[i] + px = px + b.vx * b.mass + py = py + b.vy * b.mass + pz = pz + b.vz * b.mass + end + bodies[1]:offset_momentum(px, py, pz) + return bodies +end + +function NBodySystem.new () + local obj = {bodies = create_bodies()} + return setmetatable(obj, {__index = NBodySystem}) +end + +function NBodySystem:advance (dt) + for i = 1, #self.bodies do + local i_body = self.bodies[i] + + for j = i + 1, #self.bodies do + local j_body = self.bodies[j] + local dx = i_body.x - j_body.x + local dy = i_body.y - j_body.y + local dz = i_body.z - j_body.z + + local dSquared = dx * dx + dy * dy + dz * dz + local distance = sqrt(dSquared) + local mag = dt / (dSquared * distance) + + i_body.vx = i_body.vx - dx * j_body.mass * mag + i_body.vy = i_body.vy - dy * j_body.mass * mag + i_body.vz = i_body.vz - dz * j_body.mass * mag + + j_body.vx = j_body.vx + dx * i_body.mass * mag + j_body.vy = j_body.vy + dy * i_body.mass * mag + j_body.vz = j_body.vz + dz * i_body.mass * mag + end + end + + for i = 1, #self.bodies do + local body = self.bodies[i] + body.x = body.x + dt * body.vx + body.y = body.y + dt * body.vy + body.z = body.z + dt * body.vz + end +end + +function NBodySystem:energy () + local e = 0.0 + + for i = 1, #self.bodies do + local i_body = self.bodies[i] + + e = e + 0.5 * i_body.mass * (i_body.vx * i_body.vx + + i_body.vy * i_body.vy + + i_body.vz * i_body.vz) + + for j = i + 1, #self.bodies do + local j_body = self.bodies[j] + + local dx = i_body.x - j_body.x + local dy = i_body.y - j_body.y + local dz = i_body.z - j_body.z + + local distance = sqrt(dx * dx + dy * dy + dz * dz) + e = e - (i_body.mass * j_body.mass) / distance; + end + end + return e +end + +end -- class NBodySystem + +local nbody = {} do +setmetatable(nbody, {__index = require'benchmark'}) + +function nbody:inner_benchmark_loop (inner_iterations) + local system = NBodySystem.new() + for _ = 1, inner_iterations do + system:advance(0.01) + end + return self:verify_result(system:energy(), inner_iterations) +end + +function nbody:verify_result (result, inner_iterations) + if inner_iterations == 250000 then + return result == -0.1690859889909308 + elseif inner_iterations == 1 then + return result == -0.16907495402506745 + else + print(('No verification result for %d found'):format(inner_iterations)) + print(('Result is: %.14g'):format(result)) + return false + end +end + +end -- object nbody + +return nbody diff --git a/awfy/Lua/permute.lua b/awfy/Lua/permute.lua new file mode 100644 index 0000000..a7c8acd --- /dev/null +++ b/awfy/Lua/permute.lua @@ -0,0 +1,58 @@ +-- This code is derived from the SOM benchmarks, see AUTHORS.md file. +-- +-- Copyright (c) 2016 Francois Perrad +-- +-- 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. + +local permute = {} do +setmetatable(permute, {__index = require'benchmark'}) + +function permute:benchmark () + self.count = 0 + self.v = {0, 0, 0, 0, 0, 0} + self:permute(6) + return self.count +end + +function permute:verify_result (result) + return 8660 == result +end + +function permute:permute (n) + self.count = self.count + 1 + if n ~= 0 then + local n1 = n - 1 + self:permute(n1) + for i = n, 1, -1 do + self:swap(n, i) + self:permute(n1) + self:swap(n, i) + end + end +end + +function permute:swap (i, j) + local tmp = self.v[i] + self.v[i] = self.v[j] + self.v[j] = tmp +end + +end -- object permute + +return permute diff --git a/awfy/Lua/queens.lua b/awfy/Lua/queens.lua new file mode 100644 index 0000000..15b0d1e --- /dev/null +++ b/awfy/Lua/queens.lua @@ -0,0 +1,77 @@ +-- This code is derived from the SOM benchmarks, see AUTHORS.md file. +-- +-- Copyright (c) 2016 Francois Perrad +-- +-- 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. + +local queens = {} do +setmetatable(queens, {__index = require'benchmark'}) + +function queens:benchmark () + local result = true + for _ = 1, 10 do + result = result and self:queens() + end + return result +end + +function queens:verify_result (result) + return result +end + +function queens:queens () + self.free_rows = {true, true, true, true, true, true, true, true} + self.free_maxs = {true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true} + self.free_mins = {true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true} + self.queen_rows = {-1, -1, -1, -1, -1, -1, -1, -1} + return self:place_queen(1) +end + +function queens:place_queen (c) + for r = 1, 8 do + if self:get_row_column(r, c) then + self.queen_rows[r] = c + self:set_row_column(r, c, false) + if c == 8 then + return true + end + if self:place_queen(c + 1) then + return true + end + self:set_row_column(r, c, true) + end + end + return false +end + +function queens:get_row_column (r, c) + return self.free_rows[r] and self.free_maxs[c + r] and self.free_mins[c - r + 8] +end + +function queens:set_row_column (r, c, v) + self.free_rows[r ] = v + self.free_maxs[c + r ] = v + self.free_mins[c - r + 8] = v +end + +end -- object queens + +return queens diff --git a/awfy/Lua/richards.lua b/awfy/Lua/richards.lua new file mode 100644 index 0000000..291edd4 --- /dev/null +++ b/awfy/Lua/richards.lua @@ -0,0 +1,517 @@ +-- The benchmark in its current state is a derivation from the SOM version, +-- which is derived from Mario Wolczko's Smalltalk version of DeltaBlue. +-- Ported on Lua by Francois Perrad +-- +-- The original license details are availble here: +-- http://web.archive.org/web/20050825101121/http://www.sunlabs.com/people/mario/java_benchmarking/index.html +-- +-- This file itself, and its souce control history is however based on the +-- following. It is unclear whether this still bears any relevance since the +-- nature of the code was essentially reverted back to the Smalltalk version. +-- +-- Derived from http://pws.prserv.net/dlissett/ben/bench1.htm +-- Licensed CC BY-NC-SA 1.0 + +--[[ + The module 'bit' is available with: + * LuaJIT + * LuaBitOp extension which is available for: + * Lua 5.1 + * Lua 5.2 + The module 'bit32' is available with: + * Lua 5.2 + * Lua 5.3 when compiled with LUA_COMPAT_5_2 + The bitwise operators are added to Lua 5.3 as new lexemes (there causes + lexical error in older version) +--]] +local band, bxor +if _VERSION < 'Lua 5.3' then + local bit = bit32 or require'bit' + band = bit.band + bxor = bit.bxor +else + band = assert(load'--[[band]] return function (a, b) return a & b end')() + bxor = assert(load'--[[bxor]] return function (a, b) return a ~ b end')() +end + +local NO_TASK = nil +local NO_WORK = nil + +local IDLER = 1 +local WORKER = 2 +local HANDLER_A = 3 +local HANDLER_B = 4 +local DEVICE_A = 5 +local DEVICE_B = 6 + +local TRACING = false + +local RBObject = {_CLASS = 'RBObject'} do + +function RBObject:append (packet, queue_head) + packet.link = NO_WORK + if NO_WORK == queue_head then + return packet + end + + local mouse = queue_head + + local link = mouse.link + while NO_WORK ~= link do + mouse = link + link = mouse.link + end + + mouse.link = packet + return queue_head +end + +end -- abstract RBObject + +local DeviceTaskDataRecord = {_CLASS = 'DeviceTaskDataRecord'} do +setmetatable(DeviceTaskDataRecord, {__index = RBObject}) + +function DeviceTaskDataRecord.new () + local obj = { + pending = NO_WORK, + } + return setmetatable(obj, {__index = DeviceTaskDataRecord}) +end + +end -- class DeviceTaskDataRecord + +local HandlerTaskDataRecord = {_CLASS = 'HandlerTaskDataRecord'} do +setmetatable(HandlerTaskDataRecord, {__index = RBObject}) + +function HandlerTaskDataRecord.new () + local obj = { + work_in = NO_WORK, + device_in = NO_WORK, + } + return setmetatable(obj, {__index = HandlerTaskDataRecord}) +end + +function HandlerTaskDataRecord:device_in_add (packet) + self.device_in = self:append(packet, self.device_in) +end + +function HandlerTaskDataRecord:work_in_add (packet) + self.work_in = self:append(packet, self.work_in) +end + +end -- class HandlerTaskDataRecord + +local IdleTaskDataRecord = {_CLASS = 'IdleTaskDataRecord'} do +setmetatable(IdleTaskDataRecord, {__index = RBObject}) + +function IdleTaskDataRecord.new () + local obj = { + control = 1, + count = 10000, + } + return setmetatable(obj, {__index = IdleTaskDataRecord}) +end + +end -- class IdleTaskDataRecord + +local WorkerTaskDataRecord = {_CLASS = 'WorkerTaskDataRecord'} do +setmetatable(WorkerTaskDataRecord, {__index = RBObject}) + +function WorkerTaskDataRecord.new () + local obj = { + destination = HANDLER_A, + count = 0, + } + return setmetatable(obj, {__index = WorkerTaskDataRecord}) +end + +end -- class WorkerTaskDataRecord + +local Packet = {_CLASS = 'Packet'} do +setmetatable(Packet, {__index = RBObject}) + +function Packet.new (link, identity, kind) + local obj = { + link = link, + kind = kind, + identity = identity, + datum = 1, + data = {0, 0, 0, 0}, + } + return setmetatable(obj, {__index = Packet}) +end + +end -- class Packet + +local TaskState = {_CLASS = 'TaskState'} do +setmetatable(TaskState, {__index = RBObject}) + +function TaskState.new () + local obj = { + task_holding = false, + task_waiting = false, + packt_pending = false, + } + return setmetatable(obj, {__index = TaskState}) +end + +function TaskState:is_packet_pending () + return self.packt_pending +end + +function TaskState:is_task_waiting () + return self.task_waiting +end + +function TaskState:is_task_holding () + return self.task_holding +end + +function TaskState:packet_pending () + self.packt_pending = true + self.task_waiting = false + self.task_holding = false + return self +end + +function TaskState:running () + self.packt_pending = false + self.task_waiting = false + self.task_holding = false + return self +end + +function TaskState:waiting () + self.packt_pending = false + self.task_holding = false + self.task_waiting = true + return self +end + +function TaskState:waiting_with_packet () + self.task_holding = false + self.task_waiting = true + self.packt_pending = true + return self +end + +function TaskState:is_task_holding_or_waiting () + return self.task_holding or (not self.packt_pending and self.task_waiting) +end + +function TaskState:is_waiting_with_packet () + return self.packt_pending and self.task_waiting and not self.task_holding +end + +function TaskState.create_running () + return TaskState.new():running() +end + +function TaskState.create_waiting () + return TaskState.new():waiting() +end + +function TaskState.create_waiting_with_packet () + return TaskState.new():waiting_with_packet() +end + +end -- class TaskState + +local TaskControlBlock = {_CLASS = 'TaskControlBlock'} do +setmetatable(TaskControlBlock, {__index = TaskState}) + +function TaskControlBlock.new (link, identity, priority, initial_work_queue, + initial_state, private_data, fn) + local obj = { + link = link, + identity = identity, + priority = priority, + input = initial_work_queue, + handle = private_data, + + packt_pending = initial_state:is_packet_pending(), + task_waiting = initial_state:is_task_waiting(), + task_holding = initial_state:is_task_holding(), + + fn = fn, + } + return setmetatable(obj, {__index = TaskControlBlock}) +end + +function TaskControlBlock:add_input_and_check_priority (packet, old_task) + if NO_WORK == self.input then + self.input = packet + self.packt_pending = true + if self.priority > old_task.priority then + return self + end + else + self.input = self:append(packet, self.input) + end + return old_task +end + +function TaskControlBlock:run_task () + local message + if self:is_waiting_with_packet() then + message = self.input + self.input = message.link + if NO_WORK == self.input then + self:running() + else + self:packet_pending() + end + else + message = NO_WORK + end + return self.fn(message, self.handle) +end + +end -- class TaskControlBlock + +local Scheduler = {_CLASS = 'Scheduler'} do +setmetatable(Scheduler, {__index = RBObject}) + +local DEVICE_PACKET_KIND = 0 +local WORK_PACKET_KIND = 1 + +local DATA_SIZE = 4 + +function Scheduler.new () + local obj = { + -- init tracing + layout = 0, + + -- init scheduler + task_list = NO_TASK, + current_task = NO_TASK, + current_task_identity = 0, + task_table = {NO_TASK, NO_TASK, NO_TASK, NO_TASK, NO_TASK, NO_TASK}, + + queue_count = 0, + hold_count = 0, + } + return setmetatable(obj, {__index = Scheduler}) +end + +function Scheduler:create_device (identity, priority, work, state) + self:create_task(identity, priority, work, state, + DeviceTaskDataRecord.new(), + function (packet, data) + if NO_WORK == packet then + packet = data.pending + if NO_WORK == packet then + return self:wait() + else + data.pending = NO_WORK + return self:queue_packet(packet) + end + else + data.pending = packet + if TRACING then + self:trace(packet.datum) + end + return self:hold_self() + end + end) +end + +function Scheduler:create_handler (identity, priority, work, state) + self:create_task(identity, priority, work, state, + HandlerTaskDataRecord.new(), + function (packet, data) + if NO_WORK ~= packet then + if WORK_PACKET_KIND == packet.kind then + data:work_in_add(packet) + else + data:device_in_add(packet) + end + end + + local work_packet = data.work_in + if NO_WORK == work_packet then + return self:wait() + else + local count = work_packet.datum + if count > DATA_SIZE then + data.work_in = work_packet.link + return self:queue_packet(work_packet) + else + local device_packet = data.device_in + if NO_WORK == device_packet then + return self:wait() + else + data.device_in = device_packet.link + device_packet.datum = work_packet.data[count] + work_packet.datum = count + 1 + return self:queue_packet(device_packet) + end + end + end + end) +end + +function Scheduler:create_idler (identity, priority, work, state) + self:create_task(identity, priority, work, state, + IdleTaskDataRecord.new(), + function (_, data) + data.count = data.count - 1 + if 0 == data.count then + return self:hold_self() + else + if 0 == band(data.control, 1) then + data.control = data.control / 2 + return self:release(DEVICE_A) + else + data.control = bxor((data.control - 1) / 2, 53256) + return self:release(DEVICE_B) + end + end + end) +end + +function Scheduler:create_packet (link, identity, kind) + return Packet.new(link, identity, kind) +end + +function Scheduler:create_task (identity, priority, work, state, data, fn) + local tcb = TaskControlBlock.new(self.task_list, identity, priority, + work, state, data, fn) + self.task_list = tcb + self.task_table[identity] = tcb +end + +function Scheduler:create_worker (identity, priority, work, state) + self:create_task(identity, priority, work, state, + WorkerTaskDataRecord.new(), + function (packet, data) + if NO_WORK == packet then + return self:wait() + else + data.destination = (HANDLER_A == data.destination) and HANDLER_B or HANDLER_A + packet.identity = data.destination + packet.datum = 1 + for i = 1, DATA_SIZE do + data.count = data.count + 1 + if data.count > 26 then + data.count = 1 + end + packet.data[i] = 65 + data.count - 1 + end + return self:queue_packet(packet) + end + end) +end + +function Scheduler:start () + local queue + self:create_idler(IDLER, 0, NO_WORK, TaskState.create_running()) + queue = self:create_packet(NO_WORK, WORKER, WORK_PACKET_KIND) + queue = self:create_packet(queue, WORKER, WORK_PACKET_KIND) + + self:create_worker(WORKER, 1000, queue, TaskState.create_waiting_with_packet()) + queue = self:create_packet(NO_WORK, DEVICE_A, DEVICE_PACKET_KIND) + queue = self:create_packet(queue, DEVICE_A, DEVICE_PACKET_KIND) + queue = self:create_packet(queue, DEVICE_A, DEVICE_PACKET_KIND) + + self:create_handler(HANDLER_A, 2000, queue, TaskState.create_waiting_with_packet()) + queue = self:create_packet(NO_WORK, DEVICE_B, DEVICE_PACKET_KIND) + queue = self:create_packet(queue, DEVICE_B, DEVICE_PACKET_KIND) + queue = self:create_packet(queue, DEVICE_B, DEVICE_PACKET_KIND) + + self:create_handler(HANDLER_B, 3000, queue, TaskState.create_waiting_with_packet()) + self:create_device(DEVICE_A, 4000, NO_WORK, TaskState.create_waiting()) + self:create_device(DEVICE_B, 5000, NO_WORK, TaskState.create_waiting()) + + self:schedule() + + return self.queue_count == 23246 and self.hold_count == 9297 +end + +function Scheduler:find_task (identity) + local task = self.task_table[identity] + assert(task ~= NO_TASK, 'find_task failed') + return task +end + +function Scheduler:hold_self () + self.hold_count = self.hold_count + 1 + local current_task = self.current_task + current_task.task_holding = true + return current_task.link +end + +function Scheduler:queue_packet (packet) + local task = self:find_task(packet.identity) + if NO_TASK == task then + return NO_TASK + end + + self.queue_count = self.queue_count + 1 + + packet.link = NO_WORK + packet.identity = self.current_task_identity + return task:add_input_and_check_priority(packet, self.current_task) +end + +function Scheduler:release (identity) + local task = self:find_task(identity) + if NO_TASK == task then + return NO_TASK + end + + task.task_holding = false + + if task.priority > self.current_task.priority then + return task + else + return self.current_task + end +end + +function Scheduler:trace (id) + self.layout = self.layout - 1 + if 0 >= self.layout then + io.stdout:write'\n' + self.layout = 50 + end + io.stdout:write(tostring(id)) +end + +function Scheduler:wait () + local current_task = self.current_task + current_task.task_waiting = true + return current_task +end + +function Scheduler:schedule () + self.current_task = self.task_list + while self.current_task ~= NO_TASK do + if self.current_task:is_task_holding_or_waiting() then + self.current_task = self.current_task.link + else + self.current_task_identity = self.current_task.identity + if TRACING then + self:trace(self.current_task_identity - 1) + end + self.current_task = self.current_task:run_task() + end + end +end + +end -- class Scheduler + +local richards = {} do +setmetatable(richards, {__index = require'benchmark'}) + +function richards:benchmark () + return Scheduler.new():start() +end + +function richards:verify_result (result) + return result +end + +end -- object richards + +return richards diff --git a/awfy/Lua/sieve.lua b/awfy/Lua/sieve.lua new file mode 100644 index 0000000..0925a42 --- /dev/null +++ b/awfy/Lua/sieve.lua @@ -0,0 +1,55 @@ +-- This code is derived from the SOM benchmarks, see AUTHORS.md file. +-- +-- Copyright (c) 2016 Francois Perrad +-- +-- 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. + +local sieve = {} do +setmetatable(sieve, {__index = require'benchmark'}) + +function sieve:benchmark () + local flags = {} + for i = 1, 5000 do + flags[i] = true + end + return self.sieve(flags, 5000) +end + +function sieve:verify_result (result) + return result == 669 +end + +function sieve.sieve (flags, size) + local prime_count = 0 + for i = 2, size do + if flags[i - 1] then + prime_count = prime_count + 1 + local k = i + i + while k <= size do + flags[k - 1] = false + k = k + i + end + end + end + return prime_count +end + +end -- object sieve + +return sieve diff --git a/awfy/Lua/som.lua b/awfy/Lua/som.lua new file mode 100644 index 0000000..90bfbc1 --- /dev/null +++ b/awfy/Lua/som.lua @@ -0,0 +1,621 @@ +-- This code is derived from the SOM benchmarks, see AUTHORS.md file. +-- +-- Copyright (c) 2016 Francois Perrad +-- +-- 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. + +--[[ + The module 'bit' is available with: + * LuaJIT + * LuaBitOp extension which is available for: + * Lua 5.1 + * Lua 5.2 + The module 'bit32' is available with: + * Lua 5.2 + * Lua 5.3 when compiled with LUA_COMPAT_5_2 + The bitwise operators are added to Lua 5.3 as new lexemes (there causes + lexical error in older version) +--]] +local band, bxor, rshift +if _VERSION < 'Lua 5.3' then + local bit = bit32 or require'bit' + band = bit.band + bxor = bit.bxor + rshift = bit.rshift +else + band = assert(load'--[[band]] return function (a, b) return a & b end')() + bxor = assert(load'--[[bxor]] return function (a, b) return a ~ b end')() + rshift = assert(load'--[[rshift]] return function (a, b) return a >> b end')() +end + +local alloc_array +local ok, table_new = pcall(require, 'table.new') -- LuaJIT 2.1 extension +if ok then + alloc_array = function (n) + local t = table_new(n, 1) + t.n = n + return t + end +else + alloc_array = function (n) + local t = {} + t.n = n + return t + end +end + +local Vector = {_CLASS = 'Vector'} do + +local floor = math.floor +local max = math.max + +local INITIAL_SIZE = 10 + +function Vector.new (size) + local obj = { + storage = (size and size > 0) and alloc_array(size) or nil, + first_idx = 1, + last_idx = 1, + } + return setmetatable(obj, {__index = Vector}) +end + +function Vector.with (elem) + local v = Vector.new(1) + v:append(elem) + return v +end + +function Vector:at (idx) + if self.storage == nil or idx > self.storage.n then + return nil + end + return self.storage[idx] +end + +function Vector:at_put (idx, val) + if self.storage == nil then + self.storage = alloc_array(max(idx, INITIAL_SIZE)) + elseif idx > self.storage.n then + local new_n = self.storage.n + while idx > new_n do + new_n = new_n * 2 + end + + local new_storage = alloc_array(new_n) + for i = 1, self.storage.n do + new_storage[i] = self.storage[i] + end + self.storage = new_storage + end + self.storage[idx] = val + + if self.last_idx < idx + 1 then + self.last_idx = idx + 1 + end +end + +function Vector:append (elem) + if self.storage == nil then + self.storage = alloc_array(INITIAL_SIZE) + elseif self.last_idx > self.storage.n then + -- Need to expand capacity first + local new_storage = alloc_array(2 * self.storage.n) + for i = 1, self.storage.n do + new_storage[i] = self.storage[i] + end + self.storage = new_storage + end + + self.storage[self.last_idx] = elem + self.last_idx = self.last_idx + 1 +end + +function Vector:is_empty () + return self.last_idx == self.first_idx +end + +function Vector:each (fn) + for i = self.first_idx, self.last_idx - 1 do + fn(self.storage[i]) + end +end + +function Vector:has_some (fn) + for i = self.first_idx, self.last_idx - 1 do + if fn(self.storage[i]) then + return true + end + end + return false +end + +function Vector:get_one (fn) + for i = self.first_idx, self.last_idx - 1 do + local e = self.storage[i] + if fn(e) then + return e + end + end + return nil +end + +function Vector:remove_first () + if self:is_empty() then + return nil + end + + self.first_idx = self.first_idx + 1 + return self.storage[self.first_idx - 1] +end + +function Vector:remove (obj) + if self.storage == nil or self:is_empty() then + return false + end + + local new_array = alloc_array(self:capacity()) + local new_last = 1 + local found = false + + self:each(function (it) + if it == obj then + found = true + else + new_array[new_last] = it + new_last = new_last + 1 + end + end) + + self.storage = new_array + self.last_idx = new_last + self.first_idx = 1 + return found +end + +function Vector:remove_all () + self.first_idx = 1 + self.last_idx = 1 + + if self.storage ~= nil then + self.storage = alloc_array(self:capacity()) + end +end + +function Vector:size () + return self.last_idx - self.first_idx +end + +function Vector:capacity () + if self.storage == nil then + return 0 + else + return self.storage.n + end +end + +function Vector:sort (fn) + -- Make the argument, block, be the criterion for ordering elements of + -- the receiver. + -- Sort blocks with side effects may not work right. + if self:size() > 0 then + self:sort_range(self.first_idx, self.last_idx - 1, fn) + end +end + +function Vector:sort_range (i, j, fn) + assert(fn) + + -- The prefix d means the data at that index. + local n = j + 1 - i + if n <= 1 then + -- Nothing to sort + return + end + + local storage = self.storage + -- Sort di, dj + local di = storage[i] + local dj = storage[j] + + -- i.e., should di precede dj? + if not fn(di, dj) then + local tmp = storage[i] + storage[i] = storage[j] + storage[j] = tmp + local tt = di + di = dj + dj = tt + end + + -- NOTE: For DeltaBlue, this is never reached. + if n > 2 then -- More than two elements. + local ij = floor((i + j) / 2) -- ij is the midpoint of i and j. + local dij = storage[ij] -- Sort di,dij,dj. Make dij be their median. + + if fn(di, dij) then -- i.e. should di precede dij? + if not fn(dij, dj) then -- i.e., should dij precede dj? + local tmp = storage[j] + storage[j] = storage[ij] + storage[ij] = tmp + dij = dj + end + else -- i.e. di should come after dij + local tmp = storage[i] + storage[i] = storage[ij] + storage[ij] = tmp + dij = di + end + + if n > 3 then -- More than three elements. + -- Find k>i and l l then + break + end + + local tmp = storage[k] + storage[k] = storage[l] + storage[l] = tmp + end + + -- Now l < k (either 1 or 2 less), and di through dl are all + -- less than or equal to dk through dj. Sort those two segments. + self:sort_range(i, l, fn) + self:sort_range(k, j, fn) + end + end +end + +end -- class Vector + +local Set = {_CLASS = 'Set'} do + +local INITIAL_SIZE = 10 + +function Set.new (size) + local obj = { + items = Vector.new(size or INITIAL_SIZE) + } + return setmetatable(obj, {__index = Set}) +end + +function Set:size () + return self.items:size() +end + +function Set:each (fn) + self.items:each(fn) +end + +function Set:has_some (fn) + return self.items:has_some(fn) +end + +function Set:get_one (fn) + return self.items:get_one(fn) +end + +function Set:add (obj) + if not self:contains(obj) then + self.items:append(obj) + end +end + +function Set:remove_all () + self.items:remove_all() +end + +function Set:collect (fn) + local coll = Vector.new() + self:each(function (it) + coll:append(fn(it)) + end) + return coll +end + +function Set:contains (obj) + return self:has_some(function (it) return it == obj end) +end + +end -- class Set + +local IdentitySet = {_CLASS = 'IdentitySet'} do +setmetatable(IdentitySet, {__index = Set}) + +function IdentitySet.new (size) + local obj = Set.new(size) + return setmetatable(obj, {__index = IdentitySet}) +end + +function IdentitySet:contains (obj) + return self:has_some(function (it) return it == obj end) +end + +end -- class IdentitySet + +local Entry = {_CLASS = 'Entry'} do + +function Entry.new (hash, key, value, next) + local obj = { + hash = hash, + key = key, + value = value, + next = next, + } + return setmetatable(obj, {__index = Entry}) +end + +function Entry:match (hash, key) + return self.hash == hash and self.key == key +end + +end -- class Entry + +local Dictionary = {_CLASS = 'Dictionary'} do + +local INITIAL_CAPACITY = 16 + +function Dictionary.new (size) + local obj = { + buckets = alloc_array(size or INITIAL_CAPACITY), + size = 0, + } + return setmetatable(obj, {__index = Dictionary}) +end + +function Dictionary:hash (key) + if not key then + return 0 + end + local hash = key:custom_hash() + return bxor(hash, rshift(hash, 16)) +end + +function Dictionary:is_empty () + return self.size == 0 +end + +function Dictionary:get_bucket_idx (hash) + return band(self.buckets.n - 1, hash) + 1 +end + +function Dictionary:get_bucket (hash) + return self.buckets[self:get_bucket_idx(hash)] +end + +function Dictionary:at (key) + local hash = self:hash(key) + local e = self:get_bucket(hash) + + while e do + if e:match(hash, key) then + return e.value + end + e = e.next + end + return nil +end + +function Dictionary:contains_key (key) + local hash = self:hash(key) + local e = self:get_bucket(hash) + + while e do + if e.match(hash, key) then + return true + end + e = e.next + end + return false +end + +function Dictionary:at_put (key, value) + local hash = self:hash(key) + local i = self:get_bucket_idx(hash) + local current = self.buckets[i] + + if not current then + self.buckets[i] = self:new_entry(key, value, hash) + self.size = self.size + 1 + else + self:insert_bucket_entry(key, value, hash, current) + end + + if self.size > self.buckets.n then + self:resize() + end +end + +function Dictionary:new_entry (key, value, hash) + return Entry.new(hash, key, value, nil) +end + +function Dictionary:insert_bucket_entry (key, value, hash, head) + local current = head + + while true do + if current:match(hash, key) then + current.value = value + return + end + if not current.next then + self.size = self.size + 1 + current.next = self:new_entry(key, value, hash) + return + end + current = current.next + end +end + +function Dictionary:resize () + local old_storage = self.buckets + self.buckets = alloc_array(old_storage.n * 2) + self:transfer_entries(old_storage) +end + +function Dictionary:transfer_entries (old_storage) + local buckets = self.buckets + for i = 1, old_storage.n do + local current = old_storage[i] + + if current then + old_storage[i] = nil + if not current.next then + local hash = band(current.hash, buckets.n - 1) + 1 + buckets[hash] = current + else + self:split_bucket(old_storage, i, current) + end + end + end +end + +function Dictionary:split_bucket (old_storage, i, head) + local lo_head, lo_tail = nil, nil + local hi_head, hi_tail = nil, nil + local current = head + + while current do + if band(current.hash, old_storage.n) == 0 then + if not lo_tail then + lo_head = current + else + lo_tail.next = current + end + lo_tail = current + else + if not hi_tail then + hi_head = current + else + hi_tail.next = current + end + hi_tail = current + end + current = current.next + end + + if lo_tail then + lo_tail.next = nil + self.buckets[i] = lo_head + end + if hi_tail then + hi_tail.next = nil + self.buckets[i + old_storage.n] = hi_head + end +end + +function Dictionary:remove_all () + self.buckets = alloc_array(self.buckets.n) + self.size = 0 +end + +function Dictionary:keys () + local keys = Vector.new(self.size) + local buckets = self.buckets + for i = 1, buckets.n do + local current = buckets[i] + while current do + keys:append(current.key) + current = current.next + end + end + return keys +end + +function Dictionary:values () + local vals = Vector.new(self.size) + local buckets = self.buckets + for i = 1, buckets.n do + local current = buckets[i] + while current do + vals:append(current.value) + current = current.next + end + end + return vals +end + +end -- class Dictionary + +local IdEntry = {_CLASS = 'IdEntry'} do +setmetatable(IdEntry, {__index = Entry}) + +function IdEntry.new (hash, key, value, next) + local obj = Entry.new (hash, key, value, next) + return setmetatable(obj, {__index = IdEntry}) +end + +function IdEntry:match (hash, key) + return self.hash == hash and self.key == key +end + +end -- class IdEntry + +local IdentityDictionary = {_CLASS = 'IdentityDictionary'} do +setmetatable(IdentityDictionary, {__index = Dictionary}) + +function IdentityDictionary.new (size) + local obj = Dictionary.new (size) + return setmetatable(obj, {__index = Dictionary}) +end + +function IdentityDictionary:new_entry (key, value, hash) + return IdEntry.new(hash, key, value, nil) +end + +end -- class IdentityDictionary + +local Random = {_CLASS = 'Random'} do + +function Random.new () + local obj = {seed = 74755} + return setmetatable(obj, {__index = Random}) +end + +function Random:next () + self.seed = band(((self.seed * 1309) + 13849), 65535); + return self.seed; +end + +end -- class Random + +return { + Vector = Vector, + Set = Set, + IdentitySet = IdentitySet, + Dictionary = Dictionary, + IdentityDictionary = IdentityDictionary, + Random = Random, +} diff --git a/awfy/Lua/storage.lua b/awfy/Lua/storage.lua new file mode 100644 index 0000000..fdffcca --- /dev/null +++ b/awfy/Lua/storage.lua @@ -0,0 +1,56 @@ +-- This code is derived from the SOM benchmarks, see AUTHORS.md file. +-- +-- Copyright (c) 2016 Francois Perrad +-- +-- 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. + +local Random = require'som'.Random + +local storage = {} do +setmetatable(storage, {__index = require'benchmark'}) + +function storage:benchmark () + local random = Random.new() + self.count = 0 + self:build_tree_depth(7, random) + return self.count +end + +function storage:verify_result (result) + return 5461 == result +end + +function storage:build_tree_depth (depth, random) + self.count = self.count + 1 + if depth == 1 then + -- With Lua, an array cannot be pre-allocated. + -- We just compute the size like in others languages. + return {n = random:next() % 10 + 1} + else + local arr = {n = 4} + for i = 1, 4 do + arr[i] = self:build_tree_depth(depth - 1, random) + end + return arr + end +end + +end -- object storage + +return storage diff --git a/awfy/Lua/towers.lua b/awfy/Lua/towers.lua new file mode 100644 index 0000000..e8ced95 --- /dev/null +++ b/awfy/Lua/towers.lua @@ -0,0 +1,83 @@ +-- This code is derived from the SOM benchmarks, see AUTHORS.md file. +-- +-- Copyright (c) 2016 Francois Perrad +-- +-- 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. + +local towers = {} do +setmetatable(towers, {__index = require'benchmark'}) + +local function create_disk (size) + return {size = size, next = nil} +end + +function towers:benchmark () + self.piles = {} + self:build_tower_at(1, 13) + self.moves_done = 0 + self:move_disks(13, 1, 2) + return self.moves_done +end + +function towers:verify_result (result) + return 8191 == result +end + +function towers:push_disk (disk, pile) + local top = self.piles[pile] + if top and disk.size >= top.size then + error 'Cannot put a big disk on a smaller one' + end + disk.next = top + self.piles[pile] = disk +end + +function towers:pop_disk_from (pile) + local top = self.piles[pile] + assert(top, 'Attempting to remove a disk from an empty pile') + self.piles[pile] = top.next + top.next = nil + return top +end + +function towers:move_top_disk (from_pile, to_pile) + self:push_disk(self:pop_disk_from(from_pile), to_pile) + self.moves_done = self.moves_done + 1 +end + +function towers:build_tower_at (pile, disks) + for i = disks, 1, -1 do + self:push_disk(create_disk(i), pile) + end +end + +function towers:move_disks (disks, from_pile, to_pile) + if disks == 1 then + self:move_top_disk(from_pile, to_pile) + else + local other_pile = 6 - from_pile - to_pile + self:move_disks(disks - 1, from_pile, other_pile) + self:move_top_disk(from_pile, to_pile) + self:move_disks(disks - 1, other_pile, to_pile) + end +end + +end -- object towers + +return towers diff --git a/rebench.conf b/rebench.conf new file mode 100644 index 0000000..95a1412 --- /dev/null +++ b/rebench.conf @@ -0,0 +1,51 @@ +default_experiment: all +default_data_file: 'benchmark.data' + +benchmark_suites: + awfy: + gauge_adapter: RebenchLog + command: " harness.lua %(benchmark)s %(iterations)s " + max_invocation_time: 600 # seconds per vm invocation + min_iteration_time: 200 # miliseconds per iteration + invocations: 10 # the number of process executions + iterations: 30 # the number of in-process iterations + cores: [ "default" ] + location: awfy/Lua + benchmarks: &BENCHMARKS + - DeltaBlue: + extra_args: 12000 + - Richards: + extra_args: 100 + - Json: + extra_args: 100 + - CD: + extra_args: 250 + - Havlak: + extra_args: 1500 + - Bounce: + extra_args: 1500 + - List: + extra_args: 1500 + - Mandelbrot: + extra_args: 500 + - NBody: + extra_args: 250000 + - Permute: + extra_args: 1000 + - Queens: + extra_args: 1000 + - Sieve: + extra_args: 3000 + - Storage: + extra_args: 1000 + - Towers: + extra_args: 600 + +executors: + Lua: + executable: lua + +experiments: + yk: + suites: [awfy] + executions: [Lua]