Ejemplo n.º 1
0
        /// <summary>
        /// Adds or updates the weight for the given edge.
        /// </summary>
        public void AddOrUpdate(OriginalEdge edge, Shortcut <T> shortcut, WeightHandler <T> weightHandler)
        {
            Shortcut <T> existing;

            if (!_data.TryGetValue(edge, out existing))
            {
                _data[edge] = shortcut;
                return;
            }

            // update existing.
            var existingForward = weightHandler.GetMetric(existing.Forward);
            var newForward      = weightHandler.GetMetric(shortcut.Forward);

            if (existingForward == 0 ||
                existingForward > newForward)
            {
                existing.Forward = shortcut.Forward;
            }
            var existingBackward = weightHandler.GetMetric(existing.Backward);
            var newBackward      = weightHandler.GetMetric(shortcut.Backward);

            if (existingBackward == 0 ||
                existingBackward > newBackward)
            {
                existing.Backward = shortcut.Backward;
            }
            _data[edge] = existing;
        }
Ejemplo n.º 2
0
 /// <summary>
 /// Gets or sets a shortcut.
 /// </summary>
 public Shortcut <T> this[OriginalEdge edge]
 {
     get
     {
         return(_data[edge]);
     }
     set
     {
         _data[edge] = value;
     }
 }
Ejemplo n.º 3
0
        /// <summary>
        /// Builds the potential shortcuts.
        /// </summary>
        public static void BuildShortcuts <T>(this VertexInfo <T> vertexinfo, WeightHandler <T> weightHandler)
            where T : struct
        {
            var vertex    = vertexinfo.Vertex;
            var shortcuts = vertexinfo.Shortcuts;

            // loop over all edge-pairs once.
            var shortcut = new Shortcut <T>()
            {
                Backward = weightHandler.Infinite,
                Forward  = weightHandler.Infinite
            };
            var shortcutEdge = new OriginalEdge();

            for (var j = 1; j < vertexinfo.Count; j++)
            {
                var edge1       = vertexinfo[j];
                var edge1Weight = weightHandler.GetEdgeWeight(edge1);
                shortcutEdge.Vertex1 = edge1.Neighbour;

                // figure out what witness paths to calculate.
                for (var k = 0; k < j; k++)
                {
                    var edge2       = vertexinfo[k];
                    var edge2Weight = weightHandler.GetEdgeWeight(edge2);
                    shortcutEdge.Vertex2 = edge2.Neighbour;

                    if (!(edge1Weight.Direction.B && edge2Weight.Direction.F) &&
                        !(edge1Weight.Direction.F && edge2Weight.Direction.B))
                    { // impossible route, do nothing.
                        continue;
                    }

                    shortcut.Backward = weightHandler.Infinite;
                    shortcut.Forward  = weightHandler.Infinite;

                    if (edge1Weight.Direction.B && edge2Weight.Direction.F)
                    {
                        shortcut.Forward = weightHandler.Add(edge1Weight.Weight, edge2Weight.Weight);
                    }
                    if (edge1Weight.Direction.F && edge2Weight.Direction.B)
                    {
                        shortcut.Backward = weightHandler.Add(edge1Weight.Weight, edge2Weight.Weight);
                    }

                    shortcuts.AddOrUpdate(shortcutEdge, shortcut, weightHandler);
                }
            }
        }
Ejemplo n.º 4
0
 /// <summary>
 /// Tries to get the shortcut associated with the given edge.
 /// </summary>
 public bool TryGetValue(OriginalEdge edge, out Shortcut <T> shortcut)
 {
     return(_data.TryGetValue(edge, out shortcut));
 }
