/** Introduces the necessary shortcuts for endNode v in the graph. */
  int addShortcuts(int v) {
    shortcuts.clear();
    findShortcuts(addScHandler.setNode(v));
    int tmpNewShortcuts = 0;
    for (Shortcut sc : shortcuts.keySet()) {
      boolean updatedInGraph = false;
      // check if we need to update some existing shortcut in the graph
      EdgeSkipIterator iter = vehicleOutExplorer.setBaseNode(sc.from);
      while (iter.next()) {
        if (iter.isShortcut()
            && iter.getAdjNode() == sc.to
            && prepareEncoder.canBeOverwritten(iter.getFlags(), sc.flags)
            && iter.getDistance() > sc.weight) {
          iter.setFlags(sc.flags);
          iter.setSkippedEdges(sc.skippedEdge1, sc.skippedEdge2);
          iter.setDistance(sc.weight);
          setOrigEdgeCount(iter.getEdge(), sc.originalEdges);
          updatedInGraph = true;
          break;
        }
      }

      if (!updatedInGraph) {
        EdgeSkipIterState edgeState = g.shortcut(sc.from, sc.to);
        edgeState.setDistance(sc.weight).setFlags(sc.flags);
        edgeState.setSkippedEdges(sc.skippedEdge1, sc.skippedEdge2);
        setOrigEdgeCount(edgeState.getEdge(), sc.originalEdges);
        tmpNewShortcuts++;
      }
    }
    return tmpNewShortcuts;
  }
  /**
   * Calculates the priority of endNode v without changing the graph. Warning: the calculated
   * priority must NOT depend on priority(v) and therefor findShortcuts should also not depend on
   * the priority(v). Otherwise updating the priority before contracting in contractNodes() could
   * lead to a slowishor even endless loop.
   */
  int calculatePriority(int v) {
    // set of shortcuts that would be added if endNode v would be contracted next.
    findShortcuts(calcScHandler.setNode(v));

    //        System.out.println(v + "\t " + tmpShortcuts);
    // # huge influence: the bigger the less shortcuts gets created and the faster is the
    // preparation
    //
    // every endNode has an 'original edge' number associated. initially it is r=1
    // when a new shortcut is introduced then r of the associated edges is summed up:
    // r(u,w)=r(u,v)+r(v,w) now we can define
    // originalEdgesCount = σ(v) := sum_{ (u,w) ∈ shortcuts(v) } of r(u, w)
    int originalEdgesCount = calcScHandler.originalEdgesCount;
    //        for (Shortcut sc : tmpShortcuts) {
    //            originalEdgesCount += sc.originalEdges;
    //        }

    // # lowest influence on preparation speed or shortcut creation count
    // (but according to paper should speed up queries)
    //
    // number of already contracted neighbors of v
    int contractedNeighbors = 0;
    int degree = 0;
    EdgeSkipIterator iter = calcPrioAllExplorer.setBaseNode(v);
    while (iter.next()) {
      degree++;
      if (iter.isShortcut()) contractedNeighbors++;
    }

    // from shortcuts we can compute the edgeDifference
    // # low influence: with it the shortcut creation is slightly faster
    //
    // |shortcuts(v)| − |{(u, v) | v uncontracted}| − |{(v, w) | v uncontracted}|
    // meanDegree is used instead of outDegree+inDegree as if one endNode is in both directions
    // only one bucket memory is used. Additionally one shortcut could also stand for two
    // directions.
    int edgeDifference = calcScHandler.shortcuts - degree;

    // according to the paper do a simple linear combination of the properties to get the priority.
    // this is the current optimum for unterfranken:
    return 10 * edgeDifference + originalEdgesCount + contractedNeighbors;
  }
  @Test
  public void testExpandMultipleSkippedNodes() {
    LevelGraphStorage g = (LevelGraphStorage) createGraph(20);
    EdgeSkipIterator iter = g.edge(0, 1, 10, true); // 1
    g.edge(1, 2, 10, true); // 2
    g.edge(2, 3, 10, true); // 3
    g.edge(3, 4, 10, true); // 4

    g.setLevel(1, -1);
    g.setLevel(2, -1);
    g.edge(0, 3, 30, CarStreetType.flagsDefault(true)).skippedEdge(iter.edge()); // 5

    Path4Shortcuts path = new Path4Shortcuts(g, ShortestCarCalc.DEFAULT);
    path.edgeEntry = new EdgeEntry(5, 3, 30);
    path.edgeEntry.parent = new EdgeEntry(-1, 0, 0);
    path.edgeTo = new EdgeEntry(4, 3, 10);
    path.edgeTo.parent = new EdgeEntry(EdgeIterator.NO_EDGE, 4, 0);
    Path p = path.extract();
    assertEquals(Helper.createTList(0, 1, 2, 3, 4), p.calcNodes());
  }
  void initUnpackingGraph(LevelGraphStorage g, WeightCalculation w) {
    double dist = 1;
    int flags = carEncoder.flags(30, false);
    g.edge(10, 0, w.getWeight(dist, flags), flags);
    EdgeSkipIterator iter1 = g.edge(0, 1, w.getWeight(dist, flags), flags);
    EdgeSkipIterator iter2 = g.edge(1, 2, w.getWeight(dist, flags), flags);
    EdgeSkipIterator iter3 = g.edge(2, 3, w.getWeight(dist, flags), flags);
    EdgeSkipIterator iter4 = g.edge(3, 4, w.getWeight(dist, flags), flags);
    EdgeSkipIterator iter5 = g.edge(4, 5, w.getWeight(dist, flags), flags);
    EdgeSkipIterator iter6 = g.edge(5, 6, w.getWeight(dist, flags), flags);
    int oneDirFlags = new PrepareContractionHierarchies().scOneDir();

    int tmp = iter1.edge();
    iter1 = g.edge(0, 2, 2, oneDirFlags);
    iter1.skippedEdges(tmp, iter2.edge());
    tmp = iter1.edge();
    iter1 = g.edge(0, 3, 3, oneDirFlags);
    iter1.skippedEdges(tmp, iter3.edge());
    tmp = iter1.edge();
    iter1 = g.edge(0, 4, 4, oneDirFlags);
    iter1.skippedEdges(tmp, iter4.edge());
    tmp = iter1.edge();
    iter1 = g.edge(0, 5, 5, oneDirFlags);
    iter1.skippedEdges(tmp, iter5.edge());
    tmp = iter1.edge();
    iter1 = g.edge(0, 6, 6, oneDirFlags);
    iter1.skippedEdges(tmp, iter6.edge());
    g.setLevel(0, 10);
    g.setLevel(6, 9);
    g.setLevel(5, 8);
    g.setLevel(4, 7);
    g.setLevel(3, 6);
    g.setLevel(2, 5);
    g.setLevel(1, 4);
    g.setLevel(10, 3);
  }
  @Test
  public void testFindShortcuts_Roundabout() {
    LevelGraphStorage g = (LevelGraphStorage) createGraph();
    EdgeSkipIterator iter1_1 = g.edge(1, 3, 1, true);
    EdgeSkipIterator iter1_2 = g.edge(3, 4, 1, true);
    EdgeSkipIterator iter2_1 = g.edge(4, 5, 1, false);
    EdgeSkipIterator iter2_2 = g.edge(5, 6, 1, false);
    EdgeSkipIterator iter3_1 = g.edge(6, 7, 1, true);
    EdgeSkipIterator iter3_2 = g.edge(6, 8, 2, false);
    g.edge(8, 4, 1, false);
    g.setLevel(3, 3);
    g.setLevel(5, 5);
    g.setLevel(7, 7);
    g.setLevel(8, 8);

    PrepareContractionHierarchies prepare = new PrepareContractionHierarchies().graph(g);
    g.edge(1, 4, 2, prepare.scBothDir()).skippedEdges(iter1_1.edge(), iter1_2.edge());
    int f = prepare.scOneDir();
    g.edge(4, 6, 2, f).skippedEdges(iter2_1.edge(), iter2_2.edge());
    g.edge(6, 4, 3, f).skippedEdges(iter3_1.edge(), iter3_2.edge());

    prepare.initFromGraph();
    // there should be two different shortcuts for both directions!
    Collection<Shortcut> sc = prepare.testFindShortcuts(4);
    assertEquals(2, sc.size());
  }
  void contractNodes() {
    meanDegree = g.getAllEdges().getMaxId() / g.getNodes();
    int level = 1;
    counter = 0;
    int initSize = sortedNodes.getSize();
    int logSize =
        (int) Math.round(Math.max(10, sortedNodes.getSize() / 100 * logMessagesPercentage));
    if (logMessagesPercentage == 0) logSize = Integer.MAX_VALUE;

    // preparation takes longer but queries are slightly faster with preparation
    // => enable it but call not so often
    boolean periodicUpdate = true;
    StopWatch periodSW = new StopWatch();
    int updateCounter = 0;
    int periodicUpdatesCount =
        Math.max(10, sortedNodes.getSize() / 100 * periodicUpdatesPercentage);
    if (periodicUpdatesPercentage == 0) periodicUpdate = false;

    // disable as preparation is slower and query time does not benefit
    int lastNodesLazyUpdates =
        lastNodesLazyUpdatePercentage == 0
            ? 0
            : sortedNodes.getSize() / 100 * lastNodesLazyUpdatePercentage;
    StopWatch lazySW = new StopWatch();

    // Recompute priority of uncontracted neighbors.
    // Without neighborupdates preparation is faster but we need them
    // to slightly improve query time. Also if not applied too often it decreases the shortcut
    // number.
    boolean neighborUpdate = true;
    if (neighborUpdatePercentage == 0) neighborUpdate = false;

    StopWatch neighborSW = new StopWatch();
    LevelGraphStorage lg = ((LevelGraphStorage) g);
    while (!sortedNodes.isEmpty()) {
      // periodically update priorities of ALL nodes
      if (periodicUpdate && counter > 0 && counter % periodicUpdatesCount == 0) {
        periodSW.start();
        sortedNodes.clear();
        int len = g.getNodes();
        for (int node = 0; node < len; node++) {
          if (g.getLevel(node) != 0) continue;

          int priority = oldPriorities[node] = calculatePriority(node);
          sortedNodes.insert(node, priority);
        }
        periodSW.stop();
        updateCounter++;
      }

      if (counter % logSize == 0) {
        // TODO necessary?
        System.gc();
        logger.info(
            Helper.nf(counter)
                + ", updates:"
                + updateCounter
                + ", nodes: "
                + Helper.nf(sortedNodes.getSize())
                + ", shortcuts:"
                + Helper.nf(newShortcuts)
                + ", dijkstras:"
                + Helper.nf(dijkstraCount)
                + ", t(dijk):"
                + (int) dijkstraSW.getSeconds()
                + ", t(period):"
                + (int) periodSW.getSeconds()
                + ", t(lazy):"
                + (int) lazySW.getSeconds()
                + ", t(neighbor):"
                + (int) neighborSW.getSeconds()
                + ", meanDegree:"
                + (long) meanDegree
                + ", algo:"
                + algo.getMemoryUsageAsString()
                + ", "
                + Helper.getMemInfo());
        dijkstraSW = new StopWatch();
        periodSW = new StopWatch();
        lazySW = new StopWatch();
        neighborSW = new StopWatch();
      }

      counter++;
      int polledNode = sortedNodes.pollKey();
      if (sortedNodes.getSize() < lastNodesLazyUpdates) {
        lazySW.start();
        int priority = oldPriorities[polledNode] = calculatePriority(polledNode);
        if (!sortedNodes.isEmpty() && priority > sortedNodes.peekValue()) {
          // current node got more important => insert as new value and contract it later
          sortedNodes.insert(polledNode, priority);
          lazySW.stop();
          continue;
        }
        lazySW.stop();
      }

      // contract!
      newShortcuts += addShortcuts(polledNode);
      g.setLevel(polledNode, level);
      level++;

      EdgeSkipIterator iter = vehicleAllExplorer.setBaseNode(polledNode);
      while (iter.next()) {
        int nn = iter.getAdjNode();
        if (g.getLevel(nn) != 0)
          // already contracted no update necessary
          continue;

        if (neighborUpdate && rand.nextInt(100) < neighborUpdatePercentage) {
          neighborSW.start();
          int oldPrio = oldPriorities[nn];
          int priority = oldPriorities[nn] = calculatePriority(nn);
          if (priority != oldPrio) sortedNodes.update(nn, oldPrio, priority);

          neighborSW.stop();
        }

        if (removesHigher2LowerEdges) lg.disconnect(vehicleAllTmpExplorer, iter);
      }
    }

    // Preparation works only once so we can release temporary data.
    // The preparation object itself has to be intact to create the algorithm.
    close();
    logger.info(
        "took:"
            + (int) allSW.stop().getSeconds()
            + ", new shortcuts: "
            + newShortcuts
            + ", "
            + prepareWeighting
            + ", "
            + prepareEncoder
            + ", removeHigher2LowerEdges:"
            + removesHigher2LowerEdges
            + ", dijkstras:"
            + dijkstraCount
            + ", t(dijk):"
            + (int) dijkstraSW.getSeconds()
            + ", t(period):"
            + (int) periodSW.getSeconds()
            + ", t(lazy):"
            + (int) lazySW.getSeconds()
            + ", t(neighbor):"
            + (int) neighborSW.getSeconds()
            + ", meanDegree:"
            + (long) meanDegree
            + ", initSize:"
            + initSize
            + ", periodic:"
            + periodicUpdatesPercentage
            + ", lazy:"
            + lastNodesLazyUpdatePercentage
            + ", neighbor:"
            + neighborUpdatePercentage);
  }