Skip to content

Commit

Permalink
Merge pull request #160 from polydez/feat-bsearch-example
Browse files Browse the repository at this point in the history
Add binary search MASM example
  • Loading branch information
Dominik1999 authored Jan 11, 2024
2 parents c00cb89 + e9a4bf3 commit 877018b
Show file tree
Hide file tree
Showing 3 changed files with 290 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
.DS_Store
.idea
7 changes: 7 additions & 0 deletions examples/bsearch.inputs
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"
]
}
282 changes: 282 additions & 0 deletions examples/bsearch.masm
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

0 comments on commit 877018b

Please sign in to comment.