Ejemplo n.º 5
0
        /// <summary>
        /// Executes one step in the search.
        /// </summary>
        public bool Step()
        {
            // while the visit list is not empty.
            var cPointer = uint.MaxValue;

            while (cPointer == uint.MaxValue)
            { // choose the next edge.
                if (_pointerHeap.Count == 0)
                {
                    return(false);
                }
                cPointer = _pointerHeap.Pop();
            }

            // get details.
            uint cEdge, cPreviousPointer;
            T    cWeight;

            _weightHandler.GetPathTree(_pathTree, cPointer, out cEdge, out cWeight, out cPreviousPointer);
            if (_visits.Contains(cEdge))
            {
                return(true);
            }
            _visits.Add(cEdge);
            var cDirectedEdge = new DirectedEdgeId()
            {
                Raw = cEdge
            };

            // signal was found.
            if (this.WasFound != null)
            {
                this.WasFound(cPointer, cDirectedEdge, cWeight);
            }

            // move to the current edge's target vertex.
            _edgeEnumerator.MoveToEdge(cDirectedEdge.EdgeId);
            var cOriginalEdge = new OriginalEdge(_edgeEnumerator.From, _edgeEnumerator.To);

            if (!cDirectedEdge.Forward)
            {
                cOriginalEdge = cOriginalEdge.Reverse();
            }
            var cEdgeWeightAndDirection = _weightHandler.GetEdgeWeight(_edgeEnumerator);

            // calculate total weight.
            var totalWeight = _weightHandler.Add(cEdgeWeightAndDirection.Weight, cWeight);

            if (!_weightHandler.IsSmallerThan(totalWeight, _sourceMax))
            { // weight is too big.
                this.MaxReached = true;
                return(true);
            }

            // loop over all neighbours.
            _edgeEnumerator.MoveTo(cOriginalEdge.Vertex2);
            var turn = new Turn(cOriginalEdge, Constants.NO_VERTEX);

            _restrictions.Update(turn.Vertex2);
            while (_edgeEnumerator.MoveNext())
            {
                turn.Vertex3 = _edgeEnumerator.To;
                if (turn.IsUTurn ||
                    turn.IsRestrictedBy(_restrictions))
                { // turn is restricted.
                    continue;
                }

                var nDirectedEdgeId = _edgeEnumerator.DirectedEdgeId();
                if (_visits.Contains(nDirectedEdgeId.Raw))
                { // has already been choosen.
                    continue;
                }

                // get the speed from cache or calculate.
                var nWeightAndDirection = _weightHandler.GetEdgeWeight(_edgeEnumerator);
                var nWeightMetric       = _weightHandler.GetMetric(nWeightAndDirection.Weight);
                if (nWeightMetric <= 0)
                { // edge is not valid for profile.
                    continue;
                }
                if (!_backward && !nWeightAndDirection.Direction.F)
                { // cannot do forward search on edge.
                    continue;
                }
                if (_backward && !nWeightAndDirection.Direction.B)
                { // cannot do backward on edge.
                    continue;
                }

                // update the visit list.
                _pointerHeap.Push(_weightHandler.AddPathTree(_pathTree, nDirectedEdgeId.Raw, totalWeight, cPointer), _weightHandler.GetMetric(totalWeight));
            }
            return(true);
        }
