Repository: graphhopper/graphhopper
Pull request #2174 (2020-11-11 12:21:20)

Remove linkA/B fields from CH shortcuts
This PR removes the `E_LINKA/B` fields from CH shortcuts and thus reduces the shortcut storage size from 36 to 28 bit (-23%) for edge-based CH and from 28 to 20 (-29%) bits for node-based CH. The CH query speed is the same (if not faster) than current master.

The reason we do not need the `E_LINKA/B` fields anymore is that since we merged #2132 all shortcuts adjacent to the same node are already stored consecutively and such that `getLevel(nodeA) < getLevel(nodeB) if nodeA!=nodeB`. I had to adjust quite a few tests where we manually create shortcuts and violated this condition. I also inlined `EdgeAccess` /`CHEdgeAccess` into `BaseGraph`/`CHGraphImpl`, because the edge iteration is no longer the same for CH and `BaseGraph`. Previously we used the abstract `EdgeAccess` class to modify the behavior of `EdgeIteratorStateImpl` to make it work for CH, now this is separated.

Iterating the edges&shortcuts at a given node now works as follows. For each node we store the index of the last shortcut we added at this node (just as before). Starting with this shortcut we then iterate the shortcuts by decreasing the edge index by one each time we call `iter.next()`. When we are done iterating the shortcuts we continue iterating the base graph edges (using the double linked list as before). 

I also tried iterating the shortcuts going from lower to higher shortcut ids here: https://github.com/graphhopper/graphhopper/commit/4216091664765dabefcc78b1a5df6d0028689d6f, but this did not turn out to be significantly faster. The routing in this commit is correct, but doing it like this would require adjusting many more tests, because doing it this way we have to 'freeze' the CHGraph after adding the last shortcut.



karussell: Wow, really nice :+1: 

> now this is separated

:+1: 

Patch:
diff --git a/core/src/main/java/com/graphhopper/routing/ch/EdgeBasedShortcutInserter.java b/core/src/main/java/com/graphhopper/routing/ch/EdgeBasedShortcutInserter.java
index b7023fcdf..5fb30d419 100644
--- a/core/src/main/java/com/graphhopper/routing/ch/EdgeBasedShortcutInserter.java
+++ b/core/src/main/java/com/graphhopper/routing/ch/EdgeBasedShortcutInserter.java
@@ -64,8 +64,8 @@ public class EdgeBasedShortcutInserter implements EdgeBasedNodeContractor.Shortc
             // todo: performance, ideally this loop would start with the first *shortcut* not edge
             if (!iter.isShortcut())
                 continue;
-            int skip1 = getShortcutForArc(iter.getSkippedEdge1());
-            int skip2 = getShortcutForArc(iter.getSkippedEdge2());
+            int skip1 = getShortcutForPrepareEdge(iter.getSkippedEdge1());
+            int skip2 = getShortcutForPrepareEdge(iter.getSkippedEdge2());
             iter.setSkippedEdges(skip1, skip2);
         }
     }
@@ -77,7 +77,7 @@ public class EdgeBasedShortcutInserter implements EdgeBasedNodeContractor.Shortc
         shortcutsByPrepareEdges.set(index, shortcut);
     }
 
