This is an automated email from the ASF dual-hosted git repository.

dcapwell pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/cassandra-accord.git


The following commit(s) were added to refs/heads/trunk by this push:
     new a8bbed30 Fix Sorted Arrays Linear Intersection gives wrong results 
(#280)
a8bbed30 is described below

commit a8bbed30849967f264c7d108995398824a18f967
Author: Alan Wang <[email protected]>
AuthorDate: Tue Aug 11 14:03:49 2026 -0700

    Fix Sorted Arrays Linear Intersection gives wrong results (#280)
    
    patch by Alan Wang; reviewed by Ariel Weisberg, David Capwell for 
CASSANDRA-21553
---
 .../src/main/java/accord/utils/SortedArrays.java   | 11 ++-
 .../test/java/accord/utils/SortedArraysTest.java   | 91 ++++++++++++++++++++++
 2 files changed, 98 insertions(+), 4 deletions(-)

diff --git a/accord-core/src/main/java/accord/utils/SortedArrays.java 
b/accord-core/src/main/java/accord/utils/SortedArrays.java
index be6894a0..08a0ad80 100644
--- a/accord-core/src/main/java/accord/utils/SortedArrays.java
+++ b/accord-core/src/main/java/accord/utils/SortedArrays.java
@@ -480,6 +480,8 @@ public class SortedArrays
      */
     public static int[] linearIntersection(int[] left, int leftStart, int 
leftEnd, int[] right, int rightStart, int rightEnd, ArrayBuffers.IntBuffers 
buffers)
     {
+        Invariants.require(leftStart <= leftEnd && rightStart <= rightEnd);
+
         if (leftEnd - leftStart > rightEnd - rightStart)
         {
             int[] tmp = left;
@@ -515,9 +517,10 @@ public class SortedArrays
                 }
                 else
                 {
-                    resultSize = leftIdx++;
+                    resultSize = leftIdx - leftStart;
+                    leftIdx++;
                     result = buffers.getInts(resultSize + Math.min(leftEnd - 
leftIdx, rightEnd - rightIdx));
-                    System.arraycopy(left, 0, result, 0, resultSize);
+                    System.arraycopy(left, leftStart, result, 0, resultSize);
                     break;
                 }
             }
@@ -526,9 +529,9 @@ public class SortedArrays
             {
                 if (!hasMatch)
                     return left.length == 0 ? left : NO_INTS;
-                if (leftStart == 0 && leftEnd == left.length)
+                if (leftStart == 0 && leftIdx == left.length)
                     return left;
-                return Arrays.copyOfRange(left, leftStart, leftEnd);
+                return Arrays.copyOfRange(left, leftStart, leftIdx);
             }
         }
 
diff --git a/accord-core/src/test/java/accord/utils/SortedArraysTest.java 
b/accord-core/src/test/java/accord/utils/SortedArraysTest.java
index 782e99a9..82037d9f 100644
--- a/accord-core/src/test/java/accord/utils/SortedArraysTest.java
+++ b/accord-core/src/test/java/accord/utils/SortedArraysTest.java
@@ -205,6 +205,97 @@ class SortedArraysTest
         });
     }
 