Ejemplo n.º 6
0
        /// <summary>
        /// Calculates a route between the two directed edges. The route starts in the direction of the edge and ends with an arrive in the direction of the target edge.
        /// </summary>
        /// <returns></returns>
        public sealed override Result <EdgePath <T> > TryCalculateRaw <T>(IProfileInstance profileInstance, WeightHandler <T> weightHandler, long sourceDirectedEdge,
                                                                          long targetDirectedEdge, RoutingSettings <T> settings)
        {
            try
            {
                if (!_db.Supports(profileInstance.Profile))
                {
                    return(new Result <EdgePath <T> >("Routing profile is not supported.", (message) =>
                    {
                        return new Exception(message);
                    }));
                }

                var maxSearch = weightHandler.Infinite;
                if (settings != null)
                {
                    if (!settings.TryGetMaxSearch(profileInstance.Profile.FullName, out maxSearch))
                    {
                        maxSearch = weightHandler.Infinite;
                    }
                }

                var sourcePath = _db.GetPathForEdge(weightHandler, sourceDirectedEdge, true);
                var targetPath = _db.GetPathForEdge(weightHandler, targetDirectedEdge, false);

                if (sourcePath == null)
                {
                    return(new Result <EdgePath <T> >("Source edge cannot be routed along in the requested direction."));
                }
                if (targetPath == null)
                {
                    return(new Result <EdgePath <T> >("Target edge cannot be routed along in the requested direction."));
                }

                if (sourceDirectedEdge == targetDirectedEdge)
                { // when edges match, path is always the edge itself.
                    var edgePath = sourcePath;
                    if (edgePath != null)
                    {
                        return(new Result <EdgePath <T> >(edgePath));
                    }
                }

                EdgePath <T> path;
                ContractedDb contracted;

                bool useContracted = false;
                if (_db.TryGetContracted(profileInstance.Profile, out contracted))
                { // contracted calculation.
                    useContracted = true;
                    if (_db.HasComplexRestrictions(profileInstance.Profile) &&
                        (!contracted.HasEdgeBasedGraph && !contracted.NodeBasedIsEdgedBased))
                    { // there is no edge-based graph for this profile but the db has complex restrictions, don't use the contracted graph.
                        Logging.Logger.Log("Router", Logging.TraceEventType.Warning,
                                           "There is a vertex-based contracted graph but also complex restrictions. Not using the contracted graph, add an edge-based contracted graph.");
                        useContracted = false;
                    }
                    if (!contracted.HasEdgeBasedGraph && !contracted.NodeBasedIsEdgedBased)
                    {
                        Logging.Logger.Log("Router", Logging.TraceEventType.Warning,
                                           "There is a vertex-based contracted graph but it cannot be used to calculate routes with a start and end edge in a specific direction.");
                        useContracted = false;
                    }
                }

                if (useContracted)
                {  // use the contracted graph.
                    path = null;

                    List <uint> vertexPath = null;

                    if (contracted.HasEdgeBasedGraph)
                    { // use edge-based routing.
                        var bidirectionalSearch = new Algorithms.Contracted.EdgeBased.BidirectionalDykstra <T>(contracted.EdgeBasedGraph, weightHandler,
                                                                                                               new EdgePath <T>[] { sourcePath }, new EdgePath <T>[] { targetPath }, _db.GetGetRestrictions(profileInstance.Profile, null));
                        bidirectionalSearch.Run();
                        if (!bidirectionalSearch.HasSucceeded)
                        {
                            return(new Result <EdgePath <T> >(bidirectionalSearch.ErrorMessage, (message) =>
                            {
                                return new RouteNotFoundException(message);
                            }));
                        }
                        vertexPath = bidirectionalSearch.GetPath();
                    }
                    else if (contracted.HasNodeBasedGraph &&
                             contracted.NodeBasedIsEdgedBased)
                    { // use vertex-based graph for edge-based routing.
                        var sourceDirectedId = new DirectedEdgeId(sourceDirectedEdge);
                        var targetDirectedId = new DirectedEdgeId(targetDirectedEdge);

                        var bidirectionalSearch = new Itinero.Algorithms.Contracted.BidirectionalDykstra <T>(contracted.NodeBasedGraph, null, weightHandler,
                                                                                                             new EdgePath <T>[] { new EdgePath <T>(sourceDirectedId.Raw) },
                                                                                                             new EdgePath <T>[] { new EdgePath <T>(targetDirectedId.Raw) });
                        bidirectionalSearch.Run();

                        if (!bidirectionalSearch.HasSucceeded)
                        {
                            return(new Result <EdgePath <T> >(bidirectionalSearch.ErrorMessage, (message) =>
                            {
                                return new RouteNotFoundException(message);
                            }));
                        }

                        var directedEdgePath = Algorithms.Dual.BidirectionalDykstraExtensions.GetDualPath(bidirectionalSearch);

                        // convert directed edge-path to an original vertex path.
                        var enumerator = _db.Network.GetEdgeEnumerator();
                        vertexPath = new List <uint>();
                        for (var i = 0; i < directedEdgePath.Count; i++)
                        {
                            var e = new DirectedEdgeId()
                            {
                                Raw = directedEdgePath[i]
                            };

                            enumerator.MoveToEdge(e.EdgeId);
                            var original = new OriginalEdge(enumerator.From, enumerator.To);
                            if (!e.Forward)
                            {
                                original = original.Reverse();
                            }
                            if (vertexPath.Count == 0)
                            {
                                vertexPath.Add(original.Vertex1);
                            }
                            vertexPath.Add(original.Vertex2);
                        }
                    }
                    else
                    {
                        throw new Exception("Cannot use vertex-based contracted graph for edge-based calculations.");
                    }

                    // expand vertex path using the regular graph.
                    var source = _db.CreateRouterPointForEdge(sourceDirectedEdge, true);
                    var target = _db.CreateRouterPointForEdge(targetDirectedEdge, false);
                    path = _db.BuildEdgePath(weightHandler, source, target, vertexPath);
                }
                else
                { // use the regular graph.
                    var sourceSearch = new Algorithms.Default.EdgeBased.Dykstra <T>(_db.Network.GeometricGraph.Graph, weightHandler,
                                                                                    _db.GetGetRestrictions(profileInstance.Profile, true), new EdgePath <T>[] { sourcePath }, maxSearch, false);
                    var targetSearch = new Algorithms.Default.EdgeBased.Dykstra <T>(_db.Network.GeometricGraph.Graph, weightHandler,
                                                                                    _db.GetGetRestrictions(profileInstance.Profile, false), new EdgePath <T>[] { targetPath }, maxSearch, true);

                    var bidirectionalSearch = new Algorithms.Default.EdgeBased.BidirectionalDykstra <T>(sourceSearch, targetSearch, weightHandler);
                    bidirectionalSearch.Run();
                    if (!bidirectionalSearch.HasSucceeded)
                    {
                        return(new Result <EdgePath <T> >(bidirectionalSearch.ErrorMessage, (message) =>
                        {
                            return new RouteNotFoundException(message);
                        }));
                    }
                    path = bidirectionalSearch.GetPath();
                }

                return(new Result <EdgePath <T> >(path));
            }
            catch (Exception ex)
            {
                return(new Result <EdgePath <T> >(ex.Message, (m) => ex));
            }
        }
