Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
io.lacuna.bifurcan.Graph Maven / Gradle / Ivy
package io.lacuna.bifurcan;
import io.lacuna.bifurcan.utils.Functions;
import java.util.Iterator;
import java.util.Objects;
import java.util.function.BiPredicate;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.ToIntFunction;
/**
* @author ztellman
*/
public class Graph implements IGraph {
private static final Object DEFAULT = new Object();
private static class VertexSet {
final V v, w;
VertexSet(V v, V w) {
this.v = v;
this.w = w;
}
int hashCode(ToIntFunction hashFn) {
return hashFn.applyAsInt(v) ^ hashFn.applyAsInt(w);
}
boolean equals(BiPredicate equalsFn, VertexSet t) {
return (equalsFn.test(v, t.v) && equalsFn.test(w, t.w)) || (equalsFn.test(v, t.w) && equalsFn.test(w, t.v));
}
}
private final Object editor;
private Map> adjacent;
private Map, E> edges;
public Graph() {
this(Objects::hash, Objects::equals);
}
public Graph(ToIntFunction hashFn, BiPredicate equalsFn) {
this(false, new Map<>(hashFn, equalsFn), new Map<>(t -> t.hashCode(hashFn), (a, b) -> a.equals(equalsFn, b)));
}
private Graph(boolean linear, Map> adjacent, Map, E> edges) {
this.editor = linear ? new Object() : null;
this.adjacent = adjacent;
this.edges = edges;
}
@Override
public Set vertices() {
return adjacent.keys();
}
@Override
public Iterable> edges() {
return () -> edges.stream()
.map(e -> (IEdge) new Graphs.Edge<>(e.value(), e.key().v, e.key().w))
.iterator();
}
@Override
public E edge(V from, V to) {
Object e = ((Map) edges).get(new VertexSet<>(from, to), DEFAULT);
if (e == DEFAULT) {
throw new IllegalArgumentException("no such edge");
} else {
return (E) e;
}
}
@Override
public Set in(V vertex) {
return out(vertex);
}
@Override
public Set out(V vertex) {
return adjacent.get(vertex).orElseThrow(() -> new IllegalArgumentException("no such vertex " + vertex));
}
@Override
public Graph link(V from, V to, E edge, BinaryOperator merge) {
Object editor = isLinear() ? this.editor : new Object();
Map> adjacentPrime = adjacent
.update(from, s -> (s == null ? new Set() : s).add(to, editor), editor)
.update(to, s -> (s == null ? new Set() : s).add(from, editor), editor);
Map, E> edgesPrime = edges.put(new VertexSet<>(from, to), edge, merge, editor);
if (isLinear()) {
adjacent = adjacentPrime;
edges = edgesPrime;
return this;
} else {
return new Graph<>(false, adjacentPrime, edgesPrime);
}
}
@Override
public Graph unlink(V from, V to) {
VertexSet t = new VertexSet<>(from, to);
if (!edges.contains(t)) {
return this;
}
Object editor = isLinear() ? this.editor : new Object();
Map, E> edgesPrime = edges.remove(t, editor);
Map> adjacentPrime = adjacent
.update(from, s -> s.remove(to, editor), editor)
.update(to, s -> s.remove(from, editor), editor);
if (isLinear()) {
edges = edgesPrime;
return this;
} else {
return new Graph<>(false, adjacentPrime, edgesPrime);
}
}
@Override
public Graph add(V vertex) {
if (adjacent.contains(vertex)) {
return this;
}
Object editor = isLinear() ? this.editor : new Object();
Map> adjacentPrime = adjacent.put(vertex, new Set<>(), (BinaryOperator>) Graphs.MERGE_LAST_WRITE_WINS, editor);
if (isLinear()) {
adjacent = adjacentPrime;
return this;
} else {
return new Graph<>(false, adjacentPrime, edges);
}
}
@Override
public Graph remove(V vertex) {
if (!adjacent.contains(vertex)) {
return this;
}
Object editor = isLinear() ? this.editor : new Object();
Map> adjacentPrime = adjacent.linear();
Map, E> edgesPrime = edges.linear();
for (V w : adjacent.get(vertex).get()) {
adjacentPrime.update(w, s -> s.remove(vertex, editor));
edgesPrime.remove(new VertexSet<>(w, vertex), editor);
}
edgesPrime = edgesPrime.forked();
adjacentPrime = adjacentPrime.remove(vertex).forked();
if (isLinear()) {
adjacent = adjacentPrime;
edges = edgesPrime;
return this;
} else {
return new Graph<>(false, adjacentPrime, edgesPrime);
}
}
@Override
public Graph mapEdges(Function, U> f) {
return new Graph<>(
isLinear(),
adjacent,
edges.mapValues((k, v) -> f.apply(new Graphs.Edge(v, k.v, k.w))));
}
@Override
public boolean isLinear() {
return editor != null;
}
@Override
public boolean isDirected() {
return false;
}
@Override
public Graph transpose() {
return this;
}
@Override
public ToIntFunction vertexHash() {
return adjacent.keyHash();
}
@Override
public BiPredicate vertexEquality() {
return adjacent.keyEquality();
}
@Override
public Graph merge(IGraph graph, BinaryOperator merge) {
if (graph instanceof Graph) {
Graph g = (Graph) graph;
return new Graph<>(
isLinear(),
adjacent.merge(g.adjacent, Set::union),
edges.merge(g.edges, merge));
} else {
return (Graph) Graphs.merge(this, graph, merge);
}
}
@Override
public Graph forked() {
return isLinear() ? new Graph<>(false, adjacent, edges) : this;
}
@Override
public Graph linear() {
return isLinear() ? this : new Graph<>(true, adjacent, edges);
}
@Override
public int hashCode() {
return adjacent.keys().hashCode() ^ edges.hashCode();
}
@Override
public Graph clone() {
return new Graph(isLinear(), adjacent.clone(), edges.clone());
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Graph) {
Graph g = (Graph) obj;
return edges.equals(g.edges) && vertices().equals(g.vertices());
} else if (obj instanceof IGraph) {
return Graphs.equals(this, (IGraph) obj);
} else {
return false;
}
}
@Override
public String toString() {
return adjacent.toString();
}
}