From eb0d32f858dca27838541d6f30b1c1026c220d1d Mon Sep 17 00:00:00 2001
From: Ed Solovey <ed@digits.com>
Date: Thu, 28 Sep 2023 00:08:32 -0400
Subject: [PATCH] More binary search implementations

---
 .../cs2223/algorithms/week1/ArraySearch.java  | 11 ++++
 .../{ => algorithms}/week1/BinarySearch.java  | 24 ++++----
 .../week1/BinarySearchRecursive.java          | 40 +++++++++++++
 .../cs2223/algorithms/week1/LinearSearch.java | 20 +++++++
 .../week1/ArraySearchPerformanceTest.java     | 52 ++++++++++++++++
 .../algorithns/week1/ArraySearchTest.java     | 60 +++++++++++++++++++
 .../wpi/cs2223/week1/BinarySearchTest.java    | 56 -----------------
 7 files changed, 196 insertions(+), 67 deletions(-)
 create mode 100644 src/edu/wpi/cs2223/algorithms/week1/ArraySearch.java
 rename src/edu/wpi/cs2223/{ => algorithms}/week1/BinarySearch.java (50%)
 create mode 100644 src/edu/wpi/cs2223/algorithms/week1/BinarySearchRecursive.java
 create mode 100644 src/edu/wpi/cs2223/algorithms/week1/LinearSearch.java
 create mode 100644 test/edu/wpi/cs2223/algorithns/week1/ArraySearchPerformanceTest.java
 create mode 100644 test/edu/wpi/cs2223/algorithns/week1/ArraySearchTest.java
 delete mode 100644 test/edu/wpi/cs2223/week1/BinarySearchTest.java

diff --git a/src/edu/wpi/cs2223/algorithms/week1/ArraySearch.java b/src/edu/wpi/cs2223/algorithms/week1/ArraySearch.java
new file mode 100644
index 0000000..5d4dfea
--- /dev/null
+++ b/src/edu/wpi/cs2223/algorithms/week1/ArraySearch.java
@@ -0,0 +1,11 @@
+package edu.wpi.cs2223.algorithms.week1;
+
+/**
+ * Implements a search algorithm against an array of integers.
+ */
+public interface ArraySearch {
+    /**
+     * @return the index of target in sortedArray if it exists there.  Otherwise -1;
+     */
+    int find(int[] sortedArray, int target);
+}
diff --git a/src/edu/wpi/cs2223/week1/BinarySearch.java b/src/edu/wpi/cs2223/algorithms/week1/BinarySearch.java
similarity index 50%
rename from src/edu/wpi/cs2223/week1/BinarySearch.java
rename to src/edu/wpi/cs2223/algorithms/week1/BinarySearch.java
index c35917b..a28e4f5 100644
--- a/src/edu/wpi/cs2223/week1/BinarySearch.java
+++ b/src/edu/wpi/cs2223/algorithms/week1/BinarySearch.java
@@ -1,18 +1,20 @@
-package edu.wpi.cs2223.week1;
+package edu.wpi.cs2223.algorithms.week1;
 
 /**
  * Implements the binary search algorithm against a sorted array of integers.
+ * Each iteration of the algorithm looks for the target at the midpoint index of the array, if the value at
+ * the midpoint is:
+ * <li>an exact match, then return midpoint</li>
+ * <li>less than the target, repeat the iteration on the right half of the current array</li>
+ * <li>greater than the target, repeat the iteration on the left half of the current array</li>
  */