Ejemplo n.º 7
0
        /// <summary>
        /// Calculates a route between the two locations.
        /// </summary>
        /// <returns></returns>
        public sealed override Result <EdgePath <T> > TryCalculateRaw <T>(IProfileInstance profileInstance, WeightHandler <T> weightHandler, RouterPoint source, RouterPoint target,
                                                                          RoutingSettings <T> settings)
        {
            try
            {
                if (!_db.Supports(profileInstance.Profile))
                {
                    return(new Result <EdgePath <T> >("Routing profile is not supported.", (message) =>
                    {
                        return new Exception(message);
                    }));
                }

                var maxSearch = weightHandler.Infinite;
                if (settings != null)
                {
                    if (!settings.TryGetMaxSearch(profileInstance.Profile.FullName, out maxSearch))
                    {
                        maxSearch = weightHandler.Infinite;
                    }
                }

                ContractedDb contracted;

                bool useContracted = false;
                if (_db.TryGetContracted(profileInstance.Profile, out contracted))
                { // contracted calculation.
                    useContracted = true;
                    if (_db.HasComplexRestrictions(profileInstance.Profile) &&
                        (!contracted.HasEdgeBasedGraph && !contracted.NodeBasedIsEdgedBased))
                    { // there is no edge-based graph for this profile but the db has complex restrictions, don't use the contracted graph.
                        Logging.Logger.Log("Router", Logging.TraceEventType.Warning,
                                           "There is a vertex-based contracted graph but also complex restrictions. Not using the contracted graph, add an edge-based contracted graph.");
                        useContracted = false;
                    }
                }

                EdgePath <T> path = null;
                if (source.EdgeId == target.EdgeId)
                { // check for a path on the same edge.
                    var edgePath = source.EdgePathTo(_db, weightHandler, target);
                    if (edgePath != null)
                    {
                        path = edgePath;
                    }
                }

                if (useContracted)
                {  // use the contracted graph.
                    List <uint> vertexPath = null;

                    if (contracted.HasEdgeBasedGraph)
                    { // use edge-based routing.
                        var bidirectionalSearch = new Itinero.Algorithms.Contracted.EdgeBased.BidirectionalDykstra <T>(contracted.EdgeBasedGraph, weightHandler,
                                                                                                                       source.ToEdgePaths(_db, weightHandler, true), target.ToEdgePaths(_db, weightHandler, false), _db.GetGetRestrictions(profileInstance.Profile, null));
                        bidirectionalSearch.Run();
                        if (!bidirectionalSearch.HasSucceeded)
                        {
                            if (path == null)
                            {
                                return(new Result <EdgePath <T> >(bidirectionalSearch.ErrorMessage, (message) =>
                                {
                                    return new RouteNotFoundException(message);
                                }));
                            }
                        }
                        else
                        {
                            vertexPath = bidirectionalSearch.GetPath();
                        }
                    }
                    else if (contracted.NodeBasedIsEdgedBased)
                    {// use vertex-based graph for edge-based routing.
                        var sourceDirectedId1 = new DirectedEdgeId(source.EdgeId, true);
                        var sourceDirectedId2 = new DirectedEdgeId(source.EdgeId, false);
                        var targetDirectedId1 = new DirectedEdgeId(target.EdgeId, true);
                        var targetDirectedId2 = new DirectedEdgeId(target.EdgeId, false);

                        var bidirectionalSearch = new Itinero.Algorithms.Contracted.BidirectionalDykstra <T>(contracted.NodeBasedGraph, null, weightHandler,
                                                                                                             new EdgePath <T>[] { new EdgePath <T>(sourceDirectedId1.Raw), new EdgePath <T>(sourceDirectedId2.Raw) },
                                                                                                             new EdgePath <T>[] { new EdgePath <T>(targetDirectedId1.Raw), new EdgePath <T>(targetDirectedId2.Raw) });
                        bidirectionalSearch.Run();

                        if (!bidirectionalSearch.HasSucceeded)
                        {
                            return(new Result <EdgePath <T> >(bidirectionalSearch.ErrorMessage, (message) =>
                            {
                                return new RouteNotFoundException(message);
                            }));
                        }

                        var directedEdgePath = Algorithms.Dual.BidirectionalDykstraExtensions.GetDualPath(bidirectionalSearch);

                        // convert directed edge-path to an original vertex path.
                        var enumerator = _db.Network.GetEdgeEnumerator();
                        vertexPath = new List <uint>();
                        var edge = new List <OriginalEdge>();
                        for (var i = 0; i < directedEdgePath.Count; i++)
                        {
                            var e = new DirectedEdgeId()
                            {
                                Raw = directedEdgePath[i]
                            };

                            enumerator.MoveToEdge(e.EdgeId);
                            var original = new OriginalEdge(enumerator.From, enumerator.To);
                            if (!e.Forward)
                            {
                                original = original.Reverse();
                            }
                            edge.Add(original);
                            if (vertexPath.Count == 0)
                            {
                                vertexPath.Add(original.Vertex1);
                            }
                            vertexPath.Add(original.Vertex2);
                        }

                        vertexPath[0] = Constants.NO_VERTEX;
                        vertexPath[vertexPath.Count - 1] = Constants.NO_VERTEX;
                    }
                    else
                    {  // use node-based routing.
                        var bidirectionalSearch = new Itinero.Algorithms.Contracted.BidirectionalDykstra <T>(contracted.NodeBasedGraph, _db.GetRestrictions(profileInstance.Profile), weightHandler,
                                                                                                             source.ToEdgePaths(_db, weightHandler, true), target.ToEdgePaths(_db, weightHandler, false));
                        bidirectionalSearch.Run();
                        if (!bidirectionalSearch.HasSucceeded)
                        {
                            if (path == null)
                            {
                                return(new Result <EdgePath <T> >(bidirectionalSearch.ErrorMessage, (message) =>
                                {
                                    return new RouteNotFoundException(message);
                                }));
                            }
                        }
                        else
                        {
                            vertexPath = Algorithms.Contracted.BidirectionalDykstraExtensions.GetPath(bidirectionalSearch);
                        }
                    }

                    // expand vertex path using the regular graph.
                    if (vertexPath != null)
                    {
                        var localPath = _db.BuildEdgePath(weightHandler, source, target, vertexPath);
                        if (path == null ||
                            weightHandler.IsSmallerThan(localPath.Weight, path.Weight))
                        {
                            path = localPath;
                        }
                    }
                }
                else
                { // use the regular graph.
                    EdgePath <T> localPath = null;

                    if (_db.HasComplexRestrictions(profileInstance.Profile))
                    {
                        var sourceSearch = new Algorithms.Default.EdgeBased.Dykstra <T>(_db.Network.GeometricGraph.Graph, weightHandler,
                                                                                        _db.GetGetRestrictions(profileInstance.Profile, true), source.ToEdgePaths(_db, weightHandler, true), maxSearch, false);
                        var targetSearch = new Algorithms.Default.EdgeBased.Dykstra <T>(_db.Network.GeometricGraph.Graph, weightHandler,
                                                                                        _db.GetGetRestrictions(profileInstance.Profile, false), target.ToEdgePaths(_db, weightHandler, false), maxSearch, true);

                        var bidirectionalSearch = new Algorithms.Default.EdgeBased.BidirectionalDykstra <T>(sourceSearch, targetSearch, weightHandler);
                        bidirectionalSearch.Run();
                        if (!bidirectionalSearch.HasSucceeded)
                        {
                            if (path == null)
                            {
                                return(new Result <EdgePath <T> >(bidirectionalSearch.ErrorMessage, (message) =>
                                {
                                    return new RouteNotFoundException(message);
                                }));
                            }
                        }
                        else
                        {
                            localPath = bidirectionalSearch.GetPath();
                        }
                    }
                    else
                    {
                        var sourceSearch = new Dykstra <T>(_db.Network.GeometricGraph.Graph, _db.GetGetSimpleRestrictions(profileInstance.Profile), weightHandler,
                                                           source.ToEdgePaths(_db, weightHandler, true), maxSearch, false);
                        var targetSearch = new Dykstra <T>(_db.Network.GeometricGraph.Graph, _db.GetGetSimpleRestrictions(profileInstance.Profile), weightHandler,
                                                           target.ToEdgePaths(_db, weightHandler, false), maxSearch, true);

                        var bidirectionalSearch = new BidirectionalDykstra <T>(sourceSearch, targetSearch, weightHandler);
                        bidirectionalSearch.Run();
                        if (!bidirectionalSearch.HasSucceeded)
                        {
                            if (path == null)
                            {
                                return(new Result <EdgePath <T> >(bidirectionalSearch.ErrorMessage, (message) =>
                                {
                                    return new RouteNotFoundException(message);
                                }));
                            }
                        }
                        else
                        {
                            localPath = bidirectionalSearch.GetPath();
                        }
                    }

                    // choose best path.
                    if (localPath != null)
                    {
                        if (path == null ||
                            weightHandler.IsSmallerThan(localPath.Weight, path.Weight))
                        {
                            path = localPath;
                        }
                    }
                }
                return(new Result <EdgePath <T> >(path));
            }
            catch (Exception ex)
            {
                return(new Result <EdgePath <T> >(ex.Message, (m) => ex));
            }
        }
