diff --git a/base/CMakeLists.txt b/base/CMakeLists.txt index d4e2ad4bd4..22f1b391a1 100644 --- a/base/CMakeLists.txt +++ b/base/CMakeLists.txt @@ -35,6 +35,8 @@ set( newtype.hpp normalize_unicode.cpp observer_list.hpp + random.cpp + random.hpp range_iterator.hpp ref_counted.hpp rolling_hash.hpp diff --git a/base/base.pro b/base/base.pro index 7eda31055f..71073004a6 100644 --- a/base/base.pro +++ b/base/base.pro @@ -18,6 +18,7 @@ SOURCES += \ logging.cpp \ lower_case.cpp \ normalize_unicode.cpp \ + random.cpp \ shared_buffer_manager.cpp \ src_point.cpp \ string_format.cpp \ @@ -59,6 +60,7 @@ HEADERS += \ mutex.hpp \ newtype.hpp \ observer_list.hpp \ + random.hpp \ range_iterator.hpp \ ref_counted.hpp \ regexp.hpp \ diff --git a/base/random.cpp b/base/random.cpp new file mode 100644 index 0000000000..964cffa314 --- /dev/null +++ b/base/random.cpp @@ -0,0 +1,22 @@ +#include "base/random.hpp" + +#include +#include + +namespace base +{ +std::vector RandomSample(size_t n, size_t k, std::minstd_rand & rng) +{ + std::vector result(std::min(k, n)); + std::iota(result.begin(), result.end(), 0); + + for (size_t i = k; i < n; ++i) + { + size_t const j = rng() % (i + 1); + if (j < k) + result[j] = i; + } + + return result; +} +} // base diff --git a/base/random.hpp b/base/random.hpp new file mode 100644 index 0000000000..267ae22add --- /dev/null +++ b/base/random.hpp @@ -0,0 +1,10 @@ +#pragma once + +#include +#include + +namespace base +{ +// Selects a fair random subset of size min(|n|, |k|) from [0, 1, 2, ..., n - 1]. +std::vector RandomSample(size_t n, size_t k, std::minstd_rand & rng); +} // base diff --git a/routing/cross_mwm_road_graph.cpp b/routing/cross_mwm_road_graph.cpp index 3cabed95ab..c1874c172b 100644 --- a/routing/cross_mwm_road_graph.cpp +++ b/routing/cross_mwm_road_graph.cpp @@ -3,11 +3,134 @@ #include "geometry/distance_on_sphere.hpp" +#include + namespace { +using namespace routing; + inline bool IsValidEdgeWeight(EdgeWeight const & w) { return w != INVALID_EDGE_WEIGHT; } +template +class ClosestNodeFinder +{ +public: + ClosestNodeFinder(CrossNode const & node, double & minDistance, Node & resultingCrossNode) + : m_node(node), m_minDistance(minDistance), m_resultingCrossNode(resultingCrossNode) + { + } + + void operator()(Node const & crossNode) const + { + if (crossNode.m_nodeId != m_node.node) + return; + + double const dist = ms::DistanceOnEarth(m_node.point, crossNode.m_point); + if (dist < m_minDistance) + { + m_minDistance = dist; + m_resultingCrossNode = crossNode; + } + } + +private: + CrossNode const & m_node; + double & m_minDistance; + Node & m_resultingCrossNode; +}; + +double GetAdjacencyCost(CrossRoutingContextReader const & currentContext, + IngoingCrossNode const & ingoingCrossNode, + OutgoingCrossNode const & outgoingCrossNode) +{ + return currentContext.GetAdjacencyCost(ingoingCrossNode, outgoingCrossNode); } +double GetAdjacencyCost(CrossRoutingContextReader const & currentContext, + OutgoingCrossNode const & outgoingCrossNode, + IngoingCrossNode const & ingoingCrossNode) +{ + return GetAdjacencyCost(currentContext, ingoingCrossNode, outgoingCrossNode); +} + +template +class EdgesFiller +{ +public: + EdgesFiller(TRoutingMappingPtr const & currentMapping, + CrossRoutingContextReader const & currentContext, SourceNode const & startingNode, + CrossMwmGraph const & crossMwmGraph, vector & adj) + : m_currentMapping(currentMapping) + , m_currentContext(currentContext) + , m_startingNode(startingNode) + , m_crossMwmGraph(crossMwmGraph) + , m_adj(adj) + { + } + + void operator()(TargetNode const & node) const + { + TWrittenEdgeWeight const outWeight = GetAdjacencyCost(m_currentContext, m_startingNode, node); + if (outWeight != kInvalidContextEdgeWeight && outWeight != 0) + { + vector const & targets = + m_crossMwmGraph.ConstructBorderCross(m_currentMapping, node); + for (auto const & target : targets) + { + if (target.toNode.IsValid()) + m_adj.emplace_back(target, outWeight); + } + } + } + +private: + TRoutingMappingPtr const & m_currentMapping; + CrossRoutingContextReader const & m_currentContext; + SourceNode const & m_startingNode; + CrossMwmGraph const & m_crossMwmGraph; + vector & m_adj; +}; + +bool ForEachNodeNearPoint(CrossRoutingContextReader const & currentContext, + CrossNode const & crossNode, + ClosestNodeFinder const & findingNode) +{ + return currentContext.ForEachIngoingNodeNearPoint(crossNode.point, findingNode); +} + +bool ForEachNodeNearPoint(CrossRoutingContextReader const & currentContext, + CrossNode const & crossNode, + ClosestNodeFinder const & findingNode) +{ + return currentContext.ForEachOutgoingNodeNearPoint(crossNode.point, findingNode); +} + +template +void FindCrossNode(CrossRoutingContextReader const & currentContext, CrossNode const & crossNode, + Node & node) +{ + double minDistance = std::numeric_limits::max(); + ClosestNodeFinder findingNode(crossNode, minDistance, node); + CHECK(ForEachNodeNearPoint(currentContext, crossNode, findingNode), ()); + CHECK_NOT_EQUAL(minDistance, std::numeric_limits::max(), + ("crossNode.point:", crossNode.point)); +} + +template +vector const & ConstructBorderCrossImpl( + TWrittenNodeId nodeId, TRoutingMappingPtr const & currentMapping, + unordered_map, CrossMwmGraph::Hash> const & + cachedNextNodes, + Fn && borderCrossConstructor /*bool & result*/) +{ + auto const key = make_pair(nodeId, currentMapping->GetMwmId()); + auto const it = cachedNextNodes.find(key); + if (it != cachedNextNodes.end()) + return it->second; + borderCrossConstructor(key); + return cachedNextNodes.find(key)->second; +} +} // namespace + namespace routing { IRouter::ResultCode CrossMwmGraph::SetStartNode(CrossNode const & startNode) @@ -54,14 +177,15 @@ IRouter::ResultCode CrossMwmGraph::SetStartNode(CrossNode const & startNode) { if (IsValidEdgeWeight(weights[i])) { - vector const & nextCrosses = ConstructBorderCross(outgoingNodes[i], startMapping); + vector const & nextCrosses = + ConstructBorderCross(startMapping, outgoingNodes[i]); for (auto const & nextCross : nextCrosses) { if (nextCross.toNode.IsValid()) dummyEdges.emplace_back(nextCross, weights[i]); + } } } - } m_virtualEdges.insert(make_pair(startNode, dummyEdges)); return IRouter::NoError; @@ -129,24 +253,9 @@ IRouter::ResultCode CrossMwmGraph::SetFinalNode(CrossNode const & finalNode) return IRouter::NoError; } -vector const & CrossMwmGraph::ConstructBorderCross(OutgoingCrossNode const & startNode, - TRoutingMappingPtr const & currentMapping) const -{ - // Check cached crosses. - auto const key = make_pair(startNode.m_nodeId, currentMapping->GetMwmId()); - auto const it = m_cachedNextNodes.find(key); - if (it != m_cachedNextNodes.end()) - return it->second; - - // Cache miss case. - auto & crosses = m_cachedNextNodes[key]; - ConstructBorderCrossImpl(startNode, currentMapping, crosses); - return crosses; -} - -bool CrossMwmGraph::ConstructBorderCrossImpl(OutgoingCrossNode const & startNode, - TRoutingMappingPtr const & currentMapping, - vector & crosses) const +bool CrossMwmGraph::ConstructBorderCrossByOutgoingImpl(OutgoingCrossNode const & startNode, + TRoutingMappingPtr const & currentMapping, + vector & crosses) const { auto const fromCross = CrossNode(startNode.m_nodeId, currentMapping->GetMwmId(), startNode.m_point); string const & nextMwm = currentMapping->m_crossContext.GetOutgoingMwmName(startNode); @@ -156,71 +265,131 @@ bool CrossMwmGraph::ConstructBorderCrossImpl(OutgoingCrossNode const & startNode return false; ASSERT(crosses.empty(), ()); nextMapping->LoadCrossContext(); - nextMapping->m_crossContext.ForEachIngoingNodeNearPoint(startNode.m_point, [&](IngoingCrossNode const & node) - { - if (node.m_point.EqualDxDy(startNode.m_point, kMwmCrossingNodeEqualityMeters * MercatorBounds::degreeInMetres)) - { - auto const toCross = CrossNode(node.m_nodeId, nextMapping->GetMwmId(), node.m_point); - if (toCross.IsValid()) - crosses.emplace_back(fromCross, toCross); - } - }); + nextMapping->m_crossContext.ForEachIngoingNodeNearPoint( + startNode.m_point, [&](IngoingCrossNode const & node) { + if (node.m_nodeId == INVALID_NODE_ID) + return; + + if (!node.m_point.EqualDxDy( + startNode.m_point, kMwmCrossingNodeEqualityMeters * MercatorBounds::degreeInMetres)) + return; + + crosses.emplace_back(fromCross, + CrossNode(node.m_nodeId, nextMapping->GetMwmId(), node.m_point)); + }); return !crosses.empty(); } -void CrossMwmGraph::GetOutgoingEdgesList(BorderCross const & v, - vector & adj) const +bool CrossMwmGraph::ConstructBorderCrossByIngoingImpl(IngoingCrossNode const & startNode, + TRoutingMappingPtr const & currentMapping, + vector & crosses) const +{ + ASSERT(crosses.empty(), ()); + auto const toCross = CrossNode(startNode.m_nodeId, currentMapping->GetMwmId(), startNode.m_point); + vector const & neighboringMwms = currentMapping->m_crossContext.GetNeighboringMwmList(); + string const & currentMwm = currentMapping->GetMwmId().GetInfo()->GetCountryName(); + // Note. There's no field |m_ingoingIndex| in class IngoingCrossNode. Because this + // index is not saved in osrm routing section. So we need to write a workaround and + // to check all neighboring mwms. + for (string const & prevMwm : neighboringMwms) + { + TRoutingMappingPtr prevMapping = m_indexManager.GetMappingByName(prevMwm); + if (!prevMapping->IsValid()) + continue; + + prevMapping->LoadCrossContext(); + prevMapping->m_crossContext.ForEachOutgoingNodeNearPoint( + startNode.m_point, [&](OutgoingCrossNode const & node) { + if (node.m_nodeId == INVALID_NODE_ID) + return; + + if (prevMapping->m_crossContext.GetOutgoingMwmName(node) != currentMwm) + return; + + if (!node.m_point.EqualDxDy(startNode.m_point, kMwmCrossingNodeEqualityMeters * + MercatorBounds::degreeInMetres)) + return; + + crosses.emplace_back(CrossNode(node.m_nodeId, prevMapping->GetMwmId(), node.m_point), + toCross); + }); + } + return !crosses.empty(); +} + +vector const & CrossMwmGraph::ConstructBorderCross( + TRoutingMappingPtr const & currentMapping, OutgoingCrossNode const & node) const +{ + return ConstructBorderCrossImpl(node.m_nodeId, currentMapping, m_cachedNextNodesByOutgoing, + [&](std::pair const & key) { + vector crosses; + ConstructBorderCrossByOutgoingImpl(node, currentMapping, + crosses); + m_cachedNextNodesByOutgoing[key] = move(crosses); + }); +} + +vector const & CrossMwmGraph::ConstructBorderCross( + TRoutingMappingPtr const & currentMapping, IngoingCrossNode const & node) const +{ + return ConstructBorderCrossImpl(node.m_nodeId, currentMapping, m_cachedNextNodesByIngoing, + [&](std::pair const & key) { + vector crosses; + ConstructBorderCrossByIngoingImpl(node, currentMapping, + crosses); + m_cachedNextNodesByIngoing[key] = move(crosses); + }); +} + +void CrossMwmGraph::GetEdgesList(BorderCross const & v, bool isOutgoing, + vector & adj) const { // Check for virtual edges. adj.clear(); + + // Note. Code below processes virtual edges. This code does not work properly if isOutgoing == + // false. + // At the same time when this method is called with isOutgoing == false |m_virtualEdges| is empty. + if (!isOutgoing && !m_virtualEdges.empty()) + { + NOTIMPLEMENTED(); + return; + } + auto const it = m_virtualEdges.find(v.toNode); if (it != m_virtualEdges.end()) { adj.insert(adj.end(), it->second.begin(), it->second.end()); // For last map we need to load virtual shortcuts and real cross roads. It takes to account case - // when we have a path from the mwmw border to the point inside the map throuh another map. + // when we have a path from the mwm border to the point inside the map throuh another map. // See Ust-Katav test for more. if (it->second.empty() || !it->second.front().GetTarget().toNode.isVirtual) return; } // Loading cross routing section. - TRoutingMappingPtr currentMapping = m_indexManager.GetMappingById(v.toNode.mwmId); + TRoutingMappingPtr currentMapping = + m_indexManager.GetMappingById(isOutgoing ? v.toNode.mwmId : v.fromNode.mwmId); ASSERT(currentMapping->IsValid(), ()); currentMapping->LoadCrossContext(); currentMapping->FreeFileIfPossible(); CrossRoutingContextReader const & currentContext = currentMapping->m_crossContext; - // Find income node. - IngoingCrossNode ingoingNode; - bool found = false; - auto findingFn = [&ingoingNode, &v, &found](IngoingCrossNode const & node) + if (isOutgoing) { - if (node.m_nodeId == v.toNode.node) - { - found = true; - ingoingNode = node; + IngoingCrossNode ingoingNode; + FindCrossNode(currentContext, v.toNode, ingoingNode); + currentContext.ForEachOutgoingNode(EdgesFiller( + currentMapping, currentContext, ingoingNode, *this, adj)); + } + else + { + OutgoingCrossNode outgoingNode; + FindCrossNode(currentContext, v.fromNode, outgoingNode); + currentContext.ForEachIngoingNode(EdgesFiller( + currentMapping, currentContext, outgoingNode, *this, adj)); } - }; - CHECK(currentContext.ForEachIngoingNodeNearPoint(v.toNode.point, findingFn), ()); - - CHECK(found, ()); - - // Find outs. Generate adjacency list. - currentContext.ForEachOutgoingNode([&, this](OutgoingCrossNode const & node) - { - EdgeWeight const outWeight = currentContext.GetAdjacencyCost(ingoingNode, node); - if (outWeight != kInvalidContextEdgeWeight && outWeight != 0) - { - vector const & targets = ConstructBorderCross(node, currentMapping); - for (auto const & target : targets) - { - if (target.toNode.IsValid()) - adj.emplace_back(target, outWeight); - } - } - }); } double CrossMwmGraph::HeuristicCostEstimate(BorderCross const & v, BorderCross const & w) const diff --git a/routing/cross_mwm_road_graph.hpp b/routing/cross_mwm_road_graph.hpp index 7687c3246f..b138f81a17 100644 --- a/routing/cross_mwm_road_graph.hpp +++ b/routing/cross_mwm_road_graph.hpp @@ -72,6 +72,7 @@ struct BorderCross BorderCross(CrossNode const & from, CrossNode const & to) : fromNode(from), toNode(to) {} BorderCross() = default; + // TODO(bykoianko) Consider using fields |fromNode| and |toNode| in operator== and operator<. inline bool operator==(BorderCross const & a) const { return toNode == a.toNode; } inline bool operator<(BorderCross const & a) const { return toNode < a.toNode; } }; @@ -101,48 +102,10 @@ private: class CrossMwmGraph { public: + using TCachingKey = pair; using TVertexType = BorderCross; using TEdgeType = CrossWeightedEdge; - explicit CrossMwmGraph(RoutingIndexManager & indexManager) : m_indexManager(indexManager) {} - - void GetOutgoingEdgesList(BorderCross const & v, vector & adj) const; - void GetIngoingEdgesList(BorderCross const & /* v */, - vector & /* adj */) const - { - NOTIMPLEMENTED(); - } - - double HeuristicCostEstimate(BorderCross const & v, BorderCross const & w) const; - - IRouter::ResultCode SetStartNode(CrossNode const & startNode); - IRouter::ResultCode SetFinalNode(CrossNode const & finalNode); - -private: - // Cashing wrapper for the ConstructBorderCrossImpl function. - vector const & ConstructBorderCross(OutgoingCrossNode const & startNode, - TRoutingMappingPtr const & currentMapping) const; - - // Pure function to construct boder cross by outgoing cross node. - bool ConstructBorderCrossImpl(OutgoingCrossNode const & startNode, - TRoutingMappingPtr const & currentMapping, - vector & cross) const; - /*! - * Adds a virtual edge to the graph so that it is possible to represent - * the final segment of the path that leads from the map's border - * to finalNode. Addition of such virtual edges for the starting node is - * inlined elsewhere. - */ - void AddVirtualEdge(IngoingCrossNode const & node, CrossNode const & finalNode, - EdgeWeight weight); - - map > m_virtualEdges; - - mutable RoutingIndexManager m_indexManager; - - // Caching stuff. - using TCachingKey = pair; - struct Hash { size_t operator()(TCachingKey const & p) const @@ -151,7 +114,53 @@ private: } }; - mutable unordered_map, Hash> m_cachedNextNodes; + explicit CrossMwmGraph(RoutingIndexManager & indexManager) : m_indexManager(indexManager) {} + void GetOutgoingEdgesList(BorderCross const & v, vector & adj) const + { + GetEdgesList(v, true /* isOutgoing */, adj); + } + + void GetIngoingEdgesList(BorderCross const & v, vector & adj) const + { + GetEdgesList(v, false /* isOutgoing */, adj); + } + + double HeuristicCostEstimate(BorderCross const & v, BorderCross const & w) const; + + IRouter::ResultCode SetStartNode(CrossNode const & startNode); + IRouter::ResultCode SetFinalNode(CrossNode const & finalNode); + + vector const & ConstructBorderCross(TRoutingMappingPtr const & currentMapping, + OutgoingCrossNode const & node) const; + vector const & ConstructBorderCross(TRoutingMappingPtr const & currentMapping, + IngoingCrossNode const & node) const; + +private: + // Pure function to construct boder cross by outgoing cross node. + bool ConstructBorderCrossByOutgoingImpl(OutgoingCrossNode const & startNode, + TRoutingMappingPtr const & currentMapping, + vector & cross) const; + bool ConstructBorderCrossByIngoingImpl(IngoingCrossNode const & startNode, + TRoutingMappingPtr const & currentMapping, + vector & crosses) const; + + /*! + * Adds a virtual edge to the graph so that it is possible to represent + * the final segment of the path that leads from the map's border + * to finalNode. Addition of such virtual edges for the starting node is + * inlined elsewhere. + */ + void AddVirtualEdge(IngoingCrossNode const & node, CrossNode const & finalNode, + EdgeWeight weight); + void GetEdgesList(BorderCross const & v, bool isOutgoing, vector & adj) const; + + map > m_virtualEdges; + + mutable RoutingIndexManager m_indexManager; + + // @TODO(bykoianko) Consider removing key work mutable. + mutable unordered_map, Hash> m_cachedNextNodesByIngoing; + mutable unordered_map, Hash> m_cachedNextNodesByOutgoing; }; //-------------------------------------------------------------------------------------------------- diff --git a/routing/cross_routing_context.cpp b/routing/cross_routing_context.cpp index 4fc6dcf9c3..555f5e8b01 100644 --- a/routing/cross_routing_context.cpp +++ b/routing/cross_routing_context.cpp @@ -105,8 +105,8 @@ const string & CrossRoutingContextReader::GetOutgoingMwmName( return m_neighborMwmList[outgoingNode.m_outgoingIndex]; } -TWrittenEdgeWeight CrossRoutingContextReader::GetAdjacencyCost(IngoingCrossNode const & ingoing, - OutgoingCrossNode const & outgoing) const +TWrittenEdgeWeight CrossRoutingContextReader::GetAdjacencyCost( + IngoingCrossNode const & ingoing, OutgoingCrossNode const & outgoing) const { if (ingoing.m_adjacencyIndex == kInvalidAdjacencyIndex || outgoing.m_adjacencyIndex == kInvalidAdjacencyIndex) diff --git a/routing/cross_routing_context.hpp b/routing/cross_routing_context.hpp index 186ee6b78c..ec2bde2dde 100644 --- a/routing/cross_routing_context.hpp +++ b/routing/cross_routing_context.hpp @@ -91,7 +91,7 @@ class CrossRoutingContextReader public: void Load(Reader const & r); - const string & GetOutgoingMwmName(OutgoingCrossNode const & mwmIndex) const; + const string & GetOutgoingMwmName(OutgoingCrossNode const & outgoingNode) const; TWrittenEdgeWeight GetAdjacencyCost(IngoingCrossNode const & ingoing, OutgoingCrossNode const & outgoing) const; diff --git a/routing/routing_integration_tests/cross_section_tests.cpp b/routing/routing_integration_tests/cross_section_tests.cpp index b248ed583a..8b48249476 100644 --- a/routing/routing_integration_tests/cross_section_tests.cpp +++ b/routing/routing_integration_tests/cross_section_tests.cpp @@ -1,9 +1,14 @@ #include "testing/testing.hpp" +#include "routing/cross_mwm_road_graph.hpp" #include "routing/cross_routing_context.hpp" #include "routing/osrm2feature_map.hpp" #include "routing/routing_mapping.hpp" +#include "storage/country_info_getter.hpp" + +#include "indexer/index.hpp" + #include "platform/local_country_file.hpp" #include "platform/local_country_file_utils.hpp" #include "platform/platform.hpp" @@ -12,9 +17,12 @@ #include "base/buffer_vector.hpp" #include "base/logging.hpp" +#include "base/random.hpp" #include "std/limits.hpp" +#include + using namespace routing; namespace @@ -123,4 +131,98 @@ UNIT_TEST(CheckOsrmToFeatureMapping) LOG(LINFO, ("Found", localFiles.size(), "countries. In", checked, "maps with routing have", errors, "with errors.")); TEST_EQUAL(errors, 0, ("Some countries have osrm and features mismatch.")); } + +// The idea behind the test is +// 1. to go through all ingoing nodes of all mwms +// 2. to find all cross mwm outgoing edges for each ingoing node +// 3. to find all edges which are ingoing for the outgoing edges +// 4. to check that an edge mentioned in (1) there's between the ingoing edges +// Note. This test may take more than 3 hours. +UNIT_TEST(CrossMwmGraphTest) +{ + vector localFiles; + Index index; + platform::FindAllLocalMapsAndCleanup(numeric_limits::max() /* latestVersion */, + localFiles); + + for (platform::LocalCountryFile & file : localFiles) + { + file.SyncWithDisk(); + index.RegisterMap(file); + } + + for (auto it = localFiles.begin(); it != localFiles.end();) + { + string const & countryName = it->GetCountryName(); + MwmSet::MwmId const mwmId = index.GetMwmIdByCountryFile(it->GetCountryFile()); + if (countryName == "minsk-pass" || mwmId.GetInfo()->GetType() != MwmInfo::COUNTRY) + it = localFiles.erase(it); + else + ++it; + } + + Platform p; + unique_ptr infoGetter = + storage::CountryInfoReader::CreateCountryInfoReader(p); + auto countryFileGetter = [&infoGetter](m2::PointD const & pt) { + return infoGetter->GetRegionCountryId(pt); + }; + + RoutingIndexManager manager(countryFileGetter, index); + CrossMwmGraph crossMwmGraph(manager); + + auto const seed = std::chrono::system_clock::now().time_since_epoch().count(); + LOG(LINFO, ("Seed for RandomSample:", seed)); + std::minstd_rand rng(static_cast(seed)); + std::vector subset = base::RandomSample(localFiles.size(), 10 /* mwm number */, rng); + std::vector subsetCountryFiles; + for (size_t i : subset) + subsetCountryFiles.push_back(localFiles[i]); + + for (platform::LocalCountryFile const & file : subsetCountryFiles) + { + string const & countryName = file.GetCountryName(); + LOG(LINFO, ("Processing", countryName)); + MwmSet::MwmId const mwmId = index.GetMwmIdByCountryFile(file.GetCountryFile()); + + TEST(mwmId.IsAlive(), ("Mwm name:", countryName, "Subset:", subsetCountryFiles)); + TRoutingMappingPtr currentMapping = manager.GetMappingById(mwmId); + if (!currentMapping->IsValid()) + continue; // No routing sections in the mwm. + + currentMapping->LoadCrossContext(); + currentMapping->FreeFileIfPossible(); + CrossRoutingContextReader const & currentContext = currentMapping->m_crossContext; + + size_t ingoingCounter = 0; + currentContext.ForEachIngoingNode([&](IngoingCrossNode const & node) { + ++ingoingCounter; + vector const & targets = + crossMwmGraph.ConstructBorderCross(currentMapping, node); + for (BorderCross const & t : targets) + { + vector outAdjs; + crossMwmGraph.GetOutgoingEdgesList(t, outAdjs); + for (CrossWeightedEdge const & out : outAdjs) + { + vector inAdjs; + crossMwmGraph.GetIngoingEdgesList(out.GetTarget(), inAdjs); + TEST(find_if(inAdjs.cbegin(), inAdjs.cend(), + [&](CrossWeightedEdge const & e) { + return e.GetTarget() == t && out.GetWeight() == e.GetWeight(); + }) != inAdjs.cend(), + ("ForEachOutgoingNodeNearPoint() and ForEachIngoingNodeNearPoint() arn't " + "correlated. Mwm:", + countryName, "Subset:", subsetCountryFiles)); + } + } + }); + + size_t outgoingCounter = 0; + currentContext.ForEachOutgoingNode( + [&](OutgoingCrossNode const & /* node */) { ++outgoingCounter; }); + + LOG(LINFO, ("Processed:", countryName, "Exits:", outgoingCounter, "Enters:", ingoingCounter)); + } +} } // namespace diff --git a/search/pre_ranker.cpp b/search/pre_ranker.cpp index 70c849df4e..05455ced62 100644 --- a/search/pre_ranker.cpp +++ b/search/pre_ranker.cpp @@ -9,6 +9,7 @@ #include "indexer/rank_table.hpp" #include "indexer/scales.hpp" +#include "base/random.hpp" #include "base/stl_helpers.hpp" #include "std/iterator.hpp" @@ -46,22 +47,6 @@ struct ComparePreResult1 } }; -// Selects a fair random subset of size min(|n|, |k|) from [0, 1, 2, ..., n - 1]. -vector RandomSample(size_t n, size_t k, minstd_rand & rng) -{ - vector result(std::min(k, n)); - iota(result.begin(), result.end(), 0); - - for (size_t i = k; i < n; ++i) - { - size_t const j = rng() % (i + 1); - if (j < k) - result[j] = i; - } - - return result; -} - void SweepNearbyResults(double eps, vector & results) { NearbyPointsSweeper sweeper(eps); @@ -281,7 +266,7 @@ void PreRanker::FilterForViewportSearch() if (m <= old) { - for (size_t i : RandomSample(old, m, m_rng)) + for (size_t i : base::RandomSample(old, m, m_rng)) results.push_back(m_results[bucket[i]]); } else @@ -289,7 +274,7 @@ void PreRanker::FilterForViewportSearch() for (size_t i = 0; i < old; ++i) results.push_back(m_results[bucket[i]]); - for (size_t i : RandomSample(bucket.size() - old, m - old, m_rng)) + for (size_t i : base::RandomSample(bucket.size() - old, m - old, m_rng)) results.push_back(m_results[bucket[old + i]]); } } @@ -301,7 +286,7 @@ void PreRanker::FilterForViewportSearch() else { m_results.clear(); - for (size_t i : RandomSample(results.size(), BatchSize(), m_rng)) + for (size_t i : base::RandomSample(results.size(), BatchSize(), m_rng)) m_results.push_back(results[i]); } } diff --git a/xcode/base/base.xcodeproj/project.pbxproj b/xcode/base/base.xcodeproj/project.pbxproj index 2d015c3726..4d9d859463 100644 --- a/xcode/base/base.xcodeproj/project.pbxproj +++ b/xcode/base/base.xcodeproj/project.pbxproj @@ -44,6 +44,9 @@ 39FD27381CC65AD000AFF551 /* timegm_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39FD26E21CC65A0E00AFF551 /* timegm_test.cpp */; }; 39FD27391CC65AD000AFF551 /* timer_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39FD26E31CC65A0E00AFF551 /* timer_test.cpp */; }; 39FD273B1CC65B1000AFF551 /* libbase.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 675341771A3F57BF00A0A8C3 /* libbase.a */; }; + 56B1A0741E69DE4D00395022 /* random.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 56B1A0711E69DE4D00395022 /* random.cpp */; }; + 56B1A0751E69DE4D00395022 /* random.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 56B1A0721E69DE4D00395022 /* random.hpp */; }; + 56B1A0761E69DE4D00395022 /* small_set.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 56B1A0731E69DE4D00395022 /* small_set.hpp */; }; 670E39441C46C76900E9C0A6 /* sunrise_sunset.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 670E39421C46C76900E9C0A6 /* sunrise_sunset.cpp */; }; 670E39451C46C76900E9C0A6 /* sunrise_sunset.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 670E39431C46C76900E9C0A6 /* sunrise_sunset.hpp */; }; 671182F01C807C0A00CB8177 /* gmtime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 671182EE1C807C0A00CB8177 /* gmtime.cpp */; }; @@ -157,6 +160,9 @@ 39FD273D1CC65B1000AFF551 /* libplatform.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libplatform.a; path = "../../../omim-xcode-build/Debug/libplatform.a"; sourceTree = ""; }; 39FD27401CC65B2800AFF551 /* libindexer.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libindexer.a; path = "../../../omim-xcode-build/Debug/libindexer.a"; sourceTree = ""; }; 39FD27421CC65B4800AFF551 /* libcoding.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libcoding.a; path = "../../../omim-xcode-build/Debug/libcoding.a"; sourceTree = ""; }; + 56B1A0711E69DE4D00395022 /* random.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = random.cpp; sourceTree = ""; }; + 56B1A0721E69DE4D00395022 /* random.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = random.hpp; sourceTree = ""; }; + 56B1A0731E69DE4D00395022 /* small_set.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = small_set.hpp; sourceTree = ""; }; 670E39421C46C76900E9C0A6 /* sunrise_sunset.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = sunrise_sunset.cpp; sourceTree = ""; }; 670E39431C46C76900E9C0A6 /* sunrise_sunset.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = sunrise_sunset.hpp; sourceTree = ""; }; 671182EE1C807C0A00CB8177 /* gmtime.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = gmtime.cpp; sourceTree = ""; }; @@ -311,6 +317,9 @@ 675341791A3F57BF00A0A8C3 /* base */ = { isa = PBXGroup; children = ( + 56B1A0711E69DE4D00395022 /* random.cpp */, + 56B1A0721E69DE4D00395022 /* random.hpp */, + 56B1A0731E69DE4D00395022 /* small_set.hpp */, 675341851A3F57E400A0A8C3 /* array_adapters.hpp */, 675341861A3F57E400A0A8C3 /* assert.hpp */, 675341871A3F57E400A0A8C3 /* base.cpp */, @@ -420,6 +429,7 @@ 675341D11A3F57E400A0A8C3 /* cache.hpp in Headers */, 675341E31A3F57E400A0A8C3 /* math.hpp in Headers */, 3446C6731DDCA96300146687 /* levenshtein_dfa.hpp in Headers */, + 56B1A0751E69DE4D00395022 /* random.hpp in Headers */, 675341E21A3F57E400A0A8C3 /* macros.hpp in Headers */, 672DD4C51E0425600078E13C /* observer_list.hpp in Headers */, 675341EF1A3F57E400A0A8C3 /* rolling_hash.hpp in Headers */, @@ -452,6 +462,7 @@ 675341FA1A3F57E400A0A8C3 /* src_point.hpp in Headers */, 674A7E2F1C0DB03D003D48E1 /* timegm.hpp in Headers */, 675341F71A3F57E400A0A8C3 /* shared_buffer_manager.hpp in Headers */, + 56B1A0761E69DE4D00395022 /* small_set.hpp in Headers */, 67B52B611AD3C84E00664C17 /* thread_checker.hpp in Headers */, 672DD4BE1E0425600078E13C /* cancellable.hpp in Headers */, 675341CB1A3F57E400A0A8C3 /* array_adapters.hpp in Headers */, @@ -601,6 +612,7 @@ 67E40EC81E4DC0D500A6D200 /* small_set_test.cpp in Sources */, 6753420E1A3F57E400A0A8C3 /* timer.cpp in Sources */, 675341F61A3F57E400A0A8C3 /* shared_buffer_manager.cpp in Sources */, + 56B1A0741E69DE4D00395022 /* random.cpp in Sources */, 675341DA1A3F57E400A0A8C3 /* exception.cpp in Sources */, 675341F91A3F57E400A0A8C3 /* src_point.cpp in Sources */, 675342031A3F57E400A0A8C3 /* strings_bundle.cpp in Sources */,