-    private int getShortcutForArc(int prepareEdge) {
+    private int getShortcutForPrepareEdge(int prepareEdge) {
         if (prepareEdge < origEdges)
             return prepareEdge;
         int index = prepareEdge - origEdges;
diff --git a/core/src/main/java/com/graphhopper/routing/ch/NodeBasedShortcutHandler.java b/core/src/main/java/com/graphhopper/routing/ch/NodeBasedShortcutInserter.java
similarity index 97%
rename from core/src/main/java/com/graphhopper/routing/ch/NodeBasedShortcutHandler.java
rename to core/src/main/java/com/graphhopper/routing/ch/NodeBasedShortcutInserter.java
index fde50085d..b49b49fcc 100644
--- a/core/src/main/java/com/graphhopper/routing/ch/NodeBasedShortcutHandler.java
+++ b/core/src/main/java/com/graphhopper/routing/ch/NodeBasedShortcutInserter.java
@@ -25,14 +25,14 @@ import com.graphhopper.storage.CHGraph;
 import java.util.ArrayList;
 import java.util.List;
 
-class NodeBasedShortcutHandler implements NodeBasedNodeContractor.ShortcutHandler {
+class NodeBasedShortcutInserter implements NodeBasedNodeContractor.ShortcutHandler {
     private final CHGraph chGraph;
     private final int origEdges;
     // todo: maybe use a set to prevent duplicates instead?
     private final List<Shortcut> shortcuts;
     private final IntArrayList shortcutsByPrepareEdges;
 
-    NodeBasedShortcutHandler(CHGraph chGraph) {
+    NodeBasedShortcutInserter(CHGraph chGraph) {
         this.chGraph = chGraph;
         this.shortcuts = new ArrayList<>();
         this.origEdges = chGraph.getOriginalEdges();
diff --git a/core/src/main/java/com/graphhopper/routing/ch/NodeOrderingProvider.java b/core/src/main/java/com/graphhopper/routing/ch/NodeOrderingProvider.java
index da5377c18..33493ddfd 100644
--- a/core/src/main/java/com/graphhopper/routing/ch/NodeOrderingProvider.java
+++ b/core/src/main/java/com/graphhopper/routing/ch/NodeOrderingProvider.java
@@ -1,6 +1,35 @@
 package com.graphhopper.routing.ch;
 
 public interface NodeOrderingProvider {
+
+    static NodeOrderingProvider identity(int nodes) {
+        return new NodeOrderingProvider() {
+            @Override
+            public int getNodeIdForLevel(int level) {
+                return level;
+            }
+
+            @Override
+            public int getNumNodes() {
+                return nodes;
+            }
+        };
+    }
+
+    static NodeOrderingProvider fromArray(int... nodes) {
+        return new NodeOrderingProvider() {
+            @Override
+            public int getNodeIdForLevel(int level) {
+                return nodes[level];
+            }
+
+            @Override
+            public int getNumNodes() {
+                return nodes.length;
+            }
+        };
+    }
+
     int getNodeIdForLevel(int level);
 
     int getNumNodes();
diff --git a/core/src/main/java/com/graphhopper/routing/ch/PrepareContractionHierarchies.java b/core/src/main/java/com/graphhopper/routing/ch/PrepareContractionHierarchies.java
index b1d78ffe8..d582436ba 100644
--- a/core/src/main/java/com/graphhopper/routing/ch/PrepareContractionHierarchies.java
+++ b/core/src/main/java/com/graphhopper/routing/ch/PrepareContractionHierarchies.java
@@ -171,7 +171,7 @@ public class PrepareContractionHierarchies extends AbstractAlgoPreparation {
         } else {
             logger.info("Creating CH prepare graph, {}", getMemInfo());
             prepareGraph = CHPreparationGraph.nodeBased(graph.getNodes(), graph.getEdges());
-            NodeBasedNodeContractor.ShortcutHandler shortcutInserter = new NodeBasedShortcutHandler(chGraph);
+            NodeBasedNodeContractor.ShortcutHandler shortcutInserter = new NodeBasedShortcutInserter(chGraph);
             nodeContractor = new NodeBasedNodeContractor(prepareGraph, shortcutInserter, pMap);
         }
         maxLevel = nodes;
@@ -339,8 +339,8 @@ public class PrepareContractionHierarchies extends AbstractAlgoPreparation {
         if (isContracted(node))
             throw new IllegalArgumentException("Node " + node + " was contracted already");
         contractionSW.start();
-        IntContainer neighbors = nodeContractor.contractNode(node);
         chGraph.setLevel(node, level);
+        IntContainer neighbors = nodeContractor.contractNode(node);
         contractionSW.stop();
         return neighbors;
     }
diff --git a/core/src/main/java/com/graphhopper/storage/BaseGraph.java b/core/src/main/java/com/graphhopper/storage/BaseGraph.java
index 290054acf..8cd2dd6df 100644
--- a/core/src/main/java/com/graphhopper/storage/BaseGraph.java
+++ b/core/src/main/java/com/graphhopper/storage/BaseGraph.java
@@ -62,7 +62,6 @@ class BaseGraph implements Graph {
     final TurnCostStorage turnCostStorage;
     final BitUtil bitUtil;
     final EncodingManager encodingManager;
-    final EdgeAccess edgeAccess;
     private final int intsForFlags;
     // length | nodeA | nextNode | ... | nodeB
     // as we use integer index in 'egdes' area => 'geometry' area is limited to 4GB (we use pos&neg values!)
@@ -75,8 +74,8 @@ class BaseGraph implements Graph {
     protected int edgeCount;
     // node memory layout:
     protected int N_EDGE_REF, N_LAT, N_LON, N_ELE, N_TC;
-    // edge memory layout not found in EdgeAccess:
-    int E_DIST, E_GEO, E_NAME;
+    // edge memory layout:
+    int E_NODEA, E_NODEB, E_LINKA, E_LINKB, E_FLAGS, E_DIST, E_GEO, E_NAME;
     /**
      * Specifies how many entries (integers) are used per edge.
      */
@@ -105,33 +104,6 @@ class BaseGraph implements Graph {
         this.nodes = dir.find("nodes", DAType.getPreferredInt(dir.getDefaultType()));
         this.edges = dir.find("edges", DAType.getPreferredInt(dir.getDefaultType()));
         this.listener = listener;
-        this.edgeAccess = new EdgeAccess(edges) {
-            @Override
-            final int getEdgeRef(int nodeId) {
-                return nodes.getInt((long) nodeId * nodeEntryBytes + N_EDGE_REF);
-            }
-
-            @Override
-            final void setEdgeRef(int nodeId, int edgeId) {
-                nodes.setInt((long) nodeId * nodeEntryBytes + N_EDGE_REF, edgeId);
-            }
-
-            @Override
-            final long toPointer(int edgeId) {
-                assert isInBounds(edgeId) : "edgeId " + edgeId + " not in bounds [0," + edgeCount + ")";
-                return (long) edgeId * edgeEntryBytes;
-            }
-
-            @Override
-            final boolean isInBounds(int edgeId) {
-                return edgeId < edgeCount && edgeId >= 0;
-            }
-
-            @Override
-            public String toString() {
-                return "base edge access";
-            }
-        };
         this.bounds = BBox.createInverse(withElevation);
         this.nodeAccess = new GHNodeAccess(this, withElevation);
         if (withTurnCosts) {
@@ -144,6 +116,48 @@ class BaseGraph implements Graph {
         }
     }
 
+    boolean isInBounds(int edgeId) {
+        return edgeId < edgeCount && edgeId >= 0;
+    }
+
+    private void setEdgeRef(long nodeId, int edgeId) {
+        nodes.setInt(nodeId * nodeEntryBytes + N_EDGE_REF, edgeId);
+    }
+
+    int getEdgeRef(long nodeId) {
+        return nodes.getInt(nodeId * nodeEntryBytes + N_EDGE_REF);
+    }
+
+    private int getNodeA(long edgePointer) {
+        return edges.getInt(edgePointer + E_NODEA);
+    }
+
+    private int getNodeB(long edgePointer) {
+        return edges.getInt(edgePointer + E_NODEB);
+    }
+
+    private int getLinkA(long edgePointer) {
+        return edges.getInt(edgePointer + E_LINKA);
+    }
+
+    private int getLinkB(long edgePointer) {
+        return edges.getInt(edgePointer + E_LINKB);
+    }
+
+    private long toPointer(int edgeId) {
+        assert isInBounds(edgeId) : "edgeId " + edgeId + " not in bounds [0," + edgeCount + ")";
+        return (long) edgeId * edgeEntryBytes;
+    }
+
+    private int getOtherNode(int nodeThis, long edgePointer) {
+        int nodeA = getNodeA(edgePointer);
+        return nodeThis == nodeA ? getNodeB(edgePointer) : nodeA;
+    }
+
+    private boolean isAdjacentToNode(int node, long edgePointer) {
+        return getNodeA(edgePointer) == node || getNodeB(edgePointer) == node;
+    }
+
     private static boolean isTestingEnabled() {
         boolean enableIfAssert = false;
         assert (enableIfAssert = true) : true;
@@ -227,11 +241,11 @@ class BaseGraph implements Graph {
     void initStorage() {
         edgeEntryIndex = 0;
         nodeEntryIndex = 0;
-        edgeAccess.init(nextEdgeEntryIndex(4),
-                nextEdgeEntryIndex(4),
-                nextEdgeEntryIndex(4),
-                nextEdgeEntryIndex(4),
-                nextEdgeEntryIndex(encodingManager.getIntsForFlags() * 4));
+        E_NODEA = nextEdgeEntryIndex(4);
+        E_NODEB = nextEdgeEntryIndex(4);
+        E_LINKA = nextEdgeEntryIndex(4);
+        E_LINKB = nextEdgeEntryIndex(4);
+        E_FLAGS = nextEdgeEntryIndex(encodingManager.getIntsForFlags() * 4);
 
         E_DIST = nextEdgeEntryIndex(4);
         E_GEO = nextEdgeEntryIndex(4);
@@ -394,7 +408,7 @@ class BaseGraph implements Graph {
         System.out.format(Locale.ROOT, formatNodes, "#", "N_EDGE_REF", "N_LAT", "N_LON");
         NodeAccess nodeAccess = getNodeAccess();
         for (int i = 0; i < Math.min(nodeCount, printMax); ++i) {
-            System.out.format(Locale.ROOT, formatNodes, i, edgeAccess.getEdgeRef(i), nodeAccess.getLat(i), nodeAccess.getLon(i));
+            System.out.format(Locale.ROOT, formatNodes, i, getEdgeRef(i), nodeAccess.getLat(i), nodeAccess.getLon(i));
         }
         if (nodeCount > printMax) {
             System.out.format(Locale.ROOT, " ... %d more nodes\n", nodeCount - printMax);
@@ -404,13 +418,13 @@ class BaseGraph implements Graph {
         System.out.format(Locale.ROOT, formatEdges, "#", "E_NODEA", "E_NODEB", "E_LINKA", "E_LINKB", "E_FLAGS", "E_DIST");
         IntsRef intsRef = new IntsRef(intsForFlags);
         for (int i = 0; i < Math.min(edgeCount, printMax); ++i) {
-            long edgePointer = edgeAccess.toPointer(i);
-            edgeAccess.readFlags(edgePointer, intsRef);
+            long edgePointer = toPointer(i);
+            readFlags(edgePointer, intsRef);
             System.out.format(Locale.ROOT, formatEdges, i,
-                    edgeAccess.getNodeA(edgePointer),
-                    edgeAccess.getNodeB(edgePointer),
-                    edgeAccess.getLinkA(edgePointer),
-                    edgeAccess.getLinkB(edgePointer),
+                    getNodeA(edgePointer),
+                    getNodeB(edgePointer),
+                    getLinkA(edgePointer),
+                    getLinkB(edgePointer),
                     intsRef,
                     getDist(edgePointer));
         }
@@ -506,8 +520,8 @@ class BaseGraph implements Graph {
      * @return the updated iterator the properties where copied to.
      */
     EdgeIteratorState copyProperties(EdgeIteratorState from, EdgeIteratorStateImpl to) {
-        long edgePointer = edgeAccess.toPointer(to.getEdge());
-        edgeAccess.writeFlags(edgePointer, from.getFlags());
+        long edgePointer = toPointer(to.getEdge());
+        writeFlags(edgePointer, from.getFlags());
 
         // copy the rest with higher level API
         to.setDistance(from.getDistance()).
@@ -528,13 +542,49 @@ class BaseGraph implements Graph {
             throw new IllegalStateException("Cannot create edge if graph is already frozen");
 
         ensureNodeIndex(Math.max(nodeA, nodeB));
-        int edgeId = edgeAccess.internalEdgeAdd(nextEdgeId(), nodeA, nodeB, true);
-        EdgeIteratorStateImpl edge = new EdgeIteratorStateImpl(edgeAccess, this);
+        int edgeId = internalEdgeAdd(nextEdgeId(), nodeA, nodeB);
+        EdgeIteratorStateImpl edge = new EdgeIteratorStateImpl(this);
         boolean valid = edge.init(edgeId, nodeB);
         assert valid;
         return edge;
     }
 
+    /**
+     * Writes a new edge to the array of edges and adds it to the linked list of edges at nodeA and nodeB
+     */
+    final int internalEdgeAdd(int newEdgeId, int nodeA, int nodeB) {
+        writeEdge(newEdgeId, nodeA, nodeB);
+        long edgePointer = toPointer(newEdgeId);
+
+        int edge = getEdgeRef(nodeA);
+        if (edge > EdgeIterator.NO_EDGE)
+            edges.setInt(E_LINKA + edgePointer, edge);
+        setEdgeRef(nodeA, newEdgeId);
+
+        if (nodeA != nodeB) {
+            edge = getEdgeRef(nodeB);
+            if (edge > EdgeIterator.NO_EDGE)
+                edges.setInt(E_LINKB + edgePointer, edge);
+            setEdgeRef(nodeB, newEdgeId);
+        }
+        return newEdgeId;
+    }
+
+    /**
+     * Writes plain edge information to the edges index
+     */
+    private long writeEdge(int edgeId, int nodeA, int nodeB) {
+        if (!EdgeIterator.Edge.isValid(edgeId))
+            throw new IllegalStateException("Cannot write edge with illegal ID:" + edgeId + "; nodeA:" + nodeA + ", nodeB:" + nodeB);
+
+        long edgePointer = toPointer(edgeId);
+        edges.setInt(edgePointer + E_NODEA, nodeA);
+        edges.setInt(edgePointer + E_NODEB, nodeB);
+        edges.setInt(edgePointer + E_LINKA, EdgeIterator.NO_EDGE);
+        edges.setInt(edgePointer + E_LINKB, EdgeIterator.NO_EDGE);
+        return edgePointer;
+    }
+
     // for test only
     void setEdgeCount(int cnt) {
         edgeCount = cnt;
@@ -557,10 +607,10 @@ class BaseGraph implements Graph {
 
     @Override
     public EdgeIteratorState getEdgeIteratorState(int edgeId, int adjNode) {
-        if (!edgeAccess.isInBounds(edgeId))
+        if (!isInBounds(edgeId))
             throw new IllegalStateException("edgeId " + edgeId + " out of bounds");
         checkAdjNodeBounds(adjNode);
-        EdgeIteratorStateImpl edge = new EdgeIteratorStateImpl(edgeAccess, this);
+        EdgeIteratorStateImpl edge = new EdgeIteratorStateImpl(this);
         if (edge.init(edgeId, adjNode))
             return edge;
         // if edgeId exists but adjacent nodes do not match
@@ -569,7 +619,7 @@ class BaseGraph implements Graph {
 
     @Override
     public EdgeIteratorState getEdgeIteratorStateForKey(int edgeKey) {
-        EdgeIteratorStateImpl edge = new EdgeIteratorStateImpl(edgeAccess, this);
+        EdgeIteratorStateImpl edge = new EdgeIteratorStateImpl(this);
         edge.init(edgeKey);
         return edge;
     }
@@ -581,7 +631,7 @@ class BaseGraph implements Graph {
 
     @Override
     public EdgeExplorer createEdgeExplorer(EdgeFilter filter) {
-        return new EdgeIteratorImpl(this, edgeAccess, filter);
+        return new EdgeIteratorImpl(this, filter);
     }
 
     @Override
@@ -591,7 +641,7 @@ class BaseGraph implements Graph {
 
     @Override
     public AllEdgesIterator getAllEdges() {
-        return new AllEdgeIterator(this, edgeAccess);
+        return new AllEdgeIterator(this);
     }
 
     @Override
@@ -654,16 +704,29 @@ class BaseGraph implements Graph {
 
     @Override
     public int getOtherNode(int edge, int node) {
-        long edgePointer = edgeAccess.toPointer(edge);
-        return edgeAccess.getOtherNode(node, edgePointer);
+        long edgePointer = toPointer(edge);
+        return getOtherNode(node, edgePointer);
     }
 
     @Override
     public boolean isAdjacentToNode(int edge, int node) {
-        long edgePointer = edgeAccess.toPointer(edge);
-        return edgeAccess.isAdjacentToNode(node, edgePointer);
+        long edgePointer = toPointer(edge);
+        return isAdjacentToNode(node, edgePointer);
     }
 
+    private void readFlags(long edgePointer, IntsRef edgeFlags) {
+        int size = edgeFlags.ints.length;
+        for (int i = 0; i < size; i++) {
+            edgeFlags.ints[i] = edges.getInt(edgePointer + E_FLAGS + i * 4);
+        }
+    }
+
+    private void writeFlags(long edgePointer, IntsRef edgeFlags) {
+        int size = edgeFlags.ints.length;
+        for (int i = 0; i < size; i++) {
+            edges.setInt(edgePointer + E_FLAGS + i * 4, edgeFlags.ints[i]);
+        }
+    }
 
     private void setDist(long edgePointer, double distance) {
         edges.setInt(edgePointer + E_DIST, distToInt(distance));
@@ -847,27 +910,18 @@ class BaseGraph implements Graph {
         final EdgeFilter filter;
         int nextEdgeId;
 
-        public EdgeIteratorImpl(BaseGraph baseGraph, EdgeAccess edgeAccess, EdgeFilter filter) {
-            super(edgeAccess, baseGraph);
+        public EdgeIteratorImpl(BaseGraph baseGraph, EdgeFilter filter) {
+            super(baseGraph);
 
             if (filter == null)
                 throw new IllegalArgumentException("Instead null filter use EdgeFilter.ALL_EDGES");
             this.filter = filter;
         }
 
-        final void setEdgeId(int edgeId) {
-            this.nextEdgeId = this.edgeId = edgeId;
-        }
-
-        final void _setBaseNode(int baseNode) {
-            this.baseNode = baseNode;
-        }
-
         @Override
         public EdgeIterator setBaseNode(int baseNode) {
-            // always use base graph edge access
-            setEdgeId(baseGraph.edgeAccess.getEdgeRef(baseNode));
-            _setBaseNode(baseNode);
+            nextEdgeId = edgeId = baseGraph.getEdgeRef(baseNode);
+            this.baseNode = baseNode;
             return this;
         }
 
@@ -884,16 +938,16 @@ class BaseGraph implements Graph {
         }
 
         void goToNext() {
-            edgePointer = edgeAccess.toPointer(nextEdgeId);
+            edgePointer = baseGraph.toPointer(nextEdgeId);
             edgeId = nextEdgeId;
-            int nodeA = edgeAccess.getNodeA(edgePointer);
+            int nodeA = baseGraph.getNodeA(edgePointer);
             boolean baseNodeIsNodeA = baseNode == nodeA;
-            adjNode = baseNodeIsNodeA ? edgeAccess.getNodeB(edgePointer) : nodeA;
+            adjNode = baseNodeIsNodeA ? baseGraph.getNodeB(edgePointer) : nodeA;
             reverse = !baseNodeIsNodeA;
             freshFlags = false;
 
             // position to next edge
-            nextEdgeId = baseNodeIsNodeA ? edgeAccess.getLinkA(edgePointer) : edgeAccess.getLinkB(edgePointer);
+            nextEdgeId = baseNodeIsNodeA ? baseGraph.getLinkA(edgePointer) : baseGraph.getLinkB(edgePointer);
             assert nextEdgeId != edgeId : ("endless loop detected for base node: " + baseNode + ", adj node: " + adjNode
                     + ", edge pointer: " + edgePointer + ", edge: " + edgeId);
         }
@@ -911,11 +965,7 @@ class BaseGraph implements Graph {
      */
     protected static class AllEdgeIterator extends EdgeIteratorStateImpl implements AllEdgesIterator {
         public AllEdgeIterator(BaseGraph baseGraph) {
-            this(baseGraph, baseGraph.edgeAccess);
-        }
-
-        private AllEdgeIterator(BaseGraph baseGraph, EdgeAccess edgeAccess) {
-            super(edgeAccess, baseGraph);
+            super(baseGraph);
         }
 
         @Override
@@ -928,9 +978,9 @@ class BaseGraph implements Graph {
             edgeId++;
             if (edgeId >= baseGraph.edgeCount)
                 return false;
-            edgePointer = edgeAccess.toPointer(edgeId);
-            baseNode = edgeAccess.getNodeA(edgePointer);
-            adjNode = edgeAccess.getNodeB(edgePointer);
+            edgePointer = baseGraph.toPointer(edgeId);
+            baseNode = baseGraph.getNodeA(edgePointer);
+            adjNode = baseGraph.getNodeB(edgePointer);
             freshFlags = false;
             reverse = false;
             return true;
@@ -941,7 +991,7 @@ class BaseGraph implements Graph {
             if (edgePointer < 0)
                 throw new IllegalStateException("call next before detaching");
 
-            AllEdgeIterator iter = new AllEdgeIterator(baseGraph, edgeAccess);
+            AllEdgeIterator iter = new AllEdgeIterator(baseGraph);
             iter.edgeId = edgeId;
             iter.edgePointer = edgePointer;
             if (reverseArg) {
@@ -962,15 +1012,13 @@ class BaseGraph implements Graph {
         long edgePointer = -1;
         int baseNode;
         int adjNode;
-        EdgeAccess edgeAccess;
         // we need reverse if detach is called
         boolean reverse = false;
         boolean freshFlags;
         int edgeId = -1;
         private final IntsRef edgeFlags;
 
-        public EdgeIteratorStateImpl(EdgeAccess edgeAccess, BaseGraph baseGraph) {
-            this.edgeAccess = edgeAccess;
+        public EdgeIteratorStateImpl(BaseGraph baseGraph) {
             this.baseGraph = baseGraph;
             this.edgeFlags = new IntsRef(baseGraph.intsForFlags);
         }
@@ -982,9 +1030,9 @@ class BaseGraph implements Graph {
             if (!EdgeIterator.Edge.isValid(edgeId))
                 throw new IllegalArgumentException("fetching the edge requires a valid edgeId but was " + edgeId);
             this.edgeId = edgeId;
-            edgePointer = edgeAccess.toPointer(edgeId);
-            baseNode = edgeAccess.getNodeA(edgePointer);
-            adjNode = edgeAccess.getNodeB(edgePointer);
+            edgePointer = baseGraph.toPointer(edgeId);
+            baseNode = baseGraph.getNodeA(edgePointer);
+            adjNode = baseGraph.getNodeB(edgePointer);
             freshFlags = false;
 
             if (expectedAdjNode == adjNode || expectedAdjNode == Integer.MIN_VALUE) {
@@ -1007,9 +1055,9 @@ class BaseGraph implements Graph {
             if (edgeKey < 0)
                 throw new IllegalArgumentException("edge keys must not be negative, given: " + edgeKey);
             this.edgeId = GHUtility.getEdgeFromEdgeKey(edgeKey);
-            edgePointer = edgeAccess.toPointer(edgeId);
-            baseNode = edgeAccess.getNodeA(edgePointer);
-            adjNode = edgeAccess.getNodeB(edgePointer);
+            edgePointer = baseGraph.toPointer(edgeId);
+            baseNode = baseGraph.getNodeA(edgePointer);
+            adjNode = baseGraph.getNodeB(edgePointer);
             freshFlags = false;
 
             if (edgeKey % 2 == 0 || baseNode == adjNode) {
@@ -1046,7 +1094,7 @@ class BaseGraph implements Graph {
         @Override
         public IntsRef getFlags() {
             if (!freshFlags) {
-                edgeAccess.readFlags(edgePointer, edgeFlags);
+                baseGraph.readFlags(edgePointer, edgeFlags);
                 freshFlags = true;
             }
             return edgeFlags;
@@ -1055,7 +1103,7 @@ class BaseGraph implements Graph {
         @Override
         public final EdgeIteratorState setFlags(IntsRef edgeFlags) {
             assert edgeId < baseGraph.edgeCount : "must be edge but was shortcut: " + edgeId + " >= " + baseGraph.edgeCount + ". Use setFlagsAndWeight";
-            edgeAccess.writeFlags(edgePointer, edgeFlags);
+            baseGraph.writeFlags(edgePointer, edgeFlags);
             for (int i = 0; i < edgeFlags.ints.length; i++) {
                 this.edgeFlags.ints[i] = edgeFlags.ints[i];
             }
@@ -1071,7 +1119,7 @@ class BaseGraph implements Graph {
         @Override
         public EdgeIteratorState set(BooleanEncodedValue property, boolean value) {
             property.setBool(reverse, getFlags(), value);
-            edgeAccess.writeFlags(edgePointer, getFlags());
+            baseGraph.writeFlags(edgePointer, getFlags());
             return this;
         }
 
@@ -1083,7 +1131,7 @@ class BaseGraph implements Graph {
         @Override
         public EdgeIteratorState setReverse(BooleanEncodedValue property, boolean value) {
             property.setBool(!reverse, getFlags(), value);
-            edgeAccess.writeFlags(edgePointer, getFlags());
+            baseGraph.writeFlags(edgePointer, getFlags());
             return this;
         }
 
@@ -1095,7 +1143,7 @@ class BaseGraph implements Graph {
         @Override
         public EdgeIteratorState set(IntEncodedValue property, int value) {
             property.setInt(reverse, getFlags(), value);
-            edgeAccess.writeFlags(edgePointer, getFlags());
+            baseGraph.writeFlags(edgePointer, getFlags());
             return this;
         }
 
@@ -1107,7 +1155,7 @@ class BaseGraph implements Graph {
         @Override
         public EdgeIteratorState setReverse(IntEncodedValue property, int value) {
             property.setInt(!reverse, getFlags(), value);
-            edgeAccess.writeFlags(edgePointer, getFlags());
+            baseGraph.writeFlags(edgePointer, getFlags());
             return this;
         }
 
@@ -1119,7 +1167,7 @@ class BaseGraph implements Graph {
         @Override
         public EdgeIteratorState set(DecimalEncodedValue property, double value) {
             property.setDecimal(reverse, getFlags(), value);
-            edgeAccess.writeFlags(edgePointer, getFlags());
+            baseGraph.writeFlags(edgePointer, getFlags());
             return this;
         }
 
@@ -1131,7 +1179,7 @@ class BaseGraph implements Graph {
         @Override
         public EdgeIteratorState setReverse(DecimalEncodedValue property, double value) {
             property.setDecimal(!reverse, getFlags(), value);
-            edgeAccess.writeFlags(edgePointer, getFlags());
+            baseGraph.writeFlags(edgePointer, getFlags());
             return this;
         }
 
@@ -1143,7 +1191,7 @@ class BaseGraph implements Graph {
         @Override
         public <T extends Enum> EdgeIteratorState set(EnumEncodedValue<T> property, T value) {
             property.setEnum(reverse, getFlags(), value);
-            edgeAccess.writeFlags(edgePointer, getFlags());
+            baseGraph.writeFlags(edgePointer, getFlags());
             return this;
         }
 
@@ -1155,7 +1203,7 @@ class BaseGraph implements Graph {
         @Override
         public <T extends Enum> EdgeIteratorState setReverse(EnumEncodedValue<T> property, T value) {
             property.setEnum(!reverse, getFlags(), value);
-            edgeAccess.writeFlags(edgePointer, getFlags());
+            baseGraph.writeFlags(edgePointer, getFlags());
             return this;
         }
 
@@ -1213,7 +1261,7 @@ class BaseGraph implements Graph {
         public EdgeIteratorState detach(boolean reverseArg) {
             if (!EdgeIterator.Edge.isValid(edgeId))
                 throw new IllegalStateException("call setEdgeId before detaching (edgeId:" + edgeId + ")");
-            EdgeIteratorStateImpl edge = new EdgeIteratorStateImpl(edgeAccess, baseGraph);
+            EdgeIteratorStateImpl edge = new EdgeIteratorStateImpl(baseGraph);
             boolean valid = edge.init(edgeId, reverseArg ? baseNode : adjNode);
             assert valid;
             if (reverseArg) {
diff --git a/core/src/main/java/com/graphhopper/storage/CHGraph.java b/core/src/main/java/com/graphhopper/storage/CHGraph.java
index 77ecc46c7..96eeb722e 100644
--- a/core/src/main/java/com/graphhopper/storage/CHGraph.java
+++ b/core/src/main/java/com/graphhopper/storage/CHGraph.java
@@ -110,4 +110,5 @@ public interface CHGraph {
      */
     boolean isAdjacentToNode(int edge, int node);
 
+    void debugPrint();
 }
diff --git a/core/src/main/java/com/graphhopper/storage/CHGraphImpl.java b/core/src/main/java/com/graphhopper/storage/CHGraphImpl.java
index 089d6c03a..062e62de9 100644
--- a/core/src/main/java/com/graphhopper/storage/CHGraphImpl.java
+++ b/core/src/main/java/com/graphhopper/storage/CHGraphImpl.java
@@ -54,14 +54,12 @@ public class CHGraphImpl implements CHGraph, Storable<CHGraph> {
     final int scDirMask = PrepareEncoder.getScDirMask();
     private final CHConfig chConfig;
     private final BaseGraph baseGraph;
-    private final CHEdgeAccess chEdgeAccess;
-    int N_CH_REF;
-    int shortcutEntryBytes;
-    // the nodesCH storage is limited via baseGraph.nodeCount too
-    int nodeCHEntryBytes;
-    private int N_LEVEL;
-    // shortcut memory layout is synced with edges indices until E_FLAGS, then:
-    private int S_SKIP_EDGE1, S_SKIP_EDGE2, S_ORIG_FIRST, S_ORIG_LAST;
+    // CH node memory layout, there are as many entries has baseGraph.nodeCount
+    private int N_LEVEL, N_CH_REF;
+    private int nodeCHEntryBytes;
+    // shortcut memory layout
+    private int E_NODEA, E_NODEB, S_WEIGHT, S_SKIP_EDGE1, S_SKIP_EDGE2, S_ORIG_FIRST, S_ORIG_LAST;
+    private int shortcutEntryBytes;
     private int shortcutCount = 0;
     private boolean isReadyForContraction;
 
@@ -73,7 +71,6 @@ public class CHGraphImpl implements CHGraph, Storable<CHGraph> {
         final String name = chConfig.getName();
         this.nodesCH = dir.find("nodes_ch_" + name, DAType.getPreferredInt(dir.getDefaultType()));
         this.shortcuts = dir.find("shortcuts_" + name, DAType.getPreferredInt(dir.getDefaultType()));
-        this.chEdgeAccess = new CHEdgeAccess();
         if (segmentSize >= 0) {
             nodesCH.setSegmentSize(segmentSize);
             shortcuts.setSegmentSize(segmentSize);
@@ -111,18 +108,115 @@ public class CHGraphImpl implements CHGraph, Storable<CHGraph> {
     public int shortcut(int a, int b, int accessFlags, double weight, int skippedEdge1, int skippedEdge2) {
         if (!baseGraph.isFrozen())
             throw new IllegalStateException("Cannot create shortcut if graph is not yet frozen");
-
         checkNodeId(a);
         checkNodeId(b);
-
+        // shortcuts must be inserted ordered by increasing level of node a
+        if (getLevel(a) >= baseGraph.getNodes() || getLevel(a) < 0)
+            throw new IllegalArgumentException("Invalid level for node " + a + ": " + getLevel(a) + ". Node a must" +
+                    " be assigned a valid level before we add shortcuts a->b or a<-b");
+        if (a != b && getLevel(a) == getLevel(b))
+            throw new IllegalArgumentException("Different nodes must not have the same level, got levels " + getLevel(a)
+                    + " and " + getLevel(b) + " for nodes " + a + " and " + b);
+        if (a != b && getLevel(a) > getLevel(b))
+            throw new IllegalArgumentException("The level of nodeA must be smaller than the level of nodeB, but got: " +
+                    getLevel(a) + " and " + getLevel(b) + ". When inserting shortcut: " + a + "-" + b);
+        if (shortcutCount > 0) {
+            int prevNodeA = getNodeA(toPointer(shortcutCount + baseGraph.edgeCount - 1));
+            int prevLevelA = getLevel(prevNodeA);
+            if (getLevel(a) < prevLevelA) {
+                throw new IllegalArgumentException("Invalid level for node " + a + ": " + getLevel(a) + ". The level " +
+                        "must be equal to or larger than the lower level node of the previous shortcut (node: " + prevNodeA +
+                        ", level: " + prevLevelA + ")");
+            }
+        }
         // we do not register the edge at node b which should be the higher level node (so no need to 'see' the lower
         // level node a)
-        int scId = chEdgeAccess.internalEdgeAdd(nextShortcutId(), a, b, false);
-        // do not create CHEdgeIteratorImpl object
-        long edgePointer = chEdgeAccess.toPointer(scId);
-        chEdgeAccess.setAccessAndWeight(edgePointer, accessFlags & scDirMask, weight);
-        chEdgeAccess.setSkippedEdges(edgePointer, skippedEdge1, skippedEdge2);
-        return scId;
+        int shortcutId = nextShortcutId();
+        writeShortcut(shortcutId, a, b);
+        // we keep track of the last shortcut for each node (-1 if there are no shortcuts)
+        setEdgeRef(a, shortcutId);
+        long edgePointer = toPointer(shortcutId);
+        setAccessAndWeight(edgePointer, accessFlags & scDirMask, weight);
+        setSkippedEdges(edgePointer, skippedEdge1, skippedEdge2);
+        return shortcutId;
+    }
+
+    void setShortcutFlags(long edgePointer, int flags) {
+        shortcuts.setInt(edgePointer + S_WEIGHT, flags);
+    }
+
+    int getShortcutFlags(long edgePointer) {
+        return shortcuts.getInt(edgePointer + S_WEIGHT);
+    }
+
+    void setShortcutWeight(long edgePointer, double weight) {
+        int accessFlags = getShortcutFlags(edgePointer) & scDirMask;
+        setAccessAndWeight(edgePointer, accessFlags, weight);
+    }
+
+    void setAccessAndWeight(long edgePointer, int accessFlags, double weight) {
+        int weightFlags = weightToWeightFlags(edgePointer, weight);
+        setShortcutFlags(edgePointer, weightFlags | accessFlags);
+    }
+
+    int weightToWeightFlags(long edgePointer, double weight) {
+        if (weight < 0)
+            throw new IllegalArgumentException("weight cannot be negative but was " + weight);
+
+        int weightInt;
+
+        if (weight < MIN_WEIGHT) {
+            NodeAccess nodeAccess = baseGraph.getNodeAccess();
+            // todo: how to get edge id
+            int edgeId = -1;
+            LOGGER.warn("Setting weights smaller than " + MIN_WEIGHT + " is not allowed in CHGraphImpl#setWeight. " +
+                    "You passed: " + weight + " for the edge " + edgeId +
+                    " nodeA " + nodeAccess.getLat(getNodeA(edgePointer)) + "," + nodeAccess.getLon(getNodeA(edgePointer)) +
+                    " nodeB " + nodeAccess.getLat(getNodeB(edgePointer)) + "," + nodeAccess.getLon(getNodeB(edgePointer)));
+            weight = MIN_WEIGHT;
+        }
+        if (weight > MAX_WEIGHT)
+            weightInt = MAX_WEIGHT_31;
+        else
+            weightInt = ((int) Math.round(weight * WEIGHT_FACTOR)) << 2;
+        return weightInt;
+    }
+
+    double getShortcutWeight(long edgePointer) {
+        // no need for reverseFlags call (shortcut has identical weight if both dies) and also no need for 64bit
+        long flags32bit = getShortcutFlags(edgePointer);
+        double weight = (flags32bit >>> 2) / WEIGHT_FACTOR;
+        if (weight >= MAX_WEIGHT)
+            return Double.POSITIVE_INFINITY;
+
+        return weight;
+    }
+
+    void setSkippedEdges(long edgePointer, int edge1, int edge2) {
+        if (EdgeIterator.Edge.isValid(edge1) != EdgeIterator.Edge.isValid(edge2)) {
+            throw new IllegalStateException("Skipped edges of a shortcut needs "
+                    + "to be both valid or invalid but they were not " + edge1 + ", " + edge2);
+        }
+        shortcuts.setInt(edgePointer + S_SKIP_EDGE1, edge1);
+        shortcuts.setInt(edgePointer + S_SKIP_EDGE2, edge2);
+    }
+
+    public void setFirstAndLastOrigEdges(long edgePointer, int origFirst, int origLast) {
+        if (!chConfig.isEdgeBased()) {
+            throw new IllegalStateException("Edge-based shortcuts should only be added when CHGraph is edge-based");
+        }
+        shortcuts.setInt(edgePointer + S_ORIG_FIRST, origFirst);
+        shortcuts.setInt(edgePointer + S_ORIG_LAST, origLast);
+    }
+
+    private long toPointer(int shortcutId) {
+        assert isInBounds(shortcutId) : "shortcutId " + shortcutId + " not in bounds [" + baseGraph.edgeCount + ", " + (baseGraph.edgeCount + shortcutCount) + ")";
+        return (long) (shortcutId - baseGraph.edgeCount) * shortcutEntryBytes;
+    }
+
+    private boolean isInBounds(int shortcutId) {
+        int tmp = shortcutId - baseGraph.edgeCount;
+        return tmp < shortcutCount && tmp >= 0;
     }
 
     @Override
@@ -131,7 +225,7 @@ public class CHGraphImpl implements CHGraph, Storable<CHGraph> {
             throw new IllegalStateException("Edge-based shortcuts should only be added when CHGraph is edge-based");
         }
         int scId = shortcut(a, b, accessFlags, weight, skippedEdge1, skippedEdge2);
-        chEdgeAccess.setFirstAndLastOrigEdges(chEdgeAccess.toPointer(scId), origFirst, origLast);
+        setFirstAndLastOrigEdges(toPointer(scId), origFirst, origLast);
         return scId;
     }
 
@@ -152,7 +246,7 @@ public class CHGraphImpl implements CHGraph, Storable<CHGraph> {
 
     @Override
     public CHEdgeExplorer createEdgeExplorer(EdgeFilter filter) {
-        return new CHEdgeIteratorImpl(baseGraph, chEdgeAccess, filter);
+        return new CHEdgeIteratorImpl(baseGraph, filter);
     }
 
     @Override
@@ -168,11 +262,11 @@ public class CHGraphImpl implements CHGraph, Storable<CHGraph> {
     @Override
     public final CHEdgeIteratorState getEdgeIteratorState(int edgeId, int endNode) {
         if (isShortcut(edgeId)) {
-            if (!chEdgeAccess.isInBounds(edgeId))
+            if (!isInBounds(edgeId))
                 throw new IllegalStateException("shortcutId " + edgeId + " out of bounds");
-        } else if (!baseGraph.edgeAccess.isInBounds(edgeId))
+        } else if (!baseGraph.isInBounds(edgeId))
             throw new IllegalStateException("edgeId " + edgeId + " out of bounds");
-        CHEdgeIteratorStateImpl edge = new CHEdgeIteratorStateImpl(new BaseGraph.EdgeIteratorStateImpl(chEdgeAccess, baseGraph));
+        CHEdgeIteratorStateImpl edge = new CHEdgeIteratorStateImpl(new BaseGraph.EdgeIteratorStateImpl(baseGraph));
         if (edge.init(edgeId, endNode))
             return edge;
         // if edgeId exists but adjacent nodes do not match
@@ -201,41 +295,53 @@ public class CHGraphImpl implements CHGraph, Storable<CHGraph> {
 
     @Override
     public int getOtherNode(int edge, int node) {
-        EdgeAccess edgeAccess = isShortcut(edge) ? chEdgeAccess : baseGraph.edgeAccess;
-        long edgePointer = edgeAccess.toPointer(edge);
-        return edgeAccess.getOtherNode(node, edgePointer);
+        if (isShortcut(edge)) {
+            long edgePointer = toPointer(edge);
+            int nodeA = getNodeA(edgePointer);
+            return node == nodeA ? getNodeB(edgePointer) : nodeA;
+        } else {
+            return baseGraph.getOtherNode(edge, node);
+        }
     }
 
     @Override
     public boolean isAdjacentToNode(int edge, int node) {
-        EdgeAccess edgeAccess = isShortcut(edge) ? chEdgeAccess : baseGraph.edgeAccess;
-        long edgePointer = edgeAccess.toPointer(edge);
-        return edgeAccess.isAdjacentToNode(node, edgePointer);
+        if (isShortcut(edge)) {
+            long edgePointer = toPointer(edge);
+            return getNodeA(edgePointer) == node || getNodeB(edgePointer) == node;
+        } else {
+            return baseGraph.isAdjacentToNode(edge, node);
+        }
     }
 
     void _prepareForContraction() {
-        if (isReadyForContraction) {
+        if (isReadyForContraction)
             return;
-        }
         long maxCapacity = ((long) getNodes()) * nodeCHEntryBytes;
         nodesCH.ensureCapacity(maxCapacity);
-        long baseCapacity = baseGraph.nodes.getCapacity();
-
         // copy normal edge refs into ch edge refs
-        for (long pointer = N_CH_REF, basePointer = baseGraph.N_EDGE_REF;
-             pointer < maxCapacity;
-             pointer += nodeCHEntryBytes, basePointer += baseGraph.nodeEntryBytes) {
-            if (basePointer >= baseCapacity)
-                throw new IllegalStateException("Cannot copy edge refs into ch graph. "
-                        + "pointer:" + pointer + ", cap:" + maxCapacity + ", basePtr:" + basePointer + ", baseCap:" + baseCapacity);
-
-            nodesCH.setInt(pointer, baseGraph.nodes.getInt(basePointer));
-        }
+        for (int node = 0; node < getNodes(); node++)
+            setEdgeRef(node, baseGraph.getEdgeRef(node));
         isReadyForContraction = true;
     }
 
+    /**
+     * Writes plain edge information to the edges index
+     */
+    private long writeShortcut(int edgeId, int nodeA, int nodeB) {
+        if (!EdgeIterator.Edge.isValid(edgeId))
+            throw new IllegalStateException("Cannot write edge with illegal ID:" + edgeId + "; nodeA:" + nodeA + ", nodeB:" + nodeB);
+
+        long edgePointer = toPointer(edgeId);
+        shortcuts.setInt(edgePointer + E_NODEA, nodeA);
+        shortcuts.setInt(edgePointer + E_NODEB, nodeB);
+        return edgePointer;
+    }
+
     String toDetailsString() {
-        return toString() + ", shortcuts:" + nf(shortcutCount) + ", nodesCH:(" + nodesCH.getCapacity() / Helper.MB + "MB)";
+        return toString() +
+                ", shortcuts:" + nf(shortcutCount) + " (" + nf(shortcuts.getCapacity() / Helper.MB) + "MB)" +
+                ", nodesCH:" + nf(getNodes()) + " (" + nf(nodesCH.getCapacity() / Helper.MB) + "MB)";
     }
 
     @Override
@@ -269,10 +375,11 @@ public class CHGraphImpl implements CHGraph, Storable<CHGraph> {
     }
 
     void initStorage() {
-        EdgeAccess ea = baseGraph.edgeAccess;
-        chEdgeAccess.init(ea.E_NODEA, ea.E_NODEB, ea.E_LINKA, ea.E_LINKB, ea.E_FLAGS);
         // shortcuts
-        S_SKIP_EDGE1 = ea.E_FLAGS + 4;
+        E_NODEA = 0;
+        E_NODEB = E_NODEA + 4;
+        S_WEIGHT = E_NODEB + 4;
+        S_SKIP_EDGE1 = S_WEIGHT + +4;
         S_SKIP_EDGE2 = S_SKIP_EDGE1 + 4;
         if (chConfig.isEdgeBased()) {
             S_ORIG_FIRST = S_SKIP_EDGE2 + 4;
@@ -340,28 +447,26 @@ public class CHGraphImpl implements CHGraph, Storable<CHGraph> {
         String formatNodes = "%12s | %12s | %12s \n";
         System.out.format(Locale.ROOT, formatNodes, "#", "N_CH_REF", "N_LEVEL");
         for (int i = 0; i < Math.min(baseGraph.getNodes(), printMax); ++i) {
-            System.out.format(Locale.ROOT, formatNodes, i, chEdgeAccess.getEdgeRef(i), getLevel(i));
+            System.out.format(Locale.ROOT, formatNodes, i, getEdgeRef(i), getLevel(i));
         }
         if (baseGraph.getNodes() > printMax) {
             System.out.format(Locale.ROOT, " ... %d more nodes", baseGraph.getNodes() - printMax);
         }
         System.out.println("shortcuts:");
-        String formatShortcutsBase = "%12s | %12s | %12s | %12s | %12s | %12s | %12s | %12s";
+        String formatShortcutsBase = "%12s | %12s | %12s | %12s | %12s | %12s";
         String formatShortcutExt = " | %12s | %12s";
-        String header = String.format(Locale.ROOT, formatShortcutsBase, "#", "E_NODEA", "E_NODEB", "E_LINKA", "E_LINKB", "E_FLAGS", "S_SKIP_EDGE1", "S_SKIP_EDGE2");
+        String header = String.format(Locale.ROOT, formatShortcutsBase, "#", "E_NODEA", "E_NODEB", "S_WEIGHT", "S_SKIP_EDGE1", "S_SKIP_EDGE2");
         if (chConfig.isEdgeBased()) {
             header += String.format(Locale.ROOT, formatShortcutExt, "S_ORIG_FIRST", "S_ORIG_LAST");
         }
         System.out.println(header);
         for (int i = baseGraph.edgeCount; i < baseGraph.edgeCount + Math.min(shortcutCount, printMax); ++i) {
-            long edgePointer = chEdgeAccess.toPointer(i);
+            long edgePointer = toPointer(i);
             String edgeString = String.format(Locale.ROOT, formatShortcutsBase,
                     i,
-                    chEdgeAccess.getNodeA(edgePointer),
-                    chEdgeAccess.getNodeB(edgePointer),
-                    chEdgeAccess.getLinkA(edgePointer),
-                    chEdgeAccess.getLinkB(edgePointer),
-                    chEdgeAccess.getShortcutFlags(edgePointer),
+                    getNodeA(edgePointer),
+                    getNodeB(edgePointer),
+                    getShortcutFlags(edgePointer),
                     shortcuts.getInt(edgePointer + S_SKIP_EDGE1),
                     shortcuts.getInt(edgePointer + S_SKIP_EDGE2));
             if (chConfig.isEdgeBased()) {
@@ -376,6 +481,14 @@ public class CHGraphImpl implements CHGraph, Storable<CHGraph> {
         }
     }
 
+    private int getNodeA(long edgePointer) {
+        return shortcuts.getInt(edgePointer + E_NODEA);
+    }
+
+    private int getNodeB(long edgePointer) {
+        return shortcuts.getInt(edgePointer + E_NODEB);
+    }
+
     public NodeOrderingProvider getNodeOrderingProvider() {
         int numNodes = getNodes();
         final int[] nodeOrdering = new int[numNodes];
@@ -386,52 +499,62 @@ public class CHGraphImpl implements CHGraph, Storable<CHGraph> {
             int level = getLevel(i);
             nodeOrdering[level] = i;
         }
-        return new NodeOrderingProvider() {
-            @Override
-            public int getNodeIdForLevel(int level) {
-                return nodeOrdering[level];
-            }
-
-            @Override
-            public int getNumNodes() {
-                return nodeOrdering.length;
-            }
-        };
+        return NodeOrderingProvider.fromArray(nodeOrdering);
     }
 
     class CHEdgeIteratorImpl extends CHEdgeIteratorStateImpl implements CHEdgeExplorer, CHEdgeIterator {
         private final EdgeIteratorImpl baseIterator;
+        private int nextEdgeId;
 
-        public CHEdgeIteratorImpl(BaseGraph baseGraph, EdgeAccess edgeAccess, EdgeFilter filter) {
-            super(new EdgeIteratorImpl(baseGraph, edgeAccess, filter));
+        public CHEdgeIteratorImpl(BaseGraph baseGraph, EdgeFilter filter) {
+            super(new EdgeIteratorImpl(baseGraph, filter));
             this.baseIterator = (EdgeIteratorImpl) super.edgeIterable;
         }
 
         public final CHEdgeIterator setBaseNode(int baseNode) {
             assert baseIterator.baseGraph.isFrozen() : "Traversing CHGraph is only possible if BaseGraph is frozen";
 
-            // always use ch edge access
-            baseIterator.setEdgeId(chEdgeAccess.getEdgeRef(baseNode));
-            baseIterator._setBaseNode(baseNode);
+            baseIterator.nextEdgeId = baseIterator.edgeId = baseGraph.getEdgeRef(baseNode);
+            baseIterator.baseNode = baseNode;
+
+            nextEdgeId = edgeId = CHGraphImpl.this.getEdgeRef(baseNode);
             return this;
         }
 
         @Override
         public boolean next() {
+            // todo: note that it would be more efficient to separate in/out edges, especially for edge-based where we
+            //       do not use bidirectional shortcuts
+            while (true) {
+                if (!EdgeIterator.Edge.isValid(nextEdgeId) || nextEdgeId < baseGraph.edgeCount)
+                    break;
+                edgeId = nextEdgeId;
+                edgePointer = toPointer(edgeId);
+                baseNode = getNodeA(edgePointer);
+                adjNode = getNodeB(edgePointer);
+                nextEdgeId = edgeId - 1;
+                if (nextEdgeId < baseGraph.edgeCount || getNodeA(toPointer(nextEdgeId)) != baseNode)
+                    nextEdgeId = edgeIterable.edgeId;
+                reverse = false;
+                freshFlags = false;
+                if (baseIterator.filter.accept(this))
+                    return true;
+            }
+
             while (true) {
                 if (!EdgeIterator.Edge.isValid(baseIterator.nextEdgeId))
                     return false;
-                selectEdgeAccess(baseIterator.nextEdgeId);
                 baseIterator.goToNext();
-                if (baseIterator.filter.accept(this)) {
+                // we update edgeId even when iterating base edges
+                edgeId = baseIterator.edgeId;
+                if (baseIterator.filter.accept(this))
                     return true;
-                }
             }
         }
 
         @Override
         public String toString() {
-            return baseIterator.toString();
+            return getEdge() + " " + getBaseNode() + "-" + getAdjNode();
         }
 
     }
@@ -446,16 +569,20 @@ public class CHGraphImpl implements CHGraph, Storable<CHGraph> {
 
         @Override
         public boolean next() {
-            allEdgeIterator.edgeId++;
-            if (allEdgeIterator.edgeId >= baseGraph.edgeCount + shortcutCount)
+            edgeId++;
+            if (edgeId < baseGraph.edgeCount) {
+                allEdgeIterator.next();
+                return true;
+            } else if (edgeId < baseGraph.edgeCount + shortcutCount) {
+                edgePointer = toPointer(edgeId);
+                baseNode = getNodeA(edgePointer);
+                adjNode = getNodeB(edgePointer);
+                freshFlags = false;
+                reverse = false;
+                return true;
+            } else {
                 return false;
-            selectEdgeAccess(allEdgeIterator.edgeId);
-            allEdgeIterator.edgePointer = allEdgeIterator.edgeAccess.toPointer(allEdgeIterator.edgeId);
-            allEdgeIterator.baseNode = allEdgeIterator.edgeAccess.getNodeA(allEdgeIterator.edgePointer);
-            allEdgeIterator.adjNode = allEdgeIterator.edgeAccess.getNodeB(allEdgeIterator.edgePointer);
-            allEdgeIterator.freshFlags = false;
-            allEdgeIterator.reverse = false;
-            return true;
+            }
         }
 
         @Override
@@ -465,7 +592,7 @@ public class CHGraphImpl implements CHGraph, Storable<CHGraph> {
 
         @Override
         public int getEdge() {
-            return allEdgeIterator.edgeId;
+            return edgeId;
         }
 
         @Override
@@ -475,111 +602,31 @@ public class CHGraphImpl implements CHGraph, Storable<CHGraph> {
 
         @Override
         public final boolean isShortcut() {
-            assert baseGraph.isFrozen() : "level graph not yet frozen";
-            return edgeIterable.edgeAccess == chEdgeAccess;
+            return edgeId >= baseGraph.edgeCount;
         }
     }
 
-    private class CHEdgeAccess extends EdgeAccess {
-
-        public CHEdgeAccess() {
-            super(shortcuts);
-        }
-
-        @Override
-        final int getEdgeRef(int nodeId) {
-            return nodesCH.getInt((long) nodeId * nodeCHEntryBytes + N_CH_REF);
-        }
-
-        @Override
-        final void setEdgeRef(int nodeId, int edgeId) {
-            nodesCH.setInt((long) nodeId * nodeCHEntryBytes + N_CH_REF, edgeId);
-        }
-
-        void setShortcutFlags(long edgePointer, int flags) {
-            edges.setInt(edgePointer + E_FLAGS, flags);
-        }
-
-        int getShortcutFlags(long edgePointer) {
-            return edges.getInt(edgePointer + E_FLAGS);
-        }
-
-        void setShortcutWeight(long edgePointer, double weight) {
-            int accessFlags = getShortcutFlags(edgePointer) & scDirMask;
-            setAccessAndWeight(edgePointer, accessFlags, weight);
-        }
-
-        void setAccessAndWeight(long edgePointer, int accessFlags, double weight) {
-            int weightFlags = weightToWeightFlags(edgePointer, weight);
-            setShortcutFlags(edgePointer, weightFlags | accessFlags);
-        }
-
-        int weightToWeightFlags(long edgePointer, double weight) {
-            if (weight < 0)
-                throw new IllegalArgumentException("weight cannot be negative but was " + weight);
-
-            int weightInt;
+    // todonow: EdgeAccess was a really bad interface: it was used to 'sneak in' some different behavior (edge access)
+    // into edge iterators so it could be used for CH. but this was very twisted and we want a different edge iteration
+    // for CH anyway. then again having some kind of 'access' class to abstract away the data access probably makes sense
+    // so maybe try this again now that edgeAccess is gone.
 
-            if (weight < MIN_WEIGHT) {
-                NodeAccess nodeAccess = baseGraph.getNodeAccess();
-                // todo: how to get edge id
-                int edgeId = -1;
-                LOGGER.warn("Setting weights smaller than " + MIN_WEIGHT + " is not allowed in CHGraphImpl#setWeight. " +
-                        "You passed: " + weight + " for the edge " + edgeId +
-                        " nodeA " + nodeAccess.getLat(getNodeA(edgePointer)) + "," + nodeAccess.getLon(getNodeA(edgePointer)) +
-                        " nodeB " + nodeAccess.getLat(getNodeB(edgePointer)) + "," + nodeAccess.getLon(getNodeB(edgePointer)));
-                weight = MIN_WEIGHT;
-            }
-            if (weight > MAX_WEIGHT)
-                weightInt = MAX_WEIGHT_31;
-            else
-                weightInt = ((int) Math.round(weight * WEIGHT_FACTOR)) << 2;
-            return weightInt;
-        }
-
-        double getShortcutWeight(long edgePointer) {
-            // no need for reverseFlags call (shortcut has identical weight if both dies) and also no need for 64bit
-            long flags32bit = getShortcutFlags(edgePointer);
-            double weight = (flags32bit >>> 2) / WEIGHT_FACTOR;
-            if (weight >= MAX_WEIGHT)
-                return Double.POSITIVE_INFINITY;
-
-            return weight;
-        }
-
-        void setSkippedEdges(long edgePointer, int edge1, int edge2) {
-            if (EdgeIterator.Edge.isValid(edge1) != EdgeIterator.Edge.isValid(edge2)) {
-                throw new IllegalStateException("Skipped edges of a shortcut needs "
-                        + "to be both valid or invalid but they were not " + edge1 + ", " + edge2);
-            }
-            shortcuts.setInt(edgePointer + S_SKIP_EDGE1, edge1);
-            shortcuts.setInt(edgePointer + S_SKIP_EDGE2, edge2);
-        }
-
-        public void setFirstAndLastOrigEdges(long edgePointer, int origFirst, int origLast) {
-            if (!chConfig.isEdgeBased()) {
-                throw new IllegalStateException("Edge-based shortcuts should only be added when CHGraph is edge-based");
-            }
-            shortcuts.setInt(edgePointer + S_ORIG_FIRST, origFirst);
-            shortcuts.setInt(edgePointer + S_ORIG_LAST, origLast);
-        }
-
-        @Override
-        final long toPointer(int shortcutId) {
-            assert isInBounds(shortcutId) : "shortcutId " + shortcutId + " not in bounds [" + baseGraph.edgeCount + ", " + (baseGraph.edgeCount + shortcutCount) + ")";
-            return (long) (shortcutId - baseGraph.edgeCount) * shortcutEntryBytes;
-        }
-
-        @Override
-        final boolean isInBounds(int shortcutId) {
-            int tmp = shortcutId - baseGraph.edgeCount;
-            return tmp < shortcutCount && tmp >= 0;
-        }
+    private int getEdgeRef(int nodeId) {
+        return nodesCH.getInt((long) nodeId * nodeCHEntryBytes + N_CH_REF);
+    }
 
+    private void setEdgeRef(int nodeId, int edgeId) {
+        nodesCH.setInt((long) nodeId * nodeCHEntryBytes + N_CH_REF, edgeId);
     }
 
     private class CHEdgeIteratorStateImpl implements CHEdgeIteratorState {
         final BaseGraph.EdgeIteratorStateImpl edgeIterable;
+        long edgePointer = -1;
+        int baseNode;
+        int adjNode;
+        boolean reverse = false;
+        boolean freshFlags;
+        int edgeId = -1;
         private int chFlags;
 
         private CHEdgeIteratorStateImpl(BaseGraph.EdgeIteratorStateImpl edgeIterable) {
@@ -587,28 +634,45 @@ public class CHGraphImpl implements CHGraph, Storable<CHGraph> {
         }
 
         boolean init(int edgeId, int expectedAdjNode) {
-            selectEdgeAccess(edgeId);
-            return edgeIterable.init(edgeId, expectedAdjNode);
-        }
-
-        void selectEdgeAccess(int edgeId) {
-            // iterate over edges or shortcuts
-            edgeIterable.edgeAccess = edgeId < edgeIterable.baseGraph.edgeCount ? edgeIterable.baseGraph.edgeAccess : chEdgeAccess;
+            if (edgeId < baseGraph.edgeCount) {
+                boolean b = edgeIterable.init(edgeId, expectedAdjNode);
+                this.edgeId = edgeIterable.edgeId;
+                return b;
+            } else {
+                if (!EdgeIterator.Edge.isValid(edgeId))
+                    throw new IllegalArgumentException("fetching the edge requires a valid edgeId but was " + edgeId);
+                this.edgeId = edgeId;
+                edgePointer = toPointer(edgeId);
+                baseNode = getNodeA(edgePointer);
+                adjNode = getNodeB(edgePointer);
+                freshFlags = false;
+
+                if (expectedAdjNode == adjNode || expectedAdjNode == Integer.MIN_VALUE) {
+                    reverse = false;
+                    return true;
+                } else if (expectedAdjNode == baseNode) {
+                    reverse = true;
+                    baseNode = adjNode;
+                    adjNode = expectedAdjNode;
+                    return true;
+                }
+                return false;
+            }
         }
 
         @Override
         public int getBaseNode() {
-            return edgeIterable.getBaseNode();
+            return edgeId < baseGraph.edgeCount ? edgeIterable.getBaseNode() : baseNode;
         }
 
         @Override
         public int getAdjNode() {
-            return edgeIterable.getAdjNode();
+            return edgeId < baseGraph.edgeCount ? edgeIterable.getAdjNode() : adjNode;
         }
 
         @Override
         public int getEdge() {
-            return edgeIterable.getEdge();
+            return edgeId < baseGraph.edgeCount ? edgeIterable.getEdge() : edgeId;
         }
 
         @Override
@@ -650,20 +714,20 @@ public class CHGraphImpl implements CHGraph, Storable<CHGraph> {
         @Override
         public final CHEdgeIteratorState setSkippedEdges(int edge1, int edge2) {
             checkShortcut(true, "setSkippedEdges");
-            chEdgeAccess.setSkippedEdges(edgeIterable.edgePointer, edge1, edge2);
+            CHGraphImpl.this.setSkippedEdges(edgePointer, edge1, edge2);
             return this;
         }
 
         @Override
         public final int getSkippedEdge1() {
             checkShortcut(true, "getSkippedEdge1");
-            return shortcuts.getInt(edgeIterable.edgePointer + S_SKIP_EDGE1);
+            return shortcuts.getInt(edgePointer + S_SKIP_EDGE1);
         }
 
         @Override
         public final int getSkippedEdge2() {
             checkShortcut(true, "getSkippedEdge2");
-            return shortcuts.getInt(edgeIterable.edgePointer + S_SKIP_EDGE2);
+            return shortcuts.getInt(edgePointer + S_SKIP_EDGE2);
         }
 
         @Override
@@ -671,7 +735,7 @@ public class CHGraphImpl implements CHGraph, Storable<CHGraph> {
             if (!isShortcut() || !chConfig.isEdgeBased()) {
                 return getEdge();
             }
-            return shortcuts.getInt(edgeIterable.edgePointer + S_ORIG_FIRST);
+            return shortcuts.getInt(edgePointer + S_ORIG_FIRST);
         }
 
         @Override
@@ -679,25 +743,24 @@ public class CHGraphImpl implements CHGraph, Storable<CHGraph> {
             if (!isShortcut() || !chConfig.isEdgeBased()) {
                 return getEdge();
             }
-            return shortcuts.getInt(edgeIterable.edgePointer + S_ORIG_LAST);
+            return shortcuts.getInt(edgePointer + S_ORIG_LAST);
         }
 
         @Override
         public boolean isShortcut() {
-            // assert baseGraph.isFrozen() : "chgraph not yet frozen";
-            return edgeIterable.edgeId >= edgeIterable.baseGraph.edgeCount;
+            return edgeId >= baseGraph.edgeCount;
         }
 
         @Override
         public boolean getFwdAccess() {
             checkShortcut(true, "getFwdAccess");
-            return (getShortcutFlags() & (edgeIterable.reverse ? PrepareEncoder.getScBwdDir() : PrepareEncoder.getScFwdDir())) != 0;
+            return (getShortcutFlags() & (reverse ? PrepareEncoder.getScBwdDir() : PrepareEncoder.getScFwdDir())) != 0;
         }
 
         @Override
         public boolean getBwdAccess() {
             checkShortcut(true, "getBwdAccess");
-            return (getShortcutFlags() & (edgeIterable.reverse ? PrepareEncoder.getScFwdDir() : PrepareEncoder.getScBwdDir())) != 0;
+            return (getShortcutFlags() & (reverse ? PrepareEncoder.getScFwdDir() : PrepareEncoder.getScBwdDir())) != 0;
         }
 
         @Override
@@ -720,22 +783,22 @@ public class CHGraphImpl implements CHGraph, Storable<CHGraph> {
         @Override
         public final CHEdgeIteratorState setWeight(double weight) {
             checkShortcut(true, "setWeight");
-            chEdgeAccess.setShortcutWeight(edgeIterable.edgePointer, weight);
+            CHGraphImpl.this.setShortcutWeight(edgePointer, weight);
             return this;
         }
 
         @Override
         public void setFlagsAndWeight(int flags, double weight) {
             checkShortcut(true, "setFlagsAndWeight");
-            chEdgeAccess.setAccessAndWeight(edgeIterable.edgePointer, flags, weight);
+            CHGraphImpl.this.setAccessAndWeight(edgePointer, flags, weight);
             chFlags = flags;
-            edgeIterable.freshFlags = true;
+            freshFlags = true;
         }
 
         @Override
         public final double getWeight() {
             checkShortcut(true, "getWeight");
-            return chEdgeAccess.getShortcutWeight(edgeIterable.edgePointer);
+            return CHGraphImpl.this.getShortcutWeight(edgePointer);
         }
 
         void checkShortcut(boolean shouldBeShortcut, String methodName) {
@@ -861,9 +924,9 @@ public class CHGraphImpl implements CHGraph, Storable<CHGraph> {
         }
 
         int getShortcutFlags() {
-            if (!edgeIterable.freshFlags) {
-                chFlags = chEdgeAccess.getShortcutFlags(edgeIterable.edgePointer);
-                edgeIterable.freshFlags = true;
+            if (!freshFlags) {
+                chFlags = CHGraphImpl.this.getShortcutFlags(edgePointer);
+                freshFlags = true;
             }
             return chFlags;
         }
diff --git a/core/src/main/java/com/graphhopper/storage/EdgeAccess.java b/core/src/main/java/com/graphhopper/storage/EdgeAccess.java
deleted file mode 100644
index 650460018..000000000
--- a/core/src/main/java/com/graphhopper/storage/EdgeAccess.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- *  Licensed to GraphHopper GmbH under one or more contributor
- *  license agreements. See the NOTICE file distributed with this work for
- *  additional information regarding copyright ownership.
- *
- *  GraphHopper GmbH licenses this file to you under the Apache License,
- *  Version 2.0 (the "License"); you may not use this file except in
- *  compliance with the License. You may obtain a copy of the License at
- *
- *       http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-package com.graphhopper.storage;
-
-import com.graphhopper.util.EdgeIterator;
-
-/**
- * @author Peter Karich
- */
-abstract class EdgeAccess {
-    final DataAccess edges;
-    int E_NODEA, E_NODEB, E_LINKA, E_LINKB, E_FLAGS;
-
-    EdgeAccess(DataAccess edges) {
-        this.edges = edges;
-    }
-
-    final void init(int E_NODEA, int E_NODEB, int E_LINKA, int E_LINKB, int E_FLAGS) {
-        this.E_NODEA = E_NODEA;
-        this.E_NODEB = E_NODEB;
-        this.E_LINKA = E_LINKA;
-        this.E_LINKB = E_LINKB;
-        this.E_FLAGS = E_FLAGS;
-    }
-
-    abstract long toPointer(int edgeOrShortcutId);
-
-    abstract boolean isInBounds(int edgeOrShortcutId);
-
-    abstract int getEdgeRef(int nodeId);
-
-    abstract void setEdgeRef(int nodeId, int edgeId);
-
-    final void readFlags(long edgePointer, IntsRef edgeFlags) {
-        int size = edgeFlags.ints.length;
-        for (int i = 0; i < size; i++) {
-            edgeFlags.ints[i] = edges.getInt(edgePointer + E_FLAGS + i * 4);
-        }
-    }
-
-    final void writeFlags(long edgePointer, IntsRef edgeFlags) {
-        int size = edgeFlags.ints.length;
-        for (int i = 0; i < size; i++) {
-            edges.setInt(edgePointer + E_FLAGS + i * 4, edgeFlags.ints[i]);
-        }
-    }
-
-    /**
-     * Writes a new edge to the array of edges and adds it to the linked list of edges at nodeA and nodeB
-     *
-     * @param connectB if false the edge is not registered at / will not be visible from nodeB, this is useful for
-     *                 CH.
-     */
-    final int internalEdgeAdd(int newEdgeId, int nodeA, int nodeB, boolean connectB) {
-        writeEdge(newEdgeId, nodeA, nodeB, EdgeIterator.NO_EDGE, EdgeIterator.NO_EDGE);
-        long edgePointer = toPointer(newEdgeId);
-
-        int edge = getEdgeRef(nodeA);
-        if (edge > EdgeIterator.NO_EDGE)
-            edges.setInt(E_LINKA + edgePointer, edge);
-        setEdgeRef(nodeA, newEdgeId);
-
-        if (connectB && nodeA != nodeB) {
-            edge = getEdgeRef(nodeB);
-            if (edge > EdgeIterator.NO_EDGE)
-                edges.setInt(E_LINKB + edgePointer, edge);
-            setEdgeRef(nodeB, newEdgeId);
-        }
-        return newEdgeId;
-    }
-
-    final int getNodeA(long edgePointer) {
-        return edges.getInt(edgePointer + E_NODEA);
-    }
-
-    final int getNodeB(long edgePointer) {
-        return edges.getInt(edgePointer + E_NODEB);
-    }
-
-    final int getLinkA(long edgePointer) {
-        return edges.getInt(edgePointer + E_LINKA);
-    }
-
-    final int getLinkB(long edgePointer) {
-        return edges.getInt(edgePointer + E_LINKB);
-    }
-
-    final int getOtherNode(int nodeThis, long edgePointer) {
-        int nodeA = getNodeA(edgePointer);
-        return nodeThis == nodeA ? getNodeB(edgePointer) : nodeA;
-    }
-
-    final boolean isAdjacentToNode(int node, long edgePointer) {
-        return getNodeA(edgePointer) == node || getNodeB(edgePointer) == node;
-    }
-
-    /**
-     * Writes plain edge information to the edges index
-     */
-    final long writeEdge(int edgeId, int nodeA, int nodeB, int nextEdgeA, int nextEdgeB) {
-        if (!EdgeIterator.Edge.isValid(edgeId))
-            throw new IllegalStateException("Cannot write edge with illegal ID:" + edgeId + "; nodeA:" + nodeA + ", nodeB:" + nodeB);
-
-        long edgePointer = toPointer(edgeId);
-        edges.setInt(edgePointer + E_NODEA, nodeA);
-        edges.setInt(edgePointer + E_NODEB, nodeB);
-        edges.setInt(edgePointer + E_LINKA, nextEdgeA);
-        edges.setInt(edgePointer + E_LINKB, nextEdgeB);
-        return edgePointer;
-    }
-
-}
diff --git a/core/src/main/java/com/graphhopper/util/Constants.java b/core/src/main/java/com/graphhopper/util/Constants.java
index 7f012f9a7..b306127ad 100644
--- a/core/src/main/java/com/graphhopper/util/Constants.java
+++ b/core/src/main/java/com/graphhopper/util/Constants.java
@@ -68,7 +68,7 @@ public class Constants {
 
     public static final int VERSION_NODE = 5;
     public static final int VERSION_EDGE = 17;
-    public static final int VERSION_SHORTCUT = 6;
+    public static final int VERSION_SHORTCUT = 7;
     public static final int VERSION_GEOMETRY = 4;
     public static final int VERSION_LOCATION_IDX = 3;
     public static final int VERSION_STRING_IDX = 5;


Test patch:
diff --git a/core/src/test/java/com/graphhopper/routing/AlternativeRouteCHTest.java b/core/src/test/java/com/graphhopper/routing/AlternativeRouteCHTest.java
index eb6ac98c4..cf81d5247 100644
--- a/core/src/test/java/com/graphhopper/routing/AlternativeRouteCHTest.java
+++ b/core/src/test/java/com/graphhopper/routing/AlternativeRouteCHTest.java
@@ -29,7 +29,6 @@ import com.graphhopper.storage.RAMDirectory;
 import com.graphhopper.util.PMap;
 import org.junit.Test;
 
-import java.util.Arrays;
 import java.util.List;
 
 import static org.junit.Assert.assertEquals;
@@ -81,19 +80,9 @@ public class AlternativeRouteCHTest {
         // Carefully construct the CH so that the forward tree and the backward tree
         // meet on all four possible paths from 5 to 10
         // 5 ---> 11 will be reachable via shortcuts, as 11 is on shortest path 5 --> 12
-        final List<Integer> nodeOrdering = Arrays.asList(0, 10, 12, 4, 3, 2, 5, 1, 6, 7, 8, 9, 11);
+        final int[] nodeOrdering = new int[]{0, 10, 12, 4, 3, 2, 5, 1, 6, 7, 8, 9, 11};
         PrepareContractionHierarchies contractionHierarchies = PrepareContractionHierarchies.fromGraphHopperStorage(graph, chConfig);
-        contractionHierarchies.useFixedNodeOrdering(new NodeOrderingProvider() {
-            @Override
-            public int getNodeIdForLevel(int level) {
-                return nodeOrdering.get(level);
-            }
-
-            @Override
-            public int getNumNodes() {
-                return nodeOrdering.size();
-            }
-        });
+        contractionHierarchies.useFixedNodeOrdering(NodeOrderingProvider.fromArray(nodeOrdering));
         contractionHierarchies.doWork();
         return graph;
     }
diff --git a/core/src/test/java/com/graphhopper/routing/CHQueryWithTurnCostsTest.java b/core/src/test/java/com/graphhopper/routing/CHQueryWithTurnCostsTest.java
index 5345f8db3..a7da5a1fb 100644
--- a/core/src/test/java/com/graphhopper/routing/CHQueryWithTurnCostsTest.java
+++ b/core/src/test/java/com/graphhopper/routing/CHQueryWithTurnCostsTest.java
@@ -148,16 +148,15 @@ public class CHQueryWithTurnCostsTest {
 
         graph.freeze();
 
-        // from contracting node 1
-        addShortcut(4, 3, 3, 4, 3, 4, 2);
-        // from contracting node 2
-        addShortcut(3, 4, 5, 6, 5, 6, 2);
+        setLevelEqualToNodeIdForAllNodes();
+        // from contracting nodes 1&2
+        addShortcut(3, 4, 3, 4, 3, 4, 2, true);
+        addShortcut(3, 4, 5, 6, 5, 6, 2, false);
         // from contracting node 3
-        addShortcut(4, 4, 3, 6, 9, 10, 4);
+        addShortcut(4, 4, 3, 6, 9, 10, 4, false);
         // from contracting node 4
-        addShortcut(8, 4, 2, 6, 2, 11, 5);
-        addShortcut(8, 6, 2, 7, 12, 7, 6);
-        setLevelEqualToNodeIdForAllNodes();
+        addShortcut(4, 8, 2, 6, 2, 11, 5, true);
+        addShortcut(6, 8, 2, 7, 12, 7, 6, true);
 
         testPathCalculation(0, 5, 9, IntArrayList.from(0, 7, 8, 4, 1, 3, 2, 4, 6, 5));
     }
@@ -184,16 +183,15 @@ public class CHQueryWithTurnCostsTest {
         setRestriction(1, 4, 7);
         graph.freeze();
 
-        // from contracting node 1
-        addShortcut(4, 3, 2, 3, 2, 3, 2);
-        // from contracting node 2
-        addShortcut(3, 4, 4, 5, 4, 5, 2);
+        setLevelEqualToNodeIdForAllNodes();
+        // from contracting nodes 1&2
+        addShortcut(3, 4, 2, 3, 2, 3, 2, true);
+        addShortcut(3, 4, 4, 5, 4, 5, 2, false);
         // from contracting node 3
-        addShortcut(4, 4, 2, 5, 9, 10, 4);
+        addShortcut(4, 4, 2, 5, 9, 10, 4, false);
         // from contracting node 4
-        addShortcut(6, 4, 1, 5, 1, 11, 5);
-        addShortcut(6, 7, 1, 6, 12, 6, 6);
-        setLevelEqualToNodeIdForAllNodes();
+        addShortcut(4, 6, 1, 5, 1, 11, 5, true);
+        addShortcut(6, 7, 1, 6, 12, 6, 6, false);
 
         testPathCalculation(5, 0, 9, IntArrayList.from(5, 6, 4, 1, 3, 2, 4, 7, 8, 0));
     }
@@ -214,8 +212,8 @@ public class CHQueryWithTurnCostsTest {
         graph.freeze();
 
         // only when node 0 is contracted a shortcut is added
-        addShortcut(2, 3, 1, 2, 1, 2, 7);
         setLevelEqualToNodeIdForAllNodes();
+        addShortcut(2, 3, 1, 2, 1, 2, 7, false);
 
         // when we are searching a path to the highest level node, the backward search will not expand any edges
         testPathCalculation(1, 4, 11, IntArrayList.from(1, 2, 0, 3, 4), 8);
@@ -231,21 +229,21 @@ public class CHQueryWithTurnCostsTest {
     public void testFindPathWithTurnCosts_directed_single_shortcut_fwdSearchStopsQuickly() {
         //     0
         //    / \
-        // 1-3-s-2-4
-        graph.edge(1, 3, 2, false);
-        graph.edge(3, 0, 3, false);
-        graph.edge(0, 2, 1, false);
-        graph.edge(2, 4, 3, false);
+        // 1-2-s-3-4
+        graph.edge(1, 2, 2, false);
+        graph.edge(2, 0, 3, false);
+        graph.edge(0, 3, 1, false);
+        graph.edge(3, 4, 3, false);
         graph.freeze();
 
-        setTurnCost(1, 3, 0, 2);
-        setTurnCost(0, 2, 4, 4);
+        setTurnCost(1, 2, 0, 2);
+        setTurnCost(0, 3, 4, 4);
 
-        // from contracting node 0
-        addShortcut(3, 2, 1, 2, 1, 2, 4);
         setLevelEqualToNodeIdForAllNodes();
+        // from contracting node 0
+        addShortcut(2, 3, 1, 2, 1, 2, 4, false);
 
-        testPathCalculation(1, 4, 9, IntArrayList.from(1, 3, 0, 2, 4), 6);
+        testPathCalculation(1, 4, 9, IntArrayList.from(1, 2, 0, 3, 4), 6);
     }
 
     @Test
@@ -263,10 +261,10 @@ public class CHQueryWithTurnCostsTest {
         setTurnCost(1, 0, 4, 1);
         graph.freeze();
 
-        // contraction of node 0 and 1 each yield a single shortcut
-        addShortcut(1, 4, 2, 3, 2, 3, 6);
-        addShortcut(3, 4, 1, 3, 1, 4, 10);
         setLevelEqualToNodeIdForAllNodes();
+        // contraction of node 0 and 1 each yield a single shortcut
+        addShortcut(1, 4, 2, 3, 2, 3, 6, false);
+        addShortcut(3, 4, 1, 3, 1, 4, 10, false);
 
         // the turn costs have to be accounted for also when the shortcuts are used
         testPathCalculation(2, 4, 11, IntArrayList.from(2, 3, 1, 0, 4), 8);
@@ -345,8 +343,8 @@ public class CHQueryWithTurnCostsTest {
         graph.edge(0, 3, 14, false);
         graph.edge(3, 2, 9, false);
         graph.freeze();
-        addShortcut(1, 3, 0, 1, 0, 1, 23);
         setLevelEqualToNodeIdForAllNodes();
+        addShortcut(1, 3, 0, 1, 0, 1, 23, false);
         testPathCalculation(0, 2, 23, IntArrayList.from(0, 3, 2));
     }
 
@@ -406,8 +404,12 @@ public class CHQueryWithTurnCostsTest {
         setRestriction(0, 3, nodeB);
 
         // one shortcut when contracting node 3
-        addShortcut(nodeA, nodeB, e3toA.getEdge(), e3toB.getEdge(), e3toA.getEdge(), e3toB.getEdge(), 2);
         setLevelEqualToNodeIdForAllNodes();
+        if (toLowerLevelNode) {
+            addShortcut(nodeB, nodeA, e3toA.getEdge(), e3toB.getEdge(), e3toA.getEdge(), e3toB.getEdge(), 2, true);
+        } else {
+            addShortcut(nodeA, nodeB, e3toA.getEdge(), e3toB.getEdge(), e3toA.getEdge(), e3toB.getEdge(), 2, false);
+        }
 
         // without u-turns the only 'possible' path 0-3-A-3-B-2 is forbidden
         testPathCalculation(0, 2, -1, IntArrayList.from());
@@ -498,9 +500,9 @@ public class CHQueryWithTurnCostsTest {
         setRestriction(edge1, edge4, 2);
         graph.freeze();
 
-        // contracting node 0 yields (the only) shortcut - and its a loop
-        addShortcut(2, 2, edge2.getEdge(), edge3.getEdge(), edge2.getEdge(), edge3.getEdge(), 2);
         setLevelEqualToNodeIdForAllNodes();
+        // contracting node 0 yields (the only) shortcut - and its a loop
+        addShortcut(2, 2, edge2.getEdge(), edge3.getEdge(), edge2.getEdge(), edge3.getEdge(), 2, false);
 
         // node 2 is the bridge node where the forward and backward searches meet (highest level). since there is a turn restriction
         // at node 2 we cannot go from 4 to 1 directly, but we need to take the loop at 2 first. when the backward
@@ -528,11 +530,11 @@ public class CHQueryWithTurnCostsTest {
         setRestriction(3, 4, 2);
         graph.freeze();
 
+        setLevelEqualToNodeIdForAllNodes();
         // contracting node 0
-        addShortcut(4, 1, 1, 2, 1, 2, 4);
+        addShortcut(1, 4, 1, 2, 1, 2, 4, true);
         // contracting node 1
-        addShortcut(4, 4, 1, 3, 5, 3, 9);
-        setLevelEqualToNodeIdForAllNodes();
+        addShortcut(4, 4, 1, 3, 5, 3, 9, false);
 
         testPathCalculation(3, 2, 15, IntArrayList.from(3, 4, 0, 1, 4, 2));
     }
@@ -573,10 +575,10 @@ public class CHQueryWithTurnCostsTest {
         graph.edge(nodeB, 7, 2, false);
         setRestriction(nodeA, 5, nodeB);
         graph.freeze();
-        addShortcut(3, 5, 4, 5, 4, 5, 3);
-        addShortcut(5, 3, 2, 3, 2, 3, 3);
-        addShortcut(5, 5, 2, 5, 9, 8, 6);
         setLevelEqualToNodeIdForAllNodes();
+        addShortcut(3, 5, 4, 5, 4, 5, 3, false);
+        addShortcut(3, 5, 2, 3, 2, 3, 3, true);
+        addShortcut(5, 5, 2, 5, 9, 8, 6, false);
 
         testPathCalculation(4, 7, 12, IntArrayList.from(4, nodeA, 5, 2, 3, 1, 5, nodeB, 7));
     }
@@ -614,18 +616,17 @@ public class CHQueryWithTurnCostsTest {
         setRestriction(e5to7, e4to7, 7);
         graph.freeze();
 
-        // contracting node 0 and 1
-        addShortcut(6, 1, 2, 0, 2, 0, 6);
-        addShortcut(6, 6, 2, 1, 9, 1, 7);
-        // contracting node 2 and 3
-        addShortcut(6, 3, 3, 4, 3, 4, 8);
-        addShortcut(6, 6, 3, 5, 11, 5, 10);
+        setLevelEqualToNodeIdForAllNodes();
+        // contracting node 0,1,2,3
+        addShortcut(1, 6, 2, 0, 2, 0, 6, true);
+        addShortcut(3, 6, 3, 4, 3, 4, 8, true);
+        addShortcut(6, 6, 2, 1, 9, 1, 7, false);
+        addShortcut(6, 6, 3, 5, 10, 5, 10, false);
         // contracting node 4 and 5 yields no shortcuts
         // contracting node 6 --> three shortcuts to account for double loop (we nest shortcuts inside each other)
-        addShortcut(7, 6, 6, 1, 6, 10, 8);
-        addShortcut(7, 6, 6, 5, 13, 12, 18);
-        addShortcut(7, 7, 6, 6, 14, 6, 19);
-        setLevelEqualToNodeIdForAllNodes();
+        addShortcut(6, 7, 6, 1, 6, 11, 8, true);
+        addShortcut(6, 7, 6, 5, 13, 12, 18, true);
+        addShortcut(7, 7, 6, 6, 14, 6, 19, false);
 
         testPathCalculation(4, 5, 24, IntArrayList.from(4, 7, 6, 0, 1, 6, 2, 3, 6, 7, 5));
         testPathCalculation(5, 4, 24, IntArrayList.from(5, 7, 6, 0, 1, 6, 2, 3, 6, 7, 4));
@@ -656,19 +657,19 @@ public class CHQueryWithTurnCostsTest {
         setRestriction(3, 6, 2);
         graph.freeze();
 
+        setLevelEqualToNodeIdForAllNodes();
         // contracting node 0
-        addShortcut(5, 1, 2, 0, 2, 0, 3);
+        addShortcut(1, 5, 2, 0, 2, 0, 3, true);
         // contracting node 1
-        addShortcut(5, 5, 2, 1, 8, 1, 4);
+        addShortcut(5, 5, 2, 1, 8, 1, 4, false);
         // contracting node 2 & 3 does not yield any shortcuts
         // contracting node 4
-        addShortcut(5, 6, 3, 5, 3, 5, 9);
+        addShortcut(5, 6, 3, 5, 3, 5, 9, false);
         // contracting node 5 --> two shortcuts to account for loop (we nest shortcuts inside each other)
-        addShortcut(6, 5, 4, 1, 4, 9, 7);
-        addShortcut(6, 6, 4, 4, 11, 4, 10);
+        addShortcut(5, 6, 4, 1, 4, 9, 7, true);
+        addShortcut(6, 6, 4, 4, 11, 4, 10, false);
         // contracting node 6 --> no more shortcuts
 
-        setLevelEqualToNodeIdForAllNodes();
 
         List<List<Integer>> distMatrix = Arrays.asList(
                 // -1 if no path is expected
@@ -715,8 +716,9 @@ public class CHQueryWithTurnCostsTest {
                 new DijkstraBidirectionEdgeCHNoSOD(new RoutingCHGraphImpl(chGraph));
     }
 
-    private void addShortcut(int from, int to, int firstOrigEdge, int lastOrigEdge, int skipped1, int skipped2, double weight) {
-        chGraph.shortcutEdgeBased(from, to, PrepareEncoder.getScFwdDir(), weight, skipped1, skipped2, firstOrigEdge, lastOrigEdge);
+    private void addShortcut(int from, int to, int firstOrigEdge, int lastOrigEdge, int skipped1, int skipped2, double weight, boolean reverse) {
+        int flags = reverse ? PrepareEncoder.getScBwdDir() : PrepareEncoder.getScFwdDir();
+        chGraph.shortcutEdgeBased(from, to, flags, weight, skipped1, skipped2, firstOrigEdge, lastOrigEdge);
     }
 
     private void setLevelEqualToNodeIdForAllNodes() {
diff --git a/core/src/test/java/com/graphhopper/routing/DijkstraBidirectionCHTest.java b/core/src/test/java/com/graphhopper/routing/DijkstraBidirectionCHTest.java
index 302b4bce7..e96db8f60 100644
--- a/core/src/test/java/com/graphhopper/routing/DijkstraBidirectionCHTest.java
+++ b/core/src/test/java/com/graphhopper/routing/DijkstraBidirectionCHTest.java
@@ -20,7 +20,6 @@ package com.graphhopper.routing;
 import com.carrotsearch.hppc.IntArrayList;
 import com.graphhopper.routing.ch.CHRoutingAlgorithmFactory;
 import com.graphhopper.routing.ch.PrepareContractionHierarchies;
-import com.graphhopper.routing.ch.PrepareEncoder;
 import com.graphhopper.routing.ev.DecimalEncodedValue;
 import com.graphhopper.routing.util.EncodingManager;
 import com.graphhopper.routing.util.FlagEncoder;
@@ -29,7 +28,6 @@ import com.graphhopper.routing.weighting.ShortestWeighting;
 import com.graphhopper.routing.weighting.Weighting;
 import com.graphhopper.storage.*;
 import com.graphhopper.util.EdgeIteratorState;
-import com.graphhopper.util.GHUtility;
 import com.graphhopper.util.PMap;
 import org.junit.Test;
 
@@ -49,59 +47,16 @@ public class DijkstraBidirectionCHTest {
 
     private final EncodingManager encodingManager;
     private final FlagEncoder carEncoder;
-    private final FlagEncoder footEncoder;
     private final FlagEncoder bike2Encoder;
     private final FlagEncoder motorCycleEncoder;
 
     public DijkstraBidirectionCHTest() {
         encodingManager = EncodingManager.create("car,foot,bike2,motorcycle");
         carEncoder = encodingManager.getEncoder("car");
-        footEncoder = encodingManager.getEncoder("foot");
         bike2Encoder = encodingManager.getEncoder("bike2");
         motorCycleEncoder = encodingManager.getEncoder("motorcycle");
     }
 
-    @Test
-    public void testPathRecursiveUnpacking() {
-        // use an encoder where it is possible to store 2 weights per edge        
-        ShortestWeighting weighting = new ShortestWeighting(bike2Encoder);
-        GraphHopperStorage g = createGHStorage(weighting);
-        g.edge(0, 1, 1, true);
-        EdgeIteratorState iter1_1 = g.edge(0, 2, 1.4, false);
-        EdgeIteratorState iter1_2 = g.edge(2, 5, 1.4, false);
-        g.edge(1, 2, 1, true);
-        g.edge(1, 3, 3, true);
-        g.edge(2, 3, 1, true);
-        g.edge(4, 3, 1, true);
-        g.edge(2, 5, 1.4, true);
-        g.edge(3, 5, 1, true);
-        g.edge(5, 6, 1, true);
-        g.edge(4, 6, 1, true);
-        g.edge(6, 7, 1, true);
-        EdgeIteratorState iter2_2 = g.edge(5, 7);
-        iter2_2.setDistance(1.4).setFlags(GHUtility.setProperties(encodingManager.createEdgeFlags(), bike2Encoder, 10, true, false));
-        g.freeze();
-
-        CHGraph lg = g.getCHGraph();
-        // simulate preparation
-        int sc2_1 = lg.shortcut(0, 5, PrepareEncoder.getScFwdDir(), 1, iter1_1.getEdge(), iter1_2.getEdge());
-        lg.shortcut(0, 7, PrepareEncoder.getScFwdDir(), 1, sc2_1, iter2_2.getEdge());
-        lg.setLevel(1, 0);
-        lg.setLevel(3, 1);
-        lg.setLevel(4, 2);
-        lg.setLevel(6, 3);
-        lg.setLevel(2, 4);
-        lg.setLevel(5, 5);
-        lg.setLevel(7, 6);
-        lg.setLevel(0, 7);
-
-        Path p = createCHAlgo(g.getRoutingCHGraph(lg.getCHConfig().getName()), true).calcPath(0, 7);
-
-        assertEquals(IntArrayList.from(0, 2, 5, 7), p.calcNodes());
-        assertEquals(1064, p.getTime());
-        assertEquals(4.2, p.getDistance(), 1e-5);
-    }
-
     @Test
     public void testBaseGraph() {
         ShortestWeighting weighting = new ShortestWeighting(carEncoder);
diff --git a/core/src/test/java/com/graphhopper/routing/QueryRoutingCHGraphTest.java b/core/src/test/java/com/graphhopper/routing/QueryRoutingCHGraphTest.java
index 23c52d1a1..7be4da891 100644
--- a/core/src/test/java/com/graphhopper/routing/QueryRoutingCHGraphTest.java
+++ b/core/src/test/java/com/graphhopper/routing/QueryRoutingCHGraphTest.java
@@ -103,6 +103,7 @@ class QueryRoutingCHGraphTest {
         graph.edge(1, 2, 10, true);
         graph.freeze();
         assertEquals(2, graph.getEdges());
+        setIdentityLevels(chGraph);
         chGraph.shortcut(0, 2, PrepareEncoder.getScFwdDir(), 20, 0, 1);
 
         QueryGraph queryGraph = QueryGraph.create(graph, Collections.<Snap>emptyList());
@@ -202,6 +203,7 @@ class QueryRoutingCHGraphTest {
         addEdge(graph, 1, 2);
         graph.freeze();
         assertEquals(2, graph.getEdges());
+        setIdentityLevels(chGraph);
         chGraph.shortcut(0, 2, PrepareEncoder.getScFwdDir(), 20, 0, 1);
 
         Snap snap = new Snap(50.00, 10.05);
@@ -265,6 +267,7 @@ class QueryRoutingCHGraphTest {
         EdgeIteratorState edge = addEdge(graph, 0, 1);
         addEdge(graph, 1, 2);
         graph.freeze();
+        setIdentityLevels(chGraph);
         chGraph.shortcut(0, 2, PrepareEncoder.getScFwdDir(), 20, 0, 1);
 
         Snap snap = new Snap(50.00, 10.05);
@@ -330,6 +333,7 @@ class QueryRoutingCHGraphTest {
                 .setReverse(encoder.getAverageSpeedEnc(), 30);
         addEdge(graph, 1, 2);
         graph.freeze();
+        setIdentityLevels(chGraph);
         chGraph.shortcut(0, 2, PrepareEncoder.getScDirMask(), 20, 0, 1);
 
         // without query graph
@@ -501,6 +505,7 @@ class QueryRoutingCHGraphTest {
         DecimalEncodedValue turnCostEnc = encodingManager.getDecimalEncodedValue(TurnCost.key(encoder.toString()));
         graph.getTurnCostStorage().set(turnCostEnc, 0, 1, 1, 5);
         graph.freeze();
+        setIdentityLevels(chGraph);
         chGraph.shortcut(0, 2, PrepareEncoder.getScDirMask(), 20, 0, 1);
 
         // without virtual nodes
@@ -542,6 +547,12 @@ class QueryRoutingCHGraphTest {
         assertEquals(5, queryCHGraph.getTurnWeight(expectedEdge31, 1, expectedEdge14));
     }
 
+    public static void setIdentityLevels(CHGraph chGraph) {
+        for (int i = 0; i < chGraph.getNodes(); i++) {
+            chGraph.setLevel(i, i);
+        }
+    }
+
     private void assertGetEdgeIteratorState(RoutingCHGraph graph, int base, int adj, int origEdge) {
         int chEdge = getCHEdge(graph.createOutEdgeExplorer(), base, adj);
         assertEdgeState(graph.getEdgeIteratorState(chEdge, adj), base, adj, origEdge);
diff --git a/core/src/test/java/com/graphhopper/routing/ch/CHTurnCostTest.java b/core/src/test/java/com/graphhopper/routing/ch/CHTurnCostTest.java
index 6fcae5d21..fd8dcbcee 100644
--- a/core/src/test/java/com/graphhopper/routing/ch/CHTurnCostTest.java
+++ b/core/src/test/java/com/graphhopper/routing/ch/CHTurnCostTest.java
@@ -135,7 +135,7 @@ public class CHTurnCostTest {
         graph.edge(2, 4, 3, false);
         setRestriction(3, 2, 4);
         graph.freeze();
-        compareCHWithDijkstra(10, Arrays.asList(0, 1, 2, 3, 4));
+        compareCHWithDijkstra(10, new int[]{0, 1, 2, 3, 4});
     }
 
     @Test
@@ -149,7 +149,7 @@ public class CHTurnCostTest {
         graph.edge(1, 2, 21.862000, true);
         graph.edge(2, 3, 52.987000, true);
         graph.freeze();
-        compareCHWithDijkstra(1000, Arrays.asList(0, 1, 2, 3));
+        compareCHWithDijkstra(1000, new int[]{0, 1, 2, 3});
     }
 
     @RepeatedTest(100)
@@ -193,7 +193,7 @@ public class CHTurnCostTest {
         setRandomCostOrRestriction(6, 7, 8, rnd);
         setRandomCostOrRestriction(6, 7, 9, rnd);
 
-        prepareCH(Arrays.asList(6, 0, 1, 2, 8, 9, 10, 5, 3, 4, 7));
+        prepareCH(6, 0, 1, 2, 8, 9, 10, 5, 3, 4, 7);
         // run queries for all cases (target/source edge possibly restricted/has costs)
         checkStrict = false;
         compareCHQueryWithDijkstra(2, 10);
@@ -223,7 +223,7 @@ public class CHTurnCostTest {
         setRestriction(1, 5, 6);
         setRestriction(4, 7, 9);
 
-        prepareCH(Arrays.asList(6, 0, 1, 2, 8, 9, 10, 5, 3, 4, 7));
+        prepareCH(6, 0, 1, 2, 8, 9, 10, 5, 3, 4, 7);
         compareCHQueryWithDijkstra(2, 9);
     }
 
@@ -236,10 +236,71 @@ public class CHTurnCostTest {
         graph.edge(2, 3, 1, false);
         graph.edge(2, 3, 1, false);
         graph.edge(3, 4, 1, false);
-        List<Integer> contractionOrder = Arrays.asList(2, 3, 0, 4, 1);
-        compareCHWithDijkstra(100, contractionOrder);
+        compareCHWithDijkstra(100, new int[]{2, 3, 0, 4, 1});
     }
 
+    @Test
+    public void testFindPath_chain() {
+        // 0   2   4   6   8
+        //  \ / \ / \ / \ /
+        //   1   3   5   7
+        graph.edge(0, 1, 1, false);
+        graph.edge(1, 2, 1, false);
+        graph.edge(2, 3, 1, false);
+        graph.edge(3, 4, 1, false);
+        graph.edge(4, 5, 1, false);
+        graph.edge(5, 6, 1, false);
+        graph.edge(6, 7, 1, false);
+        graph.edge(7, 8, 1, false);
+        graph.freeze();
+        setTurnCost(1, 2, 3, 4);
+        setTurnCost(3, 4, 5, 2);
+        setTurnCost(5, 6, 7, 3);
+
+        // we contract the graph such that only a few shortcuts are created and that the fwd/bwd searches for the
+        // 0-8 query meet at node 4 (make sure we include all three cases where turn cost times might come to play:
+        // fwd/bwd search and meeting point)
+        checkPathUsingCH(ArrayUtil.iota(9), 8, 9, 0, 8, new int[]{1, 3, 5, 7, 0, 8, 2, 6, 4});
+    }
+
+    @Test
+    public void testFindPath_bidir_chain() {
+        //   5 3 2 1 4    turn costs ->
+        // 0-1-2-3-4-5-6
+        //   0 1 4 2 3    turn costs <-
+        EdgeIteratorState edge0 = graph.edge(0, 1, 1, true);
+        EdgeIteratorState edge1 = graph.edge(1, 2, 1, true);
+        EdgeIteratorState edge2 = graph.edge(2, 3, 1, true);
+        EdgeIteratorState edge3 = graph.edge(3, 4, 1, true);
+        EdgeIteratorState edge4 = graph.edge(4, 5, 1, true);
+        EdgeIteratorState edge5 = graph.edge(5, 6, 1, true);
+        graph.freeze();
+
+        // turn costs ->
+        setTurnCost(edge0, edge1, 1, 5);
+        setTurnCost(edge1, edge2, 2, 3);
+        setTurnCost(edge2, edge3, 3, 2);
+        setTurnCost(edge3, edge4, 4, 1);
+        setTurnCost(edge4, edge5, 5, 4);
+        // turn costs <-
+        setTurnCost(edge5, edge4, 5, 3);
+        setTurnCost(edge4, edge3, 4, 2);
+        setTurnCost(edge3, edge2, 3, 4);
+        setTurnCost(edge2, edge1, 2, 1);
+        setTurnCost(edge1, edge0, 1, 0);
+
+        prepareCH(1, 3, 5, 2, 4, 0, 6);
+
+        Path pathFwd = createAlgo().calcPath(0, 6);
+        assertEquals(IntArrayList.from(0, 1, 2, 3, 4, 5, 6), pathFwd.calcNodes());
+        assertEquals(6 + 15, pathFwd.getWeight(), 1.e-6);
+
+        Path pathBwd = createAlgo().calcPath(6, 0);
+        assertEquals(IntArrayList.from(6, 5, 4, 3, 2, 1, 0), pathBwd.calcNodes());
+        assertEquals(6 + 10, pathBwd.getWeight(), 1.e-6);
+    }
+
+
     @RepeatedTest(10)
     public void testFindPath_randomContractionOrder_simpleLoop() {
         //      2
@@ -484,7 +545,7 @@ public class CHTurnCostTest {
         setRestriction(5, 6, 1);
 
         final IntArrayList expectedPath = IntArrayList.from(5, 6, 4, 0, 3, 2, 4, 6, 1);
-        checkPath(expectedPath, 8, 0, 5, 1, Arrays.asList(0, 1, 2, 3, 4, 5, 6));
+        checkPath(expectedPath, 8, 0, 5, 1, new int[]{0, 1, 2, 3, 4, 5, 6});
     }
 
 
@@ -509,7 +570,7 @@ public class CHTurnCostTest {
         setRestriction(5, 6, 7);
 
         final IntArrayList expectedPath = IntArrayList.from(5, 6, 1, 4, 0, 3, 2, 4, 1, 6, 7);
-        checkPath(expectedPath, 10, 0, 5, 7, Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7));
+        checkPath(expectedPath, 10, 0, 5, 7, new int[]{0, 1, 2, 3, 4, 5, 6, 7});
     }
 
     @RepeatedTest(10)
@@ -590,9 +651,9 @@ public class CHTurnCostTest {
             }
         }
 
-        List<Integer> contractionOrder = getRandomIntegerSequence(graph.getNodes(), rnd);
+        IntArrayList contractionOrder = getRandomIntegerSequence(graph.getNodes(), rnd);
         checkStrict = false;
-        compareCHWithDijkstra(numQueries, contractionOrder);
+        compareCHWithDijkstra(numQueries, contractionOrder.toArray());
     }
 
     @Test
@@ -605,8 +666,7 @@ public class CHTurnCostTest {
         setRestriction(3, 1, 2);
         graph.freeze();
 
-        List<Integer> contractionOrder = Arrays.asList(1, 0, 3, 2, 4);
-        compareCHWithDijkstra(100, contractionOrder);
+        compareCHWithDijkstra(100, new int[]{1, 0, 3, 2, 4});
     }
 
     @Test
@@ -618,8 +678,7 @@ public class CHTurnCostTest {
         graph.edge(2, 4, 46.184000, true);
         graph.freeze();
 
-        List<Integer> contractionOrder = Arrays.asList(1, 0, 3, 2, 4);
-        compareCHWithDijkstra(1000, contractionOrder);
+        compareCHWithDijkstra(1000, new int[]{1, 0, 3, 2, 4});
     }
 
     @Test
@@ -641,7 +700,7 @@ public class CHTurnCostTest {
         setRestriction(8, 4, 6);
         graph.freeze();
 
-        prepareCH(Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8));
+        prepareCH(0, 1, 2, 3, 4, 5, 6, 7, 8);
         compareCHQueryWithDijkstra(0, 5);
     }
 
@@ -662,7 +721,7 @@ public class CHTurnCostTest {
         setRestriction(0, 3, 1);
         graph.freeze();
         chConfig = chConfigs.get(1);
-        prepareCH(Arrays.asList(4, 0, 2, 3, 1));
+        prepareCH(4, 0, 2, 3, 1);
         Path path = createAlgo().calcPath(0, 1);
         assertEquals(IntArrayList.from(0, 3, 4, 3, 1), path.calcNodes());
         compareCHQueryWithDijkstra(0, 1);
@@ -684,7 +743,7 @@ public class CHTurnCostTest {
         setTurnCost(edge0, edge4, 1, 8);
         setRestriction(edge0, edge3, 1);
         graph.freeze();
-        checkPath(IntArrayList.from(2, 1, 0, 4), 3, 8, 2, 4, Arrays.asList(2, 0, 1, 3, 4));
+        checkPath(IntArrayList.from(2, 1, 0, 4), 3, 8, 2, 4, new int[]{2, 0, 1, 3, 4});
     }
 
     @Test
@@ -700,7 +759,7 @@ public class CHTurnCostTest {
         setRestriction(edge0, edge2, 1);
         graph.freeze();
         final IntArrayList expectedPath = IntArrayList.from(0, 1, 1, 2, 3);
-        checkPath(expectedPath, 4, 1, 0, 3, Arrays.asList(0, 2, 1, 3));
+        checkPath(expectedPath, 4, 1, 0, 3, new int[]{0, 2, 1, 3});
     }
 
     @Test
@@ -731,8 +790,7 @@ public class CHTurnCostTest {
         graph.edge(3, 4, 1, false);
         graph.freeze();
         IntArrayList expectedPath = IntArrayList.from(0, 1, 2, 3, 4);
-        List<Integer> contractionOrder = Arrays.asList(2, 0, 4, 1, 3);
-        checkPath(expectedPath, 4, 0, 0, 4, contractionOrder);
+        checkPath(expectedPath, 4, 0, 0, 4, new int[]{2, 0, 4, 1, 3});
     }
 
     @Test
@@ -753,8 +811,7 @@ public class CHTurnCostTest {
         setRestriction(edge2, edge5, 3);
         graph.freeze();
         IntArrayList expectedPath = IntArrayList.from(0, 1, 2, 3, 3, 4);
-        List<Integer> contractionOrder = Arrays.asList(2, 0, 4, 1, 3);
-        checkPath(expectedPath, 4, 4, 0, 4, contractionOrder);
+        checkPath(expectedPath, 4, 4, 0, 4, new int[]{2, 0, 4, 1, 3});
     }
 
     @Test
@@ -874,7 +931,7 @@ public class CHTurnCostTest {
         // cannot go, 2-3-1
         setRestriction(edge1, edge0, 3);
         graph.freeze();
-        prepareCH(Arrays.asList(0, 1, 2, 3, 4, 5));
+        prepareCH(0, 1, 2, 3, 4, 5);
         assertEquals(5, chGraph.getBaseGraph().getEdges());
         assertEquals("expected two shortcuts: 3->5 and 5->3", 7, chGraph.getEdges());
         // there should be no path from 2 to 1, because of the turn restriction and because u-turns are not allowed
@@ -971,7 +1028,7 @@ public class CHTurnCostTest {
         setRestriction(2, 1, 5);
         graph.freeze();
         chConfig = chConfigs.get(1);
-        prepareCH(Arrays.asList(0, 1, 2, 3, 4, 5, 6));
+        prepareCH(0, 1, 2, 3, 4, 5, 6);
         LocationIndexTree index = new LocationIndexTree(graph, new RAMDirectory());
         index.prepareIndex();
         GHPoint virtualPoint = new GHPoint(0.1, 0.35);
@@ -1023,8 +1080,8 @@ public class CHTurnCostTest {
         GHUtility.addRandomTurnCosts(graph, seed, encodingManager, encoder, maxCost, turnCostStorage);
         graph.freeze();
         checkStrict = false;
-        List<Integer> contractionOrder = getRandomIntegerSequence(graph.getNodes(), rnd);
-        compareCHWithDijkstra(100, contractionOrder);
+        IntArrayList contractionOrder = getRandomIntegerSequence(graph.getNodes(), rnd);
+        compareCHWithDijkstra(100, contractionOrder.toArray());
     }
 
     /**
@@ -1063,11 +1120,11 @@ public class CHTurnCostTest {
     }
 
     private void checkPathUsingRandomContractionOrder(IntArrayList expectedPath, int expectedWeight, int expectedTurnCosts, int from, int to) {
-        List<Integer> contractionOrder = getRandomIntegerSequence(graph.getNodes());
-        checkPath(expectedPath, expectedWeight, expectedTurnCosts, from, to, contractionOrder);
+        IntArrayList contractionOrder = getRandomIntegerSequence(graph.getNodes(), new Random());
+        checkPath(expectedPath, expectedWeight, expectedTurnCosts, from, to, contractionOrder.toArray());
     }
 
-    private void checkPath(IntArrayList expectedPath, int expectedEdgeWeight, int expectedTurnCosts, int from, int to, List<Integer> contractionOrder) {
+    private void checkPath(IntArrayList expectedPath, int expectedEdgeWeight, int expectedTurnCosts, int from, int to, int[] contractionOrder) {
         checkPathUsingDijkstra(expectedPath, expectedEdgeWeight, expectedTurnCosts, from, to);
         checkPathUsingCH(expectedPath, expectedEdgeWeight, expectedTurnCosts, from, to, contractionOrder);
     }
@@ -1083,7 +1140,7 @@ public class CHTurnCostTest {
         assertEquals("Normal Dijkstra did not calculate expected time.", expectedTime, dijkstraPath.getTime(), 1.e-6);
     }
 
-    private void checkPathUsingCH(IntArrayList expectedPath, int expectedEdgeWeight, int expectedTurnCosts, int from, int to, List<Integer> contractionOrder) {
+    private void checkPathUsingCH(IntArrayList expectedPath, int expectedEdgeWeight, int expectedTurnCosts, int from, int to, int[] contractionOrder) {
         Path chPath = findPathUsingCH(from, to, contractionOrder);
         int expectedWeight = expectedEdgeWeight + expectedTurnCosts;
         int expectedDistance = expectedEdgeWeight;
@@ -1100,26 +1157,16 @@ public class CHTurnCostTest {
         return dijkstra.calcPath(from, to);
     }
 
-    private Path findPathUsingCH(int from, int to, List<Integer> contractionOrder) {
+    private Path findPathUsingCH(int from, int to, int[] contractionOrder) {
         prepareCH(contractionOrder);
         RoutingAlgorithm chAlgo = createAlgo();
         return chAlgo.calcPath(from, to);
     }
 
-    private void prepareCH(final List<Integer> contractionOrder) {
+    private void prepareCH(int... contractionOrder) {
         LOGGER.debug("Calculating CH with contraction order {}", contractionOrder);
         graph.freeze();
-        NodeOrderingProvider nodeOrderingProvider = new NodeOrderingProvider() {
-            @Override
-            public int getNodeIdForLevel(int level) {
-                return contractionOrder.get(level);
-            }
-
-            @Override
-            public int getNumNodes() {
-                return contractionOrder.size();
-            }
-        };
+        NodeOrderingProvider nodeOrderingProvider = NodeOrderingProvider.fromArray(contractionOrder);
         PrepareContractionHierarchies ch = PrepareContractionHierarchies.fromGraphHopperStorage(graph, chConfig)
                 .useFixedNodeOrdering(nodeOrderingProvider);
         ch.doWork();
@@ -1148,7 +1195,7 @@ public class CHTurnCostTest {
         }
     }
 
-    private void compareCHWithDijkstra(int numQueries, List<Integer> contractionOrder) {
+    private void compareCHWithDijkstra(int numQueries, int[] contractionOrder) {
         long seed = System.nanoTime();
         LOGGER.info("Seed used to create random routing queries: {}", seed);
         final Random rnd = new Random(seed);
@@ -1184,17 +1231,8 @@ public class CHTurnCostTest {
         return new CHRoutingAlgorithmFactory(chGraph).createAlgo(new PMap().putObject(ALGORITHM, DIJKSTRA_BI));
     }
 
-    private List<Integer> getRandomIntegerSequence(int nodes) {
-        return getRandomIntegerSequence(nodes, new Random());
-    }
-
-    private List<Integer> getRandomIntegerSequence(int nodes, Random rnd) {
-        List<Integer> contractionOrder = new ArrayList<>(nodes);
-        for (int i = 0; i < nodes; ++i) {
-            contractionOrder.add(i);
-        }
-        Collections.shuffle(contractionOrder, rnd);
-        return contractionOrder;
+    private IntArrayList getRandomIntegerSequence(int nodes, Random rnd) {
+        return ArrayUtil.shuffle(ArrayUtil.iota(nodes), rnd);
     }
 
     private void setRandomCostOrRestriction(int from, int via, int to, Random rnd) {
diff --git a/core/src/test/java/com/graphhopper/routing/ch/EdgeBasedNodeContractorTest.java b/core/src/test/java/com/graphhopper/routing/ch/EdgeBasedNodeContractorTest.java
index 6148cb662..d3498ee12 100644
--- a/core/src/test/java/com/graphhopper/routing/ch/EdgeBasedNodeContractorTest.java
+++ b/core/src/test/java/com/graphhopper/routing/ch/EdgeBasedNodeContractorTest.java
@@ -1360,15 +1360,15 @@ public class EdgeBasedNodeContractorTest {
     }
 
     private void contractNode(NodeContractor nodeContractor, int node, int level) {
-        nodeContractor.contractNode(node);
         chGraph.setLevel(node, level);
+        nodeContractor.contractNode(node);
     }
 
     private void contractAllNodesInOrder() {
         EdgeBasedNodeContractor nodeContractor = createNodeContractor();
         for (int node = 0; node < graph.getNodes(); ++node) {
-            nodeContractor.contractNode(node);
             chGraph.setLevel(node, node);
+            nodeContractor.contractNode(node);
         }
         nodeContractor.finishContraction();
     }
@@ -1380,8 +1380,8 @@ public class EdgeBasedNodeContractorTest {
     private void contractNodes(int... nodes) {
         EdgeBasedNodeContractor nodeContractor = createNodeContractor();
         for (int i = 0; i < nodes.length; ++i) {
-            nodeContractor.contractNode(nodes[i]);
             chGraph.setLevel(nodes[i], i);
+            nodeContractor.contractNode(nodes[i]);
         }
         nodeContractor.finishContraction();
     }
diff --git a/core/src/test/java/com/graphhopper/routing/ch/NodeBasedNodeContractorTest.java b/core/src/test/java/com/graphhopper/routing/ch/NodeBasedNodeContractorTest.java
index d93fb8d7a..a98f1a914 100644
--- a/core/src/test/java/com/graphhopper/routing/ch/NodeBasedNodeContractorTest.java
+++ b/core/src/test/java/com/graphhopper/routing/ch/NodeBasedNodeContractorTest.java
@@ -66,7 +66,7 @@ public class NodeBasedNodeContractorTest {
     private NodeContractor createNodeContractor(CHGraph chGraph) {
         CHPreparationGraph prepareGraph = CHPreparationGraph.nodeBased(chGraph.getNodes(), chGraph.getOriginalEdges());
         CHPreparationGraph.buildFromGraph(prepareGraph, chGraph.getBaseGraph(), chGraph.getCHConfig().getWeighting());
-        NodeBasedNodeContractor.ShortcutHandler shortcutInserter = new NodeBasedShortcutHandler(chGraph);
+        NodeBasedNodeContractor.ShortcutHandler shortcutInserter = new NodeBasedShortcutInserter(chGraph);
         NodeContractor nodeContractor = new NodeBasedNodeContractor(prepareGraph, shortcutInserter, new PMap());
         nodeContractor.initFromGraph();
         nodeContractor.prepareContraction();
@@ -333,11 +333,12 @@ public class NodeBasedNodeContractorTest {
     }
 
     private void contractInOrder(CHGraph chGraph, int... nodeIds) {
+        setMaxLevelOnAllNodes();
         NodeContractor nodeContractor = createNodeContractor(chGraph);
         int level = 0;
         for (int n : nodeIds) {
-            nodeContractor.contractNode(n);
             chGraph.setLevel(n, level);
+            nodeContractor.contractNode(n);
             level++;
         }
         nodeContractor.finishContraction();
diff --git a/core/src/test/java/com/graphhopper/routing/ch/Path4CHTest.java b/core/src/test/java/com/graphhopper/routing/ch/Path4CHTest.java
deleted file mode 100644
index 168742bde..000000000
--- a/core/src/test/java/com/graphhopper/routing/ch/Path4CHTest.java
+++ /dev/null
@@ -1,155 +0,0 @@
-package com.graphhopper.routing.ch;
-
-import com.graphhopper.routing.Path;
-import com.graphhopper.routing.RoutingAlgorithm;
-import com.graphhopper.routing.ev.DecimalEncodedValue;
-import com.graphhopper.routing.ev.EncodedValueLookup;
-import com.graphhopper.routing.ev.TurnCost;
-import com.graphhopper.routing.util.CarFlagEncoder;
-import com.graphhopper.routing.util.EncodingManager;
-import com.graphhopper.routing.util.FlagEncoder;
-import com.graphhopper.storage.CHGraph;
-import com.graphhopper.storage.GraphBuilder;
-import com.graphhopper.storage.GraphHopperStorage;
-import com.graphhopper.storage.RoutingCHGraph;
-import com.graphhopper.util.EdgeIteratorState;
-import com.graphhopper.util.PMap;
-import org.junit.Before;
-import org.junit.Test;
-
-import static com.graphhopper.util.GHUtility.getEdge;
-import static org.junit.Assert.assertEquals;
-
-public class Path4CHTest {
-    private GraphHopperStorage graph;
-    private CHGraph chGraph;
-    private RoutingCHGraph routingCHGraph;
-    private FlagEncoder encoder;
-
-    @Before
-    public void init() {
-        encoder = new CarFlagEncoder(5, 5, 10).setSpeedTwoDirections(true);
-        EncodingManager em = EncodingManager.create(encoder);
-        graph = new GraphBuilder(em)
-                .setCHConfigStrings("p1|car|fastest|edge")
-                .create();
-        chGraph = graph.getCHGraph("p1");
-        routingCHGraph = graph.getRoutingCHGraph("p1");
-    }
-
-    @Test
-    public void shortcut_chain() {
-        // 0   2   4   6   8
-        //  \ / \ / \ / \ /
-        //   1   3   5   7
-        graph.edge(0, 1, 1, false);
-        graph.edge(1, 2, 1, false);
-        graph.edge(2, 3, 1, false);
-        graph.edge(3, 4, 1, false);
-        graph.edge(4, 5, 1, false);
-        graph.edge(5, 6, 1, false);
-        graph.edge(6, 7, 1, false);
-        graph.edge(7, 8, 1, false);
-        graph.freeze();
-        setTurnCost(1, 2, 3, 4);
-        setTurnCost(3, 4, 5, 2);
-        setTurnCost(5, 6, 7, 3);
-        // we 'contract' the graph such that only a few shortcuts are created and that the fwd/bwd searches for the
-        // 0-8 query meet at node 4 (make sure we include all three cases where turn cost times might come to play:
-        // fwd/bwd search and meeting point)
-        addShortcut(0, 2, 0, 1, 0, 1, 0.12, 0);
-        addShortcut(2, 4, 2, 3, 2, 3, 0.12, 0);
-        addShortcut(4, 6, 4, 5, 4, 5, 0.12, 0);
-        addShortcut(6, 8, 6, 7, 6, 7, 0.12, 0);
-        setCHOrder(1, 3, 5, 7, 0, 8, 2, 6, 4);
-
-        // going from 0 to 8 will create shortest path tree entries that follow the shortcuts.
-        // it is important that the original edge ids are used to calculate the turn costs.
-        checkPath(0, 8, 0.48, 8, 9);
-    }
-
-    private void setCHOrder(int... nodeIds) {
-        for (int i = 0; i < nodeIds.length; i++) {
-            chGraph.setLevel(nodeIds[i], i);
-        }
-    }
-
-    @Test
-    public void paths_different_fwd_bwd_speeds() {
-        //   5 3 2 1 4    turn costs ->
-        // 0-1-2-3-4-5-6
-        //   0 1 4 2 3    turn costs <-
-        DecimalEncodedValue speedEnc = encoder.getAverageSpeedEnc();
-        double fwdSpeed = 60;
-        double bwdSpeed = 30;
-        EdgeIteratorState edge0 = graph.edge(0, 1, 1, true).set(speedEnc, fwdSpeed).setReverse(speedEnc, bwdSpeed);
-        EdgeIteratorState edge1 = graph.edge(1, 2, 1, true).set(speedEnc, fwdSpeed).setReverse(speedEnc, bwdSpeed);
-        EdgeIteratorState edge2 = graph.edge(2, 3, 1, true).set(speedEnc, fwdSpeed).setReverse(speedEnc, bwdSpeed);
-        EdgeIteratorState edge3 = graph.edge(3, 4, 1, true).set(speedEnc, fwdSpeed).setReverse(speedEnc, bwdSpeed);
-        EdgeIteratorState edge4 = graph.edge(4, 5, 1, true).set(speedEnc, fwdSpeed).setReverse(speedEnc, bwdSpeed);
-        EdgeIteratorState edge5 = graph.edge(5, 6, 1, true).set(speedEnc, fwdSpeed).setReverse(speedEnc, bwdSpeed);
-        graph.freeze();
-
-        // turn costs ->
-        setTurnCost(edge0, edge1, 1, 5);
-        setTurnCost(edge1, edge2, 2, 3);
-        setTurnCost(edge2, edge3, 3, 2);
-        setTurnCost(edge3, edge4, 4, 1);
-        setTurnCost(edge4, edge5, 5, 4);
-        // turn costs <-
-        setTurnCost(edge5, edge4, 5, 3);
-        setTurnCost(edge4, edge3, 4, 2);
-        setTurnCost(edge3, edge2, 3, 4);
-        setTurnCost(edge2, edge1, 2, 1);
-        setTurnCost(edge1, edge0, 1, 0);
-
-        // shortcuts ->
-        addShortcut(0, 2, 0, 1, 0, 1, 0.12, 5);
-        addShortcut(2, 4, 2, 3, 2, 3, 0.12, 2);
-        addShortcut(4, 6, 4, 5, 4, 5, 0.12, 4);
-        addShortcut(2, 6, 2, 5, 7, 8, 0.24, 7);
-        addShortcut(0, 6, 0, 5, 6, 9, 0.36, 12);
-
-        // shortcuts <-
-        addShortcut(6, 4, 5, 4, 5, 4, 0.24, 3);
-        addShortcut(4, 2, 3, 2, 3, 2, 0.24, 4);
-        addShortcut(2, 0, 1, 0, 1, 0, 0.24, 0);
-        addShortcut(6, 2, 5, 2, 11, 12, 0.48, 9);
-        addShortcut(6, 0, 5, 0, 14, 13, 0.60, 10);
-
-        // strictly it would be cleaner to manually build the SPT and extract the path, but for convenience we
-        // use the routing algo to build it
-        checkPath(0, 6, 0.36, 6, 15);
-        checkPath(6, 0, 0.72, 6, 10);
-        checkPath(1, 3, 0.12, 2, 3);
-        checkPath(3, 1, 0.24, 2, 1);
-        checkPath(1, 5, 0.24, 4, 6);
-        checkPath(5, 1, 0.48, 4, 7);
-    }
-
-    private void setTurnCost(EdgeIteratorState inEdge, EdgeIteratorState outEdge, int viaNode, int cost) {
-        graph.getTurnCostStorage().set(((EncodedValueLookup) graph.getEncodingManager()).getDecimalEncodedValue(TurnCost.key(encoder.toString())), inEdge.getEdge(), viaNode, outEdge.getEdge(), cost);
-    }
-
-    private void setTurnCost(int from, int via, int to, int cost) {
-        setTurnCost(getEdge(graph, from, via), getEdge(graph, via, to), via, cost);
-    }
-
-    private void addShortcut(int from, int to, int origFirst, int origLast, int skip1, int skip2, double edgeWeight, int turnCost) {
-        double weight = edgeWeight + turnCost * 1000;
-        chGraph.shortcutEdgeBased(from, to, PrepareEncoder.getScFwdDir(), weight, skip1, skip2, origFirst, origLast);
-    }
-
-    private void checkPath(int from, int to, double edgeWeight, int distance, int turnCostTime) {
-        double expectedWeight = (edgeWeight + turnCostTime);
-        Path path = createAlgo().calcPath(from, to);
-        assertEquals("wrong weight", expectedWeight, path.getWeight(), 1.e-3);
-        assertEquals("wrong distance", distance, path.getDistance(), 1.e-3);
-        assertEquals("wrong time", expectedWeight * 1000, path.getTime(), 1.e-3);
-    }
-
-    private RoutingAlgorithm createAlgo() {
-        return new CHRoutingAlgorithmFactory(routingCHGraph).createAlgo(new PMap());
-    }
-
-}
\ No newline at end of file
diff --git a/core/src/test/java/com/graphhopper/routing/ch/PrepareContractionHierarchiesTest.java b/core/src/test/java/com/graphhopper/routing/ch/PrepareContractionHierarchiesTest.java
index a1c866072..be1f732f2 100644
--- a/core/src/test/java/com/graphhopper/routing/ch/PrepareContractionHierarchiesTest.java
+++ b/core/src/test/java/com/graphhopper/routing/ch/PrepareContractionHierarchiesTest.java
@@ -271,77 +271,6 @@ public class PrepareContractionHierarchiesTest {
         assertEquals(IntArrayList.from(4, 5, 6, 7), p.calcNodes());
     }
 
-    private void initUnpackingGraph(GraphHopperStorage g, CHGraph lg, Weighting w) {
-        final IntsRef edgeFlags = encodingManager.createEdgeFlags();
-        carEncoder.getAccessEnc().setBool(false, edgeFlags, true);
-        carEncoder.getAccessEnc().setBool(true, edgeFlags, false);
-        carEncoder.getAverageSpeedEnc().setDecimal(false, edgeFlags, 30.0);
-        double dist = 1;
-        g.edge(10, 0).setDistance(dist).setFlags(edgeFlags);
-        EdgeIteratorState edgeState01 = g.edge(0, 1);
-        edgeState01.setDistance(dist).setFlags(edgeFlags);
-        EdgeIteratorState edgeState12 = g.edge(1, 2).setDistance(dist).setFlags(edgeFlags);
-        EdgeIteratorState edgeState23 = g.edge(2, 3).setDistance(dist).setFlags(edgeFlags);
-        EdgeIteratorState edgeState34 = g.edge(3, 4).setDistance(dist).setFlags(edgeFlags);
-        EdgeIteratorState edgeState45 = g.edge(4, 5).setDistance(dist).setFlags(edgeFlags);
-        EdgeIteratorState edgeState56 = g.edge(5, 6).setDistance(dist).setFlags(edgeFlags);
-        int oneDirFlags = PrepareEncoder.getScFwdDir();
-
-        int tmpEdgeId = edgeState01.getEdge();
-        g.freeze();
-        double weight = w.calcEdgeWeight(edgeState01, false) + w.calcEdgeWeight(edgeState12, false);
-        int sc0_2 = lg.shortcut(0, 2, oneDirFlags, w.calcEdgeWeight(edgeState01, false) + w.calcEdgeWeight(edgeState12, false), tmpEdgeId, edgeState12.getEdge());
-
-        tmpEdgeId = sc0_2;
-        weight += w.calcEdgeWeight(edgeState23, false);
-        int sc0_3 = lg.shortcut(0, 3, oneDirFlags, weight, tmpEdgeId, edgeState23.getEdge());
-
-        tmpEdgeId = sc0_3;
-        weight += w.calcEdgeWeight(edgeState34, false);
-        int sc0_4 = lg.shortcut(0, 4, oneDirFlags, weight, tmpEdgeId, edgeState34.getEdge());
-
-        tmpEdgeId = sc0_4;
-        weight += w.calcEdgeWeight(edgeState45, false);
-        int sc0_5 = lg.shortcut(0, 5, oneDirFlags, weight, tmpEdgeId, edgeState45.getEdge());
-
-        tmpEdgeId = sc0_5;
-        weight += w.calcEdgeWeight(edgeState56, false);
-        int sc0_6 = lg.shortcut(0, 6, oneDirFlags, weight, tmpEdgeId, edgeState56.getEdge());
-
-        lg.setLevel(0, 10);
-        lg.setLevel(6, 9);
-        lg.setLevel(5, 8);
-        lg.setLevel(4, 7);
-        lg.setLevel(3, 6);
-        lg.setLevel(2, 5);
-        lg.setLevel(1, 4);
-        lg.setLevel(10, 3);
-    }
-
-    @Test
-    public void testUnpackingOrder() {
-        initUnpackingGraph(g, lg, weighting);
-        createPrepareContractionHierarchies(g);
-        // do not call prepare.doWork() here
-        RoutingAlgorithm algo = new CHRoutingAlgorithmFactory(routingCHGraph).createAlgo(new PMap());
-        Path p = algo.calcPath(10, 6);
-        assertEquals(7, p.getDistance(), 1e-5);
-        assertEquals(IntArrayList.from(10, 0, 1, 2, 3, 4, 5, 6), p.calcNodes());
-    }
-
-    @Test
-    public void testUnpackingOrder_Fastest() {
-        Weighting w = new FastestWeighting(carEncoder);
-        initUnpackingGraph(g, lg, w);
-
-        createPrepareContractionHierarchies(g);
-        // do not call prepare.doWork() here
-        RoutingAlgorithm algo = new CHRoutingAlgorithmFactory(routingCHGraph).createAlgo(new PMap());
-        Path p = algo.calcPath(10, 6);
-        assertEquals(7, p.getDistance(), 1e-1);
-        assertEquals(IntArrayList.from(10, 0, 1, 2, 3, 4, 5, 6), p.calcNodes());
-    }
-
     @Test
     public void testDisconnects() {
         //            4
@@ -364,17 +293,7 @@ public class PrepareContractionHierarchiesTest {
         g.freeze();
 
         PrepareContractionHierarchies prepare = createPrepareContractionHierarchies(g)
-                .useFixedNodeOrdering(new NodeOrderingProvider() {
-                    @Override
-                    public int getNodeIdForLevel(int level) {
-                        return level;
-                    }
-
-                    @Override
-                    public int getNumNodes() {
-                        return g.getNodes();
-                    }
-                });
+                .useFixedNodeOrdering(NodeOrderingProvider.identity(g.getNodes()));
         prepare.doWork();
         CHEdgeExplorer explorer = lg.createEdgeExplorer();
         // shortcuts (and edges) leading to or coming from lower level nodes should be disconnected
@@ -452,17 +371,7 @@ public class PrepareContractionHierarchiesTest {
 
         // prepare ch, use node ids as levels
         PrepareContractionHierarchies pch = createPrepareContractionHierarchies(g, chConfig);
-        pch.useFixedNodeOrdering(new NodeOrderingProvider() {
-            @Override
-            public int getNodeIdForLevel(int level) {
-                return level;
-            }
-
-            @Override
-            public int getNumNodes() {
-                return g.getNodes();
-            }
-        }).doWork();
+        pch.useFixedNodeOrdering(NodeOrderingProvider.identity(g.getNodes())).doWork();
         assertEquals("there should be exactly two (bidirectional) shortcuts (2-3) and (3-4)", 2, lg.getEdges() - lg.getOriginalEdges());
 
         // insert virtual node and edges
@@ -673,17 +582,7 @@ public class PrepareContractionHierarchiesTest {
     }
 
     private void useNodeOrdering(PrepareContractionHierarchies prepare, int[] nodeOrdering) {
-        prepare.useFixedNodeOrdering(new NodeOrderingProvider() {
-            @Override
-            public int getNodeIdForLevel(int level) {
-                return nodeOrdering[level];
-            }
-
-            @Override
-            public int getNumNodes() {
-                return nodeOrdering.length;
-            }
-        });
+        prepare.useFixedNodeOrdering(NodeOrderingProvider.fromArray(nodeOrdering));
     }
 
 }
diff --git a/core/src/test/java/com/graphhopper/storage/GraphHopperStorageCHTest.java b/core/src/test/java/com/graphhopper/storage/GraphHopperStorageCHTest.java
index 854da458d..6be2e6c0d 100644
--- a/core/src/test/java/com/graphhopper/storage/GraphHopperStorageCHTest.java
+++ b/core/src/test/java/com/graphhopper/storage/GraphHopperStorageCHTest.java
@@ -124,29 +124,37 @@ public class GraphHopperStorageCHTest extends GraphHopperStorageTest {
 
     @Test
     public void testShortcutConnection() {
+        //   4 ------ 1 > 0
+        //            ^ \
+        //            3  2
         graph = createGHStorage();
         EdgeExplorer baseCarOutExplorer = graph.createEdgeExplorer(carOutFilter);
         graph.edge(4, 1, 30, true);
         graph.freeze();
 
         CHGraph lg = getGraph(graph);
+        setIdentityLevels(lg);
+        lg.shortcut(0, 1, PrepareEncoder.getScBwdDir(), 0, 12, 13);
+        lg.shortcut(1, 2, PrepareEncoder.getScDirMask(), 0, 10, 11);
+        lg.shortcut(1, 3, PrepareEncoder.getScBwdDir(), 0, 14, 15);
+
         EdgeExplorer chOutExplorer = lg.createEdgeExplorer(carOutFilter);
         EdgeExplorer chInExplorer = lg.createEdgeExplorer(carInFilter);
-        lg.shortcut(1, 2, PrepareEncoder.getScDirMask(), 0, 10, 11);
-        lg.shortcut(1, 0, PrepareEncoder.getScFwdDir(), 0, 12, 13);
-        lg.shortcut(3, 1, PrepareEncoder.getScFwdDir(), 0, 14, 15);
-        // shortcuts are only visible from the base node, for example we do not see node 1 from node 2, or node
-        // 3 from node 1
+        // shortcuts are only visible from the lower level node, for example we do not see node 1 from node 2, or node
+        // 0 from node 1
         assertEquals(0, GHUtility.count(chOutExplorer.setBaseNode(2)));
         assertEquals(0, GHUtility.count(chInExplorer.setBaseNode(2)));
 
-        assertEquals(3, GHUtility.count(chOutExplorer.setBaseNode(1)));
-        assertEquals(2, GHUtility.count(chInExplorer.setBaseNode(1)));
-        assertEquals(GHUtility.asSet(0, 2, 4), GHUtility.getNeighbors(chOutExplorer.setBaseNode(1)));
+        assertEquals(2, GHUtility.count(chOutExplorer.setBaseNode(1)));
+        assertEquals(3, GHUtility.count(chInExplorer.setBaseNode(1)));
+        assertEquals(GHUtility.asSet(2, 4), GHUtility.getNeighbors(chOutExplorer.setBaseNode(1)));
         assertEquals(GHUtility.asSet(4), GHUtility.getNeighbors(baseCarOutExplorer.setBaseNode(1)));
 
-        assertEquals(1, GHUtility.count(chOutExplorer.setBaseNode(3)));
+        assertEquals(0, GHUtility.count(chOutExplorer.setBaseNode(3)));
         assertEquals(0, GHUtility.count(chInExplorer.setBaseNode(3)));
+
+        assertEquals(0, GHUtility.count(chOutExplorer.setBaseNode(0)));
+        assertEquals(1, GHUtility.count(chInExplorer.setBaseNode(0)));
     }
 
     @Test
@@ -159,7 +167,7 @@ public class GraphHopperStorageCHTest extends GraphHopperStorageTest {
         assertFalse(g.getEdgeIteratorState(edge1.getEdge(), Integer.MIN_VALUE).isShortcut());
         assertFalse(g.getEdgeIteratorState(edge2.getEdge(), Integer.MIN_VALUE).isShortcut());
 
-
+        setIdentityLevels(g);
         // only remove edges
         int flags = PrepareEncoder.getScDirMask();
         int sc = g.shortcut(0, 1, flags, 5, NO_EDGE, NO_EDGE);
@@ -198,13 +206,14 @@ public class GraphHopperStorageCHTest extends GraphHopperStorageTest {
         ghStorage.freeze();
 
         CHGraph lg = ghStorage.getCHGraph();
+        setIdentityLevels(lg);
         int sc1 = lg.shortcut(0, 1, PrepareEncoder.getScFwdDir(), 100.123, NO_EDGE, NO_EDGE);
 
         assertEquals(100.123, lg.getEdgeIteratorState(sc1, 1).getWeight(), 1e-3);
         assertEquals(100.123, lg.getEdgeIteratorState(sc1, 0).getWeight(), 1e-3);
         assertEquals(100.123, GHUtility.getEdge(lg, 0, 1).getWeight(), 1e-3);
 
-        int sc2 = lg.shortcut(1, 0, PrepareEncoder.getScDirMask(), 1.011011, NO_EDGE, NO_EDGE);
+        int sc2 = lg.shortcut(0, 1, PrepareEncoder.getScDirMask(), 1.011011, NO_EDGE, NO_EDGE);
         assertEquals(1.011011, lg.getEdgeIteratorState(sc2, 0).getWeight(), 1e-3);
         assertEquals(1.011011, lg.getEdgeIteratorState(sc2, 1).getWeight(), 1e-3);
     }
@@ -216,6 +225,7 @@ public class GraphHopperStorageCHTest extends GraphHopperStorageTest {
         graph.edge(0, 1, 1, false);
         graph.edge(1, 2, 1, false);
         graph.freeze();
+        setIdentityLevels(chGraph);
 
         // we just make up some weights, they do not really have to be related to our previous edges.
         // 1.004+1.006 = 2.09999999999. we make sure this does not become 2.09 instead of 2.10 (due to truncation)
@@ -239,6 +249,7 @@ public class GraphHopperStorageCHTest extends GraphHopperStorageTest {
         graph.edge(8, 9);
         graph.freeze();
         CHGraph chGraph = getGraph(graph);
+        setIdentityLevels(chGraph);
 
         assertEquals(1, GHUtility.count(graph.createEdgeExplorer().setBaseNode(1)));
         assertEquals(1, GHUtility.count(chGraph.createEdgeExplorer().setBaseNode(1)));
@@ -289,6 +300,7 @@ public class GraphHopperStorageCHTest extends GraphHopperStorageTest {
         graph.freeze();
 
         CHGraph lg = getGraph(graph);
+        setIdentityLevels(lg);
         lg.shortcut(1, 4, PrepareEncoder.getScFwdDir(), 3, NO_EDGE, NO_EDGE);
 
         EdgeExplorer vehicleOutExplorer = lg.createEdgeExplorer(DefaultEdgeFilter.outEdges(carEncoder));
@@ -305,6 +317,7 @@ public class GraphHopperStorageCHTest extends GraphHopperStorageTest {
         graph.freeze();
 
         CHGraph lg = getGraph(graph);
+        setIdentityLevels(lg);
         lg.shortcut(1, 4, PrepareEncoder.getScDirMask(), 10, NO_EDGE, NO_EDGE);
 
         AllCHEdgesIterator iter = lg.getAllEdges();
@@ -325,6 +338,7 @@ public class GraphHopperStorageCHTest extends GraphHopperStorageTest {
         graph.freeze();
 
         CHGraph lg = getGraph(graph);
+        setIdentityLevels(lg);
         lg.shortcut(1, 4, PrepareEncoder.getScDirMask(), 10, edge1.getEdge(), edge2.getEdge());
 
         AllCHEdgesIterator iter = lg.getAllEdges();
@@ -353,6 +367,7 @@ public class GraphHopperStorageCHTest extends GraphHopperStorageTest {
         graph.edge(1, 2, 3, false);
         graph.freeze();
         CHGraph lg = getGraph(this.graph);
+        setIdentityLevels(lg);
         addShortcut(lg, 0, 2, true, 0, 1, 0, 1, 4);
         AllCHEdgesIterator iter = lg.getAllEdges();
         iter.next();
@@ -369,6 +384,7 @@ public class GraphHopperStorageCHTest extends GraphHopperStorageTest {
         graph.edge(1, 2, 1, false);
         graph.freeze();
         CHGraph lg = getGraph(graph);
+        setIdentityLevels(lg);
         addShortcut(lg, 0, 2, true, 0, 1, 0, 1, 2);
 
         CHEdgeIteratorState sc02 = lg.getEdgeIteratorState(2, 2);
@@ -417,10 +433,12 @@ public class GraphHopperStorageCHTest extends GraphHopperStorageTest {
         graph.freeze();
 
         CHGraph carCHGraph = graph.getCHGraph(chConfigs.get(0).getName());
+        setIdentityLevels(carCHGraph);
         // enable forward directions for car
         int carSC02 = carCHGraph.shortcut(0, 2, PrepareEncoder.getScFwdDir(), 10, NO_EDGE, NO_EDGE);
 
         CHGraph bikeCHGraph = graph.getCHGraph(chConfigs.get(1).getName());
+        setIdentityLevels(bikeCHGraph);
         // enable both directions for bike
         int bikeSC02 = bikeCHGraph.shortcut(0, 2, PrepareEncoder.getScDirMask(), 10, NO_EDGE, NO_EDGE);
 
@@ -595,6 +613,12 @@ public class GraphHopperStorageCHTest extends GraphHopperStorageTest {
         }
     }
 
+    public static void setIdentityLevels(CHGraph chGraph) {
+        for (int i = 0; i < chGraph.getNodes(); i++) {
+            chGraph.setLevel(i, i);
+        }
+    }
+
     private GraphHopperStorage createStorageWithWeightings(String... profileStrings) {
         return new GraphBuilder(encodingManager)
                 .setCHConfigStrings(profileStrings)
diff --git a/core/src/test/java/com/graphhopper/storage/ShortcutUnpackerTest.java b/core/src/test/java/com/graphhopper/storage/ShortcutUnpackerTest.java
index c176a2f6f..9e64c7644 100644
--- a/core/src/test/java/com/graphhopper/storage/ShortcutUnpackerTest.java
+++ b/core/src/test/java/com/graphhopper/storage/ShortcutUnpackerTest.java
@@ -66,13 +66,15 @@ public class ShortcutUnpackerTest {
         graph.edge(2, 3, 1, true).set(speedEnc, fwdSpeed).setReverse(speedEnc, bwdSpeed);
         graph.edge(3, 4, 1, true).set(speedEnc, fwdSpeed).setReverse(speedEnc, bwdSpeed);
         graph.edge(4, 5, 1, true).set(speedEnc, fwdSpeed).setReverse(speedEnc, bwdSpeed);
-        graph.edge(5, 6, 1, true).set(speedEnc, fwdSpeed).setReverse(speedEnc, bwdSpeed);
+        graph.edge(5, 6, 1, true).set(speedEnc, fwdSpeed).setReverse(speedEnc, bwdSpeed); // edge 5
         graph.freeze();
-        shortcut(0, 2, 0, 1, 0, 1);
-        shortcut(2, 4, 2, 3, 2, 3);
-        shortcut(4, 6, 4, 5, 4, 5);
-        shortcut(2, 6, 7, 8, 2, 5);
-        shortcut(0, 6, 6, 9, 0, 5);
+
+        setCHLevels(1, 3, 5, 4, 2, 0, 6);
+        shortcut(4, 2, 2, 3, 2, 3, true);
+        shortcut(4, 6, 4, 5, 4, 5, false);
+        shortcut(2, 0, 0, 1, 0, 1, true);
+        shortcut(2, 6, 6, 7, 2, 5, false);
+        shortcut(0, 6, 8, 9, 0, 5, false);
 
         {
             // unpack the shortcut 0->6, traverse original edges in 'forward' order (from node 0 to 6)
@@ -137,6 +139,12 @@ public class ShortcutUnpackerTest {
         }
     }
 
+    private void setCHLevels(int... order) {
+        for (int i = 0; i < order.length; i++) {
+            chGraph.setLevel(order[i], i);
+        }
+    }
+
     @Test
     public void loopShortcut() {
         Assume.assumeTrue("loop shortcuts only exist for edge-based CH", edgeBased);
@@ -155,11 +163,13 @@ public class ShortcutUnpackerTest {
         graph.edge(4, 1, 1, true).set(speedEnc, fwdSpeed).setReverse(speedEnc, bwdSpeed);
         graph.edge(1, 5, 1, true).set(speedEnc, fwdSpeed).setReverse(speedEnc, bwdSpeed);
         graph.freeze();
-        shortcut(1, 3, 1, 2, 1, 2);
-        shortcut(3, 1, 3, 4, 3, 4);
-        shortcut(1, 1, 6, 7, 1, 4);
-        shortcut(0, 1, 0, 8, 0, 4);
-        shortcut(0, 5, 9, 5, 0, 5);
+
+        setCHLevels(2, 4, 3, 1, 5, 0);
+        shortcut(3, 1, 1, 2, 1, 2, true);
+        shortcut(3, 1, 3, 4, 3, 4, false);
+        shortcut(1, 1, 6, 7, 1, 4, false);
+        shortcut(1, 0, 0, 8, 0, 4, true);
+        shortcut(5, 0, 9, 5, 0, 5, true);
 
         {
             // unpack the shortcut 0->5, traverse original edges in 'forward' order (from node 0 to 5)
@@ -248,11 +258,12 @@ public class ShortcutUnpackerTest {
         setTurnCost(edge1.getEdge(), 1, edge0.getEdge(), 0.0);
         setTurnCost(edge0.getEdge(), 0, PREV_EDGE, 1.0);
 
-        shortcut(0, 2, 0, 1, 0, 1);
-        shortcut(2, 4, 2, 3, 2, 3);
-        shortcut(4, 6, 4, 5, 4, 5);
-        shortcut(2, 6, 7, 8, 2, 5);
-        shortcut(0, 6, 6, 9, 0, 5);
+        setCHLevels(1, 3, 5, 4, 2, 0, 6);
+        shortcut(4, 2, 2, 3, 2, 3, true);
+        shortcut(4, 6, 4, 5, 4, 5, false);
+        shortcut(2, 0, 0, 1, 0, 1, true);
+        shortcut(2, 6, 6, 7, 2, 5, false);
+        shortcut(0, 6, 8, 9, 0, 5, false);
 
         {
             // unpack the shortcut 0->6, traverse original edges in 'forward' order (from node 0 to 6)
@@ -303,13 +314,14 @@ public class ShortcutUnpackerTest {
         graph.getTurnCostStorage().set(((EncodedValueLookup) encodingManager).getDecimalEncodedValue(TurnCost.key(encoder.toString())), fromEdge, viaNode, toEdge, cost);
     }
 
-    private void shortcut(int baseNode, int adjNode, int skip1, int skip2, int origFirst, int origLast) {
+    private void shortcut(int baseNode, int adjNode, int skip1, int skip2, int origFirst, int origLast, boolean reverse) {
         // shortcut weight/distance is not important for us here
         double weight = 1;
+        int flags = reverse ? PrepareEncoder.getScFwdDir() : PrepareEncoder.getScBwdDir();
         if (edgeBased) {
-            chGraph.shortcutEdgeBased(baseNode, adjNode, PrepareEncoder.getScFwdDir(), weight, skip1, skip2, origFirst, origLast);
+            chGraph.shortcutEdgeBased(baseNode, adjNode, flags, weight, skip1, skip2, origFirst, origLast);
         } else {
-            chGraph.shortcut(baseNode, adjNode, PrepareEncoder.getScFwdDir(), weight, skip1, skip2);
+            chGraph.shortcut(baseNode, adjNode, flags, weight, skip1, skip2);
         }
     }