inline bool StallAtNode(const DataFacadeT &facade,
                         const NodeID node,
                         const EdgeWeight weight,
                         QueryHeap &query_heap) const
 {
     for (auto edge : facade.GetAdjacentEdgeRange(node))
     {
         const auto &data = facade.GetEdgeData(edge);
         const bool reverse_flag = ((!forward_direction) ? data.forward : data.backward);
         if (reverse_flag)
         {
             const NodeID to = facade.GetTarget(edge);
             const int edge_weight = data.weight;
             BOOST_ASSERT_MSG(edge_weight > 0, "edge_weight invalid");
             if (query_heap.WasInserted(to))
             {
                 if (query_heap.GetKey(to) + edge_weight < weight)
                 {
                     return true;
                 }
             }
         }
     }
     return false;
 }
    inline void RelaxOutgoingEdges(const DataFacadeT &facade,
                                   const NodeID node,
                                   const EdgeWeight weight,
                                   QueryHeap &query_heap) const
    {
        for (auto edge : facade.GetAdjacentEdgeRange(node))
        {
            const auto &data = facade.GetEdgeData(edge);
            const bool direction_flag = (forward_direction ? data.forward : data.backward);
            if (direction_flag)
            {
                const NodeID to = facade.GetTarget(edge);
                const int edge_weight = data.weight;

                BOOST_ASSERT_MSG(edge_weight > 0, "edge_weight invalid");
                const int to_weight = weight + edge_weight;

                // New Node discovered -> Add to Heap + Node Info Storage
                if (!query_heap.WasInserted(to))
                {
                    query_heap.Insert(to, to_weight, node);
                }
                // Found a shorter Path -> Update weight
                else if (to_weight < query_heap.GetKey(to))
                {
                    // new parent
                    query_heap.GetData(to).parent = node;
                    query_heap.DecreaseKey(to, to_weight);
                }
            }
        }
    }
    void BackwardRoutingStep(const unsigned target_id,
                             QueryHeap &query_heap,
                             SearchSpaceWithBuckets &search_space_with_buckets) const
    {
        const NodeID node = query_heap.DeleteMin();
        const int target_distance = query_heap.GetKey(node);

        // store settled nodes in search space bucket
        search_space_with_buckets[node].emplace_back(target_id, target_distance);

        if (StallAtNode<false>(node, target_distance, query_heap))
        {
            return;
        }

        RelaxOutgoingEdges<false>(node, target_distance, query_heap);
    }
    void ForwardRoutingStep(const DataFacadeT &facade,
                            const unsigned row_idx,
                            const unsigned number_of_targets,
                            QueryHeap &query_heap,
                            const SearchSpaceWithBuckets &search_space_with_buckets,
                            std::vector<EdgeWeight> &result_table) const
    {
        const NodeID node = query_heap.DeleteMin();
        const int source_weight = query_heap.GetKey(node);

        // check if each encountered node has an entry
        const auto bucket_iterator = search_space_with_buckets.find(node);
        // iterate bucket if there exists one
        if (bucket_iterator != search_space_with_buckets.end())
        {
            const std::vector<NodeBucket> &bucket_list = bucket_iterator->second;
            for (const NodeBucket &current_bucket : bucket_list)
            {
                // get target id from bucket entry
                const unsigned column_idx = current_bucket.target_id;
                const int target_weight = current_bucket.weight;
                auto &current_weight = result_table[row_idx * number_of_targets + column_idx];
                // check if new weight is better
                const EdgeWeight new_weight = source_weight + target_weight;
                if (new_weight < 0)
                {
                    const EdgeWeight loop_weight = super::GetLoopWeight(facade, node);
                    const int new_weight_with_loop = new_weight + loop_weight;
                    if (loop_weight != INVALID_EDGE_WEIGHT && new_weight_with_loop >= 0)
                    {
                        current_weight = std::min(current_weight, new_weight_with_loop);
                    }
                }
                else if (new_weight < current_weight)
                {
                    result_table[row_idx * number_of_targets + column_idx] = new_weight;
                }
            }
        }
        if (StallAtNode<true>(facade, node, source_weight, query_heap))
        {
            return;
        }
        RelaxOutgoingEdges<true>(facade, node, source_weight, query_heap);
    }
    void BackwardRoutingStep(const DataFacadeT &facade,
                             const unsigned column_idx,
                             QueryHeap &query_heap,
                             SearchSpaceWithBuckets &search_space_with_buckets) const
    {
        const NodeID node = query_heap.DeleteMin();
        const int target_weight = query_heap.GetKey(node);

        // store settled nodes in search space bucket
        search_space_with_buckets[node].emplace_back(column_idx, target_weight);

        if (StallAtNode<false>(facade, node, target_weight, query_heap))
        {
            return;
        }

        RelaxOutgoingEdges<false>(facade, node, target_weight, query_heap);
    }
    void ForwardRoutingStep(const unsigned source_id,
                            const unsigned number_of_locations,
                            QueryHeap &query_heap,
                            const SearchSpaceWithBuckets &search_space_with_buckets,
                            std::shared_ptr<std::vector<EdgeWeight>> result_table) const
    {
        const NodeID node = query_heap.DeleteMin();
        const int source_distance = query_heap.GetKey(node);

        // check if each encountered node has an entry
        const auto bucket_iterator = search_space_with_buckets.find(node);
        // iterate bucket if there exists one
        if (bucket_iterator != search_space_with_buckets.end())
        {
            const std::vector<NodeBucket> &bucket_list = bucket_iterator->second;
            for (const NodeBucket &current_bucket : bucket_list)
            {
                // get target id from bucket entry
                const unsigned target_id = current_bucket.target_id;
                const int target_distance = current_bucket.distance;
                const EdgeWeight current_distance =
                    (*result_table)[source_id * number_of_locations + target_id];
                // check if new distance is better
                const EdgeWeight new_distance = source_distance + target_distance;
                if (new_distance >= 0 && new_distance < current_distance)
                {
                    (*result_table)[source_id * number_of_locations + target_id] =
                        (source_distance + target_distance);
                }
            }
        }
        if (StallAtNode<true>(node, source_distance, query_heap))
        {
            return;
        }
        RelaxOutgoingEdges<true>(node, source_distance, query_heap);
    }