diff --git a/primitiveCollections/src/main/java/org/lucares/collections/LongList.java b/primitiveCollections/src/main/java/org/lucares/collections/LongList.java index f3fbdb7..e705766 100644 --- a/primitiveCollections/src/main/java/org/lucares/collections/LongList.java +++ b/primitiveCollections/src/main/java/org/lucares/collections/LongList.java @@ -1,8 +1,10 @@ package org.lucares.collections; import java.io.Serializable; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Iterator; import java.util.List; import java.util.Random; import java.util.Spliterator.OfLong; @@ -41,7 +43,7 @@ public final class LongList implements Serializable, Cloneable { * Keeps track of whether or not the list is sorted. This allows us to use * binary search for {@link #indexOf(int)} and efficient algorithms for * {@link #intersection(LongList, LongList)} / - * {@link #union(LongList, LongList)} / {@link #uniq()} / + * {@link #unionInternal(LongList, LongList)} / {@link #uniq()} / * {@link #removeAll(int, int)}. An empty list is sorted. */ private boolean sorted = true; @@ -121,7 +123,7 @@ public final class LongList implements Serializable, Cloneable { * * @param startInclusive the lower bound (inclusive) * @param endInclusive the upper bound (inclusive) - * @return the {@link IntList} + * @return the {@link LongList} */ public static LongList rangeClosed(final long startInclusive, final long endInclusive) { if (startInclusive > endInclusive) { @@ -137,6 +139,32 @@ public final class LongList implements Serializable, Cloneable { } } + /** + * Returns a new list with the values from the given start index to the end of + * the list. + * + * @param startInclusive start index + * @return {@code LongList} + */ + public LongList sublist(final int startInclusive) { + return sublist(startInclusive, size); + } + + /** + * Returns a new list with the values of the given range. + * + * @param startInclusive the start index + * @param endExclusive the end index (exclusive) + * @return {@link LongList} + */ + public LongList sublist(final int startInclusive, int endExclusive) { + final LongList result = new LongList(endExclusive - startInclusive); + result.data = Arrays.copyOfRange(data, startInclusive, endExclusive); + result.size = result.data.length; + result.sorted = sorted; + return result; + } + /** * Returns {@code true} if this list contains no elements. * @@ -1114,27 +1142,32 @@ public final class LongList implements Serializable, Cloneable { } /** - * Returns a list with all elements that are in list {@code a} or {@code b} - * (logical or). + * Returns a list with all elements that are in list {@code a} or {@code b} or + * ... or {@code n} (logical or). *
* The result does not contain duplicate values. *
- * If both lists were sorted, then the output list will also be sorted. If at + * If all lists were sorted, then the output list will also be sorted. If at * least one list is unsorted, then the order is undefined. *
- * If both lists are sorted, then the time complexity is O(n+m), where n is the
- * length of the first list and m the length of the second list. If at least one
- * list is not sorted, then the time complexity is O(m*log(m)), where m is the
- * length of the longer list.
+ * TODO check time complexity If all lists are sorted, then the time complexity
+ * is O(n+m), where n is the length of the first list and m the length of the
+ * second list. If at least one list is not sorted, then the time complexity is
+ * O(m*log(m)), where m is the length of the longer list.
*
- * @param a the first list
- * @param b the second list
+ * @param longLists the lists
* @return the union of both lists
*/
- public static LongList union(final LongList a, final LongList b) {
+ public static LongList union(final LongList... longLists) {
+ return union(List.of(longLists));
+ }
+
+ private static LongList unionInternal(final LongList a, final LongList b) {
final LongList result;
- if (a.isSorted() && b.isSorted()) {
+ if (a.isEmpty() && b.isEmpty()) {
+ result = new LongList();
+ } else if (a.isSorted() && b.isSorted()) {
result = unionSorted(a, b);
} else {
result = unionUnsorted(a, b);
@@ -1142,6 +1175,35 @@ public final class LongList implements Serializable, Cloneable {
return result;
}
+ public static LongList union(final Collection
+ * 7
+ * 3
+ * 8
+ * 1
+ * 9
+ * 4
+ * 10
+ * 0
+ * 11
+ * 5
+ * 12
+ * 2
+ * 13
+ * 6
+ * 14
+ *
+ */
+ private void init() {
+ // fill leaf nodes
+ int offset = firstLeafIndex;
+ for (int j = 0; j < size; j++) {
+ final LongQueue q = longQueues.get(j);
+ heap[offset + j] = q.isEmpty() ? UNSET : q.pop();
+ }
+
+ // fill the non-leaf layers (from the leafs up to the root)
+ while (offset > 0) {
+ offset /= 2; //
+ for (int i = offset; i <= offset * 2; i++) {
+ fillWithMinOfChildren(i);
+ }
+ }
+ }
+
+ private int leftChildIndex(int i) {
+ return i * 2 + 1;
+ }
+
+ private int rightChildIndex(int i) {
+ return i * 2 + 2;
+ }
+
+ private boolean isLeaf(int i) {
+ return i >= firstLeafIndex;
+ }
+
+ private int leafIndexToListIndex(int i) {
+ assert isLeaf(i) : "index " + i + " is not a leaf";
+ return i - firstLeafIndex;
+ }
+
+ private void fillWithMinOfChildren(int index) {
+ final int leftChildIndex = index * 2 + 1; //leftChildIndex(index);
+ final int rightChildIndex = leftChildIndex+1;//rightChildIndex(index);
+
+ final long valueOfLeftChild = heap[leftChildIndex];
+ final long valueOfRightChild = heap[rightChildIndex];
+
+ final int chosenValue;
+
+ if (valueOfLeftChild == UNSET) {
+ if (valueOfRightChild == UNSET) {
+ heap[index] = UNSET;
+ return;
+ } else {
+ //left < right
+ heap[index] = valueOfRightChild;
+ chosenValue = rightChildIndex;
+ }
+ } else if (valueOfRightChild == UNSET) {
+ // left > right
+ heap[index] = valueOfLeftChild;
+ chosenValue = leftChildIndex;
+ } else {
+ if (valueOfLeftChild < valueOfRightChild) {
+ // left < right
+ heap[index] = valueOfLeftChild;
+ chosenValue = leftChildIndex;
+ } else {
+ // left >= right
+ heap[index] = valueOfRightChild;
+ chosenValue = rightChildIndex;
+ }
+ }
+
+ refillValue(chosenValue);
+ }
+
+ private void refillValue(int index) {
+ if (isLeaf(index)) {
+ final int listIndex = index - firstLeafIndex; //leafIndexToListIndex(index);
+ final LongQueue queue = longQueues.get(listIndex);
+ heap[index] = queue.isEmpty() ? UNSET : queue.pop();
+ return;
+ }
+ fillWithMinOfChildren(index);
+ }
+ }
+
+ public static void main(String[] args) {
+ for (int i = 0; i < 20; i++) {
+ System.out.println(i + " " + (Long.highestOneBit(i - 1) << 1));
+ }
+ }
+}
diff --git a/primitiveCollections/src/test/java/org/lucares/collections/LongListTest.java b/primitiveCollections/src/test/java/org/lucares/collections/LongListTest.java
index 1fc3c6f..467a59a 100644
--- a/primitiveCollections/src/test/java/org/lucares/collections/LongListTest.java
+++ b/primitiveCollections/src/test/java/org/lucares/collections/LongListTest.java
@@ -1613,6 +1613,31 @@ public class LongListTest {
Assertions.assertEquals(LongList.of(2), actual);
Assertions.assertEquals(LongList.union(a, b), LongList.union(b, a));
}
+
+ @Test
+ public void testUnionSortedLists_three() {
+ final LongList a = LongList.of(1, 2, 3);
+ final LongList b = LongList.of(2, 4, 6);
+ final LongList c = LongList.of(3, 5, 7);
+
+ final LongList actual = LongList.union(a, b, c);
+ Assertions.assertEquals(LongList.of(1,2,3,4,5,6,7), actual);
+ Assertions.assertEquals(LongList.union(a, b, c), LongList.union(b, c, a));
+ Assertions.assertEquals(LongList.union(a, b, c), LongList.union(b, a, c));
+ }
+
+ @Test
+ public void testUnionSortedLists_four_LongMinValue() {
+ final LongList a = LongList.of(Long.MIN_VALUE, 2, 3,Long.MAX_VALUE);
+ final LongList b = LongList.of(2, 4, 6, Long.MAX_VALUE);
+ final LongList c = LongList.of(Long.MIN_VALUE, 5, 7);
+ final LongList d = LongList.of(Long.MIN_VALUE, Long.MIN_VALUE);
+
+ final LongList actual = LongList.union(a, b, c, d);
+ Assertions.assertEquals(LongList.of(Long.MIN_VALUE,2,3,4,5,6,7, Long.MAX_VALUE), actual);
+ Assertions.assertEquals(LongList.union(a, b, c, d), LongList.union(b, c, a, d));
+ Assertions.assertEquals(LongList.union(a, b, c, d), LongList.union(d, b, a, c));
+ }
@Test
public void testUnionUnsortedLists() {
diff --git a/primitiveCollections/src/test/java/org/lucares/collections/MultiwayLongMergerTest.java b/primitiveCollections/src/test/java/org/lucares/collections/MultiwayLongMergerTest.java
new file mode 100644
index 0000000..d96ddca
--- /dev/null
+++ b/primitiveCollections/src/test/java/org/lucares/collections/MultiwayLongMergerTest.java
@@ -0,0 +1,59 @@
+package org.lucares.collections;
+
+import java.util.Arrays;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class MultiwayLongMergerTest {
+
+ @Test
+ public void testMergeTwoLists() {
+
+ LongList a = LongList.of(1,2,3);
+ LongList b = LongList.of(1,3,5);
+ LongList expected = LongList.of(1,2,3,5);
+
+
+ LongList union = MultiwayLongMerger.unionSorted(Arrays.asList(a,b));
+ Assertions.assertEquals(expected, union);
+ }
+
+ @Test
+ public void testMergeThreeLists() {
+
+ LongList a = LongList.of(1,2,3);
+ LongList b = LongList.of(1,3,5);
+ LongList c = LongList.of(2,3,5);
+ LongList expected = LongList.of(1,2,3,5);
+
+
+ LongList union = MultiwayLongMerger.unionSorted(Arrays.asList(a,b,c));
+ Assertions.assertEquals(expected, union);
+ }
+
+ @Test
+ public void testMergeListsWithLongMin() {
+
+ LongList a = LongList.of(Long.MIN_VALUE,2,3);
+ LongList b = LongList.of(1,3,5);
+ LongList c = LongList.of(Long.MIN_VALUE,Long.MIN_VALUE);
+ LongList expected = LongList.of(Long.MIN_VALUE,1,2,3,5);
+
+
+ LongList union = MultiwayLongMerger.unionSorted(Arrays.asList(a,b,c));
+ Assertions.assertEquals(expected, union);
+ }
+
+ @Test
+ public void testMergeEmptyLists() {
+
+ LongList a = LongList.of();
+ LongList b = LongList.of();
+ LongList expected = LongList.of();
+
+
+ LongList union = MultiwayLongMerger.unionSorted(Arrays.asList(a,b));
+ Assertions.assertEquals(expected, union);
+ }
+}