/**
   * Saves a given graph to a dot file, it also creates the file, or overwrites the old one
   *
   * @param g : The jung graph to save
   * @param filename : A string that points to the destination of the save
   * @param labeler : A node object -> Node name converter object
   * @param graphName : The name of the graph to export (usually this is set the project's name)
   * @throws IOException On IO error
   */
  public void save(Graph<V, E> g, String filename, Transformer<V, String> labeler, String graphName)
      throws IOException {
    SortedSet<V> nodes = new TreeSet<V>();
    Map<V, SortedSet<V>> successors = new HashMap<V, SortedSet<V>>();
    for (V source : g.getVertices()) {
      Collection<V> actSuccessors = g.getSuccessors(source);
      SortedSet<V> successorTree = new TreeSet<V>();
      for (V destination : actSuccessors) {
        successorTree.add(destination);
      }

      nodes.add(source);
      successors.put(source, successorTree);
    }

    BufferedWriter writer =
        new BufferedWriter(new OutputStreamWriter(new FileOutputStream(filename), "UTF-8"));
    writer.write("digraph \"" + graphName + "\" {\n");
    for (V from : nodes) {
      Collection<V> actSuccessors = successors.get(from);
      for (V to : actSuccessors) {
        writer.write(
            "\t\"" + labeler.transform(from) + "\" -> \"" + labeler.transform(to) + "\";\n");
      }

      if (g.getPredecessorCount(from) == 0 && actSuccessors.isEmpty()) {
        writer.write("\t\"" + labeler.transform(from) + "\";\n");
      }
    }

    writer.write("}");
    writer.close();
  }
 @Override
 public Box newVertexBox(Object vertex) {
   for (Class c : types.keySet()) {
     if (c.isInstance(vertex)) {
       Transformer<Object, Box> t = types.get(c);
       Box b = t.transform(vertex);
       return b;
     }
   }
   return newDefaultBox(vertex);
 }
  /**
   * Calculates betweenness scores based on the all-pairs weighted shortest paths in the graph.
   *
   * <p>NOTE: This version of the algorithm may not work correctly on all graphs; we're still
   * working out the bugs. Use at your own risk.
   *
   * @param graph the graph for which the scores are to be calculated
   * @param edge_weights the edge weights to be used in the path length calculations
   */
  public BetweennessCentrality(Graph<V, E> graph, Transformer<E, ? extends Number> edge_weights) {
    // reject negative-weight edges up front
    for (E e : graph.getEdges()) {
      double e_weight = edge_weights.transform(e).doubleValue();
      if (e_weight < 0)
        throw new IllegalArgumentException("Weight for edge '" + e + "' is < 0: " + e_weight);
    }

    initialize(graph);
    computeBetweenness(new MapBinaryHeap<V>(new BetweennessComparator()), edge_weights);
  }
 /**
  * Returns the prior probability for <code>v</code>.
  *
  * @param v the vertex whose prior probability is being queried
  * @return the prior probability for <code>v</code>
  */
 protected S getVertexPrior(V v) {
   return vertex_priors.transform(v);
 }
  protected void computeBetweenness(Queue<V> queue, Transformer<E, ? extends Number> edge_weights) {
    for (V v : graph.getVertices()) {
      // initialize the betweenness data for this new vertex
      for (V s : graph.getVertices()) this.vertex_data.put(s, new BetweennessData());

      // if (v.equals(new Integer(0)))
      // System.out.println("pause");

      vertex_data.get(v).numSPs = 1;
      vertex_data.get(v).distance = 0;

      Stack<V> stack = new Stack<V>();
      // Buffer<V> queue = new UnboundedFifoBuffer<V>();
      // queue.add(v);
      queue.offer(v);

      while (!queue.isEmpty()) {
        // V w = queue.remove();
        V w = queue.poll();
        stack.push(w);
        BetweennessData w_data = vertex_data.get(w);

        for (E e : graph.getOutEdges(w)) {
          // TODO (jrtom): change this to getOtherVertices(w, e)
          V x = graph.getOpposite(w, e);
          if (x.equals(w)) continue;
          double wx_weight = edge_weights.transform(e).doubleValue();

          // for(V x : graph.getSuccessors(w))
          // {
          // if (x.equals(w))
          // continue;

          // FIXME: the other problem is that I need to
          // keep putting the neighbors of things we've just
          // discovered in the queue, if they're undiscovered or
          // at greater distance.

          // FIXME: this is the problem, right here, I think:
          // need to update position in queue if distance changes
          // (which can only happen with weighted edges).
          // for each outgoing edge e from w, get other end x
          // if x not already visited (dist x < 0)
          // set x's distance to w's dist + edge weight
          // add x to queue; pri in queue is x's dist
          // if w's dist + edge weight < x's dist
          // update x's dist
          // update x in queue (MapBinaryHeap)
          // clear x's incoming edge list
          // if w's dist + edge weight = x's dist
          // add e to x's incoming edge list

          BetweennessData x_data = vertex_data.get(x);
          double x_potential_dist = w_data.distance + wx_weight;

          if (x_data.distance < 0) {
            // queue.add(x);
            // vertex_data.get(x).distance =
            // vertex_data.get(w).distance + 1;
            x_data.distance = x_potential_dist;
            queue.offer(x);
          }

          // note:
          // (1) this can only happen with weighted edges
          // (2) x's SP count and incoming edges are updated below
          if (x_data.distance > x_potential_dist) {
            x_data.distance = x_potential_dist;
            // invalidate previously identified incoming edges
            // (we have a new shortest path distance to x)
            x_data.incomingEdges.clear();
            // update x's position in queue
            ((MapBinaryHeap<V>) queue).update(x);
          }
          // if (vertex_data.get(x).distance ==
          // vertex_data.get(w).distance + 1)
          //
          // if (x_data.distance == x_potential_dist)
          // {
          // x_data.numSPs += w_data.numSPs;
          //// vertex_data.get(x).predecessors.add(w);
          // x_data.incomingEdges.add(e);
          // }
        }
        for (E e : graph.getOutEdges(w)) {
          V x = graph.getOpposite(w, e);
          if (x.equals(w)) continue;
          double e_weight = edge_weights.transform(e).doubleValue();
          BetweennessData x_data = vertex_data.get(x);
          double x_potential_dist = w_data.distance + e_weight;
          if (x_data.distance == x_potential_dist) {
            x_data.numSPs += w_data.numSPs;
            // vertex_data.get(x).predecessors.add(w);
            x_data.incomingEdges.add(e);
          }
        }
      }
      while (!stack.isEmpty()) {
        V x = stack.pop();

        // for (V w : vertex_data.get(x).predecessors)
        for (E e : vertex_data.get(x).incomingEdges) {
          V w = graph.getOpposite(x, e);
          double partialDependency =
              vertex_data.get(w).numSPs
                  / vertex_data.get(x).numSPs
                  * (1.0 + vertex_data.get(x).dependency);
          vertex_data.get(w).dependency += partialDependency;
          // E w_x = graph.findEdge(w, x);
          // double w_x_score = edge_scores.get(w_x).doubleValue();
          // w_x_score += partialDependency;
          // edge_scores.put(w_x, w_x_score);
          double e_score = edge_scores.get(e).doubleValue();
          edge_scores.put(e, e_score + partialDependency);
        }
        if (!x.equals(v)) {
          double x_score = vertex_scores.get(x).doubleValue();
          x_score += vertex_data.get(x).dependency;
          vertex_scores.put(x, x_score);
        }
      }
    }

    if (graph instanceof UndirectedGraph) {
      for (V v : graph.getVertices()) {
        double v_score = vertex_scores.get(v).doubleValue();
        v_score /= 2.0;
        vertex_scores.put(v, v_score);
      }
      for (E e : graph.getEdges()) {
        double e_score = edge_scores.get(e).doubleValue();
        e_score /= 2.0;
        edge_scores.put(e, e_score);
      }
    }

    vertex_data.clear();
  }