From 7a492423eb7103c5654d21529857296fd73ba20a Mon Sep 17 00:00:00 2001 From: polydez <155382956+polydez@users.noreply.github.com> Date: Sun, 7 Jan 2024 11:40:06 +0500 Subject: [PATCH 1/9] feat: add binary search MASM example --- .gitignore | 1 + examples/bsearch.inputs | 14 +++ examples/bsearch.masm | 212 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 227 insertions(+) create mode 100644 examples/bsearch.inputs create mode 100644 examples/bsearch.masm diff --git a/.gitignore b/.gitignore index e43b0f9..4befed3 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ .DS_Store +.idea diff --git a/examples/bsearch.inputs b/examples/bsearch.inputs new file mode 100644 index 0000000..6419eef --- /dev/null +++ b/examples/bsearch.inputs @@ -0,0 +1,14 @@ +{ + "note": [ + "Provide in `operand_stack`:", + "1. Value to find (12)", + "2. Array elements (0..19)", + "3. Array length (20)", + "If n == 0 or `operand_stack` is empty, tests will run" + ], + "operand_stack": [ + "12", + "19", "18", "17", "16", "15", "14", "13", "12", "11", "10", "9", "8", "7", "6", "5", "4", "3", "2", "1", "0", + "20" + ] +} diff --git a/examples/bsearch.masm b/examples/bsearch.masm new file mode 100644 index 0000000..6fda078 --- /dev/null +++ b/examples/bsearch.masm @@ -0,0 +1,212 @@ +#! This example code performs binary searching for a 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 n = 0, tests will run. + +const.STARTING_MEMORY_ADDRESS=0 + +#! Reads `n` values (`v[0], v[1], ..., v[n - 1]`) from the stack into the memory starting from address `a`. +#! Input: [a, n, v[0], v[1], ..., v[n - 1], ...] +#! Output: [r, ...] where `r` is a next vacant memory address, thus array occupies addresses `[a..r)` in the memory +proc.read_stack_to_memory + u32assert2 # [a, n, v[0], v[1], ...] + dup.1 # [n, a, c, v[0], v[1], ...] + neq.0 # [n != 0, a, n, v[0], v[1], ...] + while.true # [a, n, v[i], v[i + 1], ...] + swap.2 # [v[i], n, a, v[i + 1], ...] + dup.2 # [a, v[i], n, a, v[i + 1], ...] + mem_store # [n, a, v[i + 1], ...] + u32wrapping_sub.1 # [n', a, v[i + 1], ...], n' = n - 1 + swap # [a, n', v[i + 1], ...] + u32wrapping_add.1 # [a', n', v[i + 1], ...], a' = a + 1 + dup.1 # [n', a', n', v[i + 1], ...] + neq.0 # [n != 0, a, n, v[i + 1], ...], a = a', n = n' + end + # [r, 0, ...], r = a, remove unnecessary zero before returning: + swap # [0, r, ...] + drop # [r, ...] +end + +#! Binary searches for value `v` in the memory addresses range [l..r), sorted in ascending order. +#! Input: [l, r, v, ...] +#! Output: [found (true/false), address] where `found` is searching result (`true`/`false`), +#! `address` is an address of value (if it was found), or an address where value should be inserted. +proc.binary_search + # Check if `l < r` then start search, otherwise prepare "not found" result and exit + u32assert2 # [l, r, v, ...] + dup # [l, l, r, v, ...] + dup.2 # [r, l, l, r, v, ...] + u32lt # [l < r, l, r, v, ...] + if.true # [l, r, v, ...] + push.1 # [1, l, r, v, ...] + else # [l, r, v, ...] + # Push "not found" flag and flag to skip the loop + push.0.0 # [0, 0, address, r, v, ...], address = l + end + while.true # [l, r, v, ...] + # Calculate middle address `m` + dup.1 # [r, l, r, v, ...] + dup.1 # [l, r, l, r, v, ...] + u32wrapping_sub # [n, l, r, v, ...], n = r - l + u32div.2 # [n / 2, l, r, v, ...] + dup.1 # [l, n / 2, l, r, v, ...] + u32wrapping_add # [m, l, r, v, ...], m = l + n / 2 + + dup # [m, m, l, r, v, ...] + mem_load # [memory[m], m, l, r, v, ...] + + dup # [memory[m], memory[m], m, l, r, v, ...] + dup.5 # [v, memory[m], memory[m], m, l, r, v, ...] + eq # [memory[m] == v, memory[m], m, l, r, v, ...] + if.true # [memory[m], m, l, r, v, ...] + drop # [m, l, r, v, ...] + swap # [l, m, r, v, ...] + drop # [m, r, v, ...] + # Prepare "found" flag and exit the loop + push.1.0 # [0, 1, address, r, v, ...], address = m + else # [memory[m], m, l, r, v, ...] + dup.4 # [v, memory[m], m, l, r, v, ...] + u32lt # [memory[m] < v, m, l, r, v, ...] + if.true # [m, l, r, v, ...] + u32wrapping_add.1 # [m + 1, l, r, v, ...] + swap # [old l, l, r, v, ...], l = m + 1 + else # [m, l, r, v, ...] + swap.2 # [old r, l, r, v, ...], r = m + end + drop # [l, r, v, ...] + + dup.1 # [r, l, r, v, ...] + dup.1 # [l, r, l, r, v, ...] + eq # [l == r, l, r, v, ...] + if.true # [l, r, v, ...] + # Add "not found" flag and exit the loop + push.0.0 # [0, 0, address, r, v, ...], address = l + else # [l, r, v, ...] + # Continue the loop + push.1 # [1, l, r, v, ...] + end + end + end + # [found, address, r, v, ...] + # Cleanup 2 values after address: + swap.2 # [r, address, found, v, ...] + drop # [address, found, v, ...] + swap.2 # [v, found, address, ...] + drop # [found, address, ...] +end + +#! Converts memory address in result of `binary_search` procedure to index in array. +#! Input: [found, address, ...] +#! Output: [found, index, ...], where `index` is element index in array +proc.convert_result_address_to_index + swap # [address, found, ...] + push.STARTING_MEMORY_ADDRESS # [STARTING_MEMORY_ADDRESS, address, found, ...] + u32wrapping_sub # [index, found, ...], index = address - STARTING_MEMORY_ADDRESS + swap # [found, index, ...] +end + +#! Perform binary searching of value in ascending ordered array in stack. +#! Input: [n, v[0], v[1], ..., v[n - 1], v], +#! where: +#! `n` is number of elements in the array, +#! `v[0]`, `v[1]`, ..., `v[n - 1]` are array elements, +#! `v` is value to search +#! Output: [found, index] where `found` is searching result (`true`/`false`), +#! index `[0, n)` of `v`, if `v` was found; otherwise index in array, where `v` should be inserted. +proc.binary_search_stack + push.STARTING_MEMORY_ADDRESS # [STARTING_MEMORY_ADDRESS, n, v[0], v[1], ..., v[n - 1], v, ...] + exec.read_stack_to_memory # [r, v, ...] + + push.STARTING_MEMORY_ADDRESS # [l, r, v, ...], l = STARTING_MEMORY_ADDRESS + exec.binary_search # [address, ...] if found, otherwise [0xFFFFFFFF, address, ...] + + exec.convert_result_address_to_index # [index, ...] if found, otherwise [0xFFFFFFFF, index, ...] +end + +proc.test_empty + push.5.0 + exec.binary_search_stack + assertz # Expect "not found" + assertz # Expect insertion index is `0` +end + +proc.test_solid + push.4.5.4.3.2.1.5 + exec.binary_search_stack + assert # Expect "found" + push.3 + assert_eq # Expect index is `3` +end + +proc.test_first + push.12.50.44.36.23.12.5 + exec.binary_search_stack + assert # Expect "found" + push.0 + assert_eq # Expect index is `0` +end + +proc.test_last + push.50.50.44.36.23.12.5 + exec.binary_search_stack + assert # Expect "found" + push.4 + assert_eq # Expect index is `4` +end + +proc.test_gaps_right_found + push.44.50.44.36.23.12.5 + exec.binary_search_stack + assert # Expect "found" + push.3 + assert_eq # Expect index is `3` +end + +proc.test_gaps_left_found + push.23.50.44.36.23.12.5 + exec.binary_search_stack + assert # Expect "found" + push.1 + assert_eq # Expect index is `1` +end + +proc.test_gaps_right_not_found + push.40.50.44.36.23.12.5 + exec.binary_search_stack + assertz # Expect "not found" + push.3 + assert_eq # Expect insertion index is `3` +end + +proc.test_gaps_left_not_found + push.30.50.44.36.23.12.5 + exec.binary_search_stack + assertz # Expect "not found" + push.2 + assert_eq # Expect insertion index is `2` +end + +#! Run 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 + dup # [n, n, ...] + neq.0 # [n != 0, n, ...] + if.true # [n, v[0], v[1], ..., v[n - 1], v, ...] + exec.binary_search_stack # [found, index] + else # [0, ...] + exec.run_tests + end +end + From be2f248fd3e1682f0217ead93db79322ac186b2f Mon Sep 17 00:00:00 2001 From: polydez <155382956+polydez@users.noreply.github.com> Date: Sun, 7 Jan 2024 12:02:58 +0500 Subject: [PATCH 2/9] feat: refactored, simplified code --- examples/bsearch.masm | 57 +++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 32 deletions(-) diff --git a/examples/bsearch.masm b/examples/bsearch.masm index 6fda078..173df1d 100644 --- a/examples/bsearch.masm +++ b/examples/bsearch.masm @@ -7,28 +7,6 @@ const.STARTING_MEMORY_ADDRESS=0 -#! Reads `n` values (`v[0], v[1], ..., v[n - 1]`) from the stack into the memory starting from address `a`. -#! Input: [a, n, v[0], v[1], ..., v[n - 1], ...] -#! Output: [r, ...] where `r` is a next vacant memory address, thus array occupies addresses `[a..r)` in the memory -proc.read_stack_to_memory - u32assert2 # [a, n, v[0], v[1], ...] - dup.1 # [n, a, c, v[0], v[1], ...] - neq.0 # [n != 0, a, n, v[0], v[1], ...] - while.true # [a, n, v[i], v[i + 1], ...] - swap.2 # [v[i], n, a, v[i + 1], ...] - dup.2 # [a, v[i], n, a, v[i + 1], ...] - mem_store # [n, a, v[i + 1], ...] - u32wrapping_sub.1 # [n', a, v[i + 1], ...], n' = n - 1 - swap # [a, n', v[i + 1], ...] - u32wrapping_add.1 # [a', n', v[i + 1], ...], a' = a + 1 - dup.1 # [n', a', n', v[i + 1], ...] - neq.0 # [n != 0, a, n, v[i + 1], ...], a = a', n = n' - end - # [r, 0, ...], r = a, remove unnecessary zero before returning: - swap # [0, r, ...] - drop # [r, ...] -end - #! Binary searches for value `v` in the memory addresses range [l..r), sorted in ascending order. #! Input: [l, r, v, ...] #! Output: [found (true/false), address] where `found` is searching result (`true`/`false`), @@ -97,14 +75,26 @@ proc.binary_search drop # [found, address, ...] end -#! Converts memory address in result of `binary_search` procedure to index in array. -#! Input: [found, address, ...] -#! Output: [found, index, ...], where `index` is element index in array -proc.convert_result_address_to_index - swap # [address, found, ...] - push.STARTING_MEMORY_ADDRESS # [STARTING_MEMORY_ADDRESS, address, found, ...] - u32wrapping_sub # [index, found, ...], index = address - STARTING_MEMORY_ADDRESS - swap # [found, index, ...] +#! Reads `n` values (`v[0], v[1], ..., v[n - 1]`) from the stack into the memory starting from address `a`. +#! Input: [a, n, v[0], v[1], ..., v[n - 1], ...] +#! Output: [r, ...] where `r` is a next vacant memory address, thus array occupies addresses `[a..r)` in the memory +proc.read_stack_to_memory + u32assert2 # [a, n, v[0], v[1], ...] + dup.1 # [n, a, c, v[0], v[1], ...] + neq.0 # [n != 0, a, n, v[0], v[1], ...] + while.true # [a, n, v[i], v[i + 1], ...] + swap.2 # [v[i], n, a, v[i + 1], ...] + dup.2 # [a, v[i], n, a, v[i + 1], ...] + mem_store # [n, a, v[i + 1], ...] + u32wrapping_sub.1 # [n', a, v[i + 1], ...], n' = n - 1 + swap # [a, n', v[i + 1], ...] + u32wrapping_add.1 # [a', n', v[i + 1], ...], a' = a + 1 + dup.1 # [n', a', n', v[i + 1], ...] + neq.0 # [n != 0, a, n, v[i + 1], ...], a = a', n = n' + end + # [r, 0, ...], r = a, remove unnecessary zero before returning: + swap # [0, r, ...] + drop # [r, ...] end #! Perform binary searching of value in ascending ordered array in stack. @@ -120,9 +110,12 @@ proc.binary_search_stack exec.read_stack_to_memory # [r, v, ...] push.STARTING_MEMORY_ADDRESS # [l, r, v, ...], l = STARTING_MEMORY_ADDRESS - exec.binary_search # [address, ...] if found, otherwise [0xFFFFFFFF, address, ...] + exec.binary_search # [found, address, ...] - exec.convert_result_address_to_index # [index, ...] if found, otherwise [0xFFFFFFFF, index, ...] + swap # [address, found, ...] + push.STARTING_MEMORY_ADDRESS # [STARTING_MEMORY_ADDRESS, address, found, ...] + u32wrapping_sub # [index, found, ...], index = address - STARTING_MEMORY_ADDRESS + swap # [found, index, ...] end proc.test_empty From 3a83d4a9311e5678f12b22ba5785222a2ac7cc75 Mon Sep 17 00:00:00 2001 From: polydez <155382956+polydez@users.noreply.github.com> Date: Sun, 7 Jan 2024 12:09:59 +0500 Subject: [PATCH 3/9] fix: mistakes in docs --- examples/bsearch.masm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/bsearch.masm b/examples/bsearch.masm index 173df1d..5c24967 100644 --- a/examples/bsearch.masm +++ b/examples/bsearch.masm @@ -97,7 +97,7 @@ proc.read_stack_to_memory drop # [r, ...] end -#! Perform binary searching of value in ascending ordered array in stack. +#! Performs binary searching of value in ascending ordered array in stack. #! Input: [n, v[0], v[1], ..., v[n - 1], v], #! where: #! `n` is number of elements in the array, @@ -181,7 +181,7 @@ proc.test_gaps_left_not_found assert_eq # Expect insertion index is `2` end -#! Run tests. +#! Runs tests. proc.run_tests call.test_empty call.test_solid From cdd67f82762554afab8c34d5f3d262afb6d7efbb Mon Sep 17 00:00:00 2001 From: polydez <155382956+polydez@users.noreply.github.com> Date: Sun, 7 Jan 2024 12:28:12 +0500 Subject: [PATCH 4/9] fix: improved documentation --- examples/bsearch.masm | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/examples/bsearch.masm b/examples/bsearch.masm index 5c24967..e5a769f 100644 --- a/examples/bsearch.masm +++ b/examples/bsearch.masm @@ -3,11 +3,12 @@ #! 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 n = 0, tests will run. +#! If input values are omitted or `n = 0`, tests will run. const.STARTING_MEMORY_ADDRESS=0 #! Binary searches for value `v` in the memory addresses range [l..r), sorted in ascending order. +#! #! Input: [l, r, v, ...] #! Output: [found (true/false), address] where `found` is searching result (`true`/`false`), #! `address` is an address of value (if it was found), or an address where value should be inserted. @@ -76,6 +77,7 @@ proc.binary_search end #! Reads `n` values (`v[0], v[1], ..., v[n - 1]`) from the stack into the memory starting from address `a`. +#! #! Input: [a, n, v[0], v[1], ..., v[n - 1], ...] #! Output: [r, ...] where `r` is a next vacant memory address, thus array occupies addresses `[a..r)` in the memory proc.read_stack_to_memory @@ -98,11 +100,12 @@ proc.read_stack_to_memory end #! Performs binary searching of value in ascending ordered array in stack. +#! #! Input: [n, v[0], v[1], ..., v[n - 1], v], -#! where: -#! `n` is number of elements in the array, -#! `v[0]`, `v[1]`, ..., `v[n - 1]` are array elements, -#! `v` is value to search +#! where: +#! `n` is number of elements in the array, +#! `v[0]`, `v[1]`, ..., `v[n - 1]` are array elements, +#! `v` is value to search #! Output: [found, index] where `found` is searching result (`true`/`false`), #! index `[0, n)` of `v`, if `v` was found; otherwise index in array, where `v` should be inserted. proc.binary_search_stack From 5e059e4b559457f5e0e1217d7bfb3e36462e9552 Mon Sep 17 00:00:00 2001 From: polydez <155382956+polydez@users.noreply.github.com> Date: Wed, 10 Jan 2024 20:35:36 +0500 Subject: [PATCH 5/9] fix: compilation on `main` branch --- examples/bsearch.masm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/bsearch.masm b/examples/bsearch.masm index e5a769f..e44d8ea 100644 --- a/examples/bsearch.masm +++ b/examples/bsearch.masm @@ -17,7 +17,7 @@ proc.binary_search u32assert2 # [l, r, v, ...] dup # [l, l, r, v, ...] dup.2 # [r, l, l, r, v, ...] - u32lt # [l < r, l, r, v, ...] + u32checked_lt # [l < r, l, r, v, ...] if.true # [l, r, v, ...] push.1 # [1, l, r, v, ...] else # [l, r, v, ...] @@ -29,7 +29,7 @@ proc.binary_search dup.1 # [r, l, r, v, ...] dup.1 # [l, r, l, r, v, ...] u32wrapping_sub # [n, l, r, v, ...], n = r - l - u32div.2 # [n / 2, l, r, v, ...] + u32checked_div.2 # [n / 2, l, r, v, ...] dup.1 # [l, n / 2, l, r, v, ...] u32wrapping_add # [m, l, r, v, ...], m = l + n / 2 @@ -47,7 +47,7 @@ proc.binary_search push.1.0 # [0, 1, address, r, v, ...], address = m else # [memory[m], m, l, r, v, ...] dup.4 # [v, memory[m], m, l, r, v, ...] - u32lt # [memory[m] < v, m, l, r, v, ...] + u32checked_lt # [memory[m] < v, m, l, r, v, ...] if.true # [m, l, r, v, ...] u32wrapping_add.1 # [m + 1, l, r, v, ...] swap # [old l, l, r, v, ...], l = m + 1 From c34dd12ef0d042a2f4c36a6693e5a58919edf8bb Mon Sep 17 00:00:00 2001 From: polydez <155382956+polydez@users.noreply.github.com> Date: Wed, 10 Jan 2024 22:09:04 +0500 Subject: [PATCH 6/9] fix: renamed variables in order to have meaningful names --- examples/bsearch.inputs | 2 +- examples/bsearch.masm | 180 ++++++++++++++++++++-------------------- 2 files changed, 91 insertions(+), 91 deletions(-) diff --git a/examples/bsearch.inputs b/examples/bsearch.inputs index 6419eef..9280347 100644 --- a/examples/bsearch.inputs +++ b/examples/bsearch.inputs @@ -4,7 +4,7 @@ "1. Value to find (12)", "2. Array elements (0..19)", "3. Array length (20)", - "If n == 0 or `operand_stack` is empty, tests will run" + "If array length is 0 or `operand_stack` is empty, tests will run" ], "operand_stack": [ "12", diff --git a/examples/bsearch.masm b/examples/bsearch.masm index e44d8ea..2fa4dd6 100644 --- a/examples/bsearch.masm +++ b/examples/bsearch.masm @@ -1,123 +1,123 @@ -#! This example code performs binary searching for a given value in an ascending ordered array. +#! 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 `n = 0`, tests will run. +#! If input values are omitted or `count = 0`, tests will run. const.STARTING_MEMORY_ADDRESS=0 -#! Binary searches for value `v` in the memory addresses range [l..r), sorted in ascending order. +#! Binary searches for value `val` in the memory addresses range [start..end), sorted in ascending order. #! -#! Input: [l, r, v, ...] -#! Output: [found (true/false), address] where `found` is searching result (`true`/`false`), -#! `address` is an address of value (if it was found), or an address where value should be inserted. +#! Input: [start, end, val, ...] +#! Output: [found (true/false), addr] where `found` is searching result (`true`/`false`), +#! `addr` is an address of value (if it was found), or an address where value should be inserted. proc.binary_search - # Check if `l < r` then start search, otherwise prepare "not found" result and exit - u32assert2 # [l, r, v, ...] - dup # [l, l, r, v, ...] - dup.2 # [r, l, l, r, v, ...] - u32checked_lt # [l < r, l, r, v, ...] - if.true # [l, r, v, ...] - push.1 # [1, l, r, v, ...] - else # [l, r, v, ...] + # Check if `start < end` then start search, otherwise prepare "not found" result and exit + u32assert2 # [start, end, val, ...] + dup # [start, start, end, val, ...] + dup.2 # [end, start, start, end, val, ...] + u32checked_lt # [start < end, start, end, val, ...] + if.true # [start, end, val, ...] + push.1 # [1, start, end, val, ...] + else # [start, end, val, ...] # Push "not found" flag and flag to skip the loop - push.0.0 # [0, 0, address, r, v, ...], address = l + push.0.0 # [0, 0, addr, end, val, ...], addr = start end - while.true # [l, r, v, ...] - # Calculate middle address `m` - dup.1 # [r, l, r, v, ...] - dup.1 # [l, r, l, r, v, ...] - u32wrapping_sub # [n, l, r, v, ...], n = r - l - u32checked_div.2 # [n / 2, l, r, v, ...] - dup.1 # [l, n / 2, l, r, v, ...] - u32wrapping_add # [m, l, r, v, ...], m = l + n / 2 - - dup # [m, m, l, r, v, ...] - mem_load # [memory[m], m, l, r, v, ...] - - dup # [memory[m], memory[m], m, l, r, v, ...] - dup.5 # [v, memory[m], memory[m], m, l, r, v, ...] - eq # [memory[m] == v, memory[m], m, l, r, v, ...] - if.true # [memory[m], m, l, r, v, ...] - drop # [m, l, r, v, ...] - swap # [l, m, r, v, ...] - drop # [m, r, v, ...] + while.true # [start, end, val, ...] + # Calculate middle address `middle` + dup.1 # [end, start, end, val, ...] + dup.1 # [start, end, start, end, val, ...] + u32wrapping_sub # [count, start, end, val, ...], count = end - start + u32checked_div.2 # [count / 2, start, end, val, ...] + dup.1 # [start, count / 2, start, end, val, ...] + u32wrapping_add # [middle, start, end, val, ...], middle = start + count / 2 + + dup # [middle, middle, start, end, val, ...] + mem_load # [memory[middle], middle, start, end, val, ...] + + dup # [memory[middle], memory[middle], middle, start, end, val, ...] + dup.5 # [val, memory[middle], memory[middle], middle, start, end, val, ...] + eq # [memory[middle] == val, memory[middle], middle, start, end, val, ...] + if.true # [memory[middle], middle, start, end, val, ...] + drop # [middle, start, end, val, ...] + swap # [start, middle, end, val, ...] + drop # [middle, end, val, ...] # Prepare "found" flag and exit the loop - push.1.0 # [0, 1, address, r, v, ...], address = m - else # [memory[m], m, l, r, v, ...] - dup.4 # [v, memory[m], m, l, r, v, ...] - u32checked_lt # [memory[m] < v, m, l, r, v, ...] - if.true # [m, l, r, v, ...] - u32wrapping_add.1 # [m + 1, l, r, v, ...] - swap # [old l, l, r, v, ...], l = m + 1 - else # [m, l, r, v, ...] - swap.2 # [old r, l, r, v, ...], r = m + push.1.0 # [0, 1, addr, end, val, ...], addr = middle + else # [memory[middle], middle, start, end, val, ...] + dup.4 # [val, memory[middle], middle, start, end, val, ...] + u32checked_lt # [memory[middle] < val, middle, start, end, val, ...] + if.true # [middle, start, end, val, ...] + u32wrapping_add.1 # [middle + 1, start, end, val, ...] + swap # [old start, start, end, val, ...], start = middle + 1 + else # [middle, start, end, val, ...] + swap.2 # [old end, start, end, val, ...], end = middle end - drop # [l, r, v, ...] + drop # [start, end, val, ...] - dup.1 # [r, l, r, v, ...] - dup.1 # [l, r, l, r, v, ...] - eq # [l == r, l, r, v, ...] - if.true # [l, r, v, ...] + dup.1 # [end, start, end, val, ...] + dup.1 # [start, end, start, end, val, ...] + eq # [start == end, start, end, val, ...] + if.true # [start, end, val, ...] # Add "not found" flag and exit the loop - push.0.0 # [0, 0, address, r, v, ...], address = l - else # [l, r, v, ...] + push.0.0 # [0, 0, addr, end, val, ...], addr = start + else # [start, end, val, ...] # Continue the loop - push.1 # [1, l, r, v, ...] + push.1 # [1, start, end, val, ...] end end end - # [found, address, r, v, ...] - # Cleanup 2 values after address: - swap.2 # [r, address, found, v, ...] - drop # [address, found, v, ...] - swap.2 # [v, found, address, ...] - drop # [found, address, ...] + # [found, addr, end, val, ...] + # Cleanup 2 values after `addr`: + swap.2 # [end, addr, found, val, ...] + drop # [addr, found, val, ...] + swap.2 # [val, found, addr, ...] + drop # [found, addr, ...] end -#! Reads `n` values (`v[0], v[1], ..., v[n - 1]`) from the stack into the memory starting from address `a`. +#! Reads `count` values (`arr[0], arr[1], ..., arr[count - 1]`) from the stack into the memory starting from address `addr`. #! -#! Input: [a, n, v[0], v[1], ..., v[n - 1], ...] -#! Output: [r, ...] where `r` is a next vacant memory address, thus array occupies addresses `[a..r)` in the memory +#! 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.read_stack_to_memory - u32assert2 # [a, n, v[0], v[1], ...] - dup.1 # [n, a, c, v[0], v[1], ...] - neq.0 # [n != 0, a, n, v[0], v[1], ...] - while.true # [a, n, v[i], v[i + 1], ...] - swap.2 # [v[i], n, a, v[i + 1], ...] - dup.2 # [a, v[i], n, a, v[i + 1], ...] - mem_store # [n, a, v[i + 1], ...] - u32wrapping_sub.1 # [n', a, v[i + 1], ...], n' = n - 1 - swap # [a, n', v[i + 1], ...] - u32wrapping_add.1 # [a', n', v[i + 1], ...], a' = a + 1 - dup.1 # [n', a', n', v[i + 1], ...] - neq.0 # [n != 0, a, n, v[i + 1], ...], a = a', n = n' + u32assert2 # [addr, count, arr[0], arr[1], ...] + dup.1 # [count, addr, c, arr[0], arr[1], ...] + neq.0 # [count != 0, addr, count, arr[0], arr[1], ...] + while.true # [addr, count, arr[i], arr[i + 1], ...] + swap.2 # [arr[i], count, addr, arr[i + 1], ...] + dup.2 # [addr, arr[i], count, addr, arr[i + 1], ...] + mem_store # [count, addr, arr[i + 1], ...] + u32wrapping_sub.1 # [count', addr, arr[i + 1], ...], count' = count - 1 + swap # [addr, count', arr[i + 1], ...] + u32wrapping_add.1 # [addr', count', arr[i + 1], ...], addr' = addr + 1 + dup.1 # [count', addr', count', arr[i + 1], ...] + neq.0 # [count != 0, addr, count, arr[i + 1], ...], addr = addr', count = count' end - # [r, 0, ...], r = a, remove unnecessary zero before returning: - swap # [0, r, ...] - drop # [r, ...] + # [end, 0, ...], end = addr, remove unnecessary zero before returning: + swap # [0, end, ...] + drop # [end, ...] end #! Performs binary searching of value in ascending ordered array in stack. #! -#! Input: [n, v[0], v[1], ..., v[n - 1], v], +#! Input: [count, arr[0], arr[1], ..., arr[count - 1], val], #! where: -#! `n` is number of elements in the array, -#! `v[0]`, `v[1]`, ..., `v[n - 1]` are array elements, -#! `v` is value to search +#! `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` is searching result (`true`/`false`), -#! index `[0, n)` of `v`, if `v` was found; otherwise index in array, where `v` should be inserted. +#! index `[0, count)` of `val`, if `val` was found; otherwise index in array, where `val` should be inserted. proc.binary_search_stack - push.STARTING_MEMORY_ADDRESS # [STARTING_MEMORY_ADDRESS, n, v[0], v[1], ..., v[n - 1], v, ...] - exec.read_stack_to_memory # [r, v, ...] + push.STARTING_MEMORY_ADDRESS # [STARTING_MEMORY_ADDRESS, count, arr[0], arr[1], ..., arr[count - 1], val, ...] + exec.read_stack_to_memory # [end, val, ...] - push.STARTING_MEMORY_ADDRESS # [l, r, v, ...], l = STARTING_MEMORY_ADDRESS - exec.binary_search # [found, address, ...] + push.STARTING_MEMORY_ADDRESS # [start, end, val, ...], start = STARTING_MEMORY_ADDRESS + exec.binary_search # [found, addr, ...] - swap # [address, found, ...] - push.STARTING_MEMORY_ADDRESS # [STARTING_MEMORY_ADDRESS, address, found, ...] - u32wrapping_sub # [index, found, ...], index = address - STARTING_MEMORY_ADDRESS + swap # [addr, found, ...] + push.STARTING_MEMORY_ADDRESS # [STARTING_MEMORY_ADDRESS, addr, found, ...] + u32wrapping_sub # [index, found, ...], index = addr - STARTING_MEMORY_ADDRESS swap # [found, index, ...] end @@ -197,9 +197,9 @@ proc.run_tests end begin - dup # [n, n, ...] - neq.0 # [n != 0, n, ...] - if.true # [n, v[0], v[1], ..., v[n - 1], v, ...] + dup # [count, count, ...] + 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 From 4c3d7c83b9077e69317f676d704a38a87441863a Mon Sep 17 00:00:00 2001 From: polydez <155382956+polydez@users.noreply.github.com> Date: Thu, 11 Jan 2024 17:59:46 +0500 Subject: [PATCH 7/9] fix: addressed comments during review --- examples/bsearch.masm | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/examples/bsearch.masm b/examples/bsearch.masm index 2fa4dd6..090f4ff 100644 --- a/examples/bsearch.masm +++ b/examples/bsearch.masm @@ -10,8 +10,8 @@ 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` is searching result (`true`/`false`), -#! `addr` is an address of value (if it was found), or an address where value should be inserted. +#! 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 if `start < end` then start search, otherwise prepare "not found" result and exit u32assert2 # [start, end, val, ...] @@ -29,6 +29,7 @@ proc.binary_search dup.1 # [end, start, end, val, ...] dup.1 # [start, end, start, end, val, ...] u32wrapping_sub # [count, start, end, val, ...], count = end - start + # calculate half of the array. If `count` is odd, the result's fraction will be discarded u32checked_div.2 # [count / 2, start, end, val, ...] dup.1 # [start, count / 2, start, end, val, ...] u32wrapping_add # [middle, start, end, val, ...], middle = start + count / 2 @@ -76,13 +77,13 @@ proc.binary_search drop # [found, addr, ...] end -#! Reads `count` values (`arr[0], arr[1], ..., arr[count - 1]`) from the stack into the memory starting from address `addr`. +#! 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.read_stack_to_memory +proc.write_stack_to_memory u32assert2 # [addr, count, arr[0], arr[1], ...] - dup.1 # [count, addr, c, arr[0], arr[1], ...] + dup.1 # [count, addr, count, arr[0], arr[1], ...] neq.0 # [count != 0, addr, count, arr[0], arr[1], ...] while.true # [addr, count, arr[i], arr[i + 1], ...] swap.2 # [arr[i], count, addr, arr[i + 1], ...] @@ -106,11 +107,11 @@ end #! `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` is searching result (`true`/`false`), +#! 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 push.STARTING_MEMORY_ADDRESS # [STARTING_MEMORY_ADDRESS, count, arr[0], arr[1], ..., arr[count - 1], val, ...] - exec.read_stack_to_memory # [end, val, ...] + exec.write_stack_to_memory # [end, val, ...] push.STARTING_MEMORY_ADDRESS # [start, end, val, ...], start = STARTING_MEMORY_ADDRESS exec.binary_search # [found, addr, ...] From 42a649e8312361be1e5eb5d32bd6c7838df449fb Mon Sep 17 00:00:00 2001 From: polydez <155382956+polydez@users.noreply.github.com> Date: Thu, 11 Jan 2024 18:45:04 +0500 Subject: [PATCH 8/9] fix: updated formatting and comments to conform coding guidelines --- examples/bsearch.masm | 275 ++++++++++++++++++++++++++---------------- 1 file changed, 174 insertions(+), 101 deletions(-) diff --git a/examples/bsearch.masm b/examples/bsearch.masm index 090f4ff..4bfc118 100644 --- a/examples/bsearch.masm +++ b/examples/bsearch.masm @@ -13,68 +13,95 @@ const.STARTING_MEMORY_ADDRESS=0 #! 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 if `start < end` then start search, otherwise prepare "not found" result and exit - u32assert2 # [start, end, val, ...] - dup # [start, start, end, val, ...] - dup.2 # [end, start, start, end, val, ...] - u32checked_lt # [start < end, start, end, val, ...] - if.true # [start, end, val, ...] - push.1 # [1, start, end, val, ...] - else # [start, end, val, ...] - # Push "not found" flag and flag to skip the loop - push.0.0 # [0, 0, addr, end, val, ...], addr = start + # 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 `middle` - dup.1 # [end, start, end, val, ...] - dup.1 # [start, end, start, end, val, ...] - u32wrapping_sub # [count, start, end, val, ...], count = end - start - # calculate half of the array. If `count` is odd, the result's fraction will be discarded - u32checked_div.2 # [count / 2, start, end, val, ...] - dup.1 # [start, count / 2, start, end, val, ...] - u32wrapping_add # [middle, start, end, val, ...], middle = start + count / 2 - - dup # [middle, middle, start, end, val, ...] - mem_load # [memory[middle], middle, start, end, val, ...] - - dup # [memory[middle], memory[middle], middle, start, end, val, ...] - dup.5 # [val, memory[middle], memory[middle], middle, start, end, val, ...] - eq # [memory[middle] == val, memory[middle], middle, start, end, val, ...] - if.true # [memory[middle], middle, start, end, val, ...] - drop # [middle, start, end, val, ...] - swap # [start, middle, end, val, ...] - 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, ...] - dup.4 # [val, memory[middle], middle, start, end, val, ...] - u32checked_lt # [memory[middle] < val, middle, start, end, val, ...] - if.true # [middle, start, end, val, ...] - u32wrapping_add.1 # [middle + 1, start, end, val, ...] - swap # [old start, start, end, val, ...], start = middle + 1 - else # [middle, start, end, val, ...] - swap.2 # [old end, start, end, val, ...], end = middle + + 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 # [start, end, val, ...] - - dup.1 # [end, start, end, val, ...] - dup.1 # [start, end, start, end, val, ...] - eq # [start == end, start, end, val, ...] - if.true # [start, end, val, ...] - # Add "not found" flag and exit the loop - push.0.0 # [0, 0, addr, end, val, ...], addr = start - else # [start, end, val, ...] - # Continue the loop - push.1 # [1, start, end, val, ...] + + # 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 # [end, addr, found, val, ...] - drop # [addr, found, val, ...] - swap.2 # [val, found, addr, ...] - drop # [found, addr, ...] + # => [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`. @@ -82,22 +109,29 @@ end #! 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 - u32assert2 # [addr, count, arr[0], arr[1], ...] - dup.1 # [count, addr, count, arr[0], arr[1], ...] - neq.0 # [count != 0, addr, count, arr[0], arr[1], ...] - while.true # [addr, count, arr[i], arr[i + 1], ...] - swap.2 # [arr[i], count, addr, arr[i + 1], ...] - dup.2 # [addr, arr[i], count, addr, arr[i + 1], ...] - mem_store # [count, addr, arr[i + 1], ...] - u32wrapping_sub.1 # [count', addr, arr[i + 1], ...], count' = count - 1 - swap # [addr, count', arr[i + 1], ...] - u32wrapping_add.1 # [addr', count', arr[i + 1], ...], addr' = addr + 1 - dup.1 # [count', addr', count', arr[i + 1], ...] - neq.0 # [count != 0, addr, count, arr[i + 1], ...], addr = addr', count = count' + # 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 # [0, end, ...] - drop # [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. @@ -110,79 +144,111 @@ end #! 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 - push.STARTING_MEMORY_ADDRESS # [STARTING_MEMORY_ADDRESS, count, arr[0], arr[1], ..., arr[count - 1], val, ...] - exec.write_stack_to_memory # [end, val, ...] + # write array from stack to the memory starting at `STARTING_MEMORY_ADDRESS` + push.STARTING_MEMORY_ADDRESS exec.write_stack_to_memory + # => [end, val, ...] - push.STARTING_MEMORY_ADDRESS # [start, end, val, ...], start = STARTING_MEMORY_ADDRESS - exec.binary_search # [found, addr, ...] + # execute binary searching on array in memory, starting at `STARTING_MEMORY_ADDRESS` + push.STARTING_MEMORY_ADDRESS exec.binary_search + # => [found, addr, ...] - swap # [addr, found, ...] - push.STARTING_MEMORY_ADDRESS # [STARTING_MEMORY_ADDRESS, addr, found, ...] - u32wrapping_sub # [index, found, ...], index = addr - STARTING_MEMORY_ADDRESS - swap # [found, index, ...] + # 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 - assertz # Expect "not found" - assertz # Expect insertion index is `0` + + # 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 - assert # Expect "found" + + # expect "found" + assert + + # expect index is `3` push.3 - assert_eq # Expect index is `3` + assert_eq end proc.test_first push.12.50.44.36.23.12.5 exec.binary_search_stack - assert # Expect "found" + # expect "found" + assert + + # expect index is `0` push.0 - assert_eq # Expect index is `0` + assert_eq end proc.test_last push.50.50.44.36.23.12.5 exec.binary_search_stack - assert # Expect "found" + + # expect "found" + assert + + # expect index is `4` push.4 - assert_eq # Expect index is `4` + assert_eq end proc.test_gaps_right_found push.44.50.44.36.23.12.5 exec.binary_search_stack - assert # Expect "found" + + # expect "found" + assert + + # expect index is `3` push.3 - assert_eq # Expect index is `3` + assert_eq end proc.test_gaps_left_found push.23.50.44.36.23.12.5 exec.binary_search_stack - assert # Expect "found" + + # expect "found" + assert + + # expect index is `1` push.1 - assert_eq # Expect index is `1` + assert_eq end proc.test_gaps_right_not_found push.40.50.44.36.23.12.5 exec.binary_search_stack - assertz # Expect "not found" + + # expect "not found" + assertz + + # expect insertion index is `3` push.3 - assert_eq # Expect insertion index is `3` + assert_eq end proc.test_gaps_left_not_found push.30.50.44.36.23.12.5 exec.binary_search_stack - assertz # Expect "not found" + + # expect "not found" + assertz + + # expect insertion index is `2` push.2 - assert_eq # Expect insertion index is `2` + assert_eq end #! Runs tests. @@ -198,11 +264,18 @@ proc.run_tests end begin - dup # [count, count, ...] - neq.0 # [count != 0, count, ...] - if.true # [count, arr[0], arr[1], ..., arr[count - 1], val, ...] - exec.binary_search_stack # [found, index] - else # [0, ...] + # 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 From e9a4bf30f31991f2073c337fdc4deadca51c614a Mon Sep 17 00:00:00 2001 From: polydez <155382956+polydez@users.noreply.github.com> Date: Thu, 11 Jan 2024 20:24:57 +0500 Subject: [PATCH 9/9] fix: removed note from the inputs file --- examples/bsearch.inputs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/examples/bsearch.inputs b/examples/bsearch.inputs index 9280347..550ad98 100644 --- a/examples/bsearch.inputs +++ b/examples/bsearch.inputs @@ -1,11 +1,4 @@ { - "note": [ - "Provide in `operand_stack`:", - "1. Value to find (12)", - "2. Array elements (0..19)", - "3. Array length (20)", - "If array length is 0 or `operand_stack` is empty, tests will run" - ], "operand_stack": [ "12", "19", "18", "17", "16", "15", "14", "13", "12", "11", "10", "9", "8", "7", "6", "5", "4", "3", "2", "1", "0",