+    @Test
+    public void testIntLinearIntersection()
+    {
+        Gen<Integer[]> gen = sortedUniqueIntegerArray(10000000, 25);
+        qt().forAll(gen, Gens.random()).check((a, rs) -> {
+            int mutations = a.length == 0 ? 0 : rs.nextInt(1, a.length + 1);
+
+            List<Integer> b = new ArrayList<>();
+            while (mutations != 0)
+            {
+                if (rs.nextBoolean())
+                    b.add(a[rs.nextInt(0, a.length)]);
+                else
+                    b.add(rs.nextInt(a[0] - 100, a[a.length - 1] + 100));
+                mutations--;
+            }
+
+            b.sort(Integer::compareTo);
+
+            int[] bInt = b.stream().mapToInt(i->i).toArray();
+            int[] aInt = Arrays.stream(a).mapToInt(i->i).toArray();
+
+            int aStart = aInt.length == 0 ? 0 : rs.nextInt(0, aInt.length);
+            int bStart = b.isEmpty() ? 0 : rs.nextInt(0, b.size());
+
+            int aEnd = rs.nextInt(aStart, aInt.length + 1);
+            int bEnd = rs.nextInt(bStart, bInt.length + 1);
+
+            Set<Integer> aSet = new 
HashSet<>(Arrays.asList(Arrays.copyOfRange(a, aStart, aEnd)));
+            Set<Integer> bSet = new HashSet<>(b.subList(bStart, bEnd));
+
+            Set<Integer> intersection = Sets.intersection(aSet, bSet);
+
+            int[] expected = intersection.stream().mapToInt(i->i).toArray();
+            Arrays.sort(expected);
+
+            // Condition covers the case where we are expected to return the 
exact copy
+            // if the intersection is an exact match of the smaller array
+            if (bStart == 0 && bEnd == bInt.length && bInt.length == 
bSet.size() && ((bEnd - bStart) != (aEnd - aStart)) && aSet.containsAll(bSet))
+            {
+                Assertions.assertSame(bInt, 
SortedArrays.linearIntersection(aInt, aStart, a.length, bInt, bStart, 
bInt.length, new ArrayBuffers.IntBufferCache(4, 1 << 14)));
+                Assertions.assertSame(bInt, 
SortedArrays.linearIntersection(bInt, bStart, bInt.length, aInt, aStart, 
a.length, new ArrayBuffers.IntBufferCache(4, 1 << 14)));
+            }
+            else
+            {
+                Assertions.assertArrayEquals(expected, 
SortedArrays.linearIntersection(aInt, aStart, aEnd, bInt, bStart, bEnd, new 
ArrayBuffers.IntBufferCache(4, 1 << 14)));
+                Assertions.assertArrayEquals(expected, 
SortedArrays.linearIntersection(bInt, bStart, bEnd, aInt, aStart, aEnd, new 
ArrayBuffers.IntBufferCache(4, 1 << 14)));
+            }
+        });
+    }
+
+    @Test
+    public void testIntLinearIntersectionAdHoc()
+    {
+        Gen<Integer[]> gen = sortedUniqueIntegerArray(0).filter(a -> a.length 
> 0);
+        qt().forAll(gen, Gens.random()).check((a, rs) -> {
+            int[] left = Arrays.stream(a).mapToInt(i -> i).toArray();
+
+            int leftStart = left.length == 1 ? 0 : rs.nextInt(0, left.length);
+            int leftEnd = left.length == 1 ? 1 : rs.nextInt(leftStart + 1, 
left.length + 1);
+
+            int rightStart = left.length == 1 ? 0 : rs.nextInt(0, left.length);
+            int rightEnd = left.length == 1 ? 1 : rs.nextInt(rightStart + 1, 
left.length + 1);
+
+            var actual = SortedArrays.linearIntersection(left, leftStart, 
leftEnd,
+                                                         left, rightStart, 
rightEnd,
+                                                         
ArrayBuffers.uncachedInts());
+
+            int expectedStart = Math.max(leftStart, rightStart);
+            int expectedEnd = Math.max(expectedStart, Math.min(leftEnd, 
rightEnd));
+            int[] expected = Arrays.copyOfRange(left, expectedStart, 
expectedEnd);
+
+            Assertions.assertArrayEquals(expected, actual, String.format("(%d, 
%d], (%d, %d] -> %s", leftStart, leftEnd, rightStart, rightEnd, 
Arrays.toString(expected)));
+        });
+    }
+
+    @Test
+    public void testIntLinearIntersectionAdHoc2()
+    {
+        int[] intersection = SortedArrays.linearIntersection(new int[] {0, 2, 
4, 6}, 0, 4, new int[] {6, 7}, 0, 2, new ArrayBuffers.IntBufferCache(4, 1 << 
14));
+        Assertions.assertArrayEquals(new int[] {6}, intersection);
+    }
+
+    @Test
+    public void testIntLinearIntersectionAdHoc3()
+    {
+        int[] a = new int[] {0, 2, 8};
+        int[] intersection = SortedArrays.linearIntersection(a, 0, 3, new 
int[] {0, 2, 8, 10, 12}, 0, 5, new ArrayBuffers.IntBufferCache(4, 1 << 14));
+        Assertions.assertSame(a, intersection);
+    }
+
     @Test
     public void testLinearIntersectionWithSubset()
     {


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to