net.jqwik.engine.properties.shrinking.ShrinkingCommons Maven / Gradle / Ivy
package net.jqwik.engine.properties.shrinking;
import java.util.*;
import java.util.function.*;
import java.util.stream.*;
import net.jqwik.api.*;
import net.jqwik.engine.support.*;
import org.jspecify.annotations.*;
import static net.jqwik.engine.properties.UniquenessChecker.*;
class ShrinkingCommons {
@FunctionalInterface
interface ContainerCreator extends Function>, Shrinkable> {}
/**
* Shrink elements of a container pairwise
*
* @param elements container elements
* @param createContainer function to create shrinkable container
* @param type of container
* @param type of elements
* @return stream of shrunk containers
*/
static Stream> shrinkPairsOfElements(List extends Shrinkable> elements, ContainerCreator createContainer) {
return Combinatorics
.distinctPairs(elements.size())
.flatMap(pair -> JqwikStreamSupport.zip(
elements.get(pair.get1()).shrink(),
elements.get(pair.get2()).shrink(),
(Shrinkable s1, Shrinkable s2) -> {
List> newElements = new ArrayList<>(elements);
newElements.set(pair.get1(), s1);
newElements.set(pair.get2(), s2);
return createContainer.apply(newElements);
}
));
}
/**
* Sort elements of container pairwise
*
* @param elements Unsorted elements
* @param createContainer function to create shrinkable container
* @param type of container
* @param type of elements
* @return stream of shrunk containers
*/
static Stream> sortElements(List extends Shrinkable> elements, ContainerCreator createContainer) {
List> sortedElements = new ArrayList<>(elements);
sortedElements.sort(Comparator.comparing(Shrinkable::distance));
if (elements.equals(sortedElements)) {
return Stream.empty();
}
return JqwikStreamSupport.concat(
fullSort(sortedElements, createContainer),
pairwiseSort(elements, createContainer)
);
}
private static Stream> fullSort(List extends Shrinkable> sortedElements, ContainerCreator createContainer) {
return Stream.of(createContainer.apply(sortedElements));
}
private static Stream> pairwiseSort(List extends Shrinkable> elements, ContainerCreator createContainer) {
return Combinatorics.distinctPairs(elements.size())
.map(pair -> {
int firstIndex = Math.min(pair.get1(), pair.get2());
int secondIndex = Math.max(pair.get1(), pair.get2());
Shrinkable first = elements.get(firstIndex);
Shrinkable second = elements.get(secondIndex);
return Tuple.of(firstIndex, first, secondIndex, second);
})
.filter(quadruple -> quadruple.get2().compareTo(quadruple.get4()) > 0)
.map(quadruple -> {
List> pairSwap = new ArrayList<>(elements);
pairSwap.set(quadruple.get1(), quadruple.get4());
pairSwap.set(quadruple.get3(), quadruple.get2());
return createContainer.apply(pairSwap);
});
}
}