#ifndef OSRM_CONTRACTOR_CONTRACTED_EDGE_CONTAINER_HPP #define OSRM_CONTRACTOR_CONTRACTED_EDGE_CONTAINER_HPP #include "contractor/query_edge.hpp" #include "util/integer_range.hpp" #include "util/permutation.hpp" #include #include #include #include #include #include namespace osrm { namespace contractor { struct ContractedEdgeContainer { private: using MergedFlags = std::uint8_t; static constexpr auto ALL_FLAGS = 0xFF; static bool mergeCompare(const QueryEdge &lhs, const QueryEdge &rhs) { return std::tie(lhs.source, lhs.target, lhs.data.shortcut, lhs.data.turn_id, lhs.data.weight, lhs.data.duration, lhs.data.forward, lhs.data.backward) < std::tie(rhs.source, rhs.target, rhs.data.shortcut, rhs.data.turn_id, rhs.data.weight, rhs.data.duration, rhs.data.forward, rhs.data.backward); } static bool mergable(const QueryEdge &lhs, const QueryEdge &rhs) { // only true if both are equal return !mergeCompare(lhs, rhs) && !mergeCompare(rhs, lhs); } public: void Insert(std::vector new_edges) { BOOST_ASSERT(edges.size() == 0); BOOST_ASSERT(flags.empty()); edges = std::move(new_edges); flags.resize(edges.size(), ALL_FLAGS); } void Filter(const std::vector &filter, std::size_t index) { BOOST_ASSERT(index < sizeof(MergedFlags) * CHAR_BIT); const MergedFlags flag = 1 << index; for (auto edge_index : util::irange(0, edges.size())) { auto allowed = filter[edges[edge_index].source] && filter[edges[edge_index].target]; if (allowed) { flags[edge_index] |= flag; } else { flags[edge_index] &= ~flag; } } } void Merge(std::vector new_edges) { BOOST_ASSERT(index < sizeof(MergedFlags) * CHAR_BIT); const MergedFlags flag = 1 << index++; auto edge_iter = edges.cbegin(); auto edge_end = edges.cend(); auto flags_iter = flags.begin(); // Remove all edges that are contained in the old set of edges and set the appropriate flag. auto new_end = std::remove_if(new_edges.begin(), new_edges.end(), [&](const QueryEdge &edge) { // check if the new edge would be sorted before the currend old edge // if so it is not contained yet in the set of old edges if (edge_iter == edge_end || mergeCompare(edge, *edge_iter)) { return false; } // find the first old edge that is equal or greater then the new edge while (edge_iter != edge_end && mergeCompare(*edge_iter, edge)) { BOOST_ASSERT(flags_iter != flags.end()); edge_iter++; flags_iter++; } // all new edges will be sorted after the old edges if (edge_iter == edge_end) { return false; } BOOST_ASSERT(edge_iter != edge_end); if (mergable(edge, *edge_iter)) { *flags_iter = *flags_iter | flag; return true; } BOOST_ASSERT(mergeCompare(edge, *edge_iter)); return false; }); // append new edges edges.insert(edges.end(), new_edges.begin(), new_end); auto edges_size = edges.size(); auto new_edges_size = std::distance(new_edges.begin(), new_end); BOOST_ASSERT(static_cast(edges_size) >= new_edges_size); flags.resize(edges_size); std::fill(flags.begin() + edges_size - new_edges_size, flags.end(), flag); // enforce sorting for next merge step std::vector ordering(edges_size); std::iota(ordering.begin(), ordering.end(), 0); tbb::parallel_sort( ordering.begin(), ordering.end(), [&](const auto lhs_idx, const auto rhs_idx) { return mergeCompare(edges[lhs_idx], edges[rhs_idx]); }); auto permutation = util::orderingToPermutation(ordering); util::inplacePermutation(edges.begin(), edges.end(), permutation); util::inplacePermutation(flags.begin(), flags.end(), permutation); BOOST_ASSERT(std::is_sorted(edges.begin(), edges.end(), mergeCompare)); } auto MakeEdgeFilters() const { std::vector> filters(index); for (const auto flag_index : util::irange(0, index)) { MergedFlags mask = 1 << flag_index; for (const auto flag : flags) { filters[flag_index].push_back(flag & mask); } } return filters; } std::size_t index = 0; std::vector flags; std::vector edges; }; } } #endif