-public class BinarySearch {
-    /**
-     * @return true iff the target value exists in sortedArray
-     */
-    public boolean doesValueExist(int[] sortedArray, int target) {
+public class BinarySearch implements ArraySearch {
+    public int find(int[] sortedArray, int target) {
         int loopIterations = 0;
 
         // empty arrays contain no targets
         if (sortedArray.length == 0) {
-            return returnWithLog(false, loopIterations, sortedArray.length);
+            return returnWithLog(-1, loopIterations, sortedArray.length);
         }
 
         int lowerRange = 0;
@@ -25,7 +27,7 @@ public class BinarySearch {
             int valueAtMidPoint = sortedArray[midpoint];
 
             if (target == valueAtMidPoint) {
-                return returnWithLog(true, loopIterations, sortedArray.length);
+                return returnWithLog(midpoint, loopIterations, sortedArray.length);
             }
 
             // narrow range to either left or right half based on comparison to midpoint
@@ -35,11 +37,11 @@ public class BinarySearch {
                 upperRange = midpoint - 1;
             }
         }
-        return returnWithLog(false, loopIterations, sortedArray.length);
+        return returnWithLog(-1, loopIterations, sortedArray.length);
     }
 
-    boolean returnWithLog(boolean returnValue, int loopIterations, int inputArrayLengths) {
-        System.out.printf("Returning %b after %d iterations for an array of size %d\n", returnValue, loopIterations, inputArrayLengths);
+    int returnWithLog(int returnValue, int loopIterations, int inputArrayLengths) {
+        System.out.printf("Returning %d after %d iterations for an array of size %d\n", returnValue, loopIterations, inputArrayLengths);
         return returnValue;
     }
 }
diff --git a/src/edu/wpi/cs2223/algorithms/week1/BinarySearchRecursive.java b/src/edu/wpi/cs2223/algorithms/week1/BinarySearchRecursive.java
new file mode 100644
index 0000000..5372b56
--- /dev/null
+++ b/src/edu/wpi/cs2223/algorithms/week1/BinarySearchRecursive.java
@@ -0,0 +1,40 @@
+package edu.wpi.cs2223.algorithms.week1;
+
+/**
+ * Implements binary array search recursively.
+ */
+public class BinarySearchRecursive implements ArraySearch{
+    @Override
+    public int find(int[] sortedArray, int target) {
+        // empty array base case
+        if (sortedArray.length == 0) {
+            return -1;
+        }
+
+        return find(sortedArray, target, 0, sortedArray.length -1, 0);
+    }
+
+    int find(int[] sortedArray, int target, int low, int high, int iterations) {
+        if (low <= high) {
+            iterations++;
+            int midpoint = (high + low) / 2;
+
+            if (sortedArray[midpoint] == target) {
+                System.out.printf("Found value in array of size %d after %d iteration.\n", sortedArray.length, iterations);
+                return midpoint;
+            }
+
+            // recurse on the right half of the current range
+            if (target > sortedArray[midpoint]) {
+                return find(sortedArray, target, midpoint + 1, high, iterations);
+            }
+
+            // recurse on the left half of the current range
+            return find(sortedArray, target, low, midpoint -1, iterations);
+        }
+
+        // traversed array and did not find target
+        System.out.printf("Failed to find value in array of size %d after %d iteration.\n", sortedArray.length, iterations);
+        return -1;
+    }
+}
diff --git a/src/edu/wpi/cs2223/algorithms/week1/LinearSearch.java b/src/edu/wpi/cs2223/algorithms/week1/LinearSearch.java
new file mode 100644
index 0000000..a6f6ca1
--- /dev/null
+++ b/src/edu/wpi/cs2223/algorithms/week1/LinearSearch.java
@@ -0,0 +1,20 @@
+package edu.wpi.cs2223.algorithms.week1;
+
+public class LinearSearch implements ArraySearch {
+    @Override
+    public int find(int[] sortedArray, int target) {
+        for (int index = 0; index < sortedArray.length; index++) {
+            if (sortedArray[index] == target) {
+                returnWithLog(index, index, sortedArray.length);
+            }
+        }
+
+        // base case of empty array or case of us exhausting the array
+        return returnWithLog(-1, sortedArray.length, sortedArray.length);
+    }
+
+    int returnWithLog(int returnValue, int loopIterations, int inputArrayLengths) {
+        System.out.printf("Returning %d after %d iterations for an array of size %d\n", returnValue, loopIterations, inputArrayLengths);
+        return returnValue;
+    }
+}
diff --git a/test/edu/wpi/cs2223/algorithns/week1/ArraySearchPerformanceTest.java b/test/edu/wpi/cs2223/algorithns/week1/ArraySearchPerformanceTest.java
new file mode 100644
index 0000000..af3a5ed
--- /dev/null
+++ b/test/edu/wpi/cs2223/algorithns/week1/ArraySearchPerformanceTest.java
@@ -0,0 +1,52 @@
+package edu.wpi.cs2223.algorithns.week1;
+
+import edu.wpi.cs2223.algorithms.week1.ArraySearch;
+import edu.wpi.cs2223.algorithms.week1.BinarySearch;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Random;
+
+public class ArraySearchPerformanceTest {
+    ArraySearch search;
+
+    static final List<Integer> PERFORMANCE_ARRAY_SIZES =
+            Arrays.asList(
+                    (int) Math.pow(2, 12),
+                    (int) Math.pow(2, 13),
+                    (int) Math.pow(2, 14),
+                    (int) Math.pow(2, 15) * 10,
+                    (int) Math.pow(2, 15) * 100,
+                    (int) Math.pow(2, 15) * 1000);
+
+    @BeforeEach
+    public void setUp(){
+        search = new BinarySearch();
+//        search = new LinearSearch();
+//        search = new BinarySearchRecursive();
+    }
+
+    @Test
+    public void measurePerformance(){
+        for (int inputArraySize : PERFORMANCE_ARRAY_SIZES) {
+            int[] inputArray = generateRandomSortedArray(inputArraySize);
+            search.find(inputArray, new Random().nextInt());
+        }
+    }
+
+    /**
+     * Generate an array of sorted random integers of the specified size.
+     */
+    int[] generateRandomSortedArray(int arraySize) {
+        Random randomGenerator = new Random();
+
+        List<Integer> input = new ArrayList<>();
+        for (int generationIndex = 0; generationIndex < arraySize; generationIndex++) {
+            input.add(randomGenerator.nextInt());
+        }
+        return input.stream().sorted().mapToInt(x -> x).toArray();
+    }
+}
diff --git a/test/edu/wpi/cs2223/algorithns/week1/ArraySearchTest.java b/test/edu/wpi/cs2223/algorithns/week1/ArraySearchTest.java
new file mode 100644
index 0000000..82c1e4e
--- /dev/null
+++ b/test/edu/wpi/cs2223/algorithns/week1/ArraySearchTest.java
@@ -0,0 +1,60 @@
+package edu.wpi.cs2223.algorithns.week1;
+
+import edu.wpi.cs2223.algorithms.week1.ArraySearch;
+import edu.wpi.cs2223.algorithms.week1.BinarySearchRecursive;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class ArraySearchTest {
+    ArraySearch search;
+
+    @BeforeEach
+    public void setUp(){
+        search = new BinarySearchRecursive();
+//        search = new BinarySearch();
+//        search = new LinearSearch();
+    }
+
+    @Test
+    public void find_falseEmptyArray() {
+        int[] inputArray = new int[]{};
+        assertEquals(-1, search.find(inputArray, 101));
+    }
+
+    @Test
+    public void find_false() {
+        int[] inputArray = new int[]{1, 9, 100, 564, 999};
+        assertEquals(-1, search.find(inputArray, 101));
+    }
+
+    @Test
+    public void find_falseOutOfRangeLow() {
+        int[] inputArray = new int[]{1, 9, 100, 564, 999};
+        assertEquals(-1, search.find(inputArray, -101));
+    }
+
+    @Test
+    public void find_falseOutOfRangeHigh() {
+        int[] inputArray = new int[]{1, 9, 100, 564, 999};
+        assertEquals(-1, search.find(inputArray, 9001));
+    }
+
+    @Test
+    public void find_trueSingleValue() {
+        int[] inputArray = new int[]{1};
+        assertEquals(0, search.find(inputArray, 1));
+    }
+
+    @Test
+    public void find_trueLeftOfMidpoint() {
+        int[] inputArray = new int[]{1, 9, 100, 564, 999};
+        assertEquals(1, search.find(inputArray, 9));
+    }
+
+    @Test
+    public void find_trueRightOfMidpoint() {
+        int[] inputArray = new int[]{1, 9, 100, 564, 999};
+        assertEquals(3, search.find(inputArray, 564));
+    }
+}
\ No newline at end of file
diff --git a/test/edu/wpi/cs2223/week1/BinarySearchTest.java b/test/edu/wpi/cs2223/week1/BinarySearchTest.java
deleted file mode 100644
index d17cf84..0000000
--- a/test/edu/wpi/cs2223/week1/BinarySearchTest.java
+++ /dev/null
@@ -1,56 +0,0 @@
-package edu.wpi.cs2223.week1;
-
-
-import org.junit.jupiter.api.Test;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-
-class BinarySearchTest {
-    @Test
-    public void doesValueExist_falseEmptyArray(){
-        int[] inputArray = new int[]{};
-
-        BinarySearch search = new BinarySearch();
-        assertFalse(search.doesValueExist(inputArray, 101));
-    }
-
-    @Test
-    public void doesValueExist_false(){
-        int[] inputArray = new int[]{ 1, 9, 100, 564, 999 };
-
-        BinarySearch search = new BinarySearch();
-        assertFalse(search.doesValueExist(inputArray, 101));
-    }
-
-    @Test
-    public void doesValueExist_falseOutOfRangeLow(){
-        int[] inputArray = new int[]{ 1, 9, 100, 564, 999 };
-
-        BinarySearch search = new BinarySearch();
-        assertFalse(search.doesValueExist(inputArray, -101));
-    }
-
-    @Test
-    public void doesValueExist_falseOutOfRangeHigh(){
-        int[] inputArray = new int[]{ 1, 9, 100, 564, 999 };
-
-        BinarySearch search = new BinarySearch();
-        assertFalse(search.doesValueExist(inputArray, 9001));
-    }
-
-    @Test
-    public void doesValueExist_trueSingleValue(){
-        int[] inputArray = new int[]{ 1};
-
-        BinarySearch search = new BinarySearch();
-        assertTrue(search.doesValueExist(inputArray, 1));
-    }
-
-    @Test
-    public void doesValueExist_true(){
-        int[] inputArray = new int[]{ 1, 9, 100, 564, 999 };
-
-        BinarySearch search = new BinarySearch();
-        assertTrue(search.doesValueExist(inputArray, 9));
-    }
-}
\ No newline at end of file
-- 
GitLab