All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.g2forge.alexandria.adt.graph.HGraph Maven / Gradle / Ivy

There is a newer version: 0.0.18
Show newest version
package com.g2forge.alexandria.adt.graph;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

import com.g2forge.alexandria.java.core.helpers.HCollection;
import com.g2forge.alexandria.java.core.helpers.HCollector;
import com.g2forge.alexandria.java.function.IFunction1;

public class HGraph {
	public static  List toposort(Collection nodes, Function> accessor, boolean isOut) {
		final Function> nodeConstructor = n -> {
			final Node retVal = new Node<>(n);
			if (isOut) retVal.setIn(new HashSet<>());
			else retVal.setOut(new HashSet<>());
			return retVal;
		};
		final Map> nodeMap = nodes.stream().map(nodeConstructor).collect(Collectors.toMap(Node::getNode, IFunction1.identity(), HCollector.mergeFail(), HashMap::new));
		final LinkedList todo = new LinkedList<>(nodes);
		while (!todo.isEmpty()) {
			final N n = todo.remove();
			final Node node = nodeMap.get(n);
			final Set> edges = accessor.apply(n).stream().map(o -> {
				final Node other = nodeMap.computeIfAbsent(o, key -> {
					todo.add(key);
					return nodeConstructor.apply(key);
				});
				if (other == null) throw new NullPointerException(String.format("Node \"%s\" has a broken edge to \"%s\"!", node.getNode(), o));
				return new Edge(isOut ? node : other, isOut ? other : node);
			}).collect(Collectors.toSet());
			if (isOut) node.setOut(edges);
			else node.setIn(edges);
			edges.stream().forEach(e -> (isOut ? e.getTo().getIn() : e.getFrom().getOut()).add(e));
		}

		final List> retVal = new ArrayList<>();
		final Set> noIncoming = new HashSet<>(nodeMap.values().stream().filter(node -> node.getIn().isEmpty()).collect(Collectors.toSet()));

		// While there are more nodes with no incoming edges
		while (!noIncoming.isEmpty()) {
			// Grab a node
			final Node node = HCollection.removeAny(noIncoming);
			// Put it into the result
			retVal.add(node);

			// Remove all the outbound edges from the graph
			node.getOut().stream().forEach(e -> {
				final Set> in = e.getTo().getIn();
				in.remove(e);
				// If we just make the to node have no more incoming edges...
				if (in.isEmpty()) noIncoming.add(e.getTo());
			});
			node.setOut(new HashSet<>());
		}

		// Check to see if there's a cycle
		final List cyclic = nodeMap.values().stream().filter(node -> !node.getIn().isEmpty()).map(Node::getNode).collect(Collectors.toList());
		if (!cyclic.isEmpty()) throw new IllegalArgumentException("Input graph is cyclic, remaining nodes are: " + cyclic);

		return retVal.stream().map(Node::getNode).collect(Collectors.toList());
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy