105 lines
4.1 KiB
Java
105 lines
4.1 KiB
Java
package de.dhbwstuttgart.target.generate;
|
|
|
|
import java.util.*;
|
|
import java.util.stream.Collectors;
|
|
import java.util.stream.Stream;
|
|
|
|
public class CycleFinder {
|
|
private CycleFinder() {}
|
|
|
|
static Set<GenerateGenerics.TPH> allNodes(Set<? extends GenerateGenerics.Pair> input) {
|
|
return input.stream()
|
|
.filter(GenerateGenerics.PairLT.class::isInstance)
|
|
.map(GenerateGenerics.PairLT.class::cast)
|
|
.flatMap(pair -> Stream.of(pair.left, pair.right)).collect(Collectors.toSet());
|
|
}
|
|
|
|
static Set<GenerateGenerics.TPH> outgoingEdgesOf(GenerateGenerics.TPH tph, Set<? extends GenerateGenerics.Pair> input) {
|
|
return input.stream()
|
|
.filter(GenerateGenerics.PairLT.class::isInstance)
|
|
.map(GenerateGenerics.PairLT.class::cast)
|
|
.filter(pair -> pair.left.equals(tph))
|
|
.map(pair -> pair.right).collect(Collectors.toSet());
|
|
}
|
|
|
|
static boolean containsEdge(GenerateGenerics.TPH a, GenerateGenerics.TPH b, Set<? extends GenerateGenerics.Pair> input) {
|
|
return input.stream()
|
|
.filter(GenerateGenerics.PairLT.class::isInstance)
|
|
.map(GenerateGenerics.PairLT.class::cast)
|
|
.anyMatch(pair -> pair.left.equals(a) && pair.right.equals(b));
|
|
}
|
|
|
|
// Tiernan simple cycles algorithm
|
|
// Adapted from https://github.com/jgrapht/jgrapht/blob/master/jgrapht-core/src/main/java/org/jgrapht/alg/cycle/TiernanSimpleCycles.java
|
|
static Set<List<GenerateGenerics.TPH>> findCycles(Set<? extends GenerateGenerics.Pair> input) {
|
|
Map<GenerateGenerics.TPH, Integer> indices = new HashMap<>();
|
|
List<GenerateGenerics.TPH> path = new ArrayList<>();
|
|
Set<GenerateGenerics.TPH> pathSet = new HashSet<>();
|
|
Map<GenerateGenerics.TPH, Set<GenerateGenerics.TPH>> blocked = new HashMap<>();
|
|
Set<List<GenerateGenerics.TPH>> cycles = new HashSet<>();
|
|
|
|
int index = 0;
|
|
for (var tph : allNodes(input)) {
|
|
blocked.put(tph, new HashSet<>());
|
|
indices.put(tph, index++);
|
|
}
|
|
|
|
var vertexIterator = allNodes(input).iterator();
|
|
if (!vertexIterator.hasNext()) return cycles;
|
|
|
|
GenerateGenerics.TPH startOfPath = null;
|
|
GenerateGenerics.TPH endOfPath = vertexIterator.next();
|
|
GenerateGenerics.TPH temp = null;
|
|
int endIndex = 0;
|
|
boolean extensionFound = false;
|
|
path.add(endOfPath);
|
|
pathSet.add(endOfPath);
|
|
|
|
while (true) {
|
|
do {
|
|
extensionFound = false;
|
|
for (GenerateGenerics.TPH n : outgoingEdgesOf(endOfPath, input)) {
|
|
int cmp = indices.get(n).compareTo(indices.get(path.get(0)));
|
|
if ((cmp > 0) && !pathSet.contains(n) && !blocked.get(endOfPath).contains(n)) {
|
|
path.add(n);
|
|
pathSet.add(n);
|
|
endOfPath = n;
|
|
extensionFound = true;
|
|
break;
|
|
}
|
|
}
|
|
} while (extensionFound);
|
|
|
|
startOfPath = path.get(0);
|
|
if (containsEdge(endOfPath, startOfPath, input)) {
|
|
List<GenerateGenerics.TPH> cycle = new ArrayList<>(path);
|
|
cycles.add(cycle);
|
|
}
|
|
if (path.size() > 1) {
|
|
blocked.get(endOfPath).clear();
|
|
endIndex = path.size() - 1;
|
|
path.remove(endIndex);
|
|
pathSet.remove(endOfPath);
|
|
--endIndex;
|
|
temp = endOfPath;
|
|
endOfPath = path.get(endIndex);
|
|
blocked.get(endOfPath).add(temp);
|
|
continue;
|
|
}
|
|
if (vertexIterator.hasNext()) {
|
|
path.clear();
|
|
pathSet.clear();
|
|
endOfPath = vertexIterator.next();
|
|
path.add(endOfPath);
|
|
pathSet.add(endOfPath);
|
|
for (GenerateGenerics.TPH tph : blocked.keySet()) {
|
|
blocked.get(tph).clear();
|
|
}
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
return cycles;
|
|
}
|
|
}
|