package org.aksw.jena_sparql_api.util;
import java.util.function.Function;
import java.util.stream.Stream;
public class RewriteUtils {
public static <T> T transformUntilNoChange(T after, Function<T, T> fn) {
T before;
do {
before = after;
after = fn.apply(before);
} while(!before.equals(after));
return after;
}
// TODO The following approaches could also be seen as reductions:
// listCandidates returns a stream of reduction rules that are applicable to the baseItem
// applyCandidate then applies the reduction rule
// It should not happen that the exact same reduction occurs more than once (otherwise we may not terminate)
// We could relax this rule that the baseItems must occur more than once
public static <T, C> Stream<T> exhaustiveRewrite(
T baseItem,
Function<T, Stream<T>> reductions
//Function<T, Stream<C>> listCandidates,
//BiFunction<T, C, T> applyCandidate,
//Collection<C> usedCandidates
) {
// Terminal items are those that do not have (further) candidates
Stream<T> result = Stream.of(baseItem).flatMap(item -> {
//Stream<C> candidates = listCandidates.apply(item);
boolean[] empty = new boolean[]{true};
// We perform conditional concatenation: If the stream turned out to be empty, we append our element
Stream<T> nestedItems = reductions.apply(item)//candidates
//.filter(c -> ! usedCandidates.contains(c))
.peek(i -> empty[0] = false)
.flatMap(c -> {
//usedCandidates.add(c);
//T nextItem = applyCandidate.apply(item, c);
Stream<T> r = exhaustiveRewrite(c, reductions);
//Stream<T> r = exhaustiveRewrite(nextItem, listCandidates, applyCandidate, usedCandidates);
return r;
});
Stream<T> s = Stream.concat(nestedItems,
Stream.of(item).filter(x -> empty[0] == true));
return s;
});
return result;
}
public static <T, C, X extends Comparable<X>> T exhaustiveRewrite(
T item,
Function<T, Stream<T>> reductions,
//Function<T, Stream<C>> listCandidates,
//BiFunction<T, C, T> applyCandidate,
Function<T, X> cost
) {
//, usedCandidates
//Collection<C> usedCandidates = new HashSet<>();
Stream<T> stream = exhaustiveRewrite(item, reductions);
T result = stream
.min((a, b) -> {
X ca = cost.apply(a);
X cb = cost.apply(b);
int r = ca.compareTo(cb);
return r;
})
.orElse(null);
return result;
}
// enhance :- x = listCandidates.apply(item).findFirst().orElse(null); x != null ? applyCandidate(item, x) : null
public static <T, C> Stream<T> greedyRewrite(
T item,
Function<T, Stream<T>> reductions
//Function<T, Stream<C>> listCandidates,
//BiFunction<T, C, T> applyCandidate
) {
//Stream<T> result = it
return Stream.of(item).map(i -> greedyRewriteCore(item, reductions));
//Collection<C> usedCandidates = new HashSet<>();
}
public static <T, C> T greedyRewriteCore(
T item,
Function<T, Stream<T>> reductions
) {
T result = item;
for(;;) {
// TODO: enhance must be refactored into:
// - getCandidates -
// - applyCandidate
// C candidate = listCandidates.apply(item)
// .filter(c -> ! usedCandidates.contains(c))
// .findFirst()
// .orElse(null);
//T tmp = enhance.apply(item).findFirst().orElse(null);
T tmp = reductions.apply(result).findFirst().orElse(null);
if(tmp == null) {
break;
}
result = tmp;
//
// usedCandidates.add(candidate);
// result = applyCandidate.apply(item, candidate);
}
return result;
}
}