@Override
    public void foundShortcut(
        int u_fromNode,
        int w_toNode,
        double existingDirectWeight,
        EdgeIterator outgoingEdges,
        int skippedEdge1,
        int incomingEdgeOrigCount) {
      // FOUND shortcut
      // but be sure that it is the only shortcut in the collection
      // and also in the graph for u->w. If existing AND identical weight => update setProperties.
      // Hint: shortcuts are always one-way due to distinct level of every node but we don't
      // know yet the levels so we need to determine the correct direction or if both directions
      // minor improvement: if (shortcuts.containsKey(sc)
      // then two shortcuts with the same nodes (u<->n.endNode) exists => check current shortcut
      // against both
      Shortcut sc = new Shortcut(u_fromNode, w_toNode, existingDirectWeight);
      if (shortcuts.containsKey(sc)) {
        return;
      } else {
        Shortcut tmpSc = new Shortcut(w_toNode, u_fromNode, existingDirectWeight);
        Shortcut tmpRetSc = shortcuts.get(tmpSc);
        if (tmpRetSc != null) {
          tmpRetSc.flags = scBothDir;
          return;
        }
      }

      shortcuts.put(sc, sc);
      sc.skippedEdge1 = skippedEdge1;
      sc.skippedEdge2 = outgoingEdges.getEdge();
      sc.originalEdges = incomingEdgeOrigCount + getOrigEdgeCount(outgoingEdges.getEdge());
    }
  @Test
  public void testGetAllEdges() {
    graph = createGraph();
    graph.edge(0, 1, 2, true);
    graph.edge(3, 1, 1, false);
    graph.edge(3, 2, 1, false);

    EdgeIterator iter = graph.getAllEdges();
    assertTrue(iter.next());
    int edgeId = iter.getEdge();
    assertEquals(0, iter.getBaseNode());
    assertEquals(1, iter.getAdjNode());
    assertEquals(2, iter.getDistance(), 1e-6);

    assertTrue(iter.next());
    int edgeId2 = iter.getEdge();
    assertEquals(1, edgeId2 - edgeId);
    assertEquals(1, iter.getBaseNode());
    assertEquals(3, iter.getAdjNode());

    assertTrue(iter.next());
    assertEquals(2, iter.getBaseNode());
    assertEquals(3, iter.getAdjNode());

    assertFalse(iter.next());
  }
  /** Finds shortcuts, does not change the underlying graph. */
  void findShortcuts(ShortcutHandler sch) {
    long tmpDegreeCounter = 0;
    EdgeIterator incomingEdges = vehicleInExplorer.setBaseNode(sch.getNode());
    // collect outgoing nodes (goal-nodes) only once
    while (incomingEdges.next()) {
      int u_fromNode = incomingEdges.getAdjNode();
      // accept only uncontracted nodes
      if (g.getLevel(u_fromNode) != 0) continue;

      double v_u_weight = incomingEdges.getDistance();
      int skippedEdge1 = incomingEdges.getEdge();
      int incomingEdgeOrigCount = getOrigEdgeCount(skippedEdge1);
      // collect outgoing nodes (goal-nodes) only once
      EdgeIterator outgoingEdges = vehicleOutExplorer.setBaseNode(sch.getNode());
      // force fresh maps etc as this cannot be determined by from node alone (e.g. same from node
      // but different avoidNode)
      algo.clear();
      tmpDegreeCounter++;
      while (outgoingEdges.next()) {
        int w_toNode = outgoingEdges.getAdjNode();
        // add only uncontracted nodes
        if (g.getLevel(w_toNode) != 0 || u_fromNode == w_toNode) {
          continue;
        }

        // Limit weight as ferries or forbidden edges can increase local search too much.
        // If we decrease the correct weight we only explore less and introduce more shortcuts.
        // I.e. no change to accuracy is made.
        double existingDirectWeight = v_u_weight + outgoingEdges.getDistance();
        algo.setLimitWeight(existingDirectWeight)
            .setLimitVisitedNodes((int) meanDegree * 100)
            .setEdgeFilter(levelEdgeFilter.setAvoidNode(sch.getNode()));

        dijkstraSW.start();
        dijkstraCount++;
        int endNode = algo.findEndNode(u_fromNode, w_toNode);
        dijkstraSW.stop();

        // compare end node as the limit could force dijkstra to finish earlier
        if (endNode == w_toNode && algo.getWeight(endNode) <= existingDirectWeight)
          // FOUND witness path, so do not add shortcut
          continue;

        sch.foundShortcut(
            u_fromNode,
            w_toNode,
            existingDirectWeight,
            outgoingEdges,
            skippedEdge1,
            incomingEdgeOrigCount);
      }
    }
    if (sch instanceof AddShortcutHandler) {
      // sliding mean value when using "*2" => slower changes
      meanDegree = (meanDegree * 2 + tmpDegreeCounter) / 3;
      // meanDegree = (meanDegree + tmpDegreeCounter) / 2;
    }
  }
 @Override
 public void foundShortcut(
     int u_fromNode,
     int w_toNode,
     double existingDirectWeight,
     EdgeIterator outgoingEdges,
     int skippedEdge1,
     int incomingEdgeOrigCount) {
   shortcuts++;
   originalEdgesCount += incomingEdgeOrigCount + getOrigEdgeCount(outgoingEdges.getEdge());
 }
  @Test
  public void testExtract2() {
    Graph g = createGraph();
    g.edge(1, 2, 10, false);
    g.edge(2, 3, 20, false);
    EdgeExplorer explorer = g.createEdgeExplorer(carOutEdges);
    EdgeIterator iter = explorer.setBaseNode(1);
    iter.next();
    PathBidirRef pw = new PathBidirRef(g, carEncoder);
    pw.edgeEntry = new EdgeEntry(iter.getEdge(), 2, 10);
    pw.edgeEntry.parent = new EdgeEntry(EdgeIterator.NO_EDGE, 1, 0);

    explorer = g.createEdgeExplorer(new DefaultEdgeFilter(carEncoder, true, false));
    iter = explorer.setBaseNode(3);
    iter.next();
    pw.edgeTo = new EdgeEntry(iter.getEdge(), 2, 20);
    pw.edgeTo.parent = new EdgeEntry(EdgeIterator.NO_EDGE, 3, 0);
    Path p = pw.extract();
    assertEquals(Helper.createTList(1, 2, 3), p.calcNodes());
    assertEquals(30, p.getDistance(), 1e-4);
  }
  @Test
  public void testCreateDuplicateEdges() {
    graph = createGraph();
    graph.edge(2, 1, 12, true);
    graph.edge(2, 3, 12, true);
    graph.edge(2, 3, 13, false);
    assertEquals(3, GHUtility.count(carOutExplorer.setBaseNode(2)));

    // no exception
    graph.getEdgeProps(1, 3);

    // raise exception
    try {
      graph.getEdgeProps(4, 3);
      assertTrue(false);
    } catch (Exception ex) {
    }
    try {
      graph.getEdgeProps(-1, 3);
      assertTrue(false);
    } catch (Exception ex) {
    }

    EdgeIterator iter = carOutExplorer.setBaseNode(2);
    assertTrue(iter.next());
    EdgeIteratorState oneIter = graph.getEdgeProps(iter.getEdge(), 3);
    assertEquals(13, oneIter.getDistance(), 1e-6);
    assertEquals(2, oneIter.getBaseNode());
    assertTrue(carEncoder.isBool(oneIter.getFlags(), FlagEncoder.K_FORWARD));
    assertFalse(carEncoder.isBool(oneIter.getFlags(), FlagEncoder.K_BACKWARD));

    oneIter = graph.getEdgeProps(iter.getEdge(), 2);
    assertEquals(13, oneIter.getDistance(), 1e-6);
    assertEquals(3, oneIter.getBaseNode());
    assertFalse(carEncoder.isBool(oneIter.getFlags(), FlagEncoder.K_FORWARD));
    assertTrue(carEncoder.isBool(oneIter.getFlags(), FlagEncoder.K_BACKWARD));

    graph.edge(3, 2, 14, true);
    assertEquals(4, GHUtility.count(carOutExplorer.setBaseNode(2)));
  }
 boolean prepareEdges() {
   // In CH the setProperties (speed) are ignored as calculating the new setProperties for a
   // shortcut is often not possible.
   // Also several shortcuts would be necessary with the different modes (e.g. fastest and
   // shortest)
   // So calculate the weight and store this as weight, then use only weight instead of calcWeight
   EdgeIterator iter = g.getAllEdges();
   int c = 0;
   while (iter.next()) {
     c++;
     iter.setDistance(prepareWeighting.calcWeight(iter));
     setOrigEdgeCount(iter.getEdge(), 1);
   }
   return c > 0;
 }