Ejemplo n.º 8
0
        /// <summary>
        /// Calculates and updates the shortcuts by searching for witness paths.
        /// </summary>
        public virtual void Calculate(uint vertex, Shortcuts <T> shortcuts)
        {
            var sources      = new HashSet <OriginalEdge>();
            var waitHandlers = new List <ManualResetEvent>();

            while (true)
            {
                var source  = Constants.NO_VERTEX;
                var targets = new Dictionary <uint, Shortcut <T> >();

                foreach (var shortcut in shortcuts)
                {
                    if (source == Constants.NO_VERTEX &&
                        !sources.Contains(shortcut.Key))
                    {
                        source = shortcut.Key.Vertex1;
                        sources.Add(shortcut.Key);
                    }
                    if (shortcut.Key.Vertex1 == source)
                    {
                        targets[shortcut.Key.Vertex2] = shortcut.Value;
                        sources.Add(shortcut.Key);
                    }
                    if (shortcut.Key.Vertex2 == source)
                    {
                        targets[shortcut.Key.Vertex1] = new Shortcut <T>()
                        {
                            Backward = shortcut.Value.Forward,
                            Forward  = shortcut.Value.Backward
                        };
                        sources.Add(shortcut.Key);
                    }
                }

                if (targets.Count == 0)
                { // no more searches needed.
                    break;
                }

                Calculate(_graph, _weightHandler, vertex, source, targets, _maxSettles,
                          _hopLimit);

                foreach (var targetPair in targets)
                {
                    var          e = new OriginalEdge(source, targetPair.Key);
                    Shortcut <T> s;
                    if (shortcuts.TryGetValue(e, out s))
                    {
                        shortcuts[e] = targetPair.Value;
                    }
                    else
                    {
                        e            = e.Reverse();
                        shortcuts[e] = new Shortcut <T>()
                        {
                            Backward = targetPair.Value.Forward,
                            Forward  = targetPair.Value.Backward
                        };
                    }
                }
            }
        }
