Skip to content
Snippets Groups Projects
Commit bc5d4c51 authored by Nathan PERIER's avatar Nathan PERIER
Browse files

Added an implementation of Tarjan's algorithm to check for loops during type unification

parent c3d2870b
No related branches found
No related tags found
No related merge requests found
package fr.insarennes.nperier.minichamo.language.typing.unification;
import java.util.*;
public class TarjanAlogrithm {
private final List<TarjanNode> nodes;
private final Deque<TarjanNode> stack;
private int index;
public TarjanAlogrithm(Map<String, Set<String>> successions) {
nodes = new ArrayList<>();
stack = new ArrayDeque<>();
index = 0;
final Map<String, TarjanNode> intermMapping = new HashMap<>();
for(String s : successions.keySet()) {
intermMapping.put(s, new TarjanNode(s));
}
for(Map.Entry<String, TarjanNode> e : intermMapping.entrySet()) {
String name = e.getKey();
TarjanNode node = e.getValue();
for(String s : successions.get(name)) {
if(!name.equals(s)) { // Remove the loops (n -> n)
node.addSuccessor(intermMapping.get(s));
}
}
nodes.add(node);
}
}
public boolean hasCycles() {
if(nodes.stream().allMatch(n -> n.getSuccessors().size() > 0)) {
// If there is no node with 0 successors (different than itself), then there is necessarily a loop somewhere
return true;
}
return algo().stream().anyMatch(s -> s.size() > 1);
}
public List<Set<String>> algo() {
List<Set<String>> res = new ArrayList<>();
for(TarjanNode n : nodes) {
if(n.notIndexed()) {
strongconnect(n, res);
}
}
return res;
}
private void strongconnect(TarjanNode v, List<Set<String>> res) {
v.setIndex(index);
v.setLowLink(index);
index++;
stack.push(v);
v.setOnStack(true);
for(TarjanNode w : v.getSuccessors()) {
if(w.notIndexed()) {
strongconnect(w, res);
v.setLowLink(Math.min(v.getLowlink(), w.getLowlink()));
} else if(w.isOnStack()) {
v.setLowLink(Math.min(v.getLowlink(), w.getIndex()));
}
}
if(v.getLowlink() == v.getIndex()) {
Set<String> scc = new HashSet<>(); // strongly connected component
TarjanNode w;
do {
w = stack.pop();
w.setOnStack(false);
scc.add(w.getName());
} while(!v.equals(w));
res.add(scc);
}
}
}
package fr.insarennes.nperier.minichamo.language.typing.unification;
import java.util.ArrayList;
import java.util.List;
public class TarjanNode {
private final String name;
private final List<TarjanNode> successors;
private int index;
private int lowlink;
private boolean onStack;
public TarjanNode(final String n) {
name = n;
successors = new ArrayList<>();
index = -1;
lowlink = -1;
onStack = false;
}
public String getName() {
return name;
}
public void addSuccessor(final TarjanNode node) {
successors.add(node);
}
public List<TarjanNode> getSuccessors() {
return successors;
}
public boolean notIndexed() {
return index < 0;
}
public int getIndex() {
return index;
}
public void setIndex(final int i) {
index = i;
}
public int getLowlink() {
return lowlink;
}
public void setLowLink(final int ll) {
lowlink = ll;
}
public boolean isOnStack() {
return onStack;
}
public void setOnStack(final boolean b) {
onStack = b;
}
@Override
public boolean equals(Object o) {
if(!(o instanceof TarjanNode)) {
return false;
}
TarjanNode node = (TarjanNode) o;
return name.equals(node.name);
}
}
package fr.insarennes.nperier.minichamo.language.typing.unification;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class TarjanTest {
@Test
public void testCycles() {
Map<String, Set<String>> mapping = new HashMap<>();
mapping.put("a", Set.of("b", "d", "e"));
mapping.put("b", Set.of());
mapping.put("c", Set.of("b", "e"));
mapping.put("d", Set.of("f"));
mapping.put("e", Set.of("d", "f"));
mapping.put("f", Set.of("c"));
TarjanAlogrithm tarjan = new TarjanAlogrithm(mapping);
Assertions.assertTrue(tarjan.hasCycles());
}
@Test
public void testNoExitNode() {
Map<String, Set<String>> mapping = new HashMap<>();
mapping.put("a", Set.of("d", "e"));
mapping.put("b", Set.of("a", "c"));
mapping.put("c", Set.of("e"));
mapping.put("d", Set.of("b", "f"));
mapping.put("e", Set.of("a"));
mapping.put("f", Set.of("b", "c", "d"));
TarjanAlogrithm tarjan = new TarjanAlogrithm(mapping);
Assertions.assertTrue(tarjan.hasCycles());
}
@Test
public void testNoCycles() {
Map<String, Set<String>> mapping = new HashMap<>();
mapping.put("a", Set.of("b", "d"));
mapping.put("b", Set.of("c", "d", "e"));
mapping.put("c", Set.of("e", "f"));
mapping.put("d", Set.of("c", "f"));
mapping.put("e", Set.of());
mapping.put("f", Set.of());
TarjanAlogrithm tarjan = new TarjanAlogrithm(mapping);
Assertions.assertFalse(tarjan.hasCycles());
}
@Test
public void testNoCyclesWithLoops() {
Map<String, Set<String>> mapping = new HashMap<>();
mapping.put("a", Set.of("b", "d"));
mapping.put("b", Set.of("c", "d", "e"));
mapping.put("c", Set.of("e", "f"));
mapping.put("d", Set.of("c", "f"));
mapping.put("e", Set.of("e"));
mapping.put("f", Set.of("f"));
TarjanAlogrithm tarjan = new TarjanAlogrithm(mapping);
Assertions.assertFalse(tarjan.hasCycles());
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment