package org.aksw.combinatorics.algos;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.function.Consumer;
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.codepoetics.protonpack.functions.TriFunction;
/**
* kPermutationsOfN implementation with support for solution computation and early bailout should a solution
* turn out to be unsatisfiable.
* At the core, this implementation notifies clients about results via a callback during a recursion.
*
* A static utility method is available which collects results into a list and returns a stream.
* Hence, all valid permutations will be computed regardless of the number of items consumed from the stream.
* However, as this approach is about 5-10 times faster than the recursive stream solution,
* this approach is recommended.
*
*
* @author raven
*
* @param <A>
* @param <B>
* @param <S>
*/
public class StateCombinatoricCallback<A, B, S>
extends KPermutationsOfNCallbackBase<A, B, S>
{
protected LinkedListNode<B> remainingB;
public StateCombinatoricCallback(
// LinkedListNode<A> remainingA,
List<A> as,
LinkedListNode<B> remainingB,
TriFunction<S, A, B, Stream<S>> solutionCombiner)
{
super(as, solutionCombiner);
this.remainingB = remainingB;
this.solutionCombiner = solutionCombiner;
}
public static <A, B> Stream<CombinationStack<A, B, Void>> createKPermutationsOfN(
Collection<A> as,
Collection<B> bs) {
Void nil = null;
Stream<CombinationStack<A, B, Void>> result = createKPermutationsOfN(as, bs, nil, (s, a, b) -> Stream.of(nil));
return result;
}
/**
* This function will modify the collections.
* The collections will eventually contain the original items.
*
* @param as
* @param bs
*/
public static <A, B, S> Stream<CombinationStack<A, B, S>> createKPermutationsOfN(
Collection<A> as,
Collection<B> bs,
S baseSolution,
TriFunction<S, A, B, Stream<S>> solutionCombiner
) {
// LinkedListNode<A> nas = LinkedListNode.create(as);
List<A> xas = as instanceof List ? (List<A>)as : new ArrayList<>(as);
LinkedListNode<B> nbs = LinkedListNode.create(bs);
StateCombinatoricCallback<A, B, S> engine =
new StateCombinatoricCallback<A, B, S>(
xas,
nbs,
solutionCombiner);
Stream<CombinationStack<A, B, S>> result = engine.stream(baseSolution);
return result;
}
// [a b c] [1 2 3 4 5] -> [1, 2, 3], [1, 2, 4], [1, 2, 5], [1, 3, 4], ...
// public void nextA(S baseSolution, CombinationStack<A, B, S> stack) {
//
// // pick
// LinkedListNode<A> curr = remainingA.successor;
// if(!curr.isTail()) {
// LinkedListNode<A> pick = curr;
// A a = pick.data;
//
// pick.unlink();
//
// curr = pick.successor;
//
// // recurse
// nextB(i + 1, baseSolution, a, stack);
//
// // restore
// pick.relink();
// } else {
// completeMatch.accept(stack);
// }
// }
@Override
public void nextB(int i, S baseSolution, CombinationStack<A, B, S> stack, Consumer<CombinationStack<A, B, S>> completeMatch) {
if(i < as.size()) {
A a = as.get(i);
LinkedListNode<B> curr = remainingB.successor;
while(!curr.isTail()) {
LinkedListNode<B> pick = curr;
B b = pick.data;
pick.unlink();
curr = pick.successor;
Stream<S> partialSolutions = solutionCombiner.apply(baseSolution, a, b);
partialSolutions.forEach(partialSolution -> {
Combination<A, B, S> c = new Combination<>(a, b, partialSolution);
CombinationStack<A, B, S> newStack = new CombinationStack<>(stack, c);
// recurse
nextB(i + 1, partialSolution, newStack, completeMatch);
});
// restore
pick.relink();
}
} else {
completeMatch.accept(stack);
}
}
// public Stream<CombinationStack<A, B, S>> stream(S baseSolution) {
// Stream<CombinationStack<A, B, S>> result = as.isEmpty()
// ? Stream.empty()
// : streamNotWorking(0, baseSolution, null);
//
// return result;
// }
public static <A, B> Stream<CombinationStack<A, B, Void>> createKPermutationsOfN2(
Collection<A> as,
Collection<B> bs) {
Void nil = null;
Stream<CombinationStack<A, B, Void>> result = createKPermutationsOfN2(as, bs, nil, (s, a, b) -> Stream.of(nil));
return result;
}
public static <A, B, S> Stream<CombinationStack<A, B, S>> createKPermutationsOfN2(
Collection<A> as,
Collection<B> bs,
S baseSolution,
TriFunction<S, A, B, Stream<S>> solutionCombiner
) {
List<A> xas = as instanceof List ? (List<A>)as : new ArrayList<>(as);
LinkedListNode<B> nbs = LinkedListNode.create(bs);
List<CombinationStack<A, B, S>> list = new ArrayList<>();
StateCombinatoricCallback<A, B, S> engine =
new StateCombinatoricCallback<A, B, S>(
xas,
nbs,
solutionCombiner);
Stream<CombinationStack<A, B, S>> result = engine.stream(baseSolution);
return result;
}
public Stream<CombinationStack<A, B, S>> streamNotWorking(int i, S baseSolution, CombinationStack<A, B, S> stack) {
Stream<CombinationStack<A, B, S>> result;
if(i < as.size()) {
A a = as.get(i);
Iterable<LinkedListNode<B>> curr = () -> remainingB.nodeIterator();
result = StreamSupport.stream(curr.spliterator(), false)
.flatMap(pick -> {
B b = pick.data;
System.out.println("Depth: " + i + ": " + a + " - " + b + " - remaining bs: " + remainingB.size());
pick.unlink();
Stream<S> partialSolutions = solutionCombiner.apply(baseSolution, a, b);
Stream<CombinationStack<A, B, S>> s = partialSolutions.flatMap(partialSolution -> {
Combination<A, B, S> c = new Combination<>(a, b, partialSolution);
CombinationStack<A, B, S> newStack = new CombinationStack<>(stack, c);
// recurse
Stream<CombinationStack<A, B, S>> t = streamNotWorking(i + 1, partialSolution, newStack);
t = StreamUtils.appendAction(t, () -> pick.relink());
return t;
});
return s;
});
} else {
System.out.println("Done - with stack " + stack);
result = Stream.of(stack);
}
// closeAction.run();
return result;
}
public static void main(String[] args) {
List<String> as = new ArrayList<String>(Arrays.asList("a", "b", "c"));
List<Integer> bs = new ArrayList<Integer>(Arrays.asList(1, 2, 3, 4, 5));
createKPermutationsOfN(as, bs);
}
// This is too excessive - kept for reference;)
// public void nextAWithAllPermutationsOfA(S baseSolution) {
//
// // pick
// LinkedListNode<A> curr = remainingA.successor;
//// System.out.println("as: " + curr);
// while(!curr.isTail()) {
// LinkedListNode<A> pick = curr;
// A a = pick.data;
//
// pick.unlink();
//
// curr = pick.successor;
// //System.out.println("as unlink: " + remainingA + "; " + a);
//
// // recurse
// nextB(baseSolution, a, stack);
//
// // restore
// pick.relink();
// //System.out.println("as relink: " + remainingA + "; " + a);
// }
//
// if(remainingA.successor.isTail()) {
// //completeMatch.accept(stack, baseSolution);
// }
// }
}