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 allNodes(Set 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 outgoingEdgesOf(GenerateGenerics.TPH tph, Set 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 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> findCycles(Set input) { Map indices = new HashMap<>(); List path = new ArrayList<>(); Set pathSet = new HashSet<>(); Map> blocked = new HashMap<>(); Set> 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 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; } }