Ejemplo n.º 9
0
        /// <summary>
        /// Calculates witness paths.
        /// </summary>
        public virtual void Calculate(DirectedGraph graph, WeightHandler <T> weightHandler, uint vertex,
                                      uint source, Dictionary <uint, Shortcut <T> > targets, int maxSettles, int hopLimit)
        {
            pathTree.Clear();
            pointerHeap.Clear();

            var forwardSettled  = new HashSet <uint>();
            var backwardSettled = new HashSet <uint>();

            var forwardTargets  = new HashSet <uint>();
            var backwardTargets = new HashSet <uint>();

            var maxWeight = 0f;

            foreach (var targetPair in targets)
            {
                var target   = targetPair.Key;
                var shortcut = targetPair.Value;
                var e        = new OriginalEdge(source, target);

                var shortcutForward = weightHandler.GetMetric(shortcut.Forward);
                if (shortcutForward > 0 && shortcutForward < float.MaxValue)
                {
                    forwardTargets.Add(e.Vertex2);
                    if (shortcutForward > maxWeight)
                    {
                        maxWeight = shortcutForward;
                    }
                }
                var shortcutBackward = weightHandler.GetMetric(shortcut.Backward);
                if (shortcutBackward > 0 && shortcutBackward < float.MaxValue)
                {
                    backwardTargets.Add(e.Vertex2);
                    if (shortcutBackward > maxWeight)
                    {
                        maxWeight = shortcutBackward;
                    }
                }
            }

            // queue the source.
            pathTree.Clear();
            pointerHeap.Clear();
            var p = pathTree.AddSettledVertex(source, new WeightAndDir <float>()
            {
                Direction = new Dir(true, true),
                Weight    = 0
            }, 0);

            pointerHeap.Push(p, 0);

            // dequeue vertices until stopping conditions are reached.
            var cVertex = Constants.NO_VERTEX;
            WeightAndDir <float> cWeight;
            var cHops      = uint.MaxValue;
            var enumerator = graph.GetEdgeEnumerator();

            while (pointerHeap.Count > 0)
            {
                var cPointer = pointerHeap.Pop();
                pathTree.GetSettledVertex(cPointer, out cVertex, out cWeight, out cHops);

                if (cVertex == vertex)
                {
                    continue;
                }

                if (cWeight.Weight >= maxWeight)
                {
                    break;
                }

                if (forwardSettled.Contains(cVertex) ||
                    forwardTargets.Count == 0 ||
                    forwardSettled.Count > maxSettles)
                {
                    cWeight.Direction = new Dir(false, cWeight.Direction.B);
                }
                if (backwardSettled.Contains(cVertex) ||
                    backwardTargets.Count == 0 ||
                    backwardSettled.Count > maxSettles)
                {
                    cWeight.Direction = new Dir(cWeight.Direction.F, false);
                }

                if (cWeight.Direction.F)
                {
                    forwardSettled.Add(cVertex);
                    if (forwardTargets.Contains(cVertex))
                    { // target reached, evaluate it as a shortcut.
                        Shortcut <T> shortcut;
                        if (targets.TryGetValue(cVertex, out shortcut))
                        {
                            var shortcutForward = weightHandler.GetMetric(shortcut.Forward);
                            if (shortcutForward > cWeight.Weight)
                            { // a witness path was found, don't add a shortcut.
                                shortcut.Forward = weightHandler.Zero;
                                targets[cVertex] = shortcut;
                            }
                        }
                        forwardTargets.Remove(cVertex);
                        if (forwardTargets.Count == 0)
                        {
                            if (backwardTargets.Count == 0)
                            {
                                break;
                            }
                            cWeight.Direction = new Dir(false, cWeight.Direction.B);
                            if (!cWeight.Direction.F && !cWeight.Direction.B)
                            {
                                continue;
                            }
                        }
                    }
                }
                if (cWeight.Direction.B)
                {
                    backwardSettled.Add(cVertex);
                    if (backwardTargets.Contains(cVertex))
                    { // target reached, evaluate it as a shortcut.
                        Shortcut <T> shortcut;
                        if (targets.TryGetValue(cVertex, out shortcut))
                        {
                            var shortcutBackward = weightHandler.GetMetric(shortcut.Backward);
                            if (shortcutBackward > cWeight.Weight)
                            { // a witness path was found, don't add a shortcut.
                                shortcut.Backward = weightHandler.Zero;
                                targets[cVertex]  = shortcut;
                            }
                        }
                        backwardTargets.Remove(cVertex);
                        if (backwardTargets.Count == 0)
                        {
                            if (forwardTargets.Count == 0)
                            {
                                break;
                            }
                            cWeight.Direction = new Dir(cWeight.Direction.F, false);
                            if (!cWeight.Direction.F && !cWeight.Direction.B)
                            {
                                continue;
                            }
                        }
                    }
                }

                if (cHops + 1 >= hopLimit)
                {
                    continue;
                }

                if (forwardSettled.Count > maxSettles &&
                    backwardSettled.Count > maxSettles)
                {
                    continue;
                }

                enumerator.MoveTo(cVertex);
                while (enumerator.MoveNext())
                {
                    var nVertex = enumerator.Neighbour;
                    var nWeight = ContractedEdgeDataSerializer.Deserialize(enumerator.Data0);

                    nWeight = new WeightAndDir <float>()
                    {
                        Direction = Dir.Combine(cWeight.Direction, nWeight.Direction),
                        Weight    = cWeight.Weight + nWeight.Weight
                    };

                    if (nWeight.Direction.F &&
                        forwardSettled.Contains(nVertex))
                    {
                        nWeight.Direction = new Dir(false, nWeight.Direction.B);
                    }
                    if (nWeight.Direction.B &&
                        backwardSettled.Contains(nVertex))
                    {
                        nWeight.Direction = new Dir(nWeight.Direction.F, false);
                    }
                    if (!nWeight.Direction.F && !nWeight.Direction.B)
                    {
                        continue;
                    }

                    var nPoiner = pathTree.AddSettledVertex(nVertex, nWeight, cHops + 1);
                    pointerHeap.Push(nPoiner, nWeight.Weight);
                }
            }
        }