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

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();
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy