Skip to content

Commit

Permalink
ofCrossProduct renamed to cartesianProduct/cartesianPower; more
Browse files Browse the repository at this point in the history
corner-case tests; javaDoc, changes
  • Loading branch information
amaembo committed Sep 12, 2015
1 parent 1dc215b commit cb38a4b
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 24 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Expand Up @@ -6,6 +6,7 @@
* Added `MoreCollectors.distinctBy` collector.
* Added `StreamEx/EntryStream.distinct(keyExtractor)` intermediate operation.
* Added `EntryStream.distinctKeys/distinctValues` intermediate operations.
* Added `StreamEx.cartesianPower/cartesianProduct` static methods.
* Optimized: `MoreCollectors.least/greatest` collectors are now much faster (up to 10x depending on input).

### 0.3.7
Expand Down
26 changes: 14 additions & 12 deletions src/main/java/javax/util/streamex/CrossSpliterator.java
Expand Up @@ -116,18 +116,20 @@ public Spliterator<List<T>> trySplit() {
return trySplit();
}
long prefixEst = Long.MAX_VALUE;
long newEst = Long.MAX_VALUE;
if (est < Long.MAX_VALUE) {
newEst = spliterators[splitPos].getExactSizeIfKnown();
try {
for (int i = splitPos + 1; i < collections.length; i++) {
newEst = StrictMath.multiplyExact(newEst,
collections[i].size());
}
prefixEst = est - newEst;
} catch (ArithmeticException e) {
newEst = Long.MAX_VALUE;
}
long newEst = spliterators[splitPos].getExactSizeIfKnown();
if(newEst == -1) {
newEst = Long.MAX_VALUE;
} else {
try {
for (int i = splitPos + 1; i < collections.length; i++) {
newEst = StrictMath.multiplyExact(newEst,
collections[i].size());
}
if(est != Long.MAX_VALUE)
prefixEst = est - newEst;
} catch (ArithmeticException e) {
newEst = Long.MAX_VALUE;
}
}
Spliterator<T>[] prefixSpliterators = spliterators.clone();
Collection<T>[] prefixCollections = collections.clone();
Expand Down
71 changes: 63 additions & 8 deletions src/main/java/javax/util/streamex/StreamEx.java
Expand Up @@ -2059,16 +2059,71 @@ public static <T> StreamEx<List<T>> ofSubLists(List<T> source, int length, int s
return StreamEx.empty();
return of(new RangeBasedSpliterator.OfSubLists<>(source, length, shift));
}

public static <T> StreamEx<List<T>> ofCrossProduct(Collection<? extends Collection<T>> source) {
if(source.isEmpty())
return StreamEx.of(Collections.emptyList());

/**
* Returns a new {@code StreamEx} which elements are {@link List} objects
* containing all possible tuples of the elements of supplied collection of
* collections. The whole stream forms an n-fold Cartesian product (or
* cross-product) of the input collections.
*
* <p>
* Every stream element is the {@code List} of the same size as supplied
* collection. The first element in the list is taken from the first
* collection which appears in source and so on. The elements are ordered
* lexicographically according to the order of the input collections.
*
* <p>
* There are no guarantees on the type, mutability, serializability, or
* thread-safety of the {@code List} elements. It's however guaranteed that
* each element is the distinct object.
*
* <p>
* The supplied collection is assumed to be unchanged during the operation.
*
* @param source
* the input collection of collections which is used to generate
* the cross-product.
* @return the new stream of lists.
* @see #cartesianPower(int, Collection)
* @since 0.3.8
*/
public static <T> StreamEx<List<T>> cartesianProduct(Collection<? extends Collection<T>> source) {
if (source.isEmpty())
return of(Stream.of(Collections.emptyList()));
return of(new CrossSpliterator<>(source));
}

public static <T> StreamEx<List<T>> ofCrossProduct(int n, Collection<T> source) {
if(n == 0)
return StreamEx.of(Collections.emptyList());

/**
* Returns a new {@code StreamEx} which elements are {@link List} objects
* containing all possible n-tuples of the elements of supplied collection.
* The whole stream forms an n-fold Cartesian product of input collection
* with itself or n-ary Cartesian power of the input collection.
*
* <p>
* Every stream element is the {@code List} of the supplied size. The
* elements are ordered lexicographically according to the order of the
* input collection.
*
* <p>
* There are no guarantees on the type, mutability, serializability, or
* thread-safety of the {@code List} elements. It's however guaranteed that
* each element is the distinct object.
*
* <p>
* The supplied collection is assumed to be unchanged during the operation.
*
* @param n
* the size of the {@code List} elements of the resulting stream.
* @param source
* the input collection of collections which is used to generate
* the Cartesian power.
* @return the new stream of lists.
* @see #cartesianProduct(Collection)
* @since 0.3.8
*/
public static <T> StreamEx<List<T>> cartesianPower(int n, Collection<T> source) {
if (n == 0)
return of(Stream.of(Collections.emptyList()));
return of(new CrossSpliterator<>(Collections.nCopies(n, source)));
}
}
20 changes: 20 additions & 0 deletions src/test/java/javax/util/streamex/CrossSpliteratorTest.java
Expand Up @@ -16,10 +16,14 @@
package javax.util.streamex;

import static javax.util.streamex.TestHelpers.*;
import static org.junit.Assert.*;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Spliterator;

import org.junit.Test;

/**
Expand All @@ -39,4 +43,20 @@ public void testCross() {
checkSpliterator("cross", expected, () -> new CrossSpliterator<>(input));
}
}

@Test
public void testBigSize() {
List<List<Integer>> input = new ArrayList<>();
input.add(IntStreamEx.rangeClosed(1, 20).boxed().toList());
input.addAll(Collections.nCopies(18, IntStreamEx.rangeClosed(1, 10).boxed().toList()));
Spliterator<List<Integer>> spltr = new CrossSpliterator<>(input);
assertFalse(spltr.hasCharacteristics(Spliterator.SIZED));
assertEquals(Long.MAX_VALUE, spltr.estimateSize());
spltr.trySplit();
assertFalse(spltr.hasCharacteristics(Spliterator.SIZED));
assertEquals(Long.MAX_VALUE, spltr.estimateSize());
spltr.trySplit();
assertTrue(spltr.hasCharacteristics(Spliterator.SIZED));
assertEquals(5_000_000_000_000_000_000L, spltr.estimateSize());
}
}
15 changes: 11 additions & 4 deletions src/test/java/javax/util/streamex/StreamExTest.java
Expand Up @@ -1317,21 +1317,28 @@ public void testToFlatCollection() {
}

@Test
public void testOfCrossProduct() {
public void testCartesian() {
List<List<Integer>> expected = IntStreamEx.range(32)
.mapToObj(i -> IntStreamEx.range(5).mapToObj(n -> (i >> (4-n)) & 1).toList())
.toList();
for(StreamExSupplier<List<Integer>> supplier : streamEx(() -> StreamEx.ofCrossProduct(5, Arrays.asList(0, 1)))) {
for(StreamExSupplier<List<Integer>> supplier : streamEx(() -> StreamEx.cartesianPower(5, Arrays.asList(0, 1)))) {
assertEquals(supplier.toString(), expected, supplier.get().toList());
}
List<List<Integer>> input2 = Arrays.asList(Arrays.asList(1, 2, 3), Arrays.asList(), Arrays.asList(4, 5, 6));
for(StreamExSupplier<List<Integer>> supplier : streamEx(() -> StreamEx.ofCrossProduct(input2))) {
for(StreamExSupplier<List<Integer>> supplier : streamEx(() -> StreamEx.cartesianProduct(input2))) {
assertFalse(supplier.toString(), supplier.get().findAny().isPresent());
}
List<List<Integer>> input3 = Arrays.asList(Arrays.asList(1, 2), Arrays.asList(3), Arrays.asList(4, 5));
for(StreamExSupplier<List<Integer>> supplier : streamEx(() -> StreamEx.ofCrossProduct(input3))) {
for(StreamExSupplier<List<Integer>> supplier : streamEx(() -> StreamEx.cartesianProduct(input3))) {
assertEquals(supplier.toString(), "[1, 3, 4],[1, 3, 5],[2, 3, 4],[2, 3, 5]", supplier.get().joining(","));
}
Set<Integer> input4 = IntStreamEx.range(10).boxed().toCollection(TreeSet::new);
for(StreamExSupplier<List<Integer>> supplier : streamEx(() -> StreamEx.cartesianPower(3, input4))) {
assertEquals(supplier.toString(), IntStreamEx.range(1000).boxed().toList(),
supplier.get().map(list -> list.get(0) * 100 + list.get(1) * 10 + list.get(2)).toList());
}
assertEquals(Arrays.asList(Collections.emptyList()), StreamEx.cartesianProduct(Collections.emptyList()).toList());
assertEquals(Arrays.asList(Collections.emptyList()), StreamEx.cartesianPower(0, Arrays.asList(1,2,3)).toList());
}

@Test
Expand Down

0 comments on commit cb38a4b

Please sign in to comment.