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.function.BiFunction;
import java.util.function.BinaryOperator;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Stream;
import org.aksw.combinatorics.collections.Combination;
import org.aksw.combinatorics.collections.CombinationStack;
import com.codepoetics.protonpack.functions.TriFunction;
/**
* Cartesian product generation with support for solution computation and early bail out
* on unsatisfiable solutions
*
* @author raven
*
* @param <A>
* @param <B>
*/
public class StateCartesian<A, B, S> {
/**
* The purpose of the lookup function is, that given
* an item of A together with the so-far found solution,
* retrieve a set of candidates for further processing
*
* In the simplest case, the lookup function returns always
* an iterator over the same collection of B items.
*
*/
//protected Iterator<A> itA;
protected List<A> as;
// cached size of as
protected int asSize;
// TODO We could add a function which allows reordering the items of as
// after choosing an item b (returned by the lookup function)
/**
* Lookup function that for a given item of A, the partial solution (the stack)
* yields the candidate items of B for the given solution contribution S.
*/
protected TriFunction<A, S, CombinationStack<A, B, S>, Iterator<B>> lookupB;
protected BiFunction<A, B, S> computeSolutionContribution;
protected BinaryOperator<S> solutionCombiner;
protected Predicate<S> isUnsatisfiable;
protected Consumer<CombinationStack<A, B, S>> completeMatch;
public StateCartesian(
List<A> as,
TriFunction<A, S, CombinationStack<A, B, S>, Iterator<B>> lookupB,
BiFunction<A, B, S> computeSolutionContribution,
BinaryOperator<S> solutionCombiner,
Predicate<S> isUnsatisfiable,
Consumer<CombinationStack<A, B, S>> completeMatch)
{
super();
this.as = as;
this.lookupB = lookupB;
this.computeSolutionContribution = computeSolutionContribution;
this.solutionCombiner = solutionCombiner;
this.isUnsatisfiable = isUnsatisfiable;
this.completeMatch = completeMatch;
asSize = as.size();
}
public void run(S baseSolution) {
nextA(baseSolution, 0, null);
}
public void nextA(S baseSolution, int ia, CombinationStack<A, B, S> stack) {
if(ia >= asSize) {
completeMatch.accept(stack);
} else {
A a = as.get(ia);
Iterator<B> itB = lookupB.apply(a, baseSolution, stack);
while(itB.hasNext()) {
B b = itB.next();
S contribution = computeSolutionContribution.apply(a, b);
S combination = solutionCombiner.apply(baseSolution, contribution);
boolean unsatisfiable = isUnsatisfiable.test(combination);
if(!unsatisfiable) {
Combination<A, B, S> c = new Combination<>(a, b, combination);
CombinationStack<A, B, S> newStack = new CombinationStack<>(stack, c);
nextA(combination, ia + 1, newStack);
}
}
}
}
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));
createCartesian(as, bs).forEach(item -> {
System.out.println(item);
});
}
public static <A, B> Stream<CombinationStack<A, B, Void>> createCartesian(
Collection<A> as,
Collection<B> bs) {
Void nil = null;
Stream<CombinationStack<A, B, Void>> result = createCartesian(as, bs, nil, (k, n) -> nil, (sa, sb) -> nil, (s) -> false);
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>> createCartesian(
Collection<A> as,
Collection<B> bs,
S baseSolution,
BiFunction<A, B, S> computeSolutionContribution,
BinaryOperator<S> solutionCombiner,
Predicate<S> isUnsatisfiable
) {
List<A> las = as instanceof List ? (List<A>)as : new ArrayList<>(as);
// List<A> las = new ArrayList<>(as);
//
// // A little trick for the nested stack to hold items of A in the specified order
// // but probably not really needed; also the alternation of Bs is still reverse order
//[(a, 1; null), (b, 1; null), (c, 1; null)]
//[(a, 2; null), (b, 1; null), (c, 1; null)]
// Collections.reverse(las);
TriFunction<A, S, CombinationStack<A, B, S>, Iterator<B>> lookupB = (a, s, stack) -> bs.iterator();
List<CombinationStack<A, B, S>> list = new ArrayList<>();
StateCartesian<A, B, S> runner =
new StateCartesian<A, B, S>(
las,
lookupB,
computeSolutionContribution,
solutionCombiner,
isUnsatisfiable,
(stack) -> {
list.add(stack);
}); //System.out.println("MATCH: " + (++i[0]) + stack));
runner.run(baseSolution);
Stream<CombinationStack<A, B, S>> result = list.stream();
return result;
}
}