dcapwell commented on code in PR #6:
URL: https://github.com/apache/cassandra-accord/pull/6#discussion_r940863813


##########
accord-core/src/main/java/accord/primitives/Deps.java:
##########
@@ -0,0 +1,978 @@
+package accord.primitives;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+import com.google.common.base.Preconditions;
+
+import accord.api.Key;
+import accord.local.Command;
+import accord.local.CommandStore;
+import accord.utils.InlineHeap;
+import accord.utils.SortedArrays;
+
+import static accord.utils.SortedArrays.remap;
+import static accord.utils.SortedArrays.remapper;
+
+// TODO (now): switch to RoutingKey
+public class Deps implements Iterable<Map.Entry<Key, TxnId>>
+{
+    private static final TxnId[] NO_TXNIDS = new TxnId[0];
+    private static final int[] NO_INTS = new int[0];
+    public static final Deps NONE = new Deps(Keys.EMPTY, NO_TXNIDS, NO_INTS);
+
+    public static class Builder
+    {
+        final Keys keys;
+        final Map<TxnId, Integer> txnIdLookup = new HashMap<>(); // TODO: 
primitive map
+        TxnId[] txnIds = new TxnId[4];
+        final int[][] keysToTxnId;
+        final int[] keysToTxnIdCounts;
+
+        public Builder(Keys keys)
+        {
+            this.keys = keys;
+            this.keysToTxnId = new int[keys.size()][4];
+            this.keysToTxnIdCounts = new int[keys.size()];
+        }
+
+        public boolean isEmpty()
+        {
+            return Arrays.stream(keysToTxnIdCounts).allMatch(i -> i == 0);
+        }
+
+        public void add(Command command)
+        {
+            int idx = ensureTxnIdx(command.txnId());
+            keys.foldlIntersect(command.txn().keys, (li, ri, k, p, v) -> {
+                if (keysToTxnId[li].length == keysToTxnIdCounts[li])
+                    keysToTxnId[li] = Arrays.copyOf(keysToTxnId[li], 
keysToTxnId[li].length * 2);
+                keysToTxnId[li][keysToTxnIdCounts[li]++] = idx;
+                return 0;
+            }, 0, 0, 1);
+        }
+
+        public void add(Key key, TxnId txnId)
+        {
+            int txnIdx = ensureTxnIdx(txnId);
+            int keyIdx = keys.indexOf(key);
+            if (keysToTxnIdCounts[keyIdx] == keysToTxnId[keyIdx].length)
+                keysToTxnId[keyIdx] = Arrays.copyOf(keysToTxnId[keyIdx], 
Math.max(4, keysToTxnIdCounts[keyIdx] * 2));
+            keysToTxnId[keyIdx][keysToTxnIdCounts[keyIdx]++] = txnIdx;
+        }
+
+        public boolean contains(TxnId txnId)
+        {
+            return txnIdx(txnId) >= 0;
+        }
+
+        private int txnIdx(TxnId txnId)
+        {
+            return txnIdLookup.getOrDefault(txnId, -1);
+        }
+
+        private int ensureTxnIdx(TxnId txnId)
+        {
+            return txnIdLookup.computeIfAbsent(txnId, ignore -> {
+                if (txnIds.length == txnIdLookup.size())
+                    txnIds = Arrays.copyOf(txnIds, txnIds.length * 2);
+                return txnIdLookup.size();
+            });
+        }
+
+        public Deps build()
+        {
+            TxnId[] txnIds = txnIdLookup.keySet().toArray(TxnId[]::new);
+            Arrays.sort(txnIds, TxnId::compareTo);
+            int[] txnIdMap = new int[txnIds.length];
+            for (int i = 0 ; i < txnIdMap.length ; i++)
+                txnIdMap[txnIdLookup.get(txnIds[i])] = i;
+
+            int keyCount = 0;
+            int[] result; {
+                int count = 0;
+                for (int i = 0 ; i < keys.size() ; ++i)
+                {
+                    keyCount += keysToTxnIdCounts[i] > 0 ? 1 : 0;
+                    count += keysToTxnIdCounts[i];
+                }
+                result = new int[count + keyCount];
+            }
+
+            int keyIndex = 0;
+            int offset = keyCount;
+            for (int i = 0 ; i < keys.size() ; ++i)
+            {
+                if (keysToTxnIdCounts[i] > 0)
+                {
+                    int count = keysToTxnIdCounts[i];
+                    int[] src = keysToTxnId[i];
+                    for (int j = 0 ; j < count ; ++j)
+                        result[j + offset] = txnIdMap[src[j]];
+                    Arrays.sort(result, offset, count + offset);
+                    int dups = 0;
+                    for (int j = offset + 1 ; j < offset + count ; ++j)
+                    {
+                        if (result[j] == result[j - 1]) ++dups;
+                        else if (dups > 0) result[j - dups] = result[j];
+                    }
+                    result[keyIndex] = offset += count - dups;
+                    ++keyIndex;
+                }
+            }
+            if (offset < result.length)
+                result = Arrays.copyOf(result, offset);
+
+            Keys keys = this.keys;
+            if (keyCount < keys.size())
+            {
+                keyIndex = 0;
+                Key[] newKeys = new Key[keyCount];
+                for (int i = 0 ; i < keys.size() ; ++i)
+                {
+                    if (keysToTxnIdCounts[i] > 0)
+                        newKeys[keyIndex++] = keys.get(i);
+                }
+                keys = new Keys(newKeys);
+            }
+
+            return new Deps(keys, txnIds, result);
+        }
+    }
+
+    public static Builder builder(Keys keys)
+    {
+        return new Builder(keys);
+    }
+
+    static class MergeStream
+    {
+        final Deps source;
+        // TODO: could share backing array for all of these if we want, with 
an additional offset
+        final int[] input;
+        final int keyCount;
+        int[] remap; // TODO: use cached backing array
+        int[] keys; // TODO: use cached backing array
+        int keyIndex;
+        int index;
+        int endIndex;
+
+        MergeStream(Deps source)
+        {
+            this.source = source;
+            this.input = source.keyToTxnId;
+            this.keyCount = source.keys.size();
+        }
+
+        private void init(Keys keys, TxnId[] txnIds)
+        {
+            this.remap = remapper(source.txnIds, txnIds, true);
+            this.keys = source.keys.remapper(keys, true);
+            while (input[keyIndex] == keyCount)
+                ++keyIndex;
+            this.index = keyCount;
+            this.endIndex = input[keyIndex];
+        }
+    }
+
+    public static <T> Deps merge(Keys keys, List<T> merge, Function<T, Deps> 
getter)

Review Comment:
   I was curious why have a custom merge method rather than rely on builder, so 
wanted to create a quick benchmark so I could see if there was any noticeable 
difference, but in doing so I find that merge produces an invalidate Deps for me
   
   the simplified test (was benchmark, so had other non merge related things)
   
   ```
   private static accord.primitives.Deps benchmarkMerge(List<Deps> list) {
           Keys keys = list.get(0).test.keys();
           return accord.primitives.Deps.merge(keys, list, d -> d.test);
       }
   ```
   
   The error
   
   ```
   Seed=3706255249468386534
   Examples=100
   accord.utils.Property$PropertyError: Seed=3706255249468386534
   Examples=100
        at accord.utils.Property$SingleBuilder.check(Property.java:78)
        at accord.txn.DepsTest.hackyBenchmark(DepsTest.java:186)
   ...
   Caused by: java.lang.IllegalStateException
        at 
com.google.common.base.Preconditions.checkState(Preconditions.java:494)
        at accord.primitives.Deps.<init>(Deps.java:377)
        at accord.primitives.Deps.merge(Deps.java:345)
        at accord.txn.DepsTest.benchmarkMerge(DepsTest.java:231)
        at accord.txn.DepsTest.lambda$hackyBenchmark$15(DepsTest.java:188)
        at accord.utils.Property$SingleBuilder.check(Property.java:74)
        ... 91 more
   ```
   
   Was trying to test before I started reviewing as `merge` is very large, so 
not sure where the issue is yet



##########
accord-core/src/main/java/accord/utils/SortedArrays.java:
##########
@@ -0,0 +1,535 @@
+package accord.utils;
+
+import java.util.Arrays;
+import java.util.function.IntFunction;
+
+import org.apache.cassandra.utils.concurrent.Inline;
+
+import static java.util.Arrays.*;
+
+public class SortedArrays
+{
+    /**
+     * Given two sorted arrays, return an array containing the elements 
present in either, preferentially returning one
+     * of the inputs if it contains all elements of the other.
+     *
+     * TODO: introduce exponential search optimised version
+     */
+    public static <T extends Comparable<? super T>> T[] linearUnion(T[] left, 
T[] right, IntFunction<T[]> allocate)
+    {
+        int leftIdx = 0;
+        int rightIdx = 0;
+
+        T[] result = null;
+        int resultSize = 0;
+
+        // first, pick the superset candidate and merge the two until we find 
the first missing item
+        // if none found, return the superset candidate
+        if (left.length >= right.length)
+        {
+            while (leftIdx < left.length && rightIdx < right.length)
+            {
+                int cmp = left[leftIdx].compareTo(right[rightIdx]);
+                if (cmp <= 0)
+                {
+                    leftIdx += 1;
+                    rightIdx += cmp == 0 ? 1 : 0;
+                }
+                else
+                {
+                    resultSize = leftIdx;
+                    result = allocate.apply(resultSize + (left.length - 
leftIdx) + (right.length - (rightIdx - 1)));
+                    System.arraycopy(left, 0, result, 0, resultSize);
+                    result[resultSize++] = right[rightIdx++];
+                    break;
+                }
+            }
+
+            if (result == null)
+            {
+                if (rightIdx == right.length)
+                    return left;
+                result = allocate.apply(left.length + (right.length - 
rightIdx));
+                resultSize = leftIdx;
+                System.arraycopy(left, 0, result, 0, resultSize);
+            }
+        }
+        else
+        {
+            while (leftIdx < left.length && rightIdx < right.length)
+            {
+                int cmp = left[leftIdx].compareTo(right[rightIdx]);
+                if (cmp >= 0)
+                {
+                    rightIdx += 1;
+                    leftIdx += cmp == 0 ? 1 : 0;
+                }
+                else
+                {
+                    resultSize = rightIdx;
+                    result = allocate.apply(resultSize + (left.length - 
(leftIdx - 1)) + (right.length - rightIdx));
+                    System.arraycopy(right, 0, result, 0, resultSize);
+                    result[resultSize++] = left[leftIdx++];
+                    break;
+                }
+            }
+
+            if (result == null)
+            {
+                if (leftIdx == left.length)
+                    return right;
+                result = allocate.apply(right.length + (left.length - 
leftIdx));
+                resultSize = rightIdx;
+                System.arraycopy(right, 0, result, 0, resultSize);
+            }
+        }
+
+        while (leftIdx < left.length && rightIdx < right.length)
+        {
+            T leftKey = left[leftIdx];
+            T rightKey = right[rightIdx];
+            int cmp = leftKey.compareTo(rightKey);
+            T minKey;
+            if (cmp == 0)
+            {
+                leftIdx++;
+                rightIdx++;
+                minKey = leftKey;
+            }
+            else if (cmp < 0)
+            {
+                leftIdx++;
+                minKey = leftKey;
+            }
+            else
+            {
+                rightIdx++;
+                minKey = rightKey;
+            }
+            result[resultSize++] = minKey;
+        }
+
+        while (leftIdx < left.length)
+            result[resultSize++] = left[leftIdx++];
+
+        while (rightIdx < right.length)
+            result[resultSize++] = right[rightIdx++];
+
+        if (resultSize < result.length)
+            result = copyOf(result, resultSize);
+
+        return result;
+    }
+
+    /**
+     * Given two sorted arrays, return the elements present only in both, 
preferentially returning one of the inputs if
+     * it contains no elements not present in the other.
+     *
+     * TODO: introduce exponential search optimised version
+     */
+    @SuppressWarnings("unused") // was used until recently, might be used 
again?
+    public static <T extends Comparable<? super T>> T[] linearIntersection(T[] 
left, T[] right, IntFunction<T[]> allocate)
+    {
+        int leftIdx = 0;
+        int rightIdx = 0;
+
+        T[] result = null;
+        int resultSize = 0;
+
+        // first pick a subset candidate, and merge both until we encounter an 
element not present in the other array
+        if (left.length <= right.length)
+        {
+            while (leftIdx < left.length && rightIdx < right.length)
+            {
+                int cmp = left[leftIdx].compareTo(right[rightIdx]);
+                if (cmp >= 0)
+                {
+                    rightIdx += 1;
+                    leftIdx += cmp == 0 ? 1 : 0;
+                }
+                else
+                {
+                    resultSize = leftIdx++;
+                    result = allocate.apply(resultSize + Math.min(left.length 
- leftIdx, right.length - rightIdx));
+                    System.arraycopy(left, 0, result, 0, resultSize);
+                    break;
+                }
+            }
+
+            if (result == null)
+                return left;
+        }
+        else
+        {
+            while (leftIdx < left.length && rightIdx < right.length)
+            {
+                int cmp = left[leftIdx].compareTo(right[rightIdx]);
+                if (cmp <= 0)
+                {
+                    leftIdx += 1;
+                    rightIdx += cmp == 0 ? 1 : 0;
+                }
+                else
+                {
+                    resultSize = rightIdx++;
+                    result = allocate.apply(resultSize + Math.min(left.length 
- leftIdx, right.length - rightIdx));
+                    System.arraycopy(right, 0, result, 0, resultSize);
+                    break;
+                }
+            }
+
+            if (result == null)
+                return right;
+        }
+
+        while (leftIdx < left.length && rightIdx < right.length)
+        {
+            T leftKey = left[leftIdx];
+            int cmp = leftKey.compareTo(right[rightIdx]);
+            if (cmp == 0)
+            {
+                leftIdx++;
+                rightIdx++;
+                result[resultSize++] = leftKey;
+            }
+            else
+            {
+                if (cmp < 0) leftIdx++;
+                else rightIdx++;
+            }
+        }
+
+        if (resultSize < result.length)
+            result = Arrays.copyOf(result, resultSize);
+        
+        return result;
+    }
+
+    /**
+     * Given two sorted arrays, return the elements present only in the first, 
preferentially returning the first array
+     * itself if possible
+     */
+    @SuppressWarnings("unused") // was used until recently, might be used 
again?
+    public static <T extends Comparable<? super T>> T[] linearDifference(T[] 
left, T[] right, IntFunction<T[]> allocate)
+    {
+        int rightIdx = 0;
+        int leftIdx = 0;
+
+        T[] result = null;
+        int resultSize = 0;
+
+        while (leftIdx < left.length && rightIdx < right.length)
+        {
+            int cmp = left[leftIdx].compareTo(right[rightIdx]);
+            if (cmp == 0)
+            {
+                resultSize = leftIdx++;
+                ++rightIdx;
+                result = allocate.apply(resultSize + left.length - leftIdx);
+                System.arraycopy(left, 0, result, 0, resultSize);
+                break;
+            }
+            else if (cmp < 0)
+            {
+                ++leftIdx;
+            }
+            else
+            {
+                ++rightIdx;
+            }
+        }
+
+        if (result == null)
+            return left;
+
+        while (leftIdx < left.length && rightIdx < right.length)
+        {
+            int cmp = left[leftIdx].compareTo(right[rightIdx]);
+            if (cmp > 0)
+            {
+                result[resultSize++] = left[leftIdx++];
+            }
+            else if (cmp < 0)
+            {
+                ++rightIdx;
+            }
+            else
+            {
+                ++leftIdx;
+                ++rightIdx;
+            }
+        }
+
+        if (resultSize < result.length)
+            result = Arrays.copyOf(result, resultSize);
+
+        return result;
+    }
+
+    public static <A, R> A[] sliceWithOverlaps(A[] slice, R[] select, 
IntFunction<A[]> factory, AsymmetricComparator<A, R> cmp1, 
AsymmetricComparator<R, A> cmp2)
+    {
+        A[] result;
+        int resultCount;
+        int ai = 0, ri = 0;
+        while (true)
+        {
+            long ari = findNextIntersection(slice, ai, select, ri, cmp1, cmp2, 
Search.CEIL);
+            if (ari < 0)
+            {
+                if (ai == slice.length)
+                    return slice;
+
+                return Arrays.copyOf(slice, ai);
+            }
+
+            int nextai = (int)(ari >>> 32);
+            if (ai != nextai)
+            {
+                resultCount = ai;
+                result = factory.apply(ai + (slice.length - nextai));
+                System.arraycopy(slice, 0, result, 0, resultCount);
+                ai = nextai;
+                break;
+            }
+
+            ri = (int)ari;
+            ai = exponentialSearch(slice, nextai, slice.length, select[ri], 
cmp2, Search.FLOOR) + 1;
+        }
+
+        while (true)
+        {
+            int nextai = exponentialSearch(slice, ai, slice.length, 
select[ri], cmp2, Search.FLOOR) + 1;
+            while (ai < nextai)
+                result[resultCount++] = slice[ai++];
+
+            long ari = findNextIntersection(slice, ai, select, ri, cmp1, cmp2, 
Search.CEIL);
+            if (ari < 0)
+            {
+                if (resultCount < result.length)
+                    result = Arrays.copyOf(result, resultCount);
+
+                return result;
+            }
+
+            ai = (int)(ari >>> 32);
+            ri = (int)ari;
+        }
+    }
+
+    /**
+     * Copy-on-write insert into the provided array; returns the same array if 
item already present, or a new array
+     * with the item in the correct position if not. Linear time complexity.
+     */
+    public static <T extends Comparable<? super T>> T[] insert(T[] src, T 
item, IntFunction<T[]> factory)

Review Comment:
   nothing references



##########
accord-core/src/main/java/accord/utils/SortedArrays.java:
##########
@@ -0,0 +1,535 @@
+package accord.utils;
+
+import java.util.Arrays;
+import java.util.function.IntFunction;
+
+import org.apache.cassandra.utils.concurrent.Inline;
+
+import static java.util.Arrays.*;
+
+public class SortedArrays
+{
+    /**
+     * Given two sorted arrays, return an array containing the elements 
present in either, preferentially returning one
+     * of the inputs if it contains all elements of the other.
+     *
+     * TODO: introduce exponential search optimised version
+     */
+    public static <T extends Comparable<? super T>> T[] linearUnion(T[] left, 
T[] right, IntFunction<T[]> allocate)
+    {
+        int leftIdx = 0;
+        int rightIdx = 0;
+
+        T[] result = null;
+        int resultSize = 0;
+
+        // first, pick the superset candidate and merge the two until we find 
the first missing item
+        // if none found, return the superset candidate
+        if (left.length >= right.length)
+        {
+            while (leftIdx < left.length && rightIdx < right.length)
+            {
+                int cmp = left[leftIdx].compareTo(right[rightIdx]);
+                if (cmp <= 0)
+                {
+                    leftIdx += 1;
+                    rightIdx += cmp == 0 ? 1 : 0;
+                }
+                else
+                {
+                    resultSize = leftIdx;
+                    result = allocate.apply(resultSize + (left.length - 
leftIdx) + (right.length - (rightIdx - 1)));
+                    System.arraycopy(left, 0, result, 0, resultSize);
+                    result[resultSize++] = right[rightIdx++];
+                    break;
+                }
+            }
+
+            if (result == null)
+            {
+                if (rightIdx == right.length)
+                    return left;
+                result = allocate.apply(left.length + (right.length - 
rightIdx));
+                resultSize = leftIdx;
+                System.arraycopy(left, 0, result, 0, resultSize);
+            }
+        }
+        else
+        {
+            while (leftIdx < left.length && rightIdx < right.length)
+            {
+                int cmp = left[leftIdx].compareTo(right[rightIdx]);
+                if (cmp >= 0)
+                {
+                    rightIdx += 1;
+                    leftIdx += cmp == 0 ? 1 : 0;
+                }
+                else
+                {
+                    resultSize = rightIdx;
+                    result = allocate.apply(resultSize + (left.length - 
(leftIdx - 1)) + (right.length - rightIdx));
+                    System.arraycopy(right, 0, result, 0, resultSize);
+                    result[resultSize++] = left[leftIdx++];
+                    break;
+                }
+            }
+
+            if (result == null)
+            {
+                if (leftIdx == left.length)
+                    return right;
+                result = allocate.apply(right.length + (left.length - 
leftIdx));
+                resultSize = rightIdx;
+                System.arraycopy(right, 0, result, 0, resultSize);
+            }
+        }
+
+        while (leftIdx < left.length && rightIdx < right.length)
+        {
+            T leftKey = left[leftIdx];
+            T rightKey = right[rightIdx];
+            int cmp = leftKey.compareTo(rightKey);
+            T minKey;
+            if (cmp == 0)
+            {
+                leftIdx++;
+                rightIdx++;
+                minKey = leftKey;
+            }
+            else if (cmp < 0)
+            {
+                leftIdx++;
+                minKey = leftKey;
+            }
+            else
+            {
+                rightIdx++;
+                minKey = rightKey;
+            }
+            result[resultSize++] = minKey;
+        }
+
+        while (leftIdx < left.length)
+            result[resultSize++] = left[leftIdx++];
+
+        while (rightIdx < right.length)
+            result[resultSize++] = right[rightIdx++];
+
+        if (resultSize < result.length)
+            result = copyOf(result, resultSize);
+
+        return result;
+    }
+
+    /**
+     * Given two sorted arrays, return the elements present only in both, 
preferentially returning one of the inputs if
+     * it contains no elements not present in the other.
+     *
+     * TODO: introduce exponential search optimised version
+     */
+    @SuppressWarnings("unused") // was used until recently, might be used 
again?
+    public static <T extends Comparable<? super T>> T[] linearIntersection(T[] 
left, T[] right, IntFunction<T[]> allocate)
+    {
+        int leftIdx = 0;
+        int rightIdx = 0;
+
+        T[] result = null;
+        int resultSize = 0;
+
+        // first pick a subset candidate, and merge both until we encounter an 
element not present in the other array
+        if (left.length <= right.length)
+        {
+            while (leftIdx < left.length && rightIdx < right.length)
+            {
+                int cmp = left[leftIdx].compareTo(right[rightIdx]);
+                if (cmp >= 0)
+                {
+                    rightIdx += 1;
+                    leftIdx += cmp == 0 ? 1 : 0;
+                }
+                else
+                {
+                    resultSize = leftIdx++;
+                    result = allocate.apply(resultSize + Math.min(left.length 
- leftIdx, right.length - rightIdx));
+                    System.arraycopy(left, 0, result, 0, resultSize);
+                    break;
+                }
+            }
+
+            if (result == null)
+                return left;
+        }
+        else
+        {
+            while (leftIdx < left.length && rightIdx < right.length)
+            {
+                int cmp = left[leftIdx].compareTo(right[rightIdx]);
+                if (cmp <= 0)
+                {
+                    leftIdx += 1;
+                    rightIdx += cmp == 0 ? 1 : 0;
+                }
+                else
+                {
+                    resultSize = rightIdx++;
+                    result = allocate.apply(resultSize + Math.min(left.length 
- leftIdx, right.length - rightIdx));
+                    System.arraycopy(right, 0, result, 0, resultSize);
+                    break;
+                }
+            }
+
+            if (result == null)
+                return right;
+        }
+
+        while (leftIdx < left.length && rightIdx < right.length)
+        {
+            T leftKey = left[leftIdx];
+            int cmp = leftKey.compareTo(right[rightIdx]);
+            if (cmp == 0)
+            {
+                leftIdx++;
+                rightIdx++;
+                result[resultSize++] = leftKey;
+            }
+            else
+            {
+                if (cmp < 0) leftIdx++;
+                else rightIdx++;
+            }
+        }
+
+        if (resultSize < result.length)
+            result = Arrays.copyOf(result, resultSize);
+        
+        return result;
+    }
+
+    /**
+     * Given two sorted arrays, return the elements present only in the first, 
preferentially returning the first array
+     * itself if possible
+     */
+    @SuppressWarnings("unused") // was used until recently, might be used 
again?
+    public static <T extends Comparable<? super T>> T[] linearDifference(T[] 
left, T[] right, IntFunction<T[]> allocate)
+    {
+        int rightIdx = 0;
+        int leftIdx = 0;
+
+        T[] result = null;
+        int resultSize = 0;
+
+        while (leftIdx < left.length && rightIdx < right.length)
+        {
+            int cmp = left[leftIdx].compareTo(right[rightIdx]);
+            if (cmp == 0)
+            {
+                resultSize = leftIdx++;
+                ++rightIdx;
+                result = allocate.apply(resultSize + left.length - leftIdx);
+                System.arraycopy(left, 0, result, 0, resultSize);
+                break;
+            }
+            else if (cmp < 0)
+            {
+                ++leftIdx;
+            }
+            else
+            {
+                ++rightIdx;
+            }
+        }
+
+        if (result == null)
+            return left;
+
+        while (leftIdx < left.length && rightIdx < right.length)
+        {
+            int cmp = left[leftIdx].compareTo(right[rightIdx]);
+            if (cmp > 0)
+            {
+                result[resultSize++] = left[leftIdx++];
+            }
+            else if (cmp < 0)
+            {
+                ++rightIdx;
+            }
+            else
+            {
+                ++leftIdx;
+                ++rightIdx;
+            }
+        }
+
+        if (resultSize < result.length)
+            result = Arrays.copyOf(result, resultSize);
+
+        return result;
+    }
+
+    public static <A, R> A[] sliceWithOverlaps(A[] slice, R[] select, 
IntFunction<A[]> factory, AsymmetricComparator<A, R> cmp1, 
AsymmetricComparator<R, A> cmp2)
+    {
+        A[] result;
+        int resultCount;
+        int ai = 0, ri = 0;
+        while (true)
+        {
+            long ari = findNextIntersection(slice, ai, select, ri, cmp1, cmp2, 
Search.CEIL);
+            if (ari < 0)
+            {
+                if (ai == slice.length)
+                    return slice;
+
+                return Arrays.copyOf(slice, ai);
+            }
+
+            int nextai = (int)(ari >>> 32);
+            if (ai != nextai)
+            {
+                resultCount = ai;
+                result = factory.apply(ai + (slice.length - nextai));
+                System.arraycopy(slice, 0, result, 0, resultCount);
+                ai = nextai;
+                break;
+            }
+
+            ri = (int)ari;
+            ai = exponentialSearch(slice, nextai, slice.length, select[ri], 
cmp2, Search.FLOOR) + 1;
+        }
+
+        while (true)
+        {
+            int nextai = exponentialSearch(slice, ai, slice.length, 
select[ri], cmp2, Search.FLOOR) + 1;
+            while (ai < nextai)
+                result[resultCount++] = slice[ai++];
+
+            long ari = findNextIntersection(slice, ai, select, ri, cmp1, cmp2, 
Search.CEIL);
+            if (ari < 0)
+            {
+                if (resultCount < result.length)
+                    result = Arrays.copyOf(result, resultCount);
+
+                return result;
+            }
+
+            ai = (int)(ari >>> 32);
+            ri = (int)ari;
+        }
+    }
+
+    /**
+     * Copy-on-write insert into the provided array; returns the same array if 
item already present, or a new array
+     * with the item in the correct position if not. Linear time complexity.
+     */
+    public static <T extends Comparable<? super T>> T[] insert(T[] src, T 
item, IntFunction<T[]> factory)
+    {
+        int insertPos = Arrays.binarySearch(src, item);
+        if (insertPos >= 0)
+            return src;
+        insertPos = -1 - insertPos;
+
+        T[] trg = factory.apply(src.length + 1);
+        System.arraycopy(src, 0, trg, 0, insertPos);
+        trg[insertPos] = item;
+        System.arraycopy(src, insertPos, trg, insertPos + 1, src.length - 
insertPos);
+        return trg;
+    }
+
+    /**
+     * Equivalent to {@link Arrays#binarySearch}, only more efficient 
algorithmically for linear merges.
+     * Binary search has worst case complexity {@code O(n.lg n)} for a linear 
merge, whereas exponential search
+     * has a worst case of {@code O(n)}. However compared to a simple linear 
merge, the best case for exponential
+     * search is {@code O(lg(n))} instead of {@code O(n)}.
+     */
+    public static <T1, T2 extends Comparable<? super T1>> int 
exponentialSearch(T1[] in, int from, int to, T2 find)
+    {
+        return exponentialSearch(in, from, to, find, Comparable::compareTo, 
Search.FAST);
+    }
+
+    // TODO: check inlining elides this
+    public enum Search { FLOOR, CEIL, FAST }
+
+    @Inline
+    public static <T1, T2> int exponentialSearch(T2[] in, int from, int to, T1 
find, AsymmetricComparator<T1, T2> comparator, Search op)
+    {
+        int step = 0;
+        loop: while (from + step < to)
+        {
+            int i = from + step;
+            int c = comparator.compare(find, in[i]);
+            if (c < 0)
+            {
+                to = i;
+                break;
+            }
+            if (c > 0)
+            {
+                from = i + 1;
+            }
+            else
+            {
+                switch (op)
+                {
+                    case FAST:
+                        return i;
+
+                    case CEIL:
+                        if (step == 0)
+                            return from;
+                        to = i + 1; // could in theory avoid one extra 
comparison in this case, but would uglify things
+                        break loop;
+
+                    case FLOOR:
+                        from = i;
+                }
+            }
+            step = step * 2 + 1; // jump in perfect binary search increments
+        }
+        return binarySearch(in, from, to, find, comparator, op);
+    }
+
+    @Inline
+    public static int exponentialSearch(int[] in, int from, int to, int find)

Review Comment:
   no code touches this



##########
accord-core/src/main/java/accord/primitives/Deps.java:
##########
@@ -0,0 +1,978 @@
+package accord.primitives;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+import com.google.common.base.Preconditions;
+
+import accord.api.Key;
+import accord.local.Command;
+import accord.local.CommandStore;
+import accord.utils.InlineHeap;
+import accord.utils.SortedArrays;
+
+import static accord.utils.SortedArrays.remap;
+import static accord.utils.SortedArrays.remapper;
+
+// TODO (now): switch to RoutingKey
+public class Deps implements Iterable<Map.Entry<Key, TxnId>>
+{
+    private static final TxnId[] NO_TXNIDS = new TxnId[0];
+    private static final int[] NO_INTS = new int[0];
+    public static final Deps NONE = new Deps(Keys.EMPTY, NO_TXNIDS, NO_INTS);
+
+    public static class Builder
+    {
+        final Keys keys;
+        final Map<TxnId, Integer> txnIdLookup = new HashMap<>(); // TODO: 
primitive map
+        TxnId[] txnIds = new TxnId[4];
+        final int[][] keysToTxnId;
+        final int[] keysToTxnIdCounts;
+
+        public Builder(Keys keys)
+        {
+            this.keys = keys;
+            this.keysToTxnId = new int[keys.size()][4];
+            this.keysToTxnIdCounts = new int[keys.size()];
+        }
+
+        public boolean isEmpty()
+        {
+            return Arrays.stream(keysToTxnIdCounts).allMatch(i -> i == 0);
+        }
+
+        public void add(Command command)
+        {
+            int idx = ensureTxnIdx(command.txnId());
+            keys.foldlIntersect(command.txn().keys, (li, ri, k, p, v) -> {
+                if (keysToTxnId[li].length == keysToTxnIdCounts[li])
+                    keysToTxnId[li] = Arrays.copyOf(keysToTxnId[li], 
keysToTxnId[li].length * 2);
+                keysToTxnId[li][keysToTxnIdCounts[li]++] = idx;
+                return 0;
+            }, 0, 0, 1);
+        }
+
+        public void add(Key key, TxnId txnId)
+        {
+            int txnIdx = ensureTxnIdx(txnId);
+            int keyIdx = keys.indexOf(key);
+            if (keysToTxnIdCounts[keyIdx] == keysToTxnId[keyIdx].length)
+                keysToTxnId[keyIdx] = Arrays.copyOf(keysToTxnId[keyIdx], 
Math.max(4, keysToTxnIdCounts[keyIdx] * 2));
+            keysToTxnId[keyIdx][keysToTxnIdCounts[keyIdx]++] = txnIdx;
+        }
+
+        public boolean contains(TxnId txnId)
+        {
+            return txnIdx(txnId) >= 0;
+        }
+
+        private int txnIdx(TxnId txnId)
+        {
+            return txnIdLookup.getOrDefault(txnId, -1);
+        }
+
+        private int ensureTxnIdx(TxnId txnId)
+        {
+            return txnIdLookup.computeIfAbsent(txnId, ignore -> {
+                if (txnIds.length == txnIdLookup.size())
+                    txnIds = Arrays.copyOf(txnIds, txnIds.length * 2);
+                return txnIdLookup.size();
+            });
+        }
+
+        public Deps build()
+        {
+            TxnId[] txnIds = txnIdLookup.keySet().toArray(TxnId[]::new);
+            Arrays.sort(txnIds, TxnId::compareTo);
+            int[] txnIdMap = new int[txnIds.length];
+            for (int i = 0 ; i < txnIdMap.length ; i++)
+                txnIdMap[txnIdLookup.get(txnIds[i])] = i;
+
+            int keyCount = 0;
+            int[] result; {
+                int count = 0;
+                for (int i = 0 ; i < keys.size() ; ++i)
+                {
+                    keyCount += keysToTxnIdCounts[i] > 0 ? 1 : 0;
+                    count += keysToTxnIdCounts[i];
+                }
+                result = new int[count + keyCount];
+            }
+
+            int keyIndex = 0;
+            int offset = keyCount;
+            for (int i = 0 ; i < keys.size() ; ++i)
+            {
+                if (keysToTxnIdCounts[i] > 0)
+                {
+                    int count = keysToTxnIdCounts[i];
+                    int[] src = keysToTxnId[i];
+                    for (int j = 0 ; j < count ; ++j)
+                        result[j + offset] = txnIdMap[src[j]];
+                    Arrays.sort(result, offset, count + offset);
+                    int dups = 0;
+                    for (int j = offset + 1 ; j < offset + count ; ++j)
+                    {
+                        if (result[j] == result[j - 1]) ++dups;
+                        else if (dups > 0) result[j - dups] = result[j];
+                    }
+                    result[keyIndex] = offset += count - dups;
+                    ++keyIndex;
+                }
+            }
+            if (offset < result.length)
+                result = Arrays.copyOf(result, offset);
+
+            Keys keys = this.keys;
+            if (keyCount < keys.size())
+            {
+                keyIndex = 0;
+                Key[] newKeys = new Key[keyCount];
+                for (int i = 0 ; i < keys.size() ; ++i)
+                {
+                    if (keysToTxnIdCounts[i] > 0)
+                        newKeys[keyIndex++] = keys.get(i);
+                }
+                keys = new Keys(newKeys);
+            }
+
+            return new Deps(keys, txnIds, result);
+        }
+    }
+
+    public static Builder builder(Keys keys)
+    {
+        return new Builder(keys);
+    }
+
+    static class MergeStream
+    {
+        final Deps source;
+        // TODO: could share backing array for all of these if we want, with 
an additional offset
+        final int[] input;
+        final int keyCount;
+        int[] remap; // TODO: use cached backing array
+        int[] keys; // TODO: use cached backing array
+        int keyIndex;
+        int index;
+        int endIndex;
+
+        MergeStream(Deps source)
+        {
+            this.source = source;
+            this.input = source.keyToTxnId;
+            this.keyCount = source.keys.size();
+        }
+
+        private void init(Keys keys, TxnId[] txnIds)
+        {
+            this.remap = remapper(source.txnIds, txnIds, true);
+            this.keys = source.keys.remapper(keys, true);
+            while (input[keyIndex] == keyCount)
+                ++keyIndex;
+            this.index = keyCount;
+            this.endIndex = input[keyIndex];
+        }
+    }
+
+    public static <T> Deps merge(Keys keys, List<T> merge, Function<T, Deps> 
getter)

Review Comment:
   Switching `Keys` to be 
   
   ```
   Keys keys = list.stream().map(d -> d.test.keys()).reduce(Keys.EMPTY, (l, r) 
-> Keys.union(l, r));
   ```
   
   gets `merge` to no longer fail, so was able to get my hacked up benchmarks 
(pushed to review/minimal-deps but should remove after review is done)
   
   ```
   Benchmark[name=merge(size=6, keys=279)] avg 131.05ms, allocated 5357 MiB
   Benchmark[name=builder forEach(size=6, keys=279)] avg 398.75ms, allocated 
9418 MiB
   Benchmark[name=merge(size=14, keys=300)] avg 555.55ms, allocated 16332 MiB
   Benchmark[name=builder forEach(size=14, keys=300)] avg 898.02ms, allocated 
18628 MiB
   Benchmark[name=merge(size=11, keys=320)] avg 343.21ms, allocated 11180 MiB
   Benchmark[name=builder forEach(size=11, keys=320)] avg 736.06ms, allocated 
15173 MiB
   Benchmark[name=merge(size=15, keys=342)] avg 508.55ms, allocated 16129 MiB
   Benchmark[name=builder forEach(size=15, keys=342)] avg 919.88ms, allocated 
19336 MiB
   Benchmark[name=merge(size=16, keys=355)] avg 689.76ms, allocated 22904 MiB
   Benchmark[name=builder forEach(size=16, keys=355)] avg 1,162.81ms, allocated 
22445 MiB
   ```
   
   I do see that if you are in a loop merging `Deps` non stop that `merge` is 
faster but the amount of allocated memory isn't that much smaller than the 
`Builder` way



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


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

Reply via email to