Skip to content

Commit

Permalink
Feature input field (#145)
Browse files Browse the repository at this point in the history
BinarySearch function has two signatures:
- `int index BinarySearch(number key, table array, string approximation)`
- `int index BinarySearch(number key, int low, int high, function getValue)`

getValue is a function with the signature:
- `number value getValue(int index)`

Given that all values in the array (or returned by getValue by index) are in ascending order; BinarySearch will find the index of the value `key` being searched for, or nil if none are found.

The string `approximation` can be set to the following:
- `nil`: If not set, only exact matches will be returned.
- `nearest`: The array index with the closest value will be returned.
- `up`: Nearest value, but always rounding up if possible.
- `down`: Nearest value, but always rounding down if possible.
  • Loading branch information
Lemonymous authored Sep 18, 2022
1 parent 71800fe commit 53e69e7
Show file tree
Hide file tree
Showing 9 changed files with 1,047 additions and 2 deletions.
3 changes: 2 additions & 1 deletion scripts/mod_loader/bootstrap/__scripts.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ local scripts = {
"classes",
"event",
"modApi",
"constants"
"constants",
"binarySearch",
}

local rootpath = GetParentPath(...)
Expand Down
68 changes: 68 additions & 0 deletions scripts/mod_loader/bootstrap/binarySearch.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@

local array_mt = {
__index = function(self, key)
return self.__getValue(key)
end
}

function BinarySearch(key, array, approximation, arg4, arg5)
local low, high

if type(array) == 'table' then
low, high = 1, #array
else
local getValue

-- Pass over variables from alternate signature.
key, low, high, getValue, approximation = key, array, approximation, arg4, arg5

array = { __getValue = getValue }
setmetatable(array, array_mt)
end

if high <= low then
return nil
end

-- Run binary search until there are 2 or fewer values left.
while high - low > 1 do
local mid = math.floor((low + high) / 2)
local val = array[mid]

if key > val then
low = mid
else
high = mid
end
end

-- There are only 1 or 2 values left, 'low' and 'high'.
if approximation == "nearest" then
local low_diff = math.abs(key - array[low])
local high_diff = math.abs(key - array[high])

if low_diff < high_diff then
return low
else
return high
end
elseif approximation == "up" then
if key > array[low] then
return high
else
return low
end
elseif approximation == "down" then
if key < array[high] then
return low
else
return high
end
else
if key == array[low] then
return low
elseif key == array[high] then
return high
end
end
end
142 changes: 142 additions & 0 deletions scripts/mod_loader/tests/binarySearch.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
local testsuite = Tests.Testsuite()
testsuite.name = "BinarySearch tests"

local TEXT_ERROR = "Returned index of binary search for %s"

local function err(key)
return string.format(TEXT_ERROR, key)
end

function testsuite.test_binary_search_exact()
local array = {10, 20, 30}

Assert.Equals(1, BinarySearch(10, array), err("value 10"))
Assert.Equals(2, BinarySearch(20, array), err("value 20"))
Assert.Equals(3, BinarySearch(30, array), err("value 30"))
Assert.Equals(nil, BinarySearch(0, array), err("value 0"))
Assert.Equals(nil, BinarySearch(19, array), err("value 19"))
Assert.Equals(nil, BinarySearch(21, array), err("value 21"))
Assert.Equals(nil, BinarySearch(40, array), err("value 40"))

return true
end

function testsuite.test_binary_search_nearest()
local array = {10, 20, 30}

Assert.Equals(1, BinarySearch(10, array, "nearest"), err("value 10"))
Assert.Equals(2, BinarySearch(20, array, "nearest"), err("value 20"))
Assert.Equals(3, BinarySearch(30, array, "nearest"), err("value 30"))
Assert.Equals(1, BinarySearch(0, array, "nearest"), err("value 0"))
Assert.Equals(2, BinarySearch(19, array, "nearest"), err("value 19"))
Assert.Equals(2, BinarySearch(21, array, "nearest"), err("value 21"))
Assert.Equals(3, BinarySearch(40, array, "nearest"), err("value 40"))

return true
end

function testsuite.test_binary_search_up()
local array = {10, 20, 30}

Assert.Equals(1, BinarySearch(10, array, "up"), err("value 10"))
Assert.Equals(2, BinarySearch(20, array, "up"), err("value 20"))
Assert.Equals(3, BinarySearch(30, array, "up"), err("value 30"))
Assert.Equals(1, BinarySearch(0, array, "up"), err("value 0"))
Assert.Equals(2, BinarySearch(19, array, "up"), err("value 19"))
Assert.Equals(3, BinarySearch(21, array, "up"), err("value 21"))
Assert.Equals(3, BinarySearch(40, array, "up"), err("value 40"))

return true
end

function testsuite.test_binary_search_down()
local array = {10, 20, 30}

Assert.Equals(1, BinarySearch(10, array, "down"), err("value 10"))
Assert.Equals(2, BinarySearch(20, array, "down"), err("value 20"))
Assert.Equals(3, BinarySearch(30, array, "down"), err("value 30"))
Assert.Equals(1, BinarySearch(0, array, "down"), err("value 0"))
Assert.Equals(1, BinarySearch(19, array, "down"), err("value 19"))
Assert.Equals(2, BinarySearch(21, array, "down"), err("value 21"))
Assert.Equals(3, BinarySearch(40, array, "down"), err("value 40"))

return true
end

function testsuite.test_binary_search_noarray_exact()
local array = {10, 20, 30}
local low, high = 1, #array

local function getValue(key)
return array[key]
end

Assert.Equals(1, BinarySearch(10, low, high, getValue), err("value 10"))
Assert.Equals(2, BinarySearch(20, low, high, getValue), err("value 20"))
Assert.Equals(3, BinarySearch(30, low, high, getValue), err("value 30"))
Assert.Equals(nil, BinarySearch(0, low, high, getValue), err("value 0"))
Assert.Equals(nil, BinarySearch(19, low, high, getValue), err("value 19"))
Assert.Equals(nil, BinarySearch(21, low, high, getValue), err("value 21"))
Assert.Equals(nil, BinarySearch(40, low, high, getValue), err("value 40"))

return true
end

function testsuite.test_binary_search_noarray_nearest()
local array = {10, 20, 30}
local low, high = 1, #array

local function getValue(key)
return array[key]
end

Assert.Equals(1, BinarySearch(10, low, high, getValue, "nearest"), err("value 10"))
Assert.Equals(2, BinarySearch(20, low, high, getValue, "nearest"), err("value 20"))
Assert.Equals(3, BinarySearch(30, low, high, getValue, "nearest"), err("value 30"))
Assert.Equals(1, BinarySearch(0, low, high, getValue, "nearest"), err("value 0"))
Assert.Equals(2, BinarySearch(19, low, high, getValue, "nearest"), err("value 19"))
Assert.Equals(2, BinarySearch(21, low, high, getValue, "nearest"), err("value 21"))
Assert.Equals(3, BinarySearch(40, low, high, getValue, "nearest"), err("value 40"))

return true
end

function testsuite.test_binary_search_noarray_up()
local array = {10, 20, 30}
local low, high = 1, #array

local function getValue(key)
return array[key]
end

Assert.Equals(1, BinarySearch(10, low, high, getValue, "up"), err("value 10"))
Assert.Equals(2, BinarySearch(20, low, high, getValue, "up"), err("value 20"))
Assert.Equals(3, BinarySearch(30, low, high, getValue, "up"), err("value 30"))
Assert.Equals(1, BinarySearch(0, low, high, getValue, "up"), err("value 0"))
Assert.Equals(2, BinarySearch(19, low, high, getValue, "up"), err("value 19"))
Assert.Equals(3, BinarySearch(21, low, high, getValue, "up"), err("value 21"))
Assert.Equals(3, BinarySearch(40, low, high, getValue, "up"), err("value 40"))

return true
end

function testsuite.test_binary_search_noarray_down()
local array = {10, 20, 30}
local low, high = 1, #array

local function getValue(key)
return array[key]
end

Assert.Equals(1, BinarySearch(10, low, high, getValue, "down"), err("value 10"))
Assert.Equals(2, BinarySearch(20, low, high, getValue, "down"), err("value 20"))
Assert.Equals(3, BinarySearch(30, low, high, getValue, "down"), err("value 30"))
Assert.Equals(1, BinarySearch(0, low, high, getValue, "down"), err("value 0"))
Assert.Equals(1, BinarySearch(19, low, high, getValue, "down"), err("value 19"))
Assert.Equals(2, BinarySearch(21, low, high, getValue, "down"), err("value 21"))
Assert.Equals(3, BinarySearch(40, low, high, getValue, "down"), err("value 40"))

return true
end

return testsuite
3 changes: 2 additions & 1 deletion scripts/mod_loader/tests/main.lua
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,5 @@ Testsuites.event = require(rootpath.."event")
Testsuites.modApi = require(rootpath.."modApi")
Testsuites.vector = require(rootpath.."vector")
Testsuites.text = require(rootpath.."text")
Testsuites.deque = require(rootpath.."deque")
Testsuites.deque = require(rootpath.."deque")
Testsuites.binarySearch = require(rootpath.."binarySearch")
2 changes: 2 additions & 0 deletions scripts/mod_loader/ui/__scripts.lua
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ local scripts = {
"decorations/deco_dropdown",
"decorations/deco_animsheet",
"decorations/deco_label",
"decorations/deco_inputfield",

"widgets/base",
"widgets/draggable",
Expand All @@ -27,6 +28,7 @@ local scripts = {
"widgets/checkbox",
"widgets/dropdown",
"widgets/mainmenubutton",
"widgets/inputfield",
}

local rootpath = GetParentPath(...)
Expand Down
Loading

0 comments on commit 53e69e7

Please sign in to comment.