Created
January 4, 2014 19:26
-
-
Save inmatarian/8259581 to your computer and use it in GitHub Desktop.
Implementation of a Strided Array, a generalized data structure for accessing a single array through a multiple dimension view.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
local stride, stride_accessor, TERMINATOR | |
stride = { | |
_VERSION = "stride.lua 1.0", | |
_DESCRIPTION = [[ | |
Strided arrays are a generalized version of multi-dimensional arrays. | |
Use this instead of doing a lot of A[1+(y-1)*width+(x-1)] mayhem all over. | |
Example usage: | |
my_array = { 1, 2, 3, 4, 5, 6 } | |
view2d = stride.new(my_array, {3, 2}) | |
]], | |
new = function(...) | |
return stride.init(setmetatable({}, stride), ...) | |
end, | |
-- Data is an existing data array | |
-- Shape values should represent each dimension, starting with the lowest first, | |
-- i.e. { x, y, z } | |
-- Options is a table of key-value pairs | |
-- assertions: true runs assert to init conditions | |
-- boundingmode: string representing how to handle bounds checks | |
-- "unsafe": performs no secure check of bounds, bleed values | |
-- "safe": allows going outside of bounds, loses data or returns default | |
-- "error": disallows going outside of bounds, calls error | |
-- default: the default value for safe bounding mode | |
init = function(self, data, shape, options) | |
-- provide view onto data | |
self.data = data | |
-- copy options and shape because those tables could be reused | |
self.options = { | |
assertions = true, | |
boundingmode = "safe", | |
default = 0, | |
} | |
if options then | |
for k, v in pairs(options) do self.options[k] = v end | |
end | |
self.shape = {} | |
for i = 1, #shape do self.shape[i] = shape[i] end | |
self.stride = {} | |
local sv = 1 | |
for i = 1, #shape do | |
self.stride[#self.stride+1] = sv | |
sv = sv * shape[i] | |
end | |
if self.options.assertions then | |
assert(#data == sv, "Shape doesn't completely represent data") | |
end | |
return self | |
end, | |
__index = { | |
get = function(self, ...) | |
return stride_accessor(self, TERMINAL, ...) | |
end, | |
set = function(self, value, ...) | |
stride_accessor(self, value, ...) | |
end, | |
}, | |
} | |
do | |
-- unique value to stand in for nil | |
TERMINATOR = {} | |
stride_accessor = function(self, value, ...) | |
local doassert = false | |
if self.options.assertions then doassert = assert end | |
if doassert then doassert(select('#', ...) == #self.stride, "N-Dimensionality is inconsistent.") end | |
local boundingmode = self.options.boundingmode | |
local L, stride, shape = 0, self.stride, self.shape | |
for i = 1, #stride do | |
local x = tonumber((select(i, ...))) | |
if doassert then doassert(x~=nil, "N-Dimension must be indexed by a number") end | |
if boundingmode ~= "unsafe" then | |
if x < 1 or x > shape[i] then | |
if boundingmode == "safe" then | |
if value ~= TERMINAL then | |
return self.options.default | |
else | |
return | |
end | |
else | |
error("N-Dimension must be within bounds") | |
end | |
end | |
end | |
L = L + stride[i] * (x-1) | |
end | |
if value ~= TERMINAL then | |
self.data[1+L] = value | |
else | |
return self.data[1+L] | |
end | |
end | |
end | |
return stride |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
local stride = require 'stride' | |
do -- Testing | |
local function assert_equal(actual, expected) | |
if actual ~= expected then | |
error(("actual %s ~= expected %s"):format(tostring(actual), tostring(expected)), 2) | |
end | |
end | |
local test1 = stride.new({1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, {4, 3}) | |
assert_equal(test1:get(1, 1), 1) | |
assert_equal(test1:get(2, 2), 6) | |
assert_equal(test1:get(3, 2), 7) | |
assert_equal(test1:get(4, 3), 12) | |
local test2 = stride.new({1, 2, 3, 4, 5, 6, 7, 8}, {2, 2, 2}) | |
assert_equal(test2:get(2, 1, 1), 2) | |
assert_equal(test2:get(1, 2, 1), 3) | |
assert_equal(test2:get(1, 1, 2), 5) | |
assert_equal(test2:get(1, 2, 2), 7) | |
local test3 = stride.new({0, 0, 0, 0, 0, 0}, {3, 2}) | |
local v = 1 | |
for y = 1, 2 do | |
for x = 1, 3 do | |
test3:set(v, x, y) | |
v = v * 2 | |
end | |
end | |
v = 1 | |
for i = 1, #test3.data do | |
assert_equal(test3.data[i], v) | |
v = v * 2 | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment