-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #160 from polydez/feat-bsearch-example
Add binary search MASM example
- Loading branch information
Showing
3 changed files
with
290 additions
and
0 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
.DS_Store | ||
.idea |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{ | ||
"operand_stack": [ | ||
"12", | ||
"19", "18", "17", "16", "15", "14", "13", "12", "11", "10", "9", "8", "7", "6", "5", "4", "3", "2", "1", "0", | ||
"20" | ||
] | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,282 @@ | ||
#! This example code performs binary searching for addr given value in an ascending ordered array. | ||
#! | ||
#! Input must be provided through the operands stack. | ||
#! For input and output see documentation for `proc.binary_search_stack`. | ||
#! | ||
#! If input values are omitted or `count = 0`, tests will run. | ||
|
||
const.STARTING_MEMORY_ADDRESS=0 | ||
|
||
#! Binary searches for value `val` in the memory addresses range [start..end), sorted in ascending order. | ||
#! | ||
#! Input: [start, end, val, ...] | ||
#! Output: [found (true/false), addr] where `found` indicates the result (`true`/`false`), | ||
#! `addr` is an address in memory of value (if it was found), or an address where value should be inserted. | ||
proc.binary_search | ||
# check that `start` and `end` fit to `u32` type | ||
u32assert2 | ||
# => [start, end, val, ...] | ||
|
||
# check if `start < end` | ||
dup dup.2 u32checked_lt | ||
# => [start < end, start, end, val, ...] | ||
|
||
if.true | ||
# push the `1` flag to begin searching | ||
push.1 | ||
# => [1, start, end, val, ...] | ||
else | ||
# push "not found" flag and flag to skip the loop | ||
push.0.0 | ||
# => [0, 0, addr, end, val, ...], addr = start | ||
end | ||
|
||
while.true | ||
# => [start, end, val, ...] | ||
|
||
# calculate middle address (if array has odd size, an integer floor will be taken) | ||
dup.1 dup.1 u32wrapping_sub u32checked_div.2 dup.1 u32wrapping_add | ||
# => [middle, start, end, val, ...], middle = start + (end - start) / 2 | ||
|
||
# read middle element from the memory | ||
dup mem_load | ||
# => [memory[middle], middle, start, end, val, ...] | ||
|
||
# compare middle value with the searching value | ||
dup dup.5 eq | ||
# => [memory[middle] == val, memory[middle], middle, start, end, val, ...] | ||
|
||
if.true | ||
# => [memory[middle], middle, start, end, val, ...] | ||
|
||
# value was found, drop a part of values (rest will be dropped in the end of procedure) | ||
drop swap drop | ||
# => [middle, end, val, ...] | ||
|
||
# prepare "found" flag and exit the loop | ||
push.1.0 | ||
# => [0, 1, addr, end, val, ...], addr = middle | ||
else | ||
# => [memory[middle], middle, start, end, val, ...] | ||
|
||
# value wasn't found on this step, so we decide, where to go next | ||
# is middle value less than `val`? | ||
dup.4 u32checked_lt | ||
# => [memory[middle] < val, middle, start, end, val, ...] | ||
|
||
if.true | ||
# => [middle, start, end, val, ...] | ||
|
||
# less than `val`, so search right-side | ||
u32wrapping_add.1 swap | ||
# => [old start, start, end, val, ...], start = middle + 1 | ||
else | ||
# => [middle, start, end, val, ...] | ||
|
||
# greater than `val`, so search left-side | ||
swap.2 | ||
# => [old end, start, end, val, ...], end = middle | ||
end | ||
|
||
# drop the old value (`old start` or `old end`) | ||
drop | ||
# => [start, end, val, ...] | ||
|
||
# check, if `start == end` | ||
dup.1 dup.1 eq | ||
# => [start == end, start, end, val, ...] | ||
|
||
if.true | ||
# add "not found" flag and exit the loop | ||
push.0.0 | ||
# => [0, 0, addr, end, val, ...], addr = start | ||
else | ||
# continue the loop | ||
push.1 | ||
# => [1, start, end, val, ...] | ||
end | ||
end | ||
end | ||
# => [found, addr, end, val, ...] | ||
|
||
# cleanup 2 values after `addr`: | ||
swap.2 drop swap.2 drop | ||
# => [found, addr, ...] | ||
end | ||
|
||
#! Writes `count` values (`arr[0], arr[1], ..., arr[count - 1]`) from the stack into the memory starting from address `addr`. | ||
#! | ||
#! Input: [addr, count, arr[0], arr[1], ..., arr[count - 1], ...] | ||
#! Output: [end, ...] where `end` is addr next vacant memory address, thus array occupies addresses `[addr..end)` in the memory | ||
proc.write_stack_to_memory | ||
# check that `addr` and `count` fit to `u32` type | ||
u32assert2 | ||
|
||
# check if `count` doesn't equal to zero | ||
dup.1 neq.0 | ||
# [count != 0, addr, count, arr[0], arr[1], ...] | ||
|
||
while.true | ||
# => [addr, count, arr[i], arr[i + 1], ...] | ||
|
||
# save the `arr[i]` to the memory at `addr` address | ||
swap.2 dup.2 mem_store | ||
# => [count, addr, arr[i + 1], ...] | ||
|
||
# decrease `count`, increase `addr`, check that `count` is not zero | ||
u32wrapping_sub.1 swap u32wrapping_add.1 dup.1 neq.0 | ||
# => [count != 0, addr, count, arr[i + 1], ...], addr = addr', count = count' | ||
end | ||
# => [end, 0, ...], end = addr | ||
|
||
# remove unnecessary zero before returning: | ||
swap drop | ||
# => [end, ...] | ||
end | ||
|
||
#! Performs binary searching of value in ascending ordered array in stack. | ||
#! | ||
#! Input: [count, arr[0], arr[1], ..., arr[count - 1], val], | ||
#! where: | ||
#! `count` is number of elements in the array, | ||
#! `arr[0]`, `arr[1]`, ..., `arr[count - 1]` are array elements, | ||
#! `val` is value to search. | ||
#! Output: [found, index] where `found` indicates the result (`true`/`false`), | ||
#! index `[0, count)` of `val`, if `val` was found; otherwise index in array, where `val` should be inserted. | ||
proc.binary_search_stack | ||
# write array from stack to the memory starting at `STARTING_MEMORY_ADDRESS` | ||
push.STARTING_MEMORY_ADDRESS exec.write_stack_to_memory | ||
# => [end, val, ...] | ||
|
||
# execute binary searching on array in memory, starting at `STARTING_MEMORY_ADDRESS` | ||
push.STARTING_MEMORY_ADDRESS exec.binary_search | ||
# => [found, addr, ...] | ||
|
||
# convert address in memory to index in array | ||
swap push.STARTING_MEMORY_ADDRESS u32wrapping_sub swap | ||
# => [found, index, ...] | ||
end | ||
|
||
proc.test_empty | ||
push.5.0 | ||
exec.binary_search_stack | ||
|
||
# expect "not found" | ||
assertz | ||
|
||
# expect insertion index is `0` | ||
assertz | ||
end | ||
|
||
proc.test_solid | ||
push.4.5.4.3.2.1.5 | ||
exec.binary_search_stack | ||
|
||
# expect "found" | ||
assert | ||
|
||
# expect index is `3` | ||
push.3 | ||
assert_eq | ||
end | ||
|
||
proc.test_first | ||
push.12.50.44.36.23.12.5 | ||
exec.binary_search_stack | ||
# expect "found" | ||
assert | ||
|
||
# expect index is `0` | ||
push.0 | ||
assert_eq | ||
end | ||
|
||
proc.test_last | ||
push.50.50.44.36.23.12.5 | ||
exec.binary_search_stack | ||
|
||
# expect "found" | ||
assert | ||
|
||
# expect index is `4` | ||
push.4 | ||
assert_eq | ||
end | ||
|
||
proc.test_gaps_right_found | ||
push.44.50.44.36.23.12.5 | ||
exec.binary_search_stack | ||
|
||
# expect "found" | ||
assert | ||
|
||
# expect index is `3` | ||
push.3 | ||
assert_eq | ||
end | ||
|
||
proc.test_gaps_left_found | ||
push.23.50.44.36.23.12.5 | ||
exec.binary_search_stack | ||
|
||
# expect "found" | ||
assert | ||
|
||
# expect index is `1` | ||
push.1 | ||
assert_eq | ||
end | ||
|
||
proc.test_gaps_right_not_found | ||
push.40.50.44.36.23.12.5 | ||
exec.binary_search_stack | ||
|
||
# expect "not found" | ||
assertz | ||
|
||
# expect insertion index is `3` | ||
push.3 | ||
assert_eq | ||
end | ||
|
||
proc.test_gaps_left_not_found | ||
push.30.50.44.36.23.12.5 | ||
exec.binary_search_stack | ||
|
||
# expect "not found" | ||
assertz | ||
|
||
# expect insertion index is `2` | ||
push.2 | ||
assert_eq | ||
end | ||
|
||
#! Runs tests. | ||
proc.run_tests | ||
call.test_empty | ||
call.test_solid | ||
call.test_first | ||
call.test_last | ||
call.test_gaps_right_found | ||
call.test_gaps_left_found | ||
call.test_gaps_right_not_found | ||
call.test_gaps_left_not_found | ||
end | ||
|
||
begin | ||
# check that `count` is not equal to zero | ||
dup neq.0 | ||
# => [count != 0, count, ...] | ||
|
||
if.true | ||
# => [count, arr[0], arr[1], ..., arr[count - 1], val, ...] | ||
|
||
exec.binary_search_stack | ||
# => [found, index] | ||
else | ||
# => [0, ...] | ||
|
||
exec.run_tests | ||
end | ||
end | ||
|