Python:
from typing import List
import bisect
# Tail-call avoidance (loop-based) binary search
def binary_search_tail_recursive(arr: List[int], target: int, left: int, right: int) -> int:
while left <= right:
mid = (left + right) // 2
if arr[mid] == target:
return mid
elif arr[mid] < target:
left = mid + 1
else:
right = mid - 1
return -1
# Wrapper function for tail-recursive approach
def binary_search_tail(arr: List[int], target: int) -> int:
return binary_search_tail_recursive(arr, target, 0, len(arr) - 1)
# Standard iterative binary search
def binary_search_iterative(arr: List[int], target: int) -> int:
left, right = 0, len(arr) - 1
while left <= right:
mid = (left + right) // 2
if arr[mid] == target:
return mid
elif arr[mid] < target:
left = mid + 1
else:
right = mid - 1
return -1
# Using the bisect module for reference
def binary_search_bisect(arr: List[int], target: int) -> int:
idx = bisect.bisect_left(arr, target)
return idx if idx < len(arr) and arr[idx] == target else -1
# Test function
def run_tests():
tests = [
{"arr": [1, 2, 3, 4, 5, 6, 7, 8, 9], "target": 5, "expected": 4},
{"arr": [1, 2, 3, 4, 5], "target": 1, "expected": 0},
{"arr": [1, 2, 3, 4, 5], "target": 5, "expected": 4},
{"arr": [2, 4, 6, 8, 10], "target": 7, "expected": -1},
{"arr": [10], "target": 10, "expected": 0},
{"arr": [], "target": 3, "expected": -1},
]
print("\nRunning tests...\n")
for i, test in enumerate(tests, 1):
for name, search_func in [
("Tail-Recursive (Loop-Based)", binary_search_tail),
("Iterative", binary_search_iterative),
("Bisect", binary_search_bisect),
]:
result = search_func(test["arr"], test["target"])
assert result == test["expected"], (
f"Test {i} failed [{name}]: Expected {test['expected']}, got {result}"
)
print(f"Test {i} passed [{name}].")
print("\nAll tests passed successfully!")
# Run tests when script is executed
if __name__ == "__main__":
run_tests()