package org.aksw.combinatorics.algos;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.function.BiFunction;
import java.util.function.BinaryOperator;
import java.util.function.Predicate;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import org.aksw.combinatorics.collections.Combination;
import org.aksw.combinatorics.collections.CombinationStack;
import org.aksw.commons.collections.lists.LinkedListNode;
import org.aksw.commons.collections.utils.StreamUtils;
import com.google.common.base.Stopwatch;
public class StateCombinatoricStreamSlow<A, B, S> {
//protected Stack<Entry<A, B>> stack = new Stack<Entry<A, B>>();
protected LinkedListNode<A> remainingA;
protected LinkedListNode<B> remainingB;
protected BiFunction<A, B, S> computeSolutionContribution;
protected BinaryOperator<S> solutionCombiner;
protected Predicate<S> isUnsatisfiable;
//protected BiConsumer<Stack<Entry<A, B>>, S> completeMatch;
public StateCombinatoricStreamSlow(
LinkedListNode<A> remainingA,
LinkedListNode<B> remainingB,
BiFunction<A, B, S> computeSolutionContribution,
BinaryOperator<S> solutionCombiner,
Predicate<S> isUnsatisfiable)
{
super();
this.remainingA = remainingA;
this.remainingB = remainingB;
this.computeSolutionContribution = computeSolutionContribution;
this.solutionCombiner = solutionCombiner;
this.isUnsatisfiable = isUnsatisfiable;
}
public static <A, B> Stream<CombinationStack<A, B, Integer>> createKPermutationsOfN(Collection<A> ks, Collection<B> ns) {
LinkedListNode<A> nas = LinkedListNode.create(ks);
LinkedListNode<B> nbs = LinkedListNode.create(ns);
//int[] i = new int[]{0};
StateCombinatoricStreamSlow<A, B, Integer> runner =
new StateCombinatoricStreamSlow<A, B, Integer>(
nas,
nbs,
(a, b) -> 0,
(a, b) -> 0,
(s) -> false);
Stream<CombinationStack<A, B, Integer>> result = runner.stream(0);
return result;
}
public Stream<CombinationStack<A, B, S>> stream(S baseSolution) {
Stream<CombinationStack<A, B, S>> result = streamA(baseSolution, null);
return result;
}
// [a b c] [1 2 3 4 5] -> [1, 2, 3], [1, 2, 4], [1, 2, 5], [1, 3, 4], ...
//Stream<Stack<Entry<A, B>>>
public Stream<CombinationStack<A, B, S>> streamA(S baseSolution, CombinationStack<A, B, S> stack) {
Iterator<Stream<CombinationStack<A, B, S>>> it = new Iterator<Stream<CombinationStack<A, B, S>>>() {
LinkedListNode<A> curr = remainingA.successor;
boolean next = true;
@Override
public boolean hasNext() {
return next;
}
@Override
public Stream<CombinationStack<A, B, S>> next() {
next = false;
Stream<CombinationStack<A, B, S>> r;
if(!curr.isTail()) {
LinkedListNode<A> pick = curr;
A a = pick.data;
pick.unlink();
curr = pick.successor;
//System.out.println("as unlink: " + remainingA + "; " + a);
// recurse
r = nextB(baseSolution, a, stack);
r = StreamUtils.appendAction(r, () -> {
// restore
pick.relink();
});
} else {
//@SuppressWarnings("unchecked")
//Stack<Entry<A, B>> copy = (Stack<Entry<A, B>>)stack.clone();
//r = Stream.of(new Combination<A, B, S>(copy, baseSolution));
r = Stream.of(stack);
}
return r;
//System.out.println("picked " + a);
}
};
Iterable<Stream<CombinationStack<A, B, S>>> iterable = () -> it;
Stream<CombinationStack<A, B, S>> result = StreamSupport
.stream(iterable.spliterator(), false)
.flatMap(x -> x);
return result;
}
public Stream<CombinationStack<A, B, S>> nextB(S baseSolution, A a, CombinationStack<A, B, S> stack) {
Stream<CombinationStack<A, B, S>> result;
Iterator<Stream<CombinationStack<A, B, S>>> it = new Iterator<Stream<CombinationStack<A, B, S>>>() {
LinkedListNode<B> curr = remainingB.successor;
@Override
public boolean hasNext() {
boolean r = !curr.isTail();
return r;
}
@Override
public Stream<CombinationStack<A, B, S>> next() {
Stream<CombinationStack<A, B, S>> r = null;
LinkedListNode<B> pick = curr;
B b = pick.data;
pick.unlink();
curr = pick.successor;
S solutionContribution = computeSolutionContribution.apply(a, b);
S combined = null;
boolean unsatisfiable;
unsatisfiable = isUnsatisfiable.test(solutionContribution);
if(!unsatisfiable) {
combined = solutionCombiner.apply(baseSolution, solutionContribution);
unsatisfiable = isUnsatisfiable.test(combined);
}
if(!unsatisfiable) {
Combination<A, B, S> c = new Combination<>(a, b, combined);
//stack.push(new SimpleEntry<>(a, b));
CombinationStack<A, B, S> newStack = new CombinationStack<>(stack, c);
//System.out.println("push: " + stack);
// recurse
r = streamA(combined, newStack);
r = StreamUtils.appendAction(r, () -> {
// restore
pick.relink();
});
} else {
r = Stream.empty();
}
return r;
}
};
Iterable<Stream<CombinationStack<A, B, S>>> iterable = () -> it;
result = StreamSupport.stream(iterable.spliterator(), false).flatMap(x -> x);
return result;
}
public static void main(String[] args) {
// List<String> as = new ArrayList<String>(Arrays.asList("a", "b", "c", "d", "e", "f", "g"));
// List<Integer> bs = new ArrayList<Integer>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9));
List<String> as = new ArrayList<String>(Arrays.asList("a", "b", "c", "d", "e", "f"));
List<Integer> bs = new ArrayList<Integer>(Arrays.asList(1, 2, 3, 4, 5, 6));
// Collection<String> strs = new ArrayList<String>();
for(int i = 0; i < 1000; ++i) {
Stopwatch sw = Stopwatch.createStarted();
Stream<?> stream = null;
//stream = StateCombinatoricCallback.createKPermutationsOfN(as, bs);
//System.out.println("huh");
stream = createKPermutationsOfN(as, bs);
//IntStream stream = IntStream.range(0, 181440000);
//stream = IntStream.range(0, 181440).mapToObj(x -> "mystr" + x);
long count = stream != null ? stream.count() : 0;
System.out.println("Time taken for " + count + " items: " + sw.stop().elapsed(TimeUnit.MILLISECONDS));
}
// com.codepoetics.protonpack.StreamUtils.zipWithIndex(stream).forEach(
// y -> System.out.println(y.getIndex() + " - " + y.getValue()));
}
}