/**
  * Augments the path the vertex
  *
  * @param g the {@link Graph}
  * @param v the {@link Vertex} to start from
  * @param pred a {@link VertexArray}
  */
 private void augmentPathTo(Graph g, Vertex v, final VertexArray pred) {
   Edge e = (Edge) pred.getVertexProperty(v);
   while (e != null) {
     e.turnOverEdgeDirection();
     e = (Edge) pred.getVertexProperty(GraphUtilities.getEdgeTargetVertex(g, e));
   }
 }
 /** Release resources */
 public void nullify() {
   try {
     g = null;
     c.nullify();
     c = null;
     pot.nullify();
     pot = null;
   } catch (NullPointerException e) {
   }
 }
  /**
   * Run the algorithm
   *
   * @return an {@link EdgesSet}
   */
  public EdgesSet runAlgorithm() {
    EdgesSet result = new EdgesSet(g.getEdgesSet().getVc());
    // for all nodes(v,G) pot[v] = 0;
    // this will be done by giving pot:= VertexArray(g,new Double(0))
    if (g.getEdgesSet().isEmpty()) return new EdgesSet(g.getEdgesSet().getVc());
    // check that all edges are directed from A to B
    // this should be a precondition also
    VertexArray free = new VertexArray(g, new Boolean(true));
    VertexArray pred = new VertexArray(g, null);
    VertexArray dist = new VertexArray(g, new Double(0));
    VertexPQ PQ = new VertexPQ(g);

    switch (heuristic) {
      case NAIVE_HEURISTIC:
        Double C = new Double(0);
        Iterator<Edge> edgesIterator = g.getEdgesIterator();
        while (edgesIterator.hasNext()) {
          Edge e = edgesIterator.next();
          Double edgeC = (Double) c.getEdgeProperty(e);
          if (edgeC.compareTo(C) == 1) // bigger than...
          C = edgeC;
        }
        Iterator<Vertex> leftVertexesIterator = g.getLeftVertexSetIterator();
        while (leftVertexesIterator.hasNext()) {
          pot.setVertexProperty(leftVertexesIterator.next(), C);
        }
        break;
      case SIMPLE_HEURISTIC:
        Iterator<Vertex> leftVertexesIterator1 = g.getLeftVertexSetIterator();
        while (leftVertexesIterator1.hasNext()) {
          Vertex a = leftVertexesIterator1.next();
          Edge eMax = null;
          double C_max = 0;
          Iterator<Edge> vertexAdjEdgesIterator =
              GraphUtilities.getVertexAdjEdges(g, a).getMembers().iterator();
          while (vertexAdjEdgesIterator.hasNext()) {
            Edge e = vertexAdjEdgesIterator.next();
            if (((Double) c.getEdgeProperty(e)).doubleValue() > C_max) {
              eMax = e;
              C_max = ((Double) c.getEdgeProperty(e)).doubleValue();
            }
          }
          pot.setVertexProperty(a, new Double(C_max));
          Vertex b;
          if (eMax != null
              && ((Boolean)
                      free.getVertexProperty((b = GraphUtilities.getEdgeTargetVertex(g, eMax))))
                  .booleanValue()) {
            eMax.turnOverEdgeDirection();
            free.setVertexProperty(a, new Boolean(false));
            free.setVertexProperty(b, new Boolean(false));
          }
        }
        break;
      default:
        // REFINED_HEURISTIC
        mwbmHeuristic(g, c, pot, free);
        break;
    }
    Iterator<Vertex> leftVertexesIterator = g.getLeftVertexSetIterator();
    while (leftVertexesIterator.hasNext()) {
      Vertex a = leftVertexesIterator.next();
      if (((Boolean) free.getVertexProperty(a)).booleanValue())
        augment(g, a, c, pot, free, pred, dist, PQ);
    }
    Iterator<Vertex> rightVertexesIterator = g.getRightVertexSetIterator();
    while (rightVertexesIterator.hasNext()) {
      Vertex b = rightVertexesIterator.next();
      Iterator<Edge> outEdgesIterator =
          GraphUtilities.getVertexOutEdges(g, b).getMembers().iterator();
      while (outEdgesIterator.hasNext()) {
        result.addMember(outEdgesIterator.next());
      }
    }
    result.turnOverEdges(false);
    return result;
  }
 /**
  * The maximum weight bipartite matching heuristis
  *
  * @param g {@link BipartiteGraph}
  * @param c an {@link EdgeArray}
  * @param pot a {@link VertexArray}
  * @param free free {@link VertexArray}
  */
 private void mwbmHeuristic(BipartiteGraph g, EdgeArray c, VertexArray pot, VertexArray free) {
   Vertex a, b;
   Edge e, e2, eb;
   VertexArray secEdge = new VertexArray(g, null);
   Iterator<Vertex> leftVertexSetIterator = g.getLeftVertexSetIterator();
   while (leftVertexSetIterator.hasNext()) {
     a = (Vertex) leftVertexSetIterator.next();
     double max2 = 0, max = 0;
     eb = null;
     e2 = null;
     // compute edges with largest and second largest slack
     Iterator<Edge> aAdjEdgesIterator =
         GraphUtilities.getVertexAdjEdges(g, a).getMembers().iterator();
     while (aAdjEdgesIterator.hasNext()) {
       e = (Edge) aAdjEdgesIterator.next();
       double we =
           ((Double) c.getEdgeProperty(e)).doubleValue()
               - ((Double) pot.getVertexProperty(GraphUtilities.getEdgeTargetVertex(g, e)))
                   .doubleValue();
       if (we >= max2) {
         if (we >= max) {
           max2 = max;
           e2 = eb;
           max = we;
           eb = e;
         } else {
           max2 = we;
           e2 = e;
         }
       }
     }
     if (eb != null) {
       b = GraphUtilities.getEdgeTargetVertex(g, eb);
       if (((Boolean) free.getVertexProperty(b)).booleanValue()) {
         // match eb and change pot[] to make slack of e2 zero
         secEdge.setVertexProperty(a, e2);
         pot.setVertexProperty(a, new Double(max2));
         pot.setVertexProperty(b, new Double(max - max2));
         eb.turnOverEdgeDirection();
         free.setVertexProperty(a, new Boolean(false));
         free.setVertexProperty(b, new Boolean(false));
       } else {
         // try to augment matching along
         // path of length 3 given by sec_edge[]
         pot.setVertexProperty(a, new Double(max));
         e2 = GraphUtilities.getVertexFirstAdjEdge(g, b);
         e = (Edge) secEdge.getVertexProperty(GraphUtilities.getEdgeTargetVertex(g, e2));
         if (e != null
             && GraphUtilities.getVertexOutDeg(g, GraphUtilities.getEdgeTargetVertex(g, e)) == 0) {
           free.setVertexProperty(a, new Boolean(false));
           free.setVertexProperty(GraphUtilities.getEdgeTargetVertex(g, e), new Boolean(false));
           e.turnOverEdgeDirection();
           e2.turnOverEdgeDirection();
           eb.turnOverEdgeDirection();
         }
       }
     } else {
       pot.setVertexProperty(a, new Double(0));
     }
   }
 }
 /**
  * Augment of the graph
  *
  * @param g {@link BipartiteGraph}
  * @param a {@link Vertex}
  * @param c an {@link EdgeArray}
  * @param pot a {@link VertexArray}
  * @param free a {@link VertexArray}
  * @param pred a {@link VertexArray}
  * @param dist a {@link VertexArray}
  * @param PQ a vertex priority queue {@link VertexPQ}
  */
 private void augment(
     BipartiteGraph g,
     Vertex a,
     final EdgeArray c,
     VertexArray pot,
     VertexArray free,
     VertexArray pred,
     VertexArray dist,
     VertexPQ PQ) {
   // augument:initialization
   dist.setVertexProperty(a, new Double(0));
   Vertex bestVertexInA = a;
   double minA = ((Double) pot.getVertexProperty(a)).doubleValue();
   double Delta;
   Stack<Vertex> RA = new Stack<Vertex>(), RB = new Stack<Vertex>();
   RA.push(a);
   Vertex a1 = a;
   Edge e;
   // relax all edges out of a1
   Iterator<Edge> a1AdjEdgesIterator =
       GraphUtilities.getVertexAdjEdges(g, a1).getMembers().iterator();
   while (a1AdjEdgesIterator.hasNext()) {
     e = a1AdjEdgesIterator.next();
     Vertex b = GraphUtilities.getEdgeTargetVertex(g, e);
     double db =
         ((Double) dist.getVertexProperty(a1)).doubleValue()
             + (((Double) pot.getVertexProperty(a1)).doubleValue()
                 + ((Double) pot.getVertexProperty(b)).doubleValue()
                 - ((Double) c.getEdgeProperty(e)).doubleValue());
     if (pred.getVertexProperty(b) == null) {
       dist.setVertexProperty(b, new Double(db));
       pred.setVertexProperty(b, e);
       RB.push(b);
       PQ.insert(b, new Double(db));
     } else if (db < ((Double) dist.getVertexProperty(b)).doubleValue()) {
       dist.setVertexProperty(b, new Double(db));
       pred.setVertexProperty(b, e);
       PQ.decreaseP(b, new Double(db));
     }
   }
   while (true) {
     // select from PQ the node b with minimal distance db
     Vertex b;
     double db = 0;
     if (PQ.isEmpty()) b = null;
     else {
       b = (Vertex) PQ.deleteMin();
       db = ((Double) dist.getVertexProperty(b)).doubleValue();
     }
     // distinguish three cases
     if (b == null || db >= minA) {
       Delta = minA;
       // augmentation by path to best node in A
       augmentPathTo(g, bestVertexInA, pred);
       free.setVertexProperty(a, new Boolean(false));
       free.setVertexProperty(bestVertexInA, new Boolean(true));
       break;
     } else if (((Boolean) free.getVertexProperty(b)).booleanValue()) {
       Delta = db;
       // augmentation by path to b
       augmentPathTo(g, b, pred);
       free.setVertexProperty(a, new Boolean(false));
       free.setVertexProperty(b, new Boolean(false));
       break;
     } else {
       // continue shortest-path computation
       e = GraphUtilities.getVertexFirstAdjEdge(g, b);
       Vertex a11 = GraphUtilities.getEdgeTargetVertex(g, e);
       pred.setVertexProperty(a11, e);
       RA.push(a11);
       dist.setVertexProperty(a11, new Double(db));
       if (db + ((Double) pot.getVertexProperty(a11)).doubleValue() < minA) {
         bestVertexInA = a11;
         minA = db + ((Double) pot.getVertexProperty(a11)).doubleValue();
       }
       // relax all edges out of a11
       Iterator<Edge> a11AdjEdgesIterator =
           GraphUtilities.getVertexAdjEdges(g, a11).getMembers().iterator();
       while (a11AdjEdgesIterator.hasNext()) {
         e = (Edge) a11AdjEdgesIterator.next();
         Vertex b1 = GraphUtilities.getEdgeTargetVertex(g, e);
         double db1 =
             ((Double) dist.getVertexProperty(a11)).doubleValue()
                 + (((Double) pot.getVertexProperty(a11)).doubleValue()
                     + ((Double) pot.getVertexProperty(b1)).doubleValue()
                     - ((Double) c.getEdgeProperty(e)).doubleValue());
         if (pred.getVertexProperty(b1) == null) {
           dist.setVertexProperty(b1, new Double(db1));
           pred.setVertexProperty(b1, e);
           RB.push(b1);
           PQ.insert(b1, new Double(db1)); // PQ.insert(b1,db1)
         } else if (db1 < ((Double) dist.getVertexProperty(b1)).doubleValue()) {
           dist.setVertexProperty(b1, new Double(db1));
           pred.setVertexProperty(b1, e);
           PQ.decreaseP(b1, new Double(db1));
         }
       }
     }
   }
   // augment: potential update and re-initialization
   while (!RA.isEmpty()) {
     Vertex a12 = (Vertex) RA.pop();
     pred.setVertexProperty(a12, null);
     double potChange = Delta - ((Double) dist.getVertexProperty(a12)).doubleValue();
     if (potChange <= 0) continue;
     pot.setVertexProperty(
         a12, new Double(((Double) pot.getVertexProperty(a12)).doubleValue() - potChange));
   }
   while (!RB.isEmpty()) {
     Vertex b12 = (Vertex) RB.pop();
     pred.setVertexProperty(b12, null);
     if (PQ.member(b12)) PQ.delete(b12);
     double potChange = Delta - ((Double) dist.getVertexProperty(b12)).doubleValue();
     if (potChange <= 0) continue;
     pot.setVertexProperty(
         b12, new Double(((Double) pot.getVertexProperty(b12)).doubleValue() + potChange));
   }
 }