* speed up hop-to-hop calculations
* better and faster trip clustering: trip tries * add --write-colors to extract line colors from OSM data * refactor config parameter names, update default pfaedle.cfg * add --stats for writing a stats.json file * add --no-fast-hops, --no-a-star, --no-trie for debugging * general refactoring
This commit is contained in:
parent
f1822868c5
commit
4c29892658
126 changed files with 14576 additions and 12196 deletions
|
|
@ -7,8 +7,8 @@
|
|||
|
||||
#include <stack>
|
||||
#include "util/graph/Edge.h"
|
||||
#include "util/graph/UndirGraph.h"
|
||||
#include "util/graph/Node.h"
|
||||
#include "util/graph/UndirGraph.h"
|
||||
|
||||
namespace util {
|
||||
namespace graph {
|
||||
|
|
@ -20,13 +20,26 @@ using util::graph::Edge;
|
|||
// collection of general graph algorithms
|
||||
class Algorithm {
|
||||
public:
|
||||
template <typename N, typename E>
|
||||
struct EdgeCheckFunc {
|
||||
virtual bool operator()(const Node<N, E>* frNd,
|
||||
const Edge<N, E>* edge) const {
|
||||
UNUSED(frNd);
|
||||
UNUSED(edge);
|
||||
return true;
|
||||
};
|
||||
};
|
||||
|
||||
template <typename N, typename E>
|
||||
static std::vector<std::set<Node<N, E>*> > connectedComponents(
|
||||
const UndirGraph<N, E>& g);
|
||||
|
||||
template <typename N, typename E>
|
||||
static std::vector<std::set<Node<N, E>*> > connectedComponents(
|
||||
const UndirGraph<N, E>& g, const EdgeCheckFunc<N, E>& checkFunc);
|
||||
};
|
||||
|
||||
#include "util/graph/Algorithm.tpp"
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,18 @@
|
|||
// Copyright 2017, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
//
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
std::vector<std::set<Node<N, E>*>> Algorithm::connectedComponents(
|
||||
const UndirGraph<N, E>& g) {
|
||||
return connectedComponents(g, EdgeCheckFunc<N, E>());
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
std::vector<std::set<Node<N, E>*> > Algorithm::connectedComponents(
|
||||
const UndirGraph<N, E>& g) {
|
||||
std::vector<std::set<Node<N, E>*>> Algorithm::connectedComponents(
|
||||
const UndirGraph<N, E>& g, const EdgeCheckFunc<N, E>& checkFunc) {
|
||||
std::vector<std::set<Node<N, E>*>> ret;
|
||||
std::set<Node<N, E>*> visited;
|
||||
|
||||
|
|
@ -22,6 +29,7 @@ std::vector<std::set<Node<N, E>*> > Algorithm::connectedComponents(
|
|||
visited.insert(cur);
|
||||
|
||||
for (auto* e : cur->getAdjList()) {
|
||||
if (!checkFunc(cur, e)) continue;
|
||||
if (!visited.count(e->getOtherNd(cur))) q.push(e->getOtherNd(cur));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
7
src/util/graph/BiDijkstra.cpp
Normal file
7
src/util/graph/BiDijkstra.cpp
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
// Copyright 2017, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#include "util/graph/BiDijkstra.h"
|
||||
|
||||
size_t util::graph::BiDijkstra::ITERS = 0;
|
||||
129
src/util/graph/BiDijkstra.h
Normal file
129
src/util/graph/BiDijkstra.h
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
// Copyright 2017, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef UTIL_GRAPH_BIDIJKSTRA_H_
|
||||
#define UTIL_GRAPH_BIDIJKSTRA_H_
|
||||
|
||||
#include <limits>
|
||||
#include <list>
|
||||
#include <queue>
|
||||
#include <set>
|
||||
#include <algorithm>
|
||||
#include <unordered_map>
|
||||
#include "util/graph/Edge.h"
|
||||
#include "util/graph/Graph.h"
|
||||
#include "util/graph/Node.h"
|
||||
#include "util/graph/ShortestPath.h"
|
||||
|
||||
namespace util {
|
||||
namespace graph {
|
||||
|
||||
using util::graph::Edge;
|
||||
using util::graph::Graph;
|
||||
using util::graph::Node;
|
||||
|
||||
// bidirectional dijkstras algorithm for util graph
|
||||
class BiDijkstra : public ShortestPath<BiDijkstra> {
|
||||
public:
|
||||
template <typename N, typename E, typename C>
|
||||
struct RouteNode {
|
||||
RouteNode() : n(0), parent(0), d(), h() {}
|
||||
RouteNode(Node<N, E>* n) : n(n), parent(0), d(), h() {}
|
||||
RouteNode(Node<N, E>* n, Node<N, E>* parent, C d)
|
||||
: n(n), parent(parent), d(d), h() {}
|
||||
RouteNode(Node<N, E>* n, Node<N, E>* parent, C d, C h)
|
||||
: n(n), parent(parent), d(d), h(h) {}
|
||||
|
||||
Node<N, E>* n;
|
||||
Node<N, E>* parent;
|
||||
|
||||
// the cost so far
|
||||
C d;
|
||||
|
||||
// the heuristical remaining cost + the cost so far
|
||||
C h;
|
||||
|
||||
bool operator<(const RouteNode<N, E, C>& p) const { return h > p.h; }
|
||||
};
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
using Settled = std::unordered_map<Node<N, E>*, RouteNode<N, E, C> >;
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
using PQ = std::priority_queue<RouteNode<N, E, C> >;
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
struct CostFunc : public util::graph::CostFunc<N, E, C> {
|
||||
virtual ~CostFunc() = default; C operator()(const Edge<N, E>* from, const Node<N, E>* n,
|
||||
const Edge<N, E>* to) const {
|
||||
UNUSED(from);
|
||||
UNUSED(n);
|
||||
UNUSED(to);
|
||||
return C();
|
||||
};
|
||||
};
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
struct HeurFunc : public util::graph::HeurFunc<N, E, C> {
|
||||
virtual ~HeurFunc() = default;
|
||||
C operator()(const Edge<N, E>* from,
|
||||
const std::set<Edge<N, E>*>& to) const {
|
||||
UNUSED(from);
|
||||
UNUSED(to);
|
||||
return C();
|
||||
};
|
||||
};
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static std::unordered_map<Node<N, E>*, C> shortestPathImpl(
|
||||
Node<N, E>* from, const std::set<Node<N, E>*>& to,
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
const util::graph::HeurFunc<N, E, C>&,
|
||||
std::unordered_map<Node<N, E>*, EList<N, E>*> resEdges,
|
||||
std::unordered_map<Node<N, E>*, NList<N, E>*> resNode);
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPathImpl(const std::set<Node<N, E>*> from,
|
||||
const std::set<Node<N, E>*>& to,
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
const util::graph::HeurFunc<N, E, C>& heurFunc,
|
||||
EList<N, E>* resEdges, NList<N, E>* resNodes);
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPathImpl(Node<N, E>* from, const std::set<Node<N, E>*>& to,
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
const util::graph::HeurFunc<N, E, C>& heurFunc,
|
||||
EList<N, E>* resEdges, NList<N, E>* resNodes);
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static void relax(RouteNode<N, E, C>& cur, const std::set<Node<N, E>*>& to,
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
const util::graph::HeurFunc<N, E, C>& heurFunc,
|
||||
PQ<N, E, C>& pq);
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C relaxFwd(RouteNode<N, E, C>& cur, const std::set<Node<N, E>*>& to,
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
const util::graph::HeurFunc<N, E, C>& heurFunc,
|
||||
PQ<N, E, C>& pq, const Settled<N, E, C>& settledBwd);
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C relaxBwd(const std::set<Node<N, E>*>& froms, RouteNode<N, E, C>& cur,
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
const util::graph::HeurFunc<N, E, C>& heurFunc,
|
||||
PQ<N, E, C>& pq, const Settled<N, E, C>& settledFwd);
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static void buildPath(Node<N, E>* curN, Settled<N, E, C>& settledFwd,
|
||||
Settled<N, E, C>& settledBwd, NList<N, E>* resNodes,
|
||||
EList<N, E>* resEdges);
|
||||
|
||||
static size_t ITERS;
|
||||
};
|
||||
|
||||
#include "util/graph/BiDijkstra.tpp"
|
||||
} // namespace graph
|
||||
} // namespace util
|
||||
|
||||
#endif // UTIL_GRAPH_BIDIJKSTRA_H_
|
||||
293
src/util/graph/BiDijkstra.tpp
Normal file
293
src/util/graph/BiDijkstra.tpp
Normal file
|
|
@ -0,0 +1,293 @@
|
|||
// Copyright 2017, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E, typename C>
|
||||
C BiDijkstra::shortestPathImpl(Node<N, E>* from,
|
||||
const std::set<Node<N, E>*>& to,
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
const util::graph::HeurFunc<N, E, C>& heurFunc,
|
||||
EList<N, E>* resEdges, NList<N, E>* resNodes) {
|
||||
if (from->getOutDeg() == 0) return costFunc.inf();
|
||||
|
||||
std::set<Node<N, E>*> froms;
|
||||
froms.insert(from);
|
||||
|
||||
return shortestPathImpl(froms, to, costFunc, heurFunc, resEdges, resNodes);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E, typename C>
|
||||
C BiDijkstra::shortestPathImpl(const std::set<Node<N, E>*> from,
|
||||
const std::set<Node<N, E>*>& to,
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
const util::graph::HeurFunc<N, E, C>& heurFunc,
|
||||
EList<N, E>* resEdges, NList<N, E>* resNodes) {
|
||||
Settled<N, E, C> settledFwd, settledBwd;
|
||||
PQ<N, E, C> pqFwd, pqBwd;
|
||||
bool found = false;
|
||||
|
||||
// starter for forward search
|
||||
for (auto n : from) pqFwd.emplace(n);
|
||||
|
||||
auto l = costFunc.inf();
|
||||
|
||||
// starter for backward search
|
||||
for (auto n : to) pqBwd.emplace(n);
|
||||
|
||||
RouteNode<N, E, C> cur;
|
||||
|
||||
while (!pqFwd.empty() && !pqBwd.empty()) {
|
||||
if (costFunc.inf() <= pqFwd.top().h && costFunc.inf() <= pqBwd.top().h)
|
||||
return costFunc.inf();
|
||||
|
||||
if (pqFwd.top() < pqBwd.top()) {
|
||||
auto se = settledBwd.find(pqBwd.top().n);
|
||||
if (se != settledBwd.end()) {
|
||||
// to allow non-consistent heuristics
|
||||
if (se->second.d <= pqBwd.top().d) {
|
||||
pqBwd.pop();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
auto se = settledFwd.find(pqFwd.top().n);
|
||||
if (se != settledFwd.end()) {
|
||||
// to allow non-consistent heuristics
|
||||
if (se->second.d <= pqFwd.top().d) {
|
||||
pqFwd.pop();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BiDijkstra::ITERS++;
|
||||
|
||||
if (pqFwd.top() < pqBwd.top()) {
|
||||
cur = pqBwd.top();
|
||||
pqBwd.pop();
|
||||
settledBwd[cur.n] = cur;
|
||||
if (settledFwd.find(cur.n) != settledFwd.end()) {
|
||||
auto newL = cur.d + settledFwd.find(cur.n)->second.d;
|
||||
|
||||
if (!(newL > l)) {
|
||||
l = newL;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
C bestCost = relaxBwd(from, cur, costFunc, heurFunc, pqBwd, settledFwd);
|
||||
if (bestCost < l) l = bestCost;
|
||||
} else {
|
||||
cur = pqFwd.top();
|
||||
pqFwd.pop();
|
||||
settledFwd[cur.n] = cur;
|
||||
if (settledBwd.find(cur.n) != settledBwd.end()) {
|
||||
auto newL = cur.d + settledBwd.find(cur.n)->second.d;
|
||||
|
||||
if (!(newL > l)) {
|
||||
l = newL;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
C bestCost = relaxFwd(cur, to, costFunc, heurFunc, pqFwd, settledBwd);
|
||||
if (bestCost < l) l = bestCost;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) return costFunc.inf();
|
||||
|
||||
buildPath(cur.n, settledFwd, settledBwd, resNodes, resEdges);
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E, typename C>
|
||||
std::unordered_map<Node<N, E>*, C> BiDijkstra::shortestPathImpl(
|
||||
Node<N, E>* from, const std::set<Node<N, E>*>& to,
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
const util::graph::HeurFunc<N, E, C>& heurFunc,
|
||||
std::unordered_map<Node<N, E>*, EList<N, E>*> resEdges,
|
||||
std::unordered_map<Node<N, E>*, NList<N, E>*> resNodes) {
|
||||
UNUSED(from);
|
||||
UNUSED(to);
|
||||
UNUSED(costFunc);
|
||||
UNUSED(heurFunc);
|
||||
UNUSED(resEdges);
|
||||
UNUSED(resNodes);
|
||||
assert(false);
|
||||
// std::unordered_map<Node<N, E>*, C> costs;
|
||||
// if (to.size() == 0) return costs;
|
||||
// // init costs with inf
|
||||
// for (auto n : to) costs[n] = costFunc.inf();
|
||||
|
||||
// if (from->getOutDeg() == 0) return costs;
|
||||
// Settled<N, E, C> settled;
|
||||
// PQ<N, E, C> pq;
|
||||
|
||||
// size_t found = 0;
|
||||
|
||||
// pq.emplace(from);
|
||||
// RouteNode<N, E, C> cur;
|
||||
|
||||
// while (!pq.empty()) {
|
||||
// if (costFunc.inf() <= pq.top().h) return costs;
|
||||
// if (settled.find(pq.top().n) != settled.end()) {
|
||||
// pq.pop();
|
||||
// continue;
|
||||
// }
|
||||
// BiDijkstra::ITERS++;
|
||||
|
||||
// cur = pq.top();
|
||||
// pq.pop();
|
||||
|
||||
// settled[cur.n] = cur;
|
||||
|
||||
// if (to.find(cur.n) != to.end()) {
|
||||
// found++;
|
||||
// }
|
||||
|
||||
// if (found == to.size()) break;
|
||||
|
||||
// relax(cur, to, costFunc, heurFunc, pq);
|
||||
// }
|
||||
|
||||
// for (auto nto : to) {
|
||||
// if (!settled.count(nto)) continue;
|
||||
// Node<N, E>* curN = nto;
|
||||
// costs[nto] = settled[curN].d;
|
||||
|
||||
// buildPath(nto, settled, resNodes[nto], resEdges[nto]);
|
||||
// }
|
||||
|
||||
// return costs;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E, typename C>
|
||||
void BiDijkstra::relax(RouteNode<N, E, C>& cur, const std::set<Node<N, E>*>& to,
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
const util::graph::HeurFunc<N, E, C>& heurFunc,
|
||||
PQ<N, E, C>& pq) {
|
||||
for (auto edge : cur.n->getAdjListOut()) {
|
||||
C newC = costFunc(cur.n, edge, edge->getOtherNd(cur.n));
|
||||
newC = cur.d + newC;
|
||||
if (costFunc.inf() <= newC) continue;
|
||||
|
||||
// addition done here to avoid it in the PQ
|
||||
const C& newH = newC + heurFunc(edge->getOtherNd(cur.n), to);
|
||||
|
||||
pq.emplace(edge->getOtherNd(cur.n), cur.n, newC, newH);
|
||||
}
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E, typename C>
|
||||
C BiDijkstra::relaxFwd(RouteNode<N, E, C>& cur, const std::set<Node<N, E>*>& to,
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
const util::graph::HeurFunc<N, E, C>& heurFunc,
|
||||
PQ<N, E, C>& pq, const Settled<N, E, C>& settledBwd) {
|
||||
UNUSED(to);
|
||||
UNUSED(heurFunc);
|
||||
C ret = costFunc.inf();
|
||||
for (auto edge : cur.n->getAdjListOut()) {
|
||||
C newC = costFunc(cur.n, edge, edge->getOtherNd(cur.n));
|
||||
newC = cur.d + newC;
|
||||
if (costFunc.inf() <= newC) continue;
|
||||
|
||||
// addition done here to avoid it in the PQ
|
||||
// const C& newH = newC + heurFunc(froms, edge->getOtherNd(cur.n));
|
||||
|
||||
// TODO:
|
||||
const C& newH = newC + 0;
|
||||
|
||||
// update new best found cost
|
||||
if (settledBwd.find(edge->getOtherNd(cur.n)) != settledBwd.end()) {
|
||||
C bwdCost = settledBwd.find(edge->getOtherNd(cur.n))->second.d + newC;
|
||||
if (bwdCost < ret) ret = bwdCost;
|
||||
}
|
||||
|
||||
pq.emplace(edge->getOtherNd(cur.n), cur.n, newC, newH);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E, typename C>
|
||||
C BiDijkstra::relaxBwd(const std::set<Node<N, E>*>& froms,
|
||||
RouteNode<N, E, C>& cur,
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
const util::graph::HeurFunc<N, E, C>& heurFunc,
|
||||
PQ<N, E, C>& pq, const Settled<N, E, C>& settledFwd) {
|
||||
UNUSED(froms);
|
||||
UNUSED(heurFunc);
|
||||
C ret = costFunc.inf();
|
||||
for (auto edge : cur.n->getAdjListIn()) {
|
||||
C newC = costFunc(edge->getOtherNd(cur.n), edge, cur.n);
|
||||
newC = cur.d + newC;
|
||||
if (costFunc.inf() <= newC) continue;
|
||||
|
||||
// addition done here to avoid it in the PQ
|
||||
// const C& newH = newC + heurFunc(froms, edge->getOtherNd(cur.n));
|
||||
|
||||
// TODO:
|
||||
const C& newH = newC + 0;
|
||||
|
||||
// update new best found cost
|
||||
if (settledFwd.find(edge->getOtherNd(cur.n)) != settledFwd.end()) {
|
||||
C fwdCost = settledFwd.find(edge->getOtherNd(cur.n))->second.d + newC;
|
||||
if (fwdCost < ret) ret = fwdCost;
|
||||
}
|
||||
|
||||
pq.emplace(edge->getOtherNd(cur.n), cur.n, newC, newH);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E, typename C>
|
||||
void BiDijkstra::buildPath(Node<N, E>* curN, Settled<N, E, C>& settledFwd,
|
||||
Settled<N, E, C>& settledBwd, NList<N, E>* resNodes,
|
||||
EList<N, E>* resEdges) {
|
||||
Node<N, E>* curNFwd = curN;
|
||||
Node<N, E>* curNBwd = curN;
|
||||
|
||||
// the forward part
|
||||
while (resNodes || resEdges) {
|
||||
const RouteNode<N, E, C>& curNode = settledFwd[curNFwd];
|
||||
if (resNodes) resNodes->push_back(curNode.n);
|
||||
if (!curNode.parent) break;
|
||||
|
||||
if (resEdges) {
|
||||
for (auto e : curNode.n->getAdjListIn()) {
|
||||
if (e->getOtherNd(curNode.n) == curNode.parent) resEdges->push_back(e);
|
||||
}
|
||||
}
|
||||
curNFwd = curNode.parent;
|
||||
}
|
||||
|
||||
if (resNodes) std::reverse(resNodes->begin(), resNodes->end());
|
||||
if (resEdges) std::reverse(resEdges->begin(), resEdges->end());
|
||||
|
||||
// the backward part
|
||||
while (resNodes || resEdges) {
|
||||
const RouteNode<N, E, C>& curNode = settledBwd[curNBwd];
|
||||
if (resNodes && curNode.n != curN) resNodes->push_back(curNode.n);
|
||||
if (!curNode.parent) break;
|
||||
|
||||
if (resEdges) {
|
||||
for (auto e : curNode.n->getAdjListOut()) {
|
||||
if (e->getOtherNd(curNode.n) == curNode.parent) resEdges->push_back(e);
|
||||
}
|
||||
}
|
||||
curNBwd = curNode.parent;
|
||||
}
|
||||
|
||||
if (resNodes) std::reverse(resNodes->begin(), resNodes->end());
|
||||
if (resEdges) std::reverse(resEdges->begin(), resEdges->end());
|
||||
}
|
||||
|
|
@ -9,7 +9,6 @@
|
|||
#include <list>
|
||||
#include <queue>
|
||||
#include <set>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include "util/graph/Edge.h"
|
||||
#include "util/graph/Graph.h"
|
||||
|
|
@ -19,33 +18,32 @@
|
|||
namespace util {
|
||||
namespace graph {
|
||||
|
||||
using util::graph::Edge;
|
||||
using util::graph::Graph;
|
||||
using util::graph::Node;
|
||||
using util::graph::Edge;
|
||||
|
||||
// dijkstras algorithm for util graph
|
||||
class Dijkstra : public ShortestPath<Dijkstra> {
|
||||
public:
|
||||
template <typename N, typename E, typename C>
|
||||
struct RouteNode {
|
||||
RouteNode() : n(0), parent(0), d(), h(), e(0) {}
|
||||
RouteNode(Node<N, E>* n) : n(n), parent(0), d(), h(), e(0) {}
|
||||
RouteNode(Node<N, E>* n, Node<N, E>* parent, C d, Edge<N, E>* e)
|
||||
: n(n), parent(parent), d(d), h(), e(e) {}
|
||||
RouteNode(Node<N, E>* n, Node<N, E>* parent, C d, C h, Edge<N, E>* e)
|
||||
: n(n), parent(parent), d(d), h(h), e(e) {}
|
||||
RouteNode() : n(0), parent(0), d(), h() {}
|
||||
RouteNode(Node<N, E>* n) : n(n), parent(0), d(), h() {}
|
||||
RouteNode(Node<N, E>* n, Node<N, E>* parent, C d)
|
||||
: n(n), parent(parent), d(d), h() {}
|
||||
RouteNode(Node<N, E>* n, Node<N, E>* parent, C d, C h)
|
||||
: n(n), parent(parent), d(d), h(h) {}
|
||||
|
||||
Node<N, E>* n;
|
||||
Node<N, E>* parent;
|
||||
|
||||
// the cost so far
|
||||
C d;
|
||||
|
||||
// the heuristical remaining cost + the cost so far
|
||||
C h;
|
||||
|
||||
Edge<N, E>* e;
|
||||
|
||||
bool operator<(const RouteNode<N, E, C>& p) const {
|
||||
return h > p.h || (h == p.h && d > p.d);
|
||||
}
|
||||
bool operator<(const RouteNode<N, E, C>& p) const { return h > p.h; }
|
||||
};
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
|
|
@ -55,7 +53,8 @@ class Dijkstra : public ShortestPath<Dijkstra> {
|
|||
using PQ = std::priority_queue<RouteNode<N, E, C> >;
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
struct CostFunc : public ShortestPath::CostFunc<N, E, C> {
|
||||
struct CostFunc : public util::graph::CostFunc<N, E, C> {
|
||||
virtual ~CostFunc() = default;
|
||||
C operator()(const Edge<N, E>* from, const Node<N, E>* n,
|
||||
const Edge<N, E>* to) const {
|
||||
UNUSED(from);
|
||||
|
|
@ -66,7 +65,8 @@ class Dijkstra : public ShortestPath<Dijkstra> {
|
|||
};
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
struct HeurFunc : public ShortestPath::HeurFunc<N, E, C> {
|
||||
struct HeurFunc : public util::graph::HeurFunc<N, E, C> {
|
||||
virtual ~HeurFunc() = default;
|
||||
C operator()(const Edge<N, E>* from,
|
||||
const std::set<Edge<N, E>*>& to) const {
|
||||
UNUSED(from);
|
||||
|
|
@ -78,26 +78,29 @@ class Dijkstra : public ShortestPath<Dijkstra> {
|
|||
template <typename N, typename E, typename C>
|
||||
static std::unordered_map<Node<N, E>*, C> shortestPathImpl(
|
||||
Node<N, E>* from, const std::set<Node<N, E>*>& to,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc, const ShortestPath::HeurFunc<N, E, C>&,
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
const util::graph::HeurFunc<N, E, C>&,
|
||||
std::unordered_map<Node<N, E>*, EList<N, E>*> resEdges,
|
||||
std::unordered_map<Node<N, E>*, NList<N, E>*> resNode);
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPathImpl(const std::set<Node<N, E>*> from, const std::set<Node<N, E>*>& to,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc,
|
||||
const ShortestPath::HeurFunc<N, E, C>& heurFunc,
|
||||
static C shortestPathImpl(const std::set<Node<N, E>*> from,
|
||||
const std::set<Node<N, E>*>& to,
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
const util::graph::HeurFunc<N, E, C>& heurFunc,
|
||||
EList<N, E>* resEdges, NList<N, E>* resNodes);
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPathImpl(Node<N, E>* from, const std::set<Node<N, E>*>& to,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc,
|
||||
const ShortestPath::HeurFunc<N, E, C>& heurFunc,
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
const util::graph::HeurFunc<N, E, C>& heurFunc,
|
||||
EList<N, E>* resEdges, NList<N, E>* resNodes);
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static void relax(RouteNode<N, E, C>& cur, const std::set<Node<N, E>*>& to,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc,
|
||||
const ShortestPath::HeurFunc<N, E, C>& heurFunc, PQ<N, E, C>& pq);
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
const util::graph::HeurFunc<N, E, C>& heurFunc,
|
||||
PQ<N, E, C>& pq);
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static void buildPath(Node<N, E>* curN, Settled<N, E, C>& settled,
|
||||
|
|
@ -107,7 +110,7 @@ class Dijkstra : public ShortestPath<Dijkstra> {
|
|||
};
|
||||
|
||||
#include "util/graph/Dijkstra.tpp"
|
||||
}
|
||||
}
|
||||
} // namespace graph
|
||||
} // namespace util
|
||||
|
||||
#endif // UTIL_GRAPH_DIJKSTRA_H_
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
// _____________________________________________________________________________
|
||||
template <typename N, typename E, typename C>
|
||||
C Dijkstra::shortestPathImpl(Node<N, E>* from, const std::set<Node<N, E>*>& to,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc,
|
||||
const ShortestPath::HeurFunc<N, E, C>& heurFunc,
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
const util::graph::HeurFunc<N, E, C>& heurFunc,
|
||||
EList<N, E>* resEdges, NList<N, E>* resNodes) {
|
||||
if (from->getOutDeg() == 0) return costFunc.inf();
|
||||
|
||||
|
|
@ -18,12 +18,12 @@ C Dijkstra::shortestPathImpl(Node<N, E>* from, const std::set<Node<N, E>*>& to,
|
|||
RouteNode<N, E, C> cur;
|
||||
|
||||
while (!pq.empty()) {
|
||||
Dijkstra::ITERS++;
|
||||
|
||||
if (costFunc.inf() <= pq.top().h) return costFunc.inf();
|
||||
if (settled.find(pq.top().n) != settled.end()) {
|
||||
pq.pop();
|
||||
continue;
|
||||
}
|
||||
Dijkstra::ITERS++;
|
||||
|
||||
cur = pq.top();
|
||||
pq.pop();
|
||||
|
|
@ -49,8 +49,8 @@ C Dijkstra::shortestPathImpl(Node<N, E>* from, const std::set<Node<N, E>*>& to,
|
|||
template <typename N, typename E, typename C>
|
||||
C Dijkstra::shortestPathImpl(const std::set<Node<N, E>*> from,
|
||||
const std::set<Node<N, E>*>& to,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc,
|
||||
const ShortestPath::HeurFunc<N, E, C>& heurFunc,
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
const util::graph::HeurFunc<N, E, C>& heurFunc,
|
||||
EList<N, E>* resEdges, NList<N, E>* resNodes) {
|
||||
Settled<N, E, C> settled;
|
||||
PQ<N, E, C> pq;
|
||||
|
|
@ -61,12 +61,16 @@ C Dijkstra::shortestPathImpl(const std::set<Node<N, E>*> from,
|
|||
RouteNode<N, E, C> cur;
|
||||
|
||||
while (!pq.empty()) {
|
||||
Dijkstra::ITERS++;
|
||||
|
||||
if (settled.find(pq.top().n) != settled.end()) {
|
||||
pq.pop();
|
||||
continue;
|
||||
if (costFunc.inf() <= pq.top().h) return costFunc.inf();
|
||||
auto se = settled.find(pq.top().n);
|
||||
if (se != settled.end()) {
|
||||
// to allow non-consistent heuristics
|
||||
if (se->second.d <= pq.top().d) {
|
||||
pq.pop();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
Dijkstra::ITERS++;
|
||||
|
||||
cur = pq.top();
|
||||
pq.pop();
|
||||
|
|
@ -92,8 +96,8 @@ C Dijkstra::shortestPathImpl(const std::set<Node<N, E>*> from,
|
|||
template <typename N, typename E, typename C>
|
||||
std::unordered_map<Node<N, E>*, C> Dijkstra::shortestPathImpl(
|
||||
Node<N, E>* from, const std::set<Node<N, E>*>& to,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc,
|
||||
const ShortestPath::HeurFunc<N, E, C>& heurFunc,
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
const util::graph::HeurFunc<N, E, C>& heurFunc,
|
||||
std::unordered_map<Node<N, E>*, EList<N, E>*> resEdges,
|
||||
std::unordered_map<Node<N, E>*, NList<N, E>*> resNodes) {
|
||||
std::unordered_map<Node<N, E>*, C> costs;
|
||||
|
|
@ -112,12 +116,16 @@ std::unordered_map<Node<N, E>*, C> Dijkstra::shortestPathImpl(
|
|||
RouteNode<N, E, C> cur;
|
||||
|
||||
while (!pq.empty()) {
|
||||
Dijkstra::ITERS++;
|
||||
|
||||
if (settled.find(pq.top().n) != settled.end()) {
|
||||
pq.pop();
|
||||
continue;
|
||||
if (costFunc.inf() <= pq.top().h) return costs;
|
||||
auto se = settled.find(pq.top().n);
|
||||
if (se != settled.end()) {
|
||||
// to allow non-consistent heuristics
|
||||
if (se->second.d <= pq.top().d) {
|
||||
pq.pop();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
Dijkstra::ITERS++;
|
||||
|
||||
cur = pq.top();
|
||||
pq.pop();
|
||||
|
|
@ -147,29 +155,41 @@ std::unordered_map<Node<N, E>*, C> Dijkstra::shortestPathImpl(
|
|||
// _____________________________________________________________________________
|
||||
template <typename N, typename E, typename C>
|
||||
void Dijkstra::relax(RouteNode<N, E, C>& cur, const std::set<Node<N, E>*>& to,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc,
|
||||
const ShortestPath::HeurFunc<N, E, C>& heurFunc, PQ<N, E, C>& pq) {
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
const util::graph::HeurFunc<N, E, C>& heurFunc,
|
||||
PQ<N, E, C>& pq) {
|
||||
for (auto edge : cur.n->getAdjListOut()) {
|
||||
C newC = costFunc(cur.n, edge, edge->getOtherNd(cur.n));
|
||||
newC = cur.d + newC;
|
||||
if (newC < cur.d) continue; // cost overflow!
|
||||
if (costFunc.inf() <= newC) continue;
|
||||
|
||||
const C& newH = newC + heurFunc(edge->getOtherNd(cur.n), to);
|
||||
// addition done here to avoid it in the PQ
|
||||
auto h = heurFunc(edge->getOtherNd(cur.n), to);
|
||||
if (costFunc.inf() <= h) continue;
|
||||
|
||||
pq.emplace(edge->getOtherNd(cur.n), cur.n, newC, newH, &(*edge));
|
||||
const C& newH = newC + h;
|
||||
|
||||
if (newH < newC) continue; // cost overflow!
|
||||
|
||||
pq.emplace(edge->getOtherNd(cur.n), cur.n, newC, newH);
|
||||
}
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E, typename C>
|
||||
void Dijkstra::buildPath(Node<N, E>* curN,
|
||||
Settled<N, E, C>& settled, NList<N, E>* resNodes,
|
||||
EList<N, E>* resEdges) {
|
||||
while (true) {
|
||||
void Dijkstra::buildPath(Node<N, E>* curN, Settled<N, E, C>& settled,
|
||||
NList<N, E>* resNodes, EList<N, E>* resEdges) {
|
||||
while (resNodes || resEdges) {
|
||||
const RouteNode<N, E, C>& curNode = settled[curN];
|
||||
if (resNodes) resNodes->push_back(curNode.n);
|
||||
if (!curNode.parent) break;
|
||||
if (resEdges) resEdges->push_back(curNode.e);
|
||||
|
||||
if (resEdges) {
|
||||
for (auto e : curNode.n->getAdjListIn()) {
|
||||
if (e->getOtherNd(curNode.n) == curNode.parent) resEdges->push_back(e);
|
||||
}
|
||||
}
|
||||
curN = curNode.parent;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,8 +21,6 @@ using UndirEdge = Edge<N, E>;
|
|||
template <typename N, typename E>
|
||||
class DirGraph : public Graph<N, E> {
|
||||
public:
|
||||
explicit DirGraph();
|
||||
|
||||
using Graph<N, E>::addEdg;
|
||||
|
||||
Node<N, E>* addNd();
|
||||
|
|
|
|||
|
|
@ -2,10 +2,6 @@
|
|||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
DirGraph<N, E>::DirGraph() {}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
Node<N, E>* DirGraph<N, E>::addNd(const N& pl) {
|
||||
|
|
@ -21,7 +17,7 @@ Node<N, E>* DirGraph<N, E>::addNd() {
|
|||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
Node<N, E>* DirGraph<N, E>::addNd(DirNode<N, E>* n) {
|
||||
auto ins = Graph<N, E>::getNds()->insert(n);
|
||||
auto ins = Graph<N, E>::_nodes.insert(n);
|
||||
return *ins.first;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ void DirNode<N, E>::removeEdge(Edge<N, E>* e) {
|
|||
if (p != _adjListIn.end()) _adjListIn.erase(p);
|
||||
}
|
||||
}
|
||||
//
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
bool DirNode<N, E>::hasEdgeIn(const Edge<N, E>* e) const {
|
||||
|
|
@ -139,6 +139,8 @@ const N& DirNode<N, E>::pl() const {
|
|||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
bool DirNode<N, E>::adjInContains(const Edge<N, E>* e) const {
|
||||
// this is faster than a binary search as the adjacency lists are typically
|
||||
// very small for our use cases
|
||||
for (size_t i = 0; i < _adjListIn.size(); i++)
|
||||
if (_adjListIn[i] == e) return true;
|
||||
return false;
|
||||
|
|
@ -147,6 +149,8 @@ bool DirNode<N, E>::adjInContains(const Edge<N, E>* e) const {
|
|||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
bool DirNode<N, E>::adjOutContains(const Edge<N, E>* e) const {
|
||||
// this is faster than a binary search as the adjacency lists are typically
|
||||
// very small for our use cases
|
||||
for (size_t i = 0; i < _adjListOut.size(); i++)
|
||||
if (_adjListOut[i] == e) return true;
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -7,48 +7,84 @@
|
|||
|
||||
#include <limits>
|
||||
#include <list>
|
||||
#include <queue>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include "util/PriorityQueue.h"
|
||||
#include "util/graph/Edge.h"
|
||||
#include "util/graph/Graph.h"
|
||||
#include "util/graph/Node.h"
|
||||
#include "util/graph/ShortestPath.h"
|
||||
#include "util/graph/radix_heap.h"
|
||||
#include "util/graph/robin/robin_map.h"
|
||||
|
||||
namespace util {
|
||||
namespace graph {
|
||||
|
||||
using util::graph::Edge;
|
||||
using util::graph::Graph;
|
||||
using util::graph::Node;
|
||||
using util::graph::Edge;
|
||||
|
||||
// edge-based dijkstra - settles edges instead of nodes
|
||||
class EDijkstra : public ShortestPath<EDijkstra> {
|
||||
public:
|
||||
template <typename N, typename E, typename C>
|
||||
struct RouteEdge {
|
||||
RouteEdge() : e(0), parent(0), d(), h(), n(0) {}
|
||||
RouteEdge(Edge<N, E>* e) : e(e), parent(0), d(), h(), n(0) {}
|
||||
RouteEdge() : e(0), parent(0), d(), n(0) {}
|
||||
RouteEdge(Edge<N, E>* e) : e(e), parent(0), d(), n(0) {}
|
||||
RouteEdge(Edge<N, E>* e, Edge<N, E>* parent, Node<N, E>* n, C d)
|
||||
: e(e), parent(parent), d(d), h(), n(n) {}
|
||||
RouteEdge(Edge<N, E>* e, Edge<N, E>* parent, Node<N, E>* n, C d, C h)
|
||||
: e(e), parent(parent), d(d), h(h), n(n) {}
|
||||
: e(e), parent(parent), d(d), n(n) {}
|
||||
|
||||
Edge<N, E>* e;
|
||||
Edge<N, E>* parent;
|
||||
|
||||
// the cost so far
|
||||
C d;
|
||||
C h;
|
||||
|
||||
Node<N, E>* n;
|
||||
|
||||
bool operator<(const RouteEdge<N, E, C>& p) const {
|
||||
return h > p.h || (h == p.h && d > p.d);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
struct CostFunc : public ShortestPath::CostFunc<N, E, C> {
|
||||
struct RouteEdgeInit {
|
||||
RouteEdgeInit() : e(0), parent(0), d(), dwi(), n(0) {}
|
||||
RouteEdgeInit(Edge<N, E>* e) : e(e), parent(0), d(), dwi(), n(0) {}
|
||||
RouteEdgeInit(Edge<N, E>* e, Edge<N, E>* parent, Node<N, E>* n, C d)
|
||||
: e(e), parent(parent), d(d), dwi(), n(n) {}
|
||||
RouteEdgeInit(Edge<N, E>* e, Edge<N, E>* parent, Node<N, E>* n, C d, C dwi)
|
||||
: e(e), parent(parent), d(d), dwi(dwi), n(n) {}
|
||||
|
||||
Edge<N, E>* e;
|
||||
Edge<N, E>* parent;
|
||||
|
||||
// the cost so far
|
||||
C d;
|
||||
|
||||
// the cost without the initial costs
|
||||
C dwi;
|
||||
|
||||
Node<N, E>* n;
|
||||
};
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
struct RouteEdgeInitNoRes {
|
||||
RouteEdgeInitNoRes() : e(0), parent(0), d(), dwi() {}
|
||||
RouteEdgeInitNoRes(Edge<N, E>* e) : e(e), parent(0), d(), dwi() {}
|
||||
RouteEdgeInitNoRes(Edge<N, E>* e, Edge<N, E>* parent, C d)
|
||||
: e(e), parent(parent), d(d), dwi() {}
|
||||
RouteEdgeInitNoRes(Edge<N, E>* e, Edge<N, E>* parent, C d, C dwi)
|
||||
: e(e), parent(parent), d(d), dwi(dwi) {}
|
||||
|
||||
Edge<N, E>* e;
|
||||
Edge<N, E>* parent;
|
||||
|
||||
// the cost so far
|
||||
C d;
|
||||
|
||||
// the cost without the initial costs
|
||||
C dwi;
|
||||
};
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
struct CostFunc : public util::graph::CostFunc<N, E, C> {
|
||||
C operator()(const Node<N, E>* from, const Edge<N, E>* e,
|
||||
const Node<N, E>* to) const {
|
||||
UNUSED(from);
|
||||
|
|
@ -59,7 +95,7 @@ class EDijkstra : public ShortestPath<EDijkstra> {
|
|||
};
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
struct HeurFunc : public ShortestPath::HeurFunc<N, E, C> {
|
||||
struct HeurFunc : public util::graph::HeurFunc<N, E, C> {
|
||||
C operator()(const Node<N, E>* from,
|
||||
const std::set<Node<N, E>*>& to) const {
|
||||
UNUSED(from);
|
||||
|
|
@ -69,71 +105,119 @@ class EDijkstra : public ShortestPath<EDijkstra> {
|
|||
};
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
using Settled = std::unordered_map<Edge<N, E>*, RouteEdge<N, E, C> >;
|
||||
using Settled = tsl::robin_map<Edge<N, E>*, RouteEdge<N, E, C>>;
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
using PQ = std::priority_queue<RouteEdge<N, E, C> >;
|
||||
using PQ = radix_heap::pair_radix_heap<C, RouteEdge<N, E, C>>;
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
using SettledInit = tsl::robin_map<Edge<N, E>*, RouteEdgeInit<N, E, C>>;
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
using SettledInitNoRes = tsl::robin_map<Edge<N, E>*, RouteEdgeInitNoRes<N, E, C>>;
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
using PQInit = radix_heap::pair_radix_heap<C, RouteEdgeInit<N, E, C>>;
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
using PQInitNoRes = radix_heap::pair_radix_heap<C, RouteEdgeInitNoRes<N, E, C>>;
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPathImpl(const std::set<Edge<N, E>*> from,
|
||||
const std::set<Edge<N, E>*>& to,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc,
|
||||
const ShortestPath::HeurFunc<N, E, C>& heurFunc,
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
const util::graph::HeurFunc<N, E, C>& heurFunc,
|
||||
EList<N, E>* resEdges, NList<N, E>* resNodes);
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPathImpl(Node<N, E>* from, const std::set<Node<N, E>*>& to,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc,
|
||||
const ShortestPath::HeurFunc<N, E, C>& heurFunc,
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
const util::graph::HeurFunc<N, E, C>& heurFunc,
|
||||
EList<N, E>* resEdges, NList<N, E>* resNodes);
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPathImpl(Edge<N, E>* from, const std::set<Node<N, E>*>& to,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc,
|
||||
const ShortestPath::HeurFunc<N, E, C>& heurFunc,
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
const util::graph::HeurFunc<N, E, C>& heurFunc,
|
||||
EList<N, E>* resEdges, NList<N, E>* resNodes);
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPathImpl(const std::set<Edge<N, E>*>& from,
|
||||
const std::set<Node<N, E>*>& to,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc,
|
||||
const ShortestPath::HeurFunc<N, E, C>& heurFunc,
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
const util::graph::HeurFunc<N, E, C>& heurFunc,
|
||||
EList<N, E>* resEdges, NList<N, E>* resNodes);
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static std::unordered_map<Edge<N, E>*, C> shortestPathImpl(
|
||||
const std::set<Edge<N, E>*>& from,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc, bool rev);
|
||||
const util::graph::CostFunc<N, E, C>& costFunc, bool rev);
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static std::unordered_map<Edge<N, E>*, C> shortestPathImpl(
|
||||
Edge<N, E>* from, const std::set<Edge<N, E>*>& to,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc,
|
||||
const ShortestPath::HeurFunc<N, E, C>& heurFunc,
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
const util::graph::HeurFunc<N, E, C>& heurFunc,
|
||||
std::unordered_map<Edge<N, E>*, EList<N, E>*> resEdges,
|
||||
std::unordered_map<Edge<N, E>*, NList<N, E>*> resNodes);
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static std::unordered_map<Edge<N, E>*, C> shortestPathImpl(
|
||||
const std::set<Edge<N, E>*>& from, const std::set<Edge<N, E>*>& to,
|
||||
const std::unordered_map<Edge<N, E>*, C>& initCosts, C stall,
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
const util::graph::HeurFunc<N, E, C>& heurFunc,
|
||||
std::unordered_map<Edge<N, E>*, EList<N, E>*> resEdges,
|
||||
std::unordered_map<Edge<N, E>*, NList<N, E>*> resNodes);
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static std::unordered_map<Edge<N, E>*, std::pair<Edge<N, E>*, C>>
|
||||
shortestPathImpl(const std::set<Edge<N, E>*>& from,
|
||||
const std::set<Edge<N, E>*>& to,
|
||||
const std::unordered_map<Edge<N, E>*, C>& initCosts, C stall,
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
const util::graph::HeurFunc<N, E, C>& heurFunc);
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static void buildPath(Edge<N, E>* curE, const Settled<N, E, C>& settled,
|
||||
NList<N, E>* resNodes, EList<N, E>* resEdges);
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static void buildPathInit(Edge<N, E>* curE,
|
||||
const SettledInit<N, E, C>& settled,
|
||||
NList<N, E>* resNodes, EList<N, E>* resEdges);
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static inline void relax(RouteEdge<N, E, C>& cur,
|
||||
const std::set<Edge<N, E>*>& to,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc,
|
||||
const ShortestPath::HeurFunc<N, E, C>& heurFunc,
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
const util::graph::HeurFunc<N, E, C>& heurFunc,
|
||||
PQ<N, E, C>& pq);
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static inline void relaxInit(RouteEdgeInit<N, E, C>& cur,
|
||||
const std::set<Edge<N, E>*>& to, C stall,
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
const util::graph::HeurFunc<N, E, C>& heurFunc,
|
||||
PQInit<N, E, C>& pq);
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static inline void relaxInitNoResEdgs(RouteEdgeInitNoRes<N, E, C>& cur,
|
||||
const std::set<Edge<N, E>*>& to, C stall,
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
const util::graph::HeurFunc<N, E, C>& heurFunc,
|
||||
PQInitNoRes<N, E, C>& pq);
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static void relaxInv(RouteEdge<N, E, C>& cur,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc,
|
||||
PQ<N, E, C>& pq);
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
PQ<N, E, C>& pq);
|
||||
|
||||
static size_t ITERS;
|
||||
};
|
||||
|
||||
#include "util/graph/EDijkstra.tpp"
|
||||
}
|
||||
}
|
||||
} // namespace graph
|
||||
} // namespace util
|
||||
|
||||
#endif // UTIL_GRAPH_DIJKSTRA_H_
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
// _____________________________________________________________________________
|
||||
template <typename N, typename E, typename C>
|
||||
C EDijkstra::shortestPathImpl(Node<N, E>* from, const std::set<Node<N, E>*>& to,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc,
|
||||
const ShortestPath::HeurFunc<N, E, C>& heurFunc,
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
const util::graph::HeurFunc<N, E, C>& heurFunc,
|
||||
EList<N, E>* resEdges, NList<N, E>* resNodes) {
|
||||
std::set<Edge<N, E>*> frEs;
|
||||
std::set<Edge<N, E>*> toEs;
|
||||
|
|
@ -28,8 +28,8 @@ C EDijkstra::shortestPathImpl(Node<N, E>* from, const std::set<Node<N, E>*>& to,
|
|||
// _____________________________________________________________________________
|
||||
template <typename N, typename E, typename C>
|
||||
C EDijkstra::shortestPathImpl(Edge<N, E>* from, const std::set<Node<N, E>*>& to,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc,
|
||||
const ShortestPath::HeurFunc<N, E, C>& heurFunc,
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
const util::graph::HeurFunc<N, E, C>& heurFunc,
|
||||
EList<N, E>* resEdges, NList<N, E>* resNodes) {
|
||||
std::set<Edge<N, E>*> frEs;
|
||||
std::set<Edge<N, E>*> toEs;
|
||||
|
|
@ -49,8 +49,8 @@ C EDijkstra::shortestPathImpl(Edge<N, E>* from, const std::set<Node<N, E>*>& to,
|
|||
template <typename N, typename E, typename C>
|
||||
C EDijkstra::shortestPathImpl(const std::set<Edge<N, E>*> from,
|
||||
const std::set<Edge<N, E>*>& to,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc,
|
||||
const ShortestPath::HeurFunc<N, E, C>& heurFunc,
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
const util::graph::HeurFunc<N, E, C>& heurFunc,
|
||||
EList<N, E>* resEdges, NList<N, E>* resNodes) {
|
||||
if (from.size() == 0 || to.size() == 0) return costFunc.inf();
|
||||
|
||||
|
|
@ -63,20 +63,24 @@ C EDijkstra::shortestPathImpl(const std::set<Edge<N, E>*> from,
|
|||
for (auto e : from) {
|
||||
C c = costFunc(0, 0, e);
|
||||
C h = heurFunc(e, to);
|
||||
pq.emplace(e, (Edge<N, E>*)0, (Node<N, E>*)0, c, c + h);
|
||||
pq.push(c + h, {e, (Edge<N, E>*)0, (Node<N, E>*)0, c});
|
||||
}
|
||||
|
||||
RouteEdge<N, E, C> cur;
|
||||
|
||||
while (!pq.empty()) {
|
||||
if (costFunc.inf() <= pq.topKey()) return costFunc.inf();
|
||||
auto se = settled.find(pq.topVal().e);
|
||||
if (se != settled.end()) {
|
||||
// to allow non-consistent heuristics
|
||||
if (se->second.d <= pq.topVal().d) {
|
||||
pq.pop();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
EDijkstra::ITERS++;
|
||||
|
||||
if (settled.find(pq.top().e) != settled.end()) {
|
||||
pq.pop();
|
||||
continue;
|
||||
}
|
||||
|
||||
cur = pq.top();
|
||||
cur = pq.topVal();
|
||||
pq.pop();
|
||||
|
||||
settled[cur.e] = cur;
|
||||
|
|
@ -99,8 +103,8 @@ C EDijkstra::shortestPathImpl(const std::set<Edge<N, E>*> from,
|
|||
// _____________________________________________________________________________
|
||||
template <typename N, typename E, typename C>
|
||||
std::unordered_map<Edge<N, E>*, C> EDijkstra::shortestPathImpl(
|
||||
const std::set<Edge<N, E>*>& from, const ShortestPath::CostFunc<N, E, C>& costFunc,
|
||||
bool rev) {
|
||||
const std::set<Edge<N, E>*>& from,
|
||||
const util::graph::CostFunc<N, E, C>& costFunc, bool rev) {
|
||||
std::unordered_map<Edge<N, E>*, C> costs;
|
||||
|
||||
Settled<N, E, C> settled;
|
||||
|
|
@ -109,20 +113,23 @@ std::unordered_map<Edge<N, E>*, C> EDijkstra::shortestPathImpl(
|
|||
std::set<Edge<N, E>*> to;
|
||||
|
||||
for (auto e : from) {
|
||||
pq.emplace(e, (Edge<N, E>*)0, (Node<N, E>*)0, costFunc(0, 0, e), C());
|
||||
pq.push(C(), {e, (Edge<N, E>*)0, (Node<N, E>*)0, costFunc(0, 0, e)});
|
||||
}
|
||||
|
||||
RouteEdge<N, E, C> cur;
|
||||
|
||||
while (!pq.empty()) {
|
||||
auto se = settled.find(pq.topVal().e);
|
||||
if (se != settled.end()) {
|
||||
// to allow non-consistent heuristics
|
||||
if (se->second.d <= pq.topVal().d) {
|
||||
pq.pop();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
EDijkstra::ITERS++;
|
||||
|
||||
if (settled.find(pq.top().e) != settled.end()) {
|
||||
pq.pop();
|
||||
continue;
|
||||
}
|
||||
|
||||
cur = pq.top();
|
||||
cur = pq.topVal();
|
||||
pq.pop();
|
||||
|
||||
settled[cur.e] = cur;
|
||||
|
|
@ -143,8 +150,8 @@ std::unordered_map<Edge<N, E>*, C> EDijkstra::shortestPathImpl(
|
|||
template <typename N, typename E, typename C>
|
||||
std::unordered_map<Edge<N, E>*, C> EDijkstra::shortestPathImpl(
|
||||
Edge<N, E>* from, const std::set<Edge<N, E>*>& to,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc,
|
||||
const ShortestPath::HeurFunc<N, E, C>& heurFunc,
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
const util::graph::HeurFunc<N, E, C>& heurFunc,
|
||||
std::unordered_map<Edge<N, E>*, EList<N, E>*> resEdges,
|
||||
std::unordered_map<Edge<N, E>*, NList<N, E>*> resNodes) {
|
||||
std::unordered_map<Edge<N, E>*, C> costs;
|
||||
|
|
@ -160,19 +167,23 @@ std::unordered_map<Edge<N, E>*, C> EDijkstra::shortestPathImpl(
|
|||
|
||||
C c = costFunc(0, 0, from);
|
||||
C h = heurFunc(from, to);
|
||||
pq.emplace(from, (Edge<N, E>*)0, (Node<N, E>*)0, c, c + h);
|
||||
pq.push(c + h, {from, (Edge<N, E>*)0, (Node<N, E>*)0, c});
|
||||
|
||||
RouteEdge<N, E, C> cur;
|
||||
|
||||
while (!pq.empty()) {
|
||||
if (costFunc.inf() <= pq.topKey()) return costs;
|
||||
auto se = settled.find(pq.topVal().e);
|
||||
if (se != settled.end()) {
|
||||
// to allow non-consistent heuristics
|
||||
if (se->second.d <= pq.topVal().d) {
|
||||
pq.pop();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
EDijkstra::ITERS++;
|
||||
|
||||
if (settled.find(pq.top().e) != settled.end()) {
|
||||
pq.pop();
|
||||
continue;
|
||||
}
|
||||
|
||||
cur = pq.top();
|
||||
cur = pq.topVal();
|
||||
pq.pop();
|
||||
|
||||
settled[cur.e] = cur;
|
||||
|
|
@ -193,10 +204,148 @@ std::unordered_map<Edge<N, E>*, C> EDijkstra::shortestPathImpl(
|
|||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E, typename C>
|
||||
void EDijkstra::relaxInv(RouteEdge<N, E, C>& cur,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc,
|
||||
PQ<N, E, C>& pq) {
|
||||
std::unordered_map<Edge<N, E>*, C> EDijkstra::shortestPathImpl(
|
||||
const std::set<Edge<N, E>*>& from, const std::set<Edge<N, E>*>& to,
|
||||
const std::unordered_map<Edge<N, E>*, C>& initCosts, C stall,
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
const util::graph::HeurFunc<N, E, C>& heurFunc,
|
||||
std::unordered_map<Edge<N, E>*, EList<N, E>*> resEdges,
|
||||
std::unordered_map<Edge<N, E>*, NList<N, E>*> resNodes) {
|
||||
/**
|
||||
* Shortest paths from the set <from> to ALL nodes in <TO>, but
|
||||
* init <from> nodes with costs (this is equivalent to adding an auxiliary
|
||||
* node S, connecting it with directed edges to all <from>, setting the
|
||||
* costs of these edges to the initial costs and run a 1->N Dijkstra from S
|
||||
**/
|
||||
|
||||
std::unordered_map<Edge<N, E>*, C> costs;
|
||||
if (to.size() == 0) return costs;
|
||||
|
||||
// init costs with inf
|
||||
for (auto e : to) costs[e] = costFunc.inf();
|
||||
|
||||
SettledInit<N, E, C> settled;
|
||||
PQInit<N, E, C> pq;
|
||||
|
||||
size_t found = 0;
|
||||
|
||||
// put all nodes in from onto the PQ with their initial costs, also set
|
||||
// the initial cost as a heuristic starting point!
|
||||
for (auto e : from) {
|
||||
C iCost = initCosts.find(e)->second;
|
||||
assert(iCost + heurFunc(e, to) >= iCost);
|
||||
pq.push(iCost + heurFunc(e, to),
|
||||
{e, (Edge<N, E>*)0, (Node<N, E>*)0, iCost});
|
||||
}
|
||||
|
||||
RouteEdgeInit<N, E, C> cur;
|
||||
|
||||
while (!pq.empty()) {
|
||||
if (costFunc.inf() <= pq.topKey()) return costs;
|
||||
if (stall <= pq.topVal().dwi) return costs;
|
||||
auto se = settled.find(pq.topVal().e);
|
||||
if (se != settled.end()) {
|
||||
// to allow non-consistent heuristics
|
||||
if (se->second.d <= pq.topVal().d) {
|
||||
pq.pop();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
EDijkstra::ITERS++;
|
||||
|
||||
cur = pq.topVal();
|
||||
pq.pop();
|
||||
|
||||
settled[cur.e] = cur;
|
||||
|
||||
if (to.find(cur.e) != to.end()) {
|
||||
found++;
|
||||
costs[cur.e] = cur.d;
|
||||
buildPathInit(cur.e, settled, resNodes[cur.e], resEdges[cur.e]);
|
||||
|
||||
if (found == to.size()) return costs;
|
||||
}
|
||||
|
||||
relaxInit(cur, to, stall, costFunc, heurFunc, pq);
|
||||
}
|
||||
|
||||
return costs;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E, typename C>
|
||||
std::unordered_map<Edge<N, E>*, std::pair<Edge<N, E>*, C>>
|
||||
EDijkstra::shortestPathImpl(const std::set<Edge<N, E>*>& from,
|
||||
const std::set<Edge<N, E>*>& to,
|
||||
const std::unordered_map<Edge<N, E>*, C>& initCosts,
|
||||
C stall,
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
const util::graph::HeurFunc<N, E, C>& heurFunc) {
|
||||
/**
|
||||
* Shortest paths from the set <from> to ALL nodes in <TO>, but
|
||||
* init <from> nodes with costs (this is equivalent to adding an auxiliary
|
||||
* node S, connecting it with directed edges to all <from>, setting the
|
||||
* costs of these edges to the initial costs and run a 1->N Dijkstra from S
|
||||
**/
|
||||
|
||||
std::unordered_map<Edge<N, E>*, std::pair<Edge<N, E>*, C>> costs;
|
||||
if (to.size() == 0) return costs;
|
||||
|
||||
// init costs with inf
|
||||
for (auto e : to) costs[e] = {0, costFunc.inf()};
|
||||
|
||||
SettledInitNoRes<N, E, C> settled;
|
||||
PQInitNoRes<N, E, C> pq;
|
||||
|
||||
size_t found = 0;
|
||||
|
||||
// put all nodes in from onto the PQ with their initial costs, also set
|
||||
// the initial cost as a heuristic starting point!
|
||||
// set the parent to the edge itself - in this version, the parent is ALWAYS
|
||||
// the start edge, as we don't need the exact paths later on
|
||||
for (auto e : from) {
|
||||
C iCost = initCosts.find(e)->second;
|
||||
assert(iCost + heurFunc(e, to) >= iCost);
|
||||
pq.push(iCost + heurFunc(e, to), {e, e, iCost});
|
||||
}
|
||||
|
||||
RouteEdgeInitNoRes<N, E, C> cur;
|
||||
|
||||
while (!pq.empty()) {
|
||||
if (costFunc.inf() <= pq.topKey()) return costs;
|
||||
if (stall <= pq.topVal().dwi) return costs;
|
||||
auto se = settled.find(pq.topVal().e);
|
||||
if (se != settled.end()) {
|
||||
// to allow non-consistent heuristics
|
||||
if (se->second.d <= pq.topVal().d) {
|
||||
pq.pop();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
EDijkstra::ITERS++;
|
||||
|
||||
cur = pq.topVal();
|
||||
pq.pop();
|
||||
|
||||
settled[cur.e] = cur;
|
||||
|
||||
if (to.find(cur.e) != to.end()) {
|
||||
found++;
|
||||
costs[cur.e] = {cur.parent, cur.d};
|
||||
if (found == to.size()) return costs;
|
||||
}
|
||||
|
||||
relaxInitNoResEdgs(cur, to, stall, costFunc, heurFunc, pq);
|
||||
}
|
||||
|
||||
return costs;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E, typename C>
|
||||
void EDijkstra::relaxInv(RouteEdge<N, E, C>& cur,
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
PQ<N, E, C>& pq) {
|
||||
// handling undirected graph makes no sense here
|
||||
|
||||
for (const auto edge : cur.e->getFrom()->getAdjListIn()) {
|
||||
|
|
@ -204,42 +353,161 @@ void EDijkstra::relaxInv(RouteEdge<N, E, C>& cur,
|
|||
C newC = costFunc(edge, cur.e->getFrom(), cur.e);
|
||||
newC = cur.d + newC;
|
||||
if (costFunc.inf() <= newC) continue;
|
||||
if (newC < cur.d) continue; // cost overflow!
|
||||
|
||||
pq.emplace(edge, cur.e, cur.e->getFrom(), newC, C());
|
||||
pq.push(C(), {edge, cur.e, cur.e->getFrom(), newC});
|
||||
}
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E, typename C>
|
||||
void EDijkstra::relax(RouteEdge<N, E, C>& cur, const std::set<Edge<N, E>*>& to,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc,
|
||||
const ShortestPath::HeurFunc<N, E, C>& heurFunc,
|
||||
PQ<N, E, C>& pq) {
|
||||
if (cur.e->getFrom()->hasEdgeIn(cur.e)) {
|
||||
void EDijkstra::relaxInitNoResEdgs(
|
||||
RouteEdgeInitNoRes<N, E, C>& cur, const std::set<Edge<N, E>*>& to, C stall,
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
const util::graph::HeurFunc<N, E, C>& heurFunc, PQInitNoRes<N, E, C>& pq) {
|
||||
if (cur.e->getFrom()->hasEdgeIn(cur.e) &&
|
||||
cur.e->getFrom() != cur.e->getTo()) {
|
||||
// for undirected graphs
|
||||
for (const auto edge : cur.e->getFrom()->getAdjListOut()) {
|
||||
if (edge == cur.e) continue;
|
||||
C newC = costFunc(cur.e, cur.e->getFrom(), edge);
|
||||
C newDwi = cur.dwi + newC;
|
||||
|
||||
if (stall <= newDwi) continue;
|
||||
|
||||
if (costFunc.inf() <= newC) continue;
|
||||
newC = cur.d + newC;
|
||||
if (newC < cur.d) continue; // cost overflow!
|
||||
|
||||
if (costFunc.inf() <= newC) continue;
|
||||
|
||||
const C& h = heurFunc(edge, to);
|
||||
if (costFunc.inf() <= h) continue;
|
||||
newC = cur.d + newC;
|
||||
const C& newH = newC + h;
|
||||
if (newH < newC) continue; // cost overflow!
|
||||
|
||||
pq.emplace(edge, cur.e, cur.e->getFrom(), newC, newH);
|
||||
pq.push(newH, {edge, cur.parent, newC, newDwi});
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto edge : cur.e->getTo()->getAdjListOut()) {
|
||||
if (edge == cur.e) continue;
|
||||
C newC = costFunc(cur.e, cur.e->getTo(), edge);
|
||||
C newDwi = cur.dwi + newC;
|
||||
|
||||
if (stall <= newDwi) continue;
|
||||
|
||||
if (costFunc.inf() <= newC) continue;
|
||||
newC = cur.d + newC;
|
||||
if (newC < cur.d) continue; // cost overflow!
|
||||
|
||||
if (costFunc.inf() <= newC) continue;
|
||||
|
||||
const C& h = heurFunc(edge, to);
|
||||
if (costFunc.inf() <= h) continue;
|
||||
const C& newH = newC + h;
|
||||
if (newH < newC) continue; // cost overflow!
|
||||
|
||||
pq.emplace(edge, cur.e, cur.e->getTo(), newC, newH);
|
||||
pq.push(newH, {edge, cur.parent, newC, newDwi});
|
||||
}
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E, typename C>
|
||||
void EDijkstra::relaxInit(RouteEdgeInit<N, E, C>& cur,
|
||||
const std::set<Edge<N, E>*>& to, C stall,
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
const util::graph::HeurFunc<N, E, C>& heurFunc,
|
||||
PQInit<N, E, C>& pq) {
|
||||
if (cur.e->getFrom()->hasEdgeIn(cur.e) &&
|
||||
cur.e->getFrom() != cur.e->getTo()) {
|
||||
// for undirected graphs
|
||||
for (const auto edge : cur.e->getFrom()->getAdjListOut()) {
|
||||
if (edge == cur.e) continue;
|
||||
C newC = costFunc(cur.e, cur.e->getFrom(), edge);
|
||||
C newDwi = cur.dwi + newC;
|
||||
|
||||
if (stall <= newDwi) continue;
|
||||
|
||||
if (costFunc.inf() <= newC) continue;
|
||||
newC = cur.d + newC;
|
||||
if (newC < cur.d) continue; // cost overflow!
|
||||
|
||||
if (costFunc.inf() <= newC) continue;
|
||||
|
||||
const C& h = heurFunc(edge, to);
|
||||
if (costFunc.inf() <= h) continue;
|
||||
newC = cur.d + newC;
|
||||
const C& newH = newC + h;
|
||||
if (newH < newC) continue; // cost overflow!
|
||||
|
||||
pq.push(newH, {edge, cur.e, cur.e->getFrom(), newC, newDwi});
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto edge : cur.e->getTo()->getAdjListOut()) {
|
||||
if (edge == cur.e) continue;
|
||||
C newC = costFunc(cur.e, cur.e->getTo(), edge);
|
||||
C newDwi = cur.dwi + newC;
|
||||
|
||||
if (stall <= newDwi) continue;
|
||||
|
||||
if (costFunc.inf() <= newC) continue;
|
||||
newC = cur.d + newC;
|
||||
if (newC < cur.d) continue; // cost overflow!
|
||||
|
||||
if (costFunc.inf() <= newC) continue;
|
||||
|
||||
const C& h = heurFunc(edge, to);
|
||||
if (costFunc.inf() <= h) continue;
|
||||
const C& newH = newC + h;
|
||||
if (newH < newC) continue; // cost overflow!
|
||||
|
||||
pq.push(newH, {edge, cur.e, cur.e->getTo(), newC, newDwi});
|
||||
}
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E, typename C>
|
||||
void EDijkstra::relax(RouteEdge<N, E, C>& cur, const std::set<Edge<N, E>*>& to,
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
const util::graph::HeurFunc<N, E, C>& heurFunc,
|
||||
PQ<N, E, C>& pq) {
|
||||
if (cur.e->getFrom()->hasEdgeIn(cur.e) &&
|
||||
cur.e->getFrom() != cur.e->getTo()) {
|
||||
// for undirected graphs
|
||||
for (const auto edge : cur.e->getFrom()->getAdjListOut()) {
|
||||
if (edge == cur.e) continue;
|
||||
C newC = costFunc(cur.e, cur.e->getFrom(), edge);
|
||||
if (costFunc.inf() <= newC) continue;
|
||||
newC = cur.d + newC;
|
||||
if (newC < cur.d) continue; // cost overflow!
|
||||
if (costFunc.inf() <= newC) continue;
|
||||
|
||||
const C& h = heurFunc(edge, to);
|
||||
if (costFunc.inf() <= h) continue;
|
||||
const C& newH = newC + h;
|
||||
if (newH < newC) continue; // cost overflow!
|
||||
|
||||
pq.push(newH, {edge, cur.e, cur.e->getFrom(), newC});
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto edge : cur.e->getTo()->getAdjListOut()) {
|
||||
if (edge == cur.e) continue;
|
||||
C newC = costFunc(cur.e, cur.e->getTo(), edge);
|
||||
if (costFunc.inf() <= newC) continue;
|
||||
newC = cur.d + newC;
|
||||
if (newC < cur.d) continue; // cost overflow!
|
||||
if (costFunc.inf() <= newC) continue;
|
||||
|
||||
const C& h = heurFunc(edge, to);
|
||||
if (costFunc.inf() <= h) continue;
|
||||
const C& newH = newC + h;
|
||||
if (newH < newC) continue; // cost overflow!
|
||||
|
||||
pq.push(newH, {edge, cur.e, cur.e->getTo(), newC});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -249,7 +517,22 @@ void EDijkstra::buildPath(Edge<N, E>* curE, const Settled<N, E, C>& settled,
|
|||
NList<N, E>* resNodes, EList<N, E>* resEdges) {
|
||||
const RouteEdge<N, E, C>* curEdge = &settled.find(curE)->second;
|
||||
if (resNodes) resNodes->push_back(curEdge->e->getOtherNd(curEdge->n));
|
||||
while (true) {
|
||||
while (resNodes || resEdges) {
|
||||
if (resNodes && curEdge->n) resNodes->push_back(curEdge->n);
|
||||
if (resEdges) resEdges->push_back(curEdge->e);
|
||||
if (!curEdge->parent) break;
|
||||
curEdge = &settled.find(curEdge->parent)->second;
|
||||
}
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E, typename C>
|
||||
void EDijkstra::buildPathInit(Edge<N, E>* curE,
|
||||
const SettledInit<N, E, C>& settled,
|
||||
NList<N, E>* resNodes, EList<N, E>* resEdges) {
|
||||
const RouteEdgeInit<N, E, C>* curEdge = &settled.find(curE)->second;
|
||||
if (resNodes) resNodes->push_back(curEdge->e->getOtherNd(curEdge->n));
|
||||
while (resNodes || resEdges) {
|
||||
if (resNodes && curEdge->n) resNodes->push_back(curEdge->n);
|
||||
if (resEdges) resEdges->push_back(curEdge->e);
|
||||
if (!curEdge->parent) break;
|
||||
|
|
|
|||
|
|
@ -21,9 +21,6 @@ class Edge {
|
|||
|
||||
Node<N, E>* getOtherNd(const Node<N, E>* notNode) const;
|
||||
|
||||
void setFrom(Node<N, E>* from);
|
||||
void setTo(Node<N, E>* to);
|
||||
|
||||
E& pl();
|
||||
const E& pl() const;
|
||||
|
||||
|
|
|
|||
|
|
@ -30,15 +30,16 @@ class Graph {
|
|||
virtual Node<N, E>* mergeNds(Node<N, E>* a, Node<N, E>* b) = 0;
|
||||
|
||||
const std::set<Node<N, E>*>& getNds() const;
|
||||
std::set<Node<N, E>*>* getNds();
|
||||
|
||||
static Node<N, E>* sharedNode(const Edge<N, E>* a, const Edge<N, E>* b);
|
||||
|
||||
typename std::set<Node<N, E>*>::iterator delNd(Node<N, E>* n);
|
||||
typename std::set<Node<N, E>*>::iterator delNd(
|
||||
typename std::set<Node<N, E>*>::iterator i);
|
||||
void delEdg(Node<N, E>* from, Node<N, E>* to);
|
||||
|
||||
private:
|
||||
std::set<Node<N, E>*> _nodes;
|
||||
protected:
|
||||
std::set<Node<N, E>*> _nodes;
|
||||
};
|
||||
|
||||
#include "util/graph/Graph.tpp"
|
||||
|
|
|
|||
|
|
@ -20,12 +20,6 @@ const std::set<Node<N, E>*>& Graph<N, E>::getNds() const {
|
|||
return _nodes;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
std::set<Node<N, E>*>* Graph<N, E>::getNds() {
|
||||
return &_nodes;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
typename std::set<Node<N, E>*>::iterator Graph<N, E>::delNd(
|
||||
|
|
@ -65,6 +59,17 @@ Edge<N, E>* Graph<N, E>::getEdg(Node<N, E>* from, Node<N, E>* to) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
Node<N, E>* Graph<N, E>::sharedNode(const Edge<N, E>* a, const Edge<N, E>* b) {
|
||||
Node<N, E>* r = 0;
|
||||
if (a->getFrom() == b->getFrom() || a->getFrom() == b->getTo())
|
||||
r = a->getFrom();
|
||||
if (a->getTo() == b->getFrom() || a->getTo() == b->getTo()) r = a->getTo();
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
const Edge<N, E>* Graph<N, E>::getEdg(Node<N, E>* from, Node<N, E>* to) const {
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
#ifndef UTIL_GRAPH_NODE_H_
|
||||
#define UTIL_GRAPH_NODE_H_
|
||||
|
||||
#include <vector>
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
|
||||
namespace util {
|
||||
namespace graph {
|
||||
|
|
@ -43,6 +43,7 @@ class Node {
|
|||
template <typename N, typename E>
|
||||
inline Node<N, E>::~Node() {}
|
||||
|
||||
}}
|
||||
} // namespace graph
|
||||
} // namespace util
|
||||
|
||||
#endif // UTIL_GRAPH_NODE_H_
|
||||
|
|
|
|||
|
|
@ -19,9 +19,46 @@
|
|||
namespace util {
|
||||
namespace graph {
|
||||
|
||||
using util::graph::Edge;
|
||||
using util::graph::Graph;
|
||||
using util::graph::Node;
|
||||
using util::graph::Edge;
|
||||
|
||||
template <typename N, typename E>
|
||||
using EList = std::vector<Edge<N, E>*>;
|
||||
|
||||
template <typename N, typename E>
|
||||
using NList = std::vector<Node<N, E>*>;
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
struct CostFunc {
|
||||
virtual C operator()(const Node<N, E>* from, const Edge<N, E>* e,
|
||||
const Node<N, E>* to) const = 0;
|
||||
virtual C operator()(const Edge<N, E>* from, const Node<N, E>* n,
|
||||
const Edge<N, E>* to) const = 0;
|
||||
virtual C inf() const = 0;
|
||||
};
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
struct HeurFunc {
|
||||
virtual C operator()(const Node<N, E>* a,
|
||||
const std::set<Node<N, E>*>& b) const = 0;
|
||||
virtual C operator()(const Edge<N, E>* a,
|
||||
const std::set<Edge<N, E>*>& b) const = 0;
|
||||
};
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
struct ZeroHeurFunc : public HeurFunc<N, E, C> {
|
||||
C operator()(const Node<N, E>* a, const std::set<Node<N, E>*>& b) const {
|
||||
UNUSED(a);
|
||||
UNUSED(b);
|
||||
return C();
|
||||
}
|
||||
C operator()(const Edge<N, E>* a, const std::set<Edge<N, E>*>& b) const {
|
||||
UNUSED(a);
|
||||
UNUSED(b);
|
||||
return C();
|
||||
}
|
||||
};
|
||||
|
||||
// shortest path base class
|
||||
template <class D>
|
||||
|
|
@ -33,37 +70,6 @@ class ShortestPath {
|
|||
template <typename N, typename E>
|
||||
using NList = std::vector<Node<N, E>*>;
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
struct CostFunc {
|
||||
virtual C operator()(const Node<N, E>* from, const Edge<N, E>* e,
|
||||
const Node<N, E>* to) const = 0;
|
||||
virtual C operator()(const Edge<N, E>* from, const Node<N, E>* n,
|
||||
const Edge<N, E>* to) const = 0;
|
||||
virtual C inf() const = 0;
|
||||
};
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
struct HeurFunc {
|
||||
virtual C operator()(const Node<N, E>* a,
|
||||
const std::set<Node<N, E>*>& b) const = 0;
|
||||
virtual C operator()(const Edge<N, E>* a,
|
||||
const std::set<Edge<N, E>*>& b) const = 0;
|
||||
};
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
struct ZeroHeurFunc : public HeurFunc<N, E, C> {
|
||||
C operator()(const Node<N, E>* a, const std::set<Node<N, E>*>& b) const {
|
||||
UNUSED(a);
|
||||
UNUSED(b);
|
||||
return C();
|
||||
}
|
||||
C operator()(const Edge<N, E>* a, const std::set<Edge<N, E>*>& b) const {
|
||||
UNUSED(a);
|
||||
UNUSED(b);
|
||||
return C();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPath(Node<N, E>* from, const std::set<Node<N, E>*>& to,
|
||||
const CostFunc<N, E, C>& costFunc,
|
||||
|
|
@ -74,7 +80,8 @@ class ShortestPath {
|
|||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPath(const std::set<Node<N, E>*> from, const std::set<Node<N, E>*>& to,
|
||||
static C shortestPath(const std::set<Node<N, E>*> from,
|
||||
const std::set<Node<N, E>*>& to,
|
||||
const CostFunc<N, E, C>& costFunc,
|
||||
const HeurFunc<N, E, C>& heurFunc,
|
||||
EList<N, E>* resEdges, NList<N, E>* resNodes) {
|
||||
|
|
@ -82,6 +89,15 @@ class ShortestPath {
|
|||
resNodes);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPath(const std::set<Node<N, E>*> from,
|
||||
const std::set<Node<N, E>*>& to,
|
||||
const CostFunc<N, E, C>& costFunc,
|
||||
EList<N, E>* resEdges, NList<N, E>* resNodes) {
|
||||
return D::shortestPathImpl(from, to, costFunc, ZeroHeurFunc<N, E, C>(),
|
||||
resEdges, resNodes);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPath(Node<N, E>* from, const std::set<Node<N, E>*>& to,
|
||||
const CostFunc<N, E, C>& costFunc,
|
||||
|
|
@ -160,6 +176,14 @@ class ShortestPath {
|
|||
return shortestPath(from, tos, costFunc, resEdges, resNodes);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPath(Node<N, E>* from, const std::set<Node<N, E>*>& tos,
|
||||
const CostFunc<N, E, C>& costFunc) {
|
||||
EList<N, E>* el = 0;
|
||||
NList<N, E>* nl = 0;
|
||||
return shortestPath(from, tos, costFunc, el, nl);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPath(Node<N, E>* from, Node<N, E>* to,
|
||||
const CostFunc<N, E, C>& costFunc) {
|
||||
|
|
@ -263,11 +287,22 @@ class ShortestPath {
|
|||
return D::shortestPathImpl(from, to, costFunc, heurFunc, resEdges, nl);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPath(Edge<N, E>* from, Edge<N, E>* to,
|
||||
const CostFunc<N, E, C>& costFunc,
|
||||
const HeurFunc<N, E, C>& heurFunc,
|
||||
EList<N, E>* resEdges) {
|
||||
NList<N, E> dummyRet;
|
||||
std::set<Edge<N, E>*> froms{from};
|
||||
std::set<Edge<N, E>*> tos{to};
|
||||
return D::shortestPathImpl(froms, tos, costFunc, heurFunc, resEdges,
|
||||
&dummyRet);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static std::unordered_map<Edge<N, E>*, C> shortestPath(
|
||||
Edge<N, E>* from, const std::set<Edge<N, E>*>& to,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc,
|
||||
const HeurFunc<N, E, C>& heurFunc,
|
||||
const CostFunc<N, E, C>& costFunc, const HeurFunc<N, E, C>& heurFunc,
|
||||
std::unordered_map<Edge<N, E>*, EList<N, E>*> resEdges,
|
||||
std::unordered_map<Edge<N, E>*, NList<N, E>*> resNodes) {
|
||||
return D::shortestPathImpl(from, to, costFunc, heurFunc, resEdges,
|
||||
|
|
@ -277,8 +312,20 @@ class ShortestPath {
|
|||
template <typename N, typename E, typename C>
|
||||
static std::unordered_map<Edge<N, E>*, C> shortestPath(
|
||||
Edge<N, E>* from, const std::set<Edge<N, E>*>& to,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc,
|
||||
const HeurFunc<N, E, C>& heurFunc,
|
||||
const CostFunc<N, E, C>& costFunc,
|
||||
std::unordered_map<Edge<N, E>*, EList<N, E>*> resEdges,
|
||||
std::unordered_map<Edge<N, E>*, NList<N, E>*> resNodes) {
|
||||
return D::shortestPathImpl(from, to, costFunc, ZeroHeurFunc<N, E, C>()
|
||||
, resEdges,
|
||||
resNodes);
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static std::unordered_map<Edge<N, E>*, C> shortestPath(
|
||||
Edge<N, E>* from, const std::set<Edge<N, E>*>& to,
|
||||
const CostFunc<N, E, C>& costFunc, const HeurFunc<N, E, C>& heurFunc,
|
||||
std::unordered_map<Edge<N, E>*, EList<N, E>*> resEdges) {
|
||||
std::unordered_map<Edge<N, E>*, NList<N, E>*> dummyRet;
|
||||
return D::shortestPathImpl(from, to, costFunc, heurFunc, resEdges,
|
||||
|
|
@ -288,14 +335,35 @@ class ShortestPath {
|
|||
template <typename N, typename E, typename C>
|
||||
static std::unordered_map<Edge<N, E>*, C> shortestPath(
|
||||
Edge<N, E>* from, const std::set<Edge<N, E>*>& to,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc,
|
||||
const HeurFunc<N, E, C>& heurFunc) {
|
||||
const CostFunc<N, E, C>& costFunc, const HeurFunc<N, E, C>& heurFunc) {
|
||||
std::unordered_map<Edge<N, E>*, NList<N, E>*> dummyRet;
|
||||
std::unordered_map<Edge<N, E>*, EList<N, E>*> dummyRetE;
|
||||
return D::shortestPathImpl(from, to, costFunc, heurFunc, dummyRetE,
|
||||
dummyRet);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPath(Edge<N, E>* from,
|
||||
const std::set<Edge<N, E>*>& to,
|
||||
const CostFunc<N, E, C>& costFunc) {
|
||||
NList<N, E>* nl = 0;
|
||||
EList<N, E>* el = 0;
|
||||
std::set<Edge<N, E>*> fromS;
|
||||
fromS.insert(from);
|
||||
return D::shortestPathImpl(fromS, to, costFunc, ZeroHeurFunc<N, E, C>(), el,
|
||||
nl);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPath(const std::set<Edge<N, E>*>& from,
|
||||
const std::set<Edge<N, E>*>& to,
|
||||
const CostFunc<N, E, C>& costFunc) {
|
||||
NList<N, E>* nl = 0;
|
||||
EList<N, E>* el = 0;
|
||||
return D::shortestPathImpl(from, to, costFunc, ZeroHeurFunc<N, E, C>(), el,
|
||||
nl);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPath(const std::set<Edge<N, E>*>& from,
|
||||
const std::set<Edge<N, E>*>& to,
|
||||
|
|
@ -306,17 +374,6 @@ class ShortestPath {
|
|||
return D::shortestPathImpl(from, to, costFunc, heurFunc, el, nl);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPath(Edge<N, E>* from,
|
||||
Edge<N, E>* to,
|
||||
const CostFunc<N, E, C>& costFunc ) {
|
||||
NList<N, E>* nl = 0;
|
||||
EList<N, E>* el = 0;
|
||||
std::set<Edge<N, E>*> tos{to};
|
||||
std::set<Edge<N, E>*> froms{from};
|
||||
return D::shortestPathImpl(froms, tos, costFunc, ZeroHeurFunc<N, E, C>(), el, nl);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPath(const std::set<Edge<N, E>*>& from,
|
||||
const std::set<Edge<N, E>*>& to,
|
||||
|
|
@ -326,6 +383,91 @@ class ShortestPath {
|
|||
return D::shortestPathImpl(from, to, costFunc, heurFunc, el, nl);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static std::unordered_map<Edge<N, E>*, C> shortestPath(
|
||||
const std::set<Edge<N, E>*>& from, const std::set<Edge<N, E>*>& to,
|
||||
const std::unordered_map<Edge<N, E>*, C>& initCosts,
|
||||
const CostFunc<N, E, C>& costFunc, const HeurFunc<N, E, C>& heurFunc,
|
||||
std::unordered_map<Edge<N, E>*, EList<N, E>*> resEdges,
|
||||
std::unordered_map<Edge<N, E>*, NList<N, E>*> resNodes) {
|
||||
return D::shortestPathImpl(from, to, initCosts, costFunc.inf(), costFunc,
|
||||
heurFunc, resEdges, resNodes);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static std::unordered_map<Edge<N, E>*, C> shortestPath(
|
||||
const std::set<Edge<N, E>*>& from, const std::set<Edge<N, E>*>& to,
|
||||
const std::unordered_map<Edge<N, E>*, C>& initCosts,
|
||||
const CostFunc<N, E, C>& costFunc, const HeurFunc<N, E, C>& heurFunc,
|
||||
std::unordered_map<Edge<N, E>*, EList<N, E>*> resEdges) {
|
||||
std::unordered_map<Edge<N, E>*, NList<N, E>*> dummyRet;
|
||||
return D::shortestPathImpl(from, to, initCosts, costFunc.inf(), costFunc,
|
||||
heurFunc, resEdges, dummyRet);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static std::unordered_map<Edge<N, E>*, C> shortestPath(
|
||||
const std::set<Edge<N, E>*>& from, const std::set<Edge<N, E>*>& to,
|
||||
const std::unordered_map<Edge<N, E>*, C>& initCosts,
|
||||
const CostFunc<N, E, C>& costFunc, const HeurFunc<N, E, C>& heurFunc) {
|
||||
std::unordered_map<Edge<N, E>*, NList<N, E>*> dummyRet;
|
||||
std::unordered_map<Edge<N, E>*, EList<N, E>*> dummyRetE;
|
||||
return D::shortestPathImpl(from, to, initCosts, costFunc.inf(), costFunc,
|
||||
heurFunc, dummyRetE, dummyRet);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static std::unordered_map<Edge<N, E>*, C> shortestPath(
|
||||
const std::set<Edge<N, E>*>& from, const std::set<Edge<N, E>*>& to,
|
||||
const std::unordered_map<Edge<N, E>*, C>& initCosts, C stall,
|
||||
const CostFunc<N, E, C>& costFunc, const HeurFunc<N, E, C>& heurFunc,
|
||||
std::unordered_map<Edge<N, E>*, EList<N, E>*> resEdges,
|
||||
std::unordered_map<Edge<N, E>*, NList<N, E>*> resNodes) {
|
||||
return D::shortestPathImpl(from, to, initCosts, stall, costFunc, heurFunc,
|
||||
resEdges, resNodes);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static std::unordered_map<Edge<N, E>*, C> shortestPath(
|
||||
const std::set<Edge<N, E>*>& from, const std::set<Edge<N, E>*>& to,
|
||||
const std::unordered_map<Edge<N, E>*, C>& initCosts, C stall,
|
||||
const CostFunc<N, E, C>& costFunc, const HeurFunc<N, E, C>& heurFunc,
|
||||
std::unordered_map<Edge<N, E>*, EList<N, E>*> resEdges) {
|
||||
std::unordered_map<Edge<N, E>*, NList<N, E>*> dummyRet;
|
||||
return D::shortestPathImpl(from, to, initCosts, stall, costFunc, heurFunc,
|
||||
resEdges, dummyRet);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static std::unordered_map<Edge<N, E>*, std::pair<Edge<N, E>*, C>> shortestPath(
|
||||
const std::set<Edge<N, E>*>& from, const std::set<Edge<N, E>*>& to,
|
||||
const std::unordered_map<Edge<N, E>*, C>& initCosts, C stall,
|
||||
const CostFunc<N, E, C>& costFunc, const HeurFunc<N, E, C>& heurFunc) {
|
||||
return D::shortestPathImpl(from, to, initCosts, stall, costFunc, heurFunc);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPath(Edge<N, E>* from, Edge<N, E>* to,
|
||||
const CostFunc<N, E, C>& costFunc) {
|
||||
NList<N, E>* nl = 0;
|
||||
EList<N, E>* el = 0;
|
||||
std::set<Edge<N, E>*> tos{to};
|
||||
std::set<Edge<N, E>*> froms{from};
|
||||
return D::shortestPathImpl(froms, tos, costFunc, ZeroHeurFunc<N, E, C>(),
|
||||
el, nl);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPath(Edge<N, E>* from, Edge<N, E>* to,
|
||||
const CostFunc<N, E, C>& costFunc,
|
||||
const HeurFunc<N, E, C>& heurFunc) {
|
||||
NList<N, E>* nl = 0;
|
||||
EList<N, E>* el = 0;
|
||||
std::set<Edge<N, E>*> tos{to};
|
||||
std::set<Edge<N, E>*> froms{from};
|
||||
return D::shortestPathImpl(froms, tos, costFunc, heurFunc, el, nl);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPath(Edge<N, E>* from, const std::set<Node<N, E>*>& to,
|
||||
const CostFunc<N, E, C>& costFunc,
|
||||
|
|
@ -336,8 +478,7 @@ class ShortestPath {
|
|||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPath(Edge<N, E>* from, const std::set<Node<N, E>*>& to,
|
||||
const CostFunc<N, E, C>& costFunc,
|
||||
EList<N, E>* el,
|
||||
const CostFunc<N, E, C>& costFunc, EList<N, E>* el,
|
||||
NList<N, E>* nl) {
|
||||
return D::shortestPathImpl(from, to, costFunc, ZeroHeurFunc<N, E, C>(), el,
|
||||
nl);
|
||||
|
|
@ -363,24 +504,21 @@ class ShortestPath {
|
|||
|
||||
template <typename N, typename E, typename C>
|
||||
static std::unordered_map<Edge<N, E>*, C> shortestPath(
|
||||
Edge<N, E>* from,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc) {
|
||||
std::set<Edge<N, E>*> froms { from };
|
||||
Edge<N, E>* from, const CostFunc<N, E, C>& costFunc) {
|
||||
std::set<Edge<N, E>*> froms{from};
|
||||
return D::shortestPathImpl(froms, costFunc, false);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static std::unordered_map<Edge<N, E>*, C> shortestPathRev(
|
||||
Edge<N, E>* from,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc) {
|
||||
std::set<Edge<N, E>*> froms { from };
|
||||
Edge<N, E>* from, const CostFunc<N, E, C>& costFunc) {
|
||||
std::set<Edge<N, E>*> froms{from};
|
||||
return D::shortestPathImpl(froms, costFunc, true);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static std::unordered_map<Edge<N, E>*, C> shortestPath(
|
||||
Node<N, E>* from,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc) {
|
||||
Node<N, E>* from, const CostFunc<N, E, C>& costFunc) {
|
||||
std::set<Edge<N, E>*> froms;
|
||||
froms.insert(from->getAdjListOut().begin(), from->getAdjListOut().end());
|
||||
return D::shortestPathImpl(froms, costFunc, false);
|
||||
|
|
@ -388,14 +526,13 @@ class ShortestPath {
|
|||
|
||||
template <typename N, typename E, typename C>
|
||||
static std::unordered_map<Edge<N, E>*, C> shortestPathRev(
|
||||
Node<N, E>* from,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc) {
|
||||
Node<N, E>* from, const CostFunc<N, E, C>& costFunc) {
|
||||
std::set<Edge<N, E>*> froms;
|
||||
froms.insert(from->getAdjListOut().begin(), from->getAdjListOut().end());
|
||||
return D::shortestPathImpl(froms, costFunc, true);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
} // namespace graph
|
||||
} // namespace util
|
||||
|
||||
#endif // UTIL_GRAPH_SHORTESTPATH_H_
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ Node<N, E>* UndirGraph<N, E>::addNd() {
|
|||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
Node<N, E>* UndirGraph<N, E>::addNd(UndirNode<N, E>* n) {
|
||||
auto ins = Graph<N, E>::getNds()->insert(n);
|
||||
auto ins = Graph<N, E>::_nodes.insert(n);
|
||||
return *ins.first;
|
||||
}
|
||||
|
||||
|
|
|
|||
226
src/util/graph/radix_heap.h
Normal file
226
src/util/graph/radix_heap.h
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
// based on https://github.com/iwiwi/radix-heap
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <climits>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace radix_heap {
|
||||
namespace internal {
|
||||
template <bool Is64bit>
|
||||
class find_bucket_impl;
|
||||
|
||||
template <>
|
||||
class find_bucket_impl<false> {
|
||||
public:
|
||||
static inline constexpr size_t find_bucket(uint32_t x, uint32_t last) {
|
||||
return x == last ? 0 : 32 - __builtin_clz(x ^ last);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
class find_bucket_impl<true> {
|
||||
public:
|
||||
static inline constexpr size_t find_bucket(uint64_t x, uint64_t last) {
|
||||
return x == last ? 0 : 64 - __builtin_clzll(x ^ last);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
inline constexpr size_t find_bucket(T x, T last) {
|
||||
return find_bucket_impl<sizeof(T) == 8>::find_bucket(x, last);
|
||||
}
|
||||
|
||||
template <typename KeyType, bool IsSigned>
|
||||
class encoder_impl_integer;
|
||||
|
||||
template <typename KeyType>
|
||||
class encoder_impl_integer<KeyType, false> {
|
||||
public:
|
||||
typedef KeyType key_type;
|
||||
typedef KeyType unsigned_key_type;
|
||||
|
||||
inline static constexpr unsigned_key_type encode(key_type x) { return x; }
|
||||
|
||||
inline static constexpr key_type decode(unsigned_key_type x) { return x; }
|
||||
};
|
||||
|
||||
template <typename KeyType>
|
||||
class encoder_impl_integer<KeyType, true> {
|
||||
public:
|
||||
typedef KeyType key_type;
|
||||
typedef typename std::make_unsigned<KeyType>::type unsigned_key_type;
|
||||
|
||||
inline static constexpr unsigned_key_type encode(key_type x) {
|
||||
return static_cast<unsigned_key_type>(x) ^
|
||||
(unsigned_key_type(1) << unsigned_key_type(
|
||||
std::numeric_limits<unsigned_key_type>::digits - 1));
|
||||
}
|
||||
|
||||
inline static constexpr key_type decode(unsigned_key_type x) {
|
||||
return static_cast<key_type>(
|
||||
x ^ (unsigned_key_type(1)
|
||||
<< (std::numeric_limits<unsigned_key_type>::digits - 1)));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename KeyType, typename UnsignedKeyType>
|
||||
class encoder_impl_decimal {
|
||||
public:
|
||||
typedef KeyType key_type;
|
||||
typedef UnsignedKeyType unsigned_key_type;
|
||||
|
||||
inline static constexpr unsigned_key_type encode(key_type x) {
|
||||
return raw_cast<key_type, unsigned_key_type>(x) ^
|
||||
((-(raw_cast<key_type, unsigned_key_type>(x) >>
|
||||
(std::numeric_limits<unsigned_key_type>::digits - 1))) |
|
||||
(unsigned_key_type(1)
|
||||
<< (std::numeric_limits<unsigned_key_type>::digits - 1)));
|
||||
}
|
||||
|
||||
inline static constexpr key_type decode(unsigned_key_type x) {
|
||||
return raw_cast<unsigned_key_type, key_type>(
|
||||
x ^ (((x >> (std::numeric_limits<unsigned_key_type>::digits - 1)) - 1) |
|
||||
(unsigned_key_type(1)
|
||||
<< (std::numeric_limits<unsigned_key_type>::digits - 1))));
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename T, typename U>
|
||||
union raw_cast {
|
||||
public:
|
||||
constexpr raw_cast(T t) : t_(t) {}
|
||||
operator U() const { return u_; }
|
||||
|
||||
private:
|
||||
T t_;
|
||||
U u_;
|
||||
};
|
||||
};
|
||||
|
||||
template <typename KeyType>
|
||||
class encoder
|
||||
: public encoder_impl_integer<KeyType, std::is_signed<KeyType>::value> {};
|
||||
template <>
|
||||
class encoder<float> : public encoder_impl_decimal<float, uint32_t> {};
|
||||
template <>
|
||||
class encoder<double> : public encoder_impl_decimal<double, uint64_t> {};
|
||||
} // namespace internal
|
||||
|
||||
template <typename KeyType, typename ValueType,
|
||||
typename EncoderType = internal::encoder<KeyType>>
|
||||
class pair_radix_heap {
|
||||
public:
|
||||
typedef KeyType key_type;
|
||||
typedef ValueType value_type;
|
||||
typedef EncoderType encoder_type;
|
||||
typedef typename encoder_type::unsigned_key_type unsigned_key_type;
|
||||
|
||||
pair_radix_heap() : size_(0), last_(), buckets_() {
|
||||
buckets_min_.fill(std::numeric_limits<unsigned_key_type>::max());
|
||||
}
|
||||
|
||||
void push(key_type key, const value_type &value) {
|
||||
unsigned_key_type x = encoder_type::encode(key);
|
||||
if (last_ > x) {
|
||||
std::cerr << "Not monotone: " << last_ << " vs " << x << std::endl;
|
||||
x = last_;
|
||||
}
|
||||
++size_;
|
||||
const size_t k = internal::find_bucket(x, last_);
|
||||
buckets_[k].emplace_back(x, value);
|
||||
buckets_min_[k] = std::min(buckets_min_[k], x);
|
||||
}
|
||||
|
||||
void push(key_type key, value_type &&value) {
|
||||
unsigned_key_type x = encoder_type::encode(key);
|
||||
if (last_ > x) {
|
||||
std::cerr << "Not monotone: " << last_ << " vs " << x << std::endl;
|
||||
x = last_;
|
||||
}
|
||||
++size_;
|
||||
const size_t k = internal::find_bucket(x, last_);
|
||||
buckets_[k].emplace_back(x, std::move(value));
|
||||
buckets_min_[k] = std::min(buckets_min_[k], x);
|
||||
}
|
||||
|
||||
template <class... Args>
|
||||
void emplace(key_type key, Args &&... args) {
|
||||
unsigned_key_type x = encoder_type::encode(key);
|
||||
if (last_ > x) x = last_;
|
||||
++size_;
|
||||
const size_t k = internal::find_bucket(x, last_);
|
||||
buckets_[k].emplace_back(std::piecewise_construct, std::forward_as_tuple(x),
|
||||
std::forward_as_tuple(args...));
|
||||
buckets_min_[k] = std::min(buckets_min_[k], x);
|
||||
}
|
||||
|
||||
key_type topKey() {
|
||||
pull();
|
||||
return encoder_type::decode(last_);
|
||||
}
|
||||
|
||||
value_type &topVal() {
|
||||
pull();
|
||||
return buckets_[0].back().second;
|
||||
}
|
||||
|
||||
void pop() {
|
||||
pull();
|
||||
buckets_[0].pop_back();
|
||||
--size_;
|
||||
}
|
||||
|
||||
size_t size() const { return size_; }
|
||||
|
||||
bool empty() const { return size_ == 0; }
|
||||
|
||||
void clear() {
|
||||
size_ = 0;
|
||||
last_ = key_type();
|
||||
for (auto &b : buckets_) b.clear();
|
||||
buckets_min_.fill(std::numeric_limits<unsigned_key_type>::max());
|
||||
}
|
||||
|
||||
void swap(pair_radix_heap<KeyType, ValueType, EncoderType> &a) {
|
||||
std::swap(size_, a.size_);
|
||||
std::swap(last_, a.last_);
|
||||
buckets_.swap(a.buckets_);
|
||||
buckets_min_.swap(a.buckets_min_);
|
||||
}
|
||||
|
||||
private:
|
||||
size_t size_;
|
||||
unsigned_key_type last_;
|
||||
std::array<std::vector<std::pair<unsigned_key_type, value_type>>,
|
||||
std::numeric_limits<unsigned_key_type>::digits + 1>
|
||||
buckets_;
|
||||
std::array<unsigned_key_type,
|
||||
std::numeric_limits<unsigned_key_type>::digits + 1>
|
||||
buckets_min_;
|
||||
|
||||
void pull() {
|
||||
assert(size_ > 0);
|
||||
if (!buckets_[0].empty()) return;
|
||||
|
||||
size_t i;
|
||||
for (i = 1; buckets_[i].empty(); ++i)
|
||||
;
|
||||
last_ = buckets_min_[i];
|
||||
|
||||
for (size_t j = 0; j < buckets_[i].size(); ++j) {
|
||||
const unsigned_key_type x = buckets_[i][j].first;
|
||||
const size_t k = internal::find_bucket(x, last_);
|
||||
buckets_[k].emplace_back(std::move(buckets_[i][j]));
|
||||
buckets_min_[k] = std::min(buckets_min_[k], x);
|
||||
}
|
||||
buckets_[i].clear();
|
||||
buckets_min_[i] = std::numeric_limits<unsigned_key_type>::max();
|
||||
}
|
||||
};
|
||||
} // namespace radix_heap
|
||||
348
src/util/graph/robin/robin_growth_policy.h
Normal file
348
src/util/graph/robin/robin_growth_policy.h
Normal file
|
|
@ -0,0 +1,348 @@
|
|||
/**
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2017 Thibaut Goetghebuer-Planchon <tessil@gmx.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef TSL_ROBIN_GROWTH_POLICY_H
|
||||
#define TSL_ROBIN_GROWTH_POLICY_H
|
||||
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <climits>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <ratio>
|
||||
#include <stdexcept>
|
||||
|
||||
|
||||
#ifdef TSL_DEBUG
|
||||
# define tsl_rh_assert(expr) assert(expr)
|
||||
#else
|
||||
# define tsl_rh_assert(expr) (static_cast<void>(0))
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* If exceptions are enabled, throw the exception passed in parameter, otherwise call std::terminate.
|
||||
*/
|
||||
#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || (defined (_MSC_VER) && defined (_CPPUNWIND))) && !defined(TSL_NO_EXCEPTIONS)
|
||||
# define TSL_RH_THROW_OR_TERMINATE(ex, msg) throw ex(msg)
|
||||
#else
|
||||
# define TSL_RH_NO_EXCEPTIONS
|
||||
# ifdef NDEBUG
|
||||
# define TSL_RH_THROW_OR_TERMINATE(ex, msg) std::terminate()
|
||||
# else
|
||||
# include <iostream>
|
||||
# define TSL_RH_THROW_OR_TERMINATE(ex, msg) do { std::cerr << msg << std::endl; std::terminate(); } while(0)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
# define TSL_RH_LIKELY(exp) (__builtin_expect(!!(exp), true))
|
||||
#else
|
||||
# define TSL_RH_LIKELY(exp) (exp)
|
||||
#endif
|
||||
|
||||
|
||||
namespace tsl {
|
||||
namespace rh {
|
||||
|
||||
/**
|
||||
* Grow the hash table by a factor of GrowthFactor keeping the bucket count to a power of two. It allows
|
||||
* the table to use a mask operation instead of a modulo operation to map a hash to a bucket.
|
||||
*
|
||||
* GrowthFactor must be a power of two >= 2.
|
||||
*/
|
||||
template<std::size_t GrowthFactor>
|
||||
class power_of_two_growth_policy {
|
||||
public:
|
||||
/**
|
||||
* Called on the hash table creation and on rehash. The number of buckets for the table is passed in parameter.
|
||||
* This number is a minimum, the policy may update this value with a higher value if needed (but not lower).
|
||||
*
|
||||
* If 0 is given, min_bucket_count_in_out must still be 0 after the policy creation and
|
||||
* bucket_for_hash must always return 0 in this case.
|
||||
*/
|
||||
explicit power_of_two_growth_policy(std::size_t& min_bucket_count_in_out) {
|
||||
if(min_bucket_count_in_out > max_bucket_count()) {
|
||||
TSL_RH_THROW_OR_TERMINATE(std::length_error, "The hash table exceeds its maximum size.");
|
||||
}
|
||||
|
||||
if(min_bucket_count_in_out > 0) {
|
||||
min_bucket_count_in_out = round_up_to_power_of_two(min_bucket_count_in_out);
|
||||
m_mask = min_bucket_count_in_out - 1;
|
||||
}
|
||||
else {
|
||||
m_mask = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the bucket [0, bucket_count()) to which the hash belongs.
|
||||
* If bucket_count() is 0, it must always return 0.
|
||||
*/
|
||||
std::size_t bucket_for_hash(std::size_t hash) const noexcept {
|
||||
return hash & m_mask;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of buckets that should be used on next growth.
|
||||
*/
|
||||
std::size_t next_bucket_count() const {
|
||||
if((m_mask + 1) > max_bucket_count() / GrowthFactor) {
|
||||
TSL_RH_THROW_OR_TERMINATE(std::length_error, "The hash table exceeds its maximum size.");
|
||||
}
|
||||
|
||||
return (m_mask + 1) * GrowthFactor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the maximum number of buckets supported by the policy.
|
||||
*/
|
||||
std::size_t max_bucket_count() const {
|
||||
// Largest power of two.
|
||||
return (std::numeric_limits<std::size_t>::max() / 2) + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the growth policy as if it was created with a bucket count of 0.
|
||||
* After a clear, the policy must always return 0 when bucket_for_hash is called.
|
||||
*/
|
||||
void clear() noexcept {
|
||||
m_mask = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
static std::size_t round_up_to_power_of_two(std::size_t value) {
|
||||
if(is_power_of_two(value)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if(value == 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
--value;
|
||||
for(std::size_t i = 1; i < sizeof(std::size_t) * CHAR_BIT; i *= 2) {
|
||||
value |= value >> i;
|
||||
}
|
||||
|
||||
return value + 1;
|
||||
}
|
||||
|
||||
static constexpr bool is_power_of_two(std::size_t value) {
|
||||
return value != 0 && (value & (value - 1)) == 0;
|
||||
}
|
||||
|
||||
protected:
|
||||
static_assert(is_power_of_two(GrowthFactor) && GrowthFactor >= 2, "GrowthFactor must be a power of two >= 2.");
|
||||
|
||||
std::size_t m_mask;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Grow the hash table by GrowthFactor::num / GrowthFactor::den and use a modulo to map a hash
|
||||
* to a bucket. Slower but it can be useful if you want a slower growth.
|
||||
*/
|
||||
template<class GrowthFactor = std::ratio<3, 2>>
|
||||
class mod_growth_policy {
|
||||
public:
|
||||
explicit mod_growth_policy(std::size_t& min_bucket_count_in_out) {
|
||||
if(min_bucket_count_in_out > max_bucket_count()) {
|
||||
TSL_RH_THROW_OR_TERMINATE(std::length_error, "The hash table exceeds its maximum size.");
|
||||
}
|
||||
|
||||
if(min_bucket_count_in_out > 0) {
|
||||
m_mod = min_bucket_count_in_out;
|
||||
}
|
||||
else {
|
||||
m_mod = 1;
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t bucket_for_hash(std::size_t hash) const noexcept {
|
||||
return hash % m_mod;
|
||||
}
|
||||
|
||||
std::size_t next_bucket_count() const {
|
||||
if(m_mod == max_bucket_count()) {
|
||||
TSL_RH_THROW_OR_TERMINATE(std::length_error, "The hash table exceeds its maximum size.");
|
||||
}
|
||||
|
||||
const double next_bucket_count = std::ceil(double(m_mod) * REHASH_SIZE_MULTIPLICATION_FACTOR);
|
||||
if(!std::isnormal(next_bucket_count)) {
|
||||
TSL_RH_THROW_OR_TERMINATE(std::length_error, "The hash table exceeds its maximum size.");
|
||||
}
|
||||
|
||||
if(next_bucket_count > double(max_bucket_count())) {
|
||||
return max_bucket_count();
|
||||
}
|
||||
else {
|
||||
return std::size_t(next_bucket_count);
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t max_bucket_count() const {
|
||||
return MAX_BUCKET_COUNT;
|
||||
}
|
||||
|
||||
void clear() noexcept {
|
||||
m_mod = 1;
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr double REHASH_SIZE_MULTIPLICATION_FACTOR = 1.0 * GrowthFactor::num / GrowthFactor::den;
|
||||
static const std::size_t MAX_BUCKET_COUNT =
|
||||
std::size_t(double(
|
||||
std::numeric_limits<std::size_t>::max() / REHASH_SIZE_MULTIPLICATION_FACTOR
|
||||
));
|
||||
|
||||
static_assert(REHASH_SIZE_MULTIPLICATION_FACTOR >= 1.1, "Growth factor should be >= 1.1.");
|
||||
|
||||
std::size_t m_mod;
|
||||
};
|
||||
|
||||
|
||||
|
||||
namespace detail {
|
||||
|
||||
#if SIZE_MAX >= ULLONG_MAX
|
||||
#define TSL_RH_NB_PRIMES 51
|
||||
#elif SIZE_MAX >= ULONG_MAX
|
||||
#define TSL_RH_NB_PRIMES 40
|
||||
#else
|
||||
#define TSL_RH_NB_PRIMES 23
|
||||
#endif
|
||||
|
||||
static constexpr const std::array<std::size_t, TSL_RH_NB_PRIMES> PRIMES = {{
|
||||
1u, 5u, 17u, 29u, 37u, 53u, 67u, 79u, 97u, 131u, 193u, 257u, 389u, 521u, 769u, 1031u,
|
||||
1543u, 2053u, 3079u, 6151u, 12289u, 24593u, 49157u,
|
||||
#if SIZE_MAX >= ULONG_MAX
|
||||
98317ul, 196613ul, 393241ul, 786433ul, 1572869ul, 3145739ul, 6291469ul, 12582917ul,
|
||||
25165843ul, 50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul, 1610612741ul,
|
||||
3221225473ul, 4294967291ul,
|
||||
#endif
|
||||
#if SIZE_MAX >= ULLONG_MAX
|
||||
6442450939ull, 12884901893ull, 25769803751ull, 51539607551ull, 103079215111ull, 206158430209ull,
|
||||
412316860441ull, 824633720831ull, 1649267441651ull, 3298534883309ull, 6597069766657ull,
|
||||
#endif
|
||||
}};
|
||||
|
||||
template<unsigned int IPrime>
|
||||
static constexpr std::size_t mod(std::size_t hash) { return hash % PRIMES[IPrime]; }
|
||||
|
||||
// MOD_PRIME[iprime](hash) returns hash % PRIMES[iprime]. This table allows for faster modulo as the
|
||||
// compiler can optimize the modulo code better with a constant known at the compilation.
|
||||
static constexpr const std::array<std::size_t(*)(std::size_t), TSL_RH_NB_PRIMES> MOD_PRIME = {{
|
||||
&mod<0>, &mod<1>, &mod<2>, &mod<3>, &mod<4>, &mod<5>, &mod<6>, &mod<7>, &mod<8>, &mod<9>, &mod<10>,
|
||||
&mod<11>, &mod<12>, &mod<13>, &mod<14>, &mod<15>, &mod<16>, &mod<17>, &mod<18>, &mod<19>, &mod<20>,
|
||||
&mod<21>, &mod<22>,
|
||||
#if SIZE_MAX >= ULONG_MAX
|
||||
&mod<23>, &mod<24>, &mod<25>, &mod<26>, &mod<27>, &mod<28>, &mod<29>, &mod<30>, &mod<31>, &mod<32>,
|
||||
&mod<33>, &mod<34>, &mod<35>, &mod<36>, &mod<37> , &mod<38>, &mod<39>,
|
||||
#endif
|
||||
#if SIZE_MAX >= ULLONG_MAX
|
||||
&mod<40>, &mod<41>, &mod<42>, &mod<43>, &mod<44>, &mod<45>, &mod<46>, &mod<47>, &mod<48>, &mod<49>,
|
||||
&mod<50>,
|
||||
#endif
|
||||
}};
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Grow the hash table by using prime numbers as bucket count. Slower than tsl::rh::power_of_two_growth_policy in
|
||||
* general but will probably distribute the values around better in the buckets with a poor hash function.
|
||||
*
|
||||
* To allow the compiler to optimize the modulo operation, a lookup table is used with constant primes numbers.
|
||||
*
|
||||
* With a switch the code would look like:
|
||||
* \code
|
||||
* switch(iprime) { // iprime is the current prime of the hash table
|
||||
* case 0: hash % 5ul;
|
||||
* break;
|
||||
* case 1: hash % 17ul;
|
||||
* break;
|
||||
* case 2: hash % 29ul;
|
||||
* break;
|
||||
* ...
|
||||
* }
|
||||
* \endcode
|
||||
*
|
||||
* Due to the constant variable in the modulo the compiler is able to optimize the operation
|
||||
* by a series of multiplications, substractions and shifts.
|
||||
*
|
||||
* The 'hash % 5' could become something like 'hash - (hash * 0xCCCCCCCD) >> 34) * 5' in a 64 bits environment.
|
||||
*/
|
||||
class prime_growth_policy {
|
||||
public:
|
||||
explicit prime_growth_policy(std::size_t& min_bucket_count_in_out) {
|
||||
auto it_prime = std::lower_bound(detail::PRIMES.begin(),
|
||||
detail::PRIMES.end(), min_bucket_count_in_out);
|
||||
if(it_prime == detail::PRIMES.end()) {
|
||||
TSL_RH_THROW_OR_TERMINATE(std::length_error, "The hash table exceeds its maximum size.");
|
||||
}
|
||||
|
||||
m_iprime = static_cast<unsigned int>(std::distance(detail::PRIMES.begin(), it_prime));
|
||||
if(min_bucket_count_in_out > 0) {
|
||||
min_bucket_count_in_out = *it_prime;
|
||||
}
|
||||
else {
|
||||
min_bucket_count_in_out = 0;
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t bucket_for_hash(std::size_t hash) const noexcept {
|
||||
return detail::MOD_PRIME[m_iprime](hash);
|
||||
}
|
||||
|
||||
std::size_t next_bucket_count() const {
|
||||
if(m_iprime + 1 >= detail::PRIMES.size()) {
|
||||
TSL_RH_THROW_OR_TERMINATE(std::length_error, "The hash table exceeds its maximum size.");
|
||||
}
|
||||
|
||||
return detail::PRIMES[m_iprime + 1];
|
||||
}
|
||||
|
||||
std::size_t max_bucket_count() const {
|
||||
return detail::PRIMES.back();
|
||||
}
|
||||
|
||||
void clear() noexcept {
|
||||
m_iprime = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned int m_iprime;
|
||||
|
||||
static_assert(std::numeric_limits<decltype(m_iprime)>::max() >= detail::PRIMES.size(),
|
||||
"The type of m_iprime is not big enough.");
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
1451
src/util/graph/robin/robin_hash.h
Normal file
1451
src/util/graph/robin/robin_hash.h
Normal file
File diff suppressed because it is too large
Load diff
715
src/util/graph/robin/robin_map.h
Normal file
715
src/util/graph/robin/robin_map.h
Normal file
|
|
@ -0,0 +1,715 @@
|
|||
/**
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2017 Thibaut Goetghebuer-Planchon <tessil@gmx.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef TSL_ROBIN_MAP_H
|
||||
#define TSL_ROBIN_MAP_H
|
||||
|
||||
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "robin_hash.h"
|
||||
|
||||
|
||||
namespace tsl {
|
||||
|
||||
|
||||
/**
|
||||
* Implementation of a hash map using open-addressing and the robin hood hashing algorithm with backward shift deletion.
|
||||
*
|
||||
* For operations modifying the hash map (insert, erase, rehash, ...), the strong exception guarantee
|
||||
* is only guaranteed when the expression `std::is_nothrow_swappable<std::pair<Key, T>>::value &&
|
||||
* std::is_nothrow_move_constructible<std::pair<Key, T>>::value` is true, otherwise if an exception
|
||||
* is thrown during the swap or the move, the hash map may end up in a undefined state. Per the standard
|
||||
* a `Key` or `T` with a noexcept copy constructor and no move constructor also satisfies the
|
||||
* `std::is_nothrow_move_constructible<std::pair<Key, T>>::value` criterion (and will thus guarantee the
|
||||
* strong exception for the map).
|
||||
*
|
||||
* When `StoreHash` is true, 32 bits of the hash are stored alongside the values. It can improve
|
||||
* the performance during lookups if the `KeyEqual` function takes time (if it engenders a cache-miss for example)
|
||||
* as we then compare the stored hashes before comparing the keys. When `tsl::rh::power_of_two_growth_policy` is used
|
||||
* as `GrowthPolicy`, it may also speed-up the rehash process as we can avoid to recalculate the hash.
|
||||
* When it is detected that storing the hash will not incur any memory penalty due to alignment (i.e.
|
||||
* `sizeof(tsl::detail_robin_hash::bucket_entry<ValueType, true>) ==
|
||||
* sizeof(tsl::detail_robin_hash::bucket_entry<ValueType, false>)`) and `tsl::rh::power_of_two_growth_policy` is
|
||||
* used, the hash will be stored even if `StoreHash` is false so that we can speed-up the rehash (but it will
|
||||
* not be used on lookups unless `StoreHash` is true).
|
||||
*
|
||||
* `GrowthPolicy` defines how the map grows and consequently how a hash value is mapped to a bucket.
|
||||
* By default the map uses `tsl::rh::power_of_two_growth_policy`. This policy keeps the number of buckets
|
||||
* to a power of two and uses a mask to map the hash to a bucket instead of the slow modulo.
|
||||
* Other growth policies are available and you may define your own growth policy,
|
||||
* check `tsl::rh::power_of_two_growth_policy` for the interface.
|
||||
*
|
||||
* `std::pair<Key, T>` must be swappable.
|
||||
*
|
||||
* `Key` and `T` must be copy and/or move constructible.
|
||||
*
|
||||
* If the destructor of `Key` or `T` throws an exception, the behaviour of the class is undefined.
|
||||
*
|
||||
* Iterators invalidation:
|
||||
* - clear, operator=, reserve, rehash: always invalidate the iterators.
|
||||
* - insert, emplace, emplace_hint, operator[]: if there is an effective insert, invalidate the iterators.
|
||||
* - erase: always invalidate the iterators.
|
||||
*/
|
||||
template<class Key,
|
||||
class T,
|
||||
class Hash = std::hash<Key>,
|
||||
class KeyEqual = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<std::pair<Key, T>>,
|
||||
bool StoreHash = false,
|
||||
class GrowthPolicy = tsl::rh::power_of_two_growth_policy<2>>
|
||||
class robin_map {
|
||||
private:
|
||||
template<typename U>
|
||||
using has_is_transparent = tsl::detail_robin_hash::has_is_transparent<U>;
|
||||
|
||||
class KeySelect {
|
||||
public:
|
||||
using key_type = Key;
|
||||
|
||||
const key_type& operator()(const std::pair<Key, T>& key_value) const noexcept {
|
||||
return key_value.first;
|
||||
}
|
||||
|
||||
key_type& operator()(std::pair<Key, T>& key_value) noexcept {
|
||||
return key_value.first;
|
||||
}
|
||||
};
|
||||
|
||||
class ValueSelect {
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
const value_type& operator()(const std::pair<Key, T>& key_value) const noexcept {
|
||||
return key_value.second;
|
||||
}
|
||||
|
||||
value_type& operator()(std::pair<Key, T>& key_value) noexcept {
|
||||
return key_value.second;
|
||||
}
|
||||
};
|
||||
|
||||
using ht = detail_robin_hash::robin_hash<std::pair<Key, T>, KeySelect, ValueSelect,
|
||||
Hash, KeyEqual, Allocator, StoreHash, GrowthPolicy>;
|
||||
|
||||
public:
|
||||
using key_type = typename ht::key_type;
|
||||
using mapped_type = T;
|
||||
using value_type = typename ht::value_type;
|
||||
using size_type = typename ht::size_type;
|
||||
using difference_type = typename ht::difference_type;
|
||||
using hasher = typename ht::hasher;
|
||||
using key_equal = typename ht::key_equal;
|
||||
using allocator_type = typename ht::allocator_type;
|
||||
using reference = typename ht::reference;
|
||||
using const_reference = typename ht::const_reference;
|
||||
using pointer = typename ht::pointer;
|
||||
using const_pointer = typename ht::const_pointer;
|
||||
using iterator = typename ht::iterator;
|
||||
using const_iterator = typename ht::const_iterator;
|
||||
|
||||
|
||||
public:
|
||||
/*
|
||||
* Constructors
|
||||
*/
|
||||
robin_map(): robin_map(ht::DEFAULT_INIT_BUCKETS_SIZE) {
|
||||
}
|
||||
|
||||
explicit robin_map(size_type bucket_count,
|
||||
const Hash& hash = Hash(),
|
||||
const KeyEqual& equal = KeyEqual(),
|
||||
const Allocator& alloc = Allocator()):
|
||||
m_ht(bucket_count, hash, equal, alloc)
|
||||
{
|
||||
}
|
||||
|
||||
robin_map(size_type bucket_count,
|
||||
const Allocator& alloc): robin_map(bucket_count, Hash(), KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
robin_map(size_type bucket_count,
|
||||
const Hash& hash,
|
||||
const Allocator& alloc): robin_map(bucket_count, hash, KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
explicit robin_map(const Allocator& alloc): robin_map(ht::DEFAULT_INIT_BUCKETS_SIZE, alloc) {
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
robin_map(InputIt first, InputIt last,
|
||||
size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE,
|
||||
const Hash& hash = Hash(),
|
||||
const KeyEqual& equal = KeyEqual(),
|
||||
const Allocator& alloc = Allocator()): robin_map(bucket_count, hash, equal, alloc)
|
||||
{
|
||||
insert(first, last);
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
robin_map(InputIt first, InputIt last,
|
||||
size_type bucket_count,
|
||||
const Allocator& alloc): robin_map(first, last, bucket_count, Hash(), KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
robin_map(InputIt first, InputIt last,
|
||||
size_type bucket_count,
|
||||
const Hash& hash,
|
||||
const Allocator& alloc): robin_map(first, last, bucket_count, hash, KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
robin_map(std::initializer_list<value_type> init,
|
||||
size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE,
|
||||
const Hash& hash = Hash(),
|
||||
const KeyEqual& equal = KeyEqual(),
|
||||
const Allocator& alloc = Allocator()):
|
||||
robin_map(init.begin(), init.end(), bucket_count, hash, equal, alloc)
|
||||
{
|
||||
}
|
||||
|
||||
robin_map(std::initializer_list<value_type> init,
|
||||
size_type bucket_count,
|
||||
const Allocator& alloc):
|
||||
robin_map(init.begin(), init.end(), bucket_count, Hash(), KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
robin_map(std::initializer_list<value_type> init,
|
||||
size_type bucket_count,
|
||||
const Hash& hash,
|
||||
const Allocator& alloc):
|
||||
robin_map(init.begin(), init.end(), bucket_count, hash, KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
robin_map& operator=(std::initializer_list<value_type> ilist) {
|
||||
m_ht.clear();
|
||||
|
||||
m_ht.reserve(ilist.size());
|
||||
m_ht.insert(ilist.begin(), ilist.end());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
allocator_type get_allocator() const { return m_ht.get_allocator(); }
|
||||
|
||||
|
||||
/*
|
||||
* Iterators
|
||||
*/
|
||||
iterator begin() noexcept { return m_ht.begin(); }
|
||||
const_iterator begin() const noexcept { return m_ht.begin(); }
|
||||
const_iterator cbegin() const noexcept { return m_ht.cbegin(); }
|
||||
|
||||
iterator end() noexcept { return m_ht.end(); }
|
||||
const_iterator end() const noexcept { return m_ht.end(); }
|
||||
const_iterator cend() const noexcept { return m_ht.cend(); }
|
||||
|
||||
|
||||
/*
|
||||
* Capacity
|
||||
*/
|
||||
bool empty() const noexcept { return m_ht.empty(); }
|
||||
size_type size() const noexcept { return m_ht.size(); }
|
||||
size_type max_size() const noexcept { return m_ht.max_size(); }
|
||||
|
||||
/*
|
||||
* Modifiers
|
||||
*/
|
||||
void clear() noexcept { m_ht.clear(); }
|
||||
|
||||
|
||||
|
||||
std::pair<iterator, bool> insert(const value_type& value) {
|
||||
return m_ht.insert(value);
|
||||
}
|
||||
|
||||
template<class P, typename std::enable_if<std::is_constructible<value_type, P&&>::value>::type* = nullptr>
|
||||
std::pair<iterator, bool> insert(P&& value) {
|
||||
return m_ht.emplace(std::forward<P>(value));
|
||||
}
|
||||
|
||||
std::pair<iterator, bool> insert(value_type&& value) {
|
||||
return m_ht.insert(std::move(value));
|
||||
}
|
||||
|
||||
|
||||
iterator insert(const_iterator hint, const value_type& value) {
|
||||
return m_ht.insert_hint(hint, value);
|
||||
}
|
||||
|
||||
template<class P, typename std::enable_if<std::is_constructible<value_type, P&&>::value>::type* = nullptr>
|
||||
iterator insert(const_iterator hint, P&& value) {
|
||||
return m_ht.emplace_hint(hint, std::forward<P>(value));
|
||||
}
|
||||
|
||||
iterator insert(const_iterator hint, value_type&& value) {
|
||||
return m_ht.insert_hint(hint, std::move(value));
|
||||
}
|
||||
|
||||
|
||||
template<class InputIt>
|
||||
void insert(InputIt first, InputIt last) {
|
||||
m_ht.insert(first, last);
|
||||
}
|
||||
|
||||
void insert(std::initializer_list<value_type> ilist) {
|
||||
m_ht.insert(ilist.begin(), ilist.end());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
template<class M>
|
||||
std::pair<iterator, bool> insert_or_assign(const key_type& k, M&& obj) {
|
||||
return m_ht.insert_or_assign(k, std::forward<M>(obj));
|
||||
}
|
||||
|
||||
template<class M>
|
||||
std::pair<iterator, bool> insert_or_assign(key_type&& k, M&& obj) {
|
||||
return m_ht.insert_or_assign(std::move(k), std::forward<M>(obj));
|
||||
}
|
||||
|
||||
template<class M>
|
||||
iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj) {
|
||||
return m_ht.insert_or_assign(hint, k, std::forward<M>(obj));
|
||||
}
|
||||
|
||||
template<class M>
|
||||
iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj) {
|
||||
return m_ht.insert_or_assign(hint, std::move(k), std::forward<M>(obj));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Due to the way elements are stored, emplace will need to move or copy the key-value once.
|
||||
* The method is equivalent to insert(value_type(std::forward<Args>(args)...));
|
||||
*
|
||||
* Mainly here for compatibility with the std::unordered_map interface.
|
||||
*/
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> emplace(Args&&... args) {
|
||||
return m_ht.emplace(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Due to the way elements are stored, emplace_hint will need to move or copy the key-value once.
|
||||
* The method is equivalent to insert(hint, value_type(std::forward<Args>(args)...));
|
||||
*
|
||||
* Mainly here for compatibility with the std::unordered_map interface.
|
||||
*/
|
||||
template<class... Args>
|
||||
iterator emplace_hint(const_iterator hint, Args&&... args) {
|
||||
return m_ht.emplace_hint(hint, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> try_emplace(const key_type& k, Args&&... args) {
|
||||
return m_ht.try_emplace(k, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> try_emplace(key_type&& k, Args&&... args) {
|
||||
return m_ht.try_emplace(std::move(k), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
iterator try_emplace(const_iterator hint, const key_type& k, Args&&... args) {
|
||||
return m_ht.try_emplace_hint(hint, k, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args) {
|
||||
return m_ht.try_emplace_hint(hint, std::move(k), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
iterator erase(iterator pos) { return m_ht.erase(pos); }
|
||||
iterator erase(const_iterator pos) { return m_ht.erase(pos); }
|
||||
iterator erase(const_iterator first, const_iterator last) { return m_ht.erase(first, last); }
|
||||
size_type erase(const key_type& key) { return m_ht.erase(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash.
|
||||
*/
|
||||
size_type erase(const key_type& key, std::size_t precalculated_hash) {
|
||||
return m_ht.erase(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type erase(const K& key) { return m_ht.erase(key); }
|
||||
|
||||
/**
|
||||
* @copydoc erase(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type erase(const K& key, std::size_t precalculated_hash) {
|
||||
return m_ht.erase(key, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void swap(robin_map& other) { other.m_ht.swap(m_ht); }
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Lookup
|
||||
*/
|
||||
T& at(const Key& key) { return m_ht.at(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
T& at(const Key& key, std::size_t precalculated_hash) { return m_ht.at(key, precalculated_hash); }
|
||||
|
||||
|
||||
const T& at(const Key& key) const { return m_ht.at(key); }
|
||||
|
||||
/**
|
||||
* @copydoc at(const Key& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
const T& at(const Key& key, std::size_t precalculated_hash) const { return m_ht.at(key, precalculated_hash); }
|
||||
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
T& at(const K& key) { return m_ht.at(key); }
|
||||
|
||||
/**
|
||||
* @copydoc at(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
T& at(const K& key, std::size_t precalculated_hash) { return m_ht.at(key, precalculated_hash); }
|
||||
|
||||
|
||||
/**
|
||||
* @copydoc at(const K& key)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
const T& at(const K& key) const { return m_ht.at(key); }
|
||||
|
||||
/**
|
||||
* @copydoc at(const K& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
const T& at(const K& key, std::size_t precalculated_hash) const { return m_ht.at(key, precalculated_hash); }
|
||||
|
||||
|
||||
|
||||
|
||||
T& operator[](const Key& key) { return m_ht[key]; }
|
||||
T& operator[](Key&& key) { return m_ht[std::move(key)]; }
|
||||
|
||||
|
||||
|
||||
|
||||
size_type count(const Key& key) const { return m_ht.count(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
size_type count(const Key& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.count(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type count(const K& key) const { return m_ht.count(key); }
|
||||
|
||||
/**
|
||||
* @copydoc count(const K& key) const
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type count(const K& key, std::size_t precalculated_hash) const { return m_ht.count(key, precalculated_hash); }
|
||||
|
||||
|
||||
|
||||
|
||||
iterator find(const Key& key) { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
iterator find(const Key& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); }
|
||||
|
||||
const_iterator find(const Key& key) const { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const Key& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
const_iterator find(const Key& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.find(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
iterator find(const K& key) { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
iterator find(const K& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const K& key)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
const_iterator find(const K& key) const { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
const_iterator find(const K& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.find(key, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bool contains(const Key& key) const { return m_ht.contains(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
bool contains(const Key& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.contains(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
bool contains(const K& key) const { return m_ht.contains(key); }
|
||||
|
||||
/**
|
||||
* @copydoc contains(const K& key) const
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
bool contains(const K& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.contains(key, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
std::pair<iterator, iterator> equal_range(const Key& key) { return m_ht.equal_range(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
std::pair<iterator, iterator> equal_range(const Key& key, std::size_t precalculated_hash) {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(const Key& key) const { return m_ht.equal_range(key); }
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const Key& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
std::pair<const_iterator, const_iterator> equal_range(const Key& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
std::pair<iterator, iterator> equal_range(const K& key) { return m_ht.equal_range(key); }
|
||||
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
std::pair<iterator, iterator> equal_range(const K& key, std::size_t precalculated_hash) {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const K& key)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
std::pair<const_iterator, const_iterator> equal_range(const K& key) const { return m_ht.equal_range(key); }
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const K& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
std::pair<const_iterator, const_iterator> equal_range(const K& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Bucket interface
|
||||
*/
|
||||
size_type bucket_count() const { return m_ht.bucket_count(); }
|
||||
size_type max_bucket_count() const { return m_ht.max_bucket_count(); }
|
||||
|
||||
|
||||
/*
|
||||
* Hash policy
|
||||
*/
|
||||
float load_factor() const { return m_ht.load_factor(); }
|
||||
|
||||
float min_load_factor() const { return m_ht.min_load_factor(); }
|
||||
float max_load_factor() const { return m_ht.max_load_factor(); }
|
||||
|
||||
/**
|
||||
* Set the `min_load_factor` to `ml`. When the `load_factor` of the map goes
|
||||
* below `min_load_factor` after some erase operations, the map will be
|
||||
* shrunk when an insertion occurs. The erase method itself never shrinks
|
||||
* the map.
|
||||
*
|
||||
* The default value of `min_load_factor` is 0.0f, the map never shrinks by default.
|
||||
*/
|
||||
void min_load_factor(float ml) { m_ht.min_load_factor(ml); }
|
||||
void max_load_factor(float ml) { m_ht.max_load_factor(ml); }
|
||||
|
||||
void rehash(size_type count) { m_ht.rehash(count); }
|
||||
void reserve(size_type count) { m_ht.reserve(count); }
|
||||
|
||||
|
||||
/*
|
||||
* Observers
|
||||
*/
|
||||
hasher hash_function() const { return m_ht.hash_function(); }
|
||||
key_equal key_eq() const { return m_ht.key_eq(); }
|
||||
|
||||
/*
|
||||
* Other
|
||||
*/
|
||||
|
||||
/**
|
||||
* Convert a const_iterator to an iterator.
|
||||
*/
|
||||
iterator mutable_iterator(const_iterator pos) {
|
||||
return m_ht.mutable_iterator(pos);
|
||||
}
|
||||
|
||||
friend bool operator==(const robin_map& lhs, const robin_map& rhs) {
|
||||
if(lhs.size() != rhs.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for(const auto& element_lhs: lhs) {
|
||||
const auto it_element_rhs = rhs.find(element_lhs.first);
|
||||
if(it_element_rhs == rhs.cend() || element_lhs.second != it_element_rhs->second) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
friend bool operator!=(const robin_map& lhs, const robin_map& rhs) {
|
||||
return !operator==(lhs, rhs);
|
||||
}
|
||||
|
||||
friend void swap(robin_map& lhs, robin_map& rhs) {
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
|
||||
private:
|
||||
ht m_ht;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Same as `tsl::robin_map<Key, T, Hash, KeyEqual, Allocator, StoreHash, tsl::rh::prime_growth_policy>`.
|
||||
*/
|
||||
template<class Key,
|
||||
class T,
|
||||
class Hash = std::hash<Key>,
|
||||
class KeyEqual = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<std::pair<Key, T>>,
|
||||
bool StoreHash = false>
|
||||
using robin_pg_map = robin_map<Key, T, Hash, KeyEqual, Allocator, StoreHash, tsl::rh::prime_growth_policy>;
|
||||
|
||||
} // end namespace tsl
|
||||
|
||||
#endif
|
||||
582
src/util/graph/robin/robin_set.h
Normal file
582
src/util/graph/robin/robin_set.h
Normal file
|
|
@ -0,0 +1,582 @@
|
|||
/**
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2017 Thibaut Goetghebuer-Planchon <tessil@gmx.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef TSL_ROBIN_SET_H
|
||||
#define TSL_ROBIN_SET_H
|
||||
|
||||
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "robin_hash.h"
|
||||
|
||||
|
||||
namespace tsl {
|
||||
|
||||
|
||||
/**
|
||||
* Implementation of a hash set using open-addressing and the robin hood hashing algorithm with backward shift deletion.
|
||||
*
|
||||
* For operations modifying the hash set (insert, erase, rehash, ...), the strong exception guarantee
|
||||
* is only guaranteed when the expression `std::is_nothrow_swappable<Key>::value &&
|
||||
* std::is_nothrow_move_constructible<Key>::value` is true, otherwise if an exception
|
||||
* is thrown during the swap or the move, the hash set may end up in a undefined state. Per the standard
|
||||
* a `Key` with a noexcept copy constructor and no move constructor also satisfies the
|
||||
* `std::is_nothrow_move_constructible<Key>::value` criterion (and will thus guarantee the
|
||||
* strong exception for the set).
|
||||
*
|
||||
* When `StoreHash` is true, 32 bits of the hash are stored alongside the values. It can improve
|
||||
* the performance during lookups if the `KeyEqual` function takes time (or engenders a cache-miss for example)
|
||||
* as we then compare the stored hashes before comparing the keys. When `tsl::rh::power_of_two_growth_policy` is used
|
||||
* as `GrowthPolicy`, it may also speed-up the rehash process as we can avoid to recalculate the hash.
|
||||
* When it is detected that storing the hash will not incur any memory penalty due to alignment (i.e.
|
||||
* `sizeof(tsl::detail_robin_hash::bucket_entry<ValueType, true>) ==
|
||||
* sizeof(tsl::detail_robin_hash::bucket_entry<ValueType, false>)`) and `tsl::rh::power_of_two_growth_policy` is
|
||||
* used, the hash will be stored even if `StoreHash` is false so that we can speed-up the rehash (but it will
|
||||
* not be used on lookups unless `StoreHash` is true).
|
||||
*
|
||||
* `GrowthPolicy` defines how the set grows and consequently how a hash value is mapped to a bucket.
|
||||
* By default the set uses `tsl::rh::power_of_two_growth_policy`. This policy keeps the number of buckets
|
||||
* to a power of two and uses a mask to set the hash to a bucket instead of the slow modulo.
|
||||
* Other growth policies are available and you may define your own growth policy,
|
||||
* check `tsl::rh::power_of_two_growth_policy` for the interface.
|
||||
*
|
||||
* `Key` must be swappable.
|
||||
*
|
||||
* `Key` must be copy and/or move constructible.
|
||||
*
|
||||
* If the destructor of `Key` throws an exception, the behaviour of the class is undefined.
|
||||
*
|
||||
* Iterators invalidation:
|
||||
* - clear, operator=, reserve, rehash: always invalidate the iterators.
|
||||
* - insert, emplace, emplace_hint, operator[]: if there is an effective insert, invalidate the iterators.
|
||||
* - erase: always invalidate the iterators.
|
||||
*/
|
||||
template<class Key,
|
||||
class Hash = std::hash<Key>,
|
||||
class KeyEqual = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<Key>,
|
||||
bool StoreHash = false,
|
||||
class GrowthPolicy = tsl::rh::power_of_two_growth_policy<2>>
|
||||
class robin_set {
|
||||
private:
|
||||
template<typename U>
|
||||
using has_is_transparent = tsl::detail_robin_hash::has_is_transparent<U>;
|
||||
|
||||
class KeySelect {
|
||||
public:
|
||||
using key_type = Key;
|
||||
|
||||
const key_type& operator()(const Key& key) const noexcept {
|
||||
return key;
|
||||
}
|
||||
|
||||
key_type& operator()(Key& key) noexcept {
|
||||
return key;
|
||||
}
|
||||
};
|
||||
|
||||
using ht = detail_robin_hash::robin_hash<Key, KeySelect, void,
|
||||
Hash, KeyEqual, Allocator, StoreHash, GrowthPolicy>;
|
||||
|
||||
public:
|
||||
using key_type = typename ht::key_type;
|
||||
using value_type = typename ht::value_type;
|
||||
using size_type = typename ht::size_type;
|
||||
using difference_type = typename ht::difference_type;
|
||||
using hasher = typename ht::hasher;
|
||||
using key_equal = typename ht::key_equal;
|
||||
using allocator_type = typename ht::allocator_type;
|
||||
using reference = typename ht::reference;
|
||||
using const_reference = typename ht::const_reference;
|
||||
using pointer = typename ht::pointer;
|
||||
using const_pointer = typename ht::const_pointer;
|
||||
using iterator = typename ht::iterator;
|
||||
using const_iterator = typename ht::const_iterator;
|
||||
|
||||
|
||||
/*
|
||||
* Constructors
|
||||
*/
|
||||
robin_set(): robin_set(ht::DEFAULT_INIT_BUCKETS_SIZE) {
|
||||
}
|
||||
|
||||
explicit robin_set(size_type bucket_count,
|
||||
const Hash& hash = Hash(),
|
||||
const KeyEqual& equal = KeyEqual(),
|
||||
const Allocator& alloc = Allocator()):
|
||||
m_ht(bucket_count, hash, equal, alloc)
|
||||
{
|
||||
}
|
||||
|
||||
robin_set(size_type bucket_count,
|
||||
const Allocator& alloc): robin_set(bucket_count, Hash(), KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
robin_set(size_type bucket_count,
|
||||
const Hash& hash,
|
||||
const Allocator& alloc): robin_set(bucket_count, hash, KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
explicit robin_set(const Allocator& alloc): robin_set(ht::DEFAULT_INIT_BUCKETS_SIZE, alloc) {
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
robin_set(InputIt first, InputIt last,
|
||||
size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE,
|
||||
const Hash& hash = Hash(),
|
||||
const KeyEqual& equal = KeyEqual(),
|
||||
const Allocator& alloc = Allocator()): robin_set(bucket_count, hash, equal, alloc)
|
||||
{
|
||||
insert(first, last);
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
robin_set(InputIt first, InputIt last,
|
||||
size_type bucket_count,
|
||||
const Allocator& alloc): robin_set(first, last, bucket_count, Hash(), KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
robin_set(InputIt first, InputIt last,
|
||||
size_type bucket_count,
|
||||
const Hash& hash,
|
||||
const Allocator& alloc): robin_set(first, last, bucket_count, hash, KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
robin_set(std::initializer_list<value_type> init,
|
||||
size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE,
|
||||
const Hash& hash = Hash(),
|
||||
const KeyEqual& equal = KeyEqual(),
|
||||
const Allocator& alloc = Allocator()):
|
||||
robin_set(init.begin(), init.end(), bucket_count, hash, equal, alloc)
|
||||
{
|
||||
}
|
||||
|
||||
robin_set(std::initializer_list<value_type> init,
|
||||
size_type bucket_count,
|
||||
const Allocator& alloc):
|
||||
robin_set(init.begin(), init.end(), bucket_count, Hash(), KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
robin_set(std::initializer_list<value_type> init,
|
||||
size_type bucket_count,
|
||||
const Hash& hash,
|
||||
const Allocator& alloc):
|
||||
robin_set(init.begin(), init.end(), bucket_count, hash, KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
robin_set& operator=(std::initializer_list<value_type> ilist) {
|
||||
m_ht.clear();
|
||||
|
||||
m_ht.reserve(ilist.size());
|
||||
m_ht.insert(ilist.begin(), ilist.end());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
allocator_type get_allocator() const { return m_ht.get_allocator(); }
|
||||
|
||||
|
||||
/*
|
||||
* Iterators
|
||||
*/
|
||||
iterator begin() noexcept { return m_ht.begin(); }
|
||||
const_iterator begin() const noexcept { return m_ht.begin(); }
|
||||
const_iterator cbegin() const noexcept { return m_ht.cbegin(); }
|
||||
|
||||
iterator end() noexcept { return m_ht.end(); }
|
||||
const_iterator end() const noexcept { return m_ht.end(); }
|
||||
const_iterator cend() const noexcept { return m_ht.cend(); }
|
||||
|
||||
|
||||
/*
|
||||
* Capacity
|
||||
*/
|
||||
bool empty() const noexcept { return m_ht.empty(); }
|
||||
size_type size() const noexcept { return m_ht.size(); }
|
||||
size_type max_size() const noexcept { return m_ht.max_size(); }
|
||||
|
||||
/*
|
||||
* Modifiers
|
||||
*/
|
||||
void clear() noexcept { m_ht.clear(); }
|
||||
|
||||
|
||||
|
||||
|
||||
std::pair<iterator, bool> insert(const value_type& value) {
|
||||
return m_ht.insert(value);
|
||||
}
|
||||
|
||||
std::pair<iterator, bool> insert(value_type&& value) {
|
||||
return m_ht.insert(std::move(value));
|
||||
}
|
||||
|
||||
iterator insert(const_iterator hint, const value_type& value) {
|
||||
return m_ht.insert_hint(hint, value);
|
||||
}
|
||||
|
||||
iterator insert(const_iterator hint, value_type&& value) {
|
||||
return m_ht.insert_hint(hint, std::move(value));
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
void insert(InputIt first, InputIt last) {
|
||||
m_ht.insert(first, last);
|
||||
}
|
||||
|
||||
void insert(std::initializer_list<value_type> ilist) {
|
||||
m_ht.insert(ilist.begin(), ilist.end());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Due to the way elements are stored, emplace will need to move or copy the key-value once.
|
||||
* The method is equivalent to insert(value_type(std::forward<Args>(args)...));
|
||||
*
|
||||
* Mainly here for compatibility with the std::unordered_map interface.
|
||||
*/
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> emplace(Args&&... args) {
|
||||
return m_ht.emplace(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Due to the way elements are stored, emplace_hint will need to move or copy the key-value once.
|
||||
* The method is equivalent to insert(hint, value_type(std::forward<Args>(args)...));
|
||||
*
|
||||
* Mainly here for compatibility with the std::unordered_map interface.
|
||||
*/
|
||||
template<class... Args>
|
||||
iterator emplace_hint(const_iterator hint, Args&&... args) {
|
||||
return m_ht.emplace_hint(hint, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
|
||||
|
||||
iterator erase(iterator pos) { return m_ht.erase(pos); }
|
||||
iterator erase(const_iterator pos) { return m_ht.erase(pos); }
|
||||
iterator erase(const_iterator first, const_iterator last) { return m_ht.erase(first, last); }
|
||||
size_type erase(const key_type& key) { return m_ht.erase(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash.
|
||||
*/
|
||||
size_type erase(const key_type& key, std::size_t precalculated_hash) {
|
||||
return m_ht.erase(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type erase(const K& key) { return m_ht.erase(key); }
|
||||
|
||||
/**
|
||||
* @copydoc erase(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type erase(const K& key, std::size_t precalculated_hash) {
|
||||
return m_ht.erase(key, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void swap(robin_set& other) { other.m_ht.swap(m_ht); }
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Lookup
|
||||
*/
|
||||
size_type count(const Key& key) const { return m_ht.count(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
size_type count(const Key& key, std::size_t precalculated_hash) const { return m_ht.count(key, precalculated_hash); }
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type count(const K& key) const { return m_ht.count(key); }
|
||||
|
||||
/**
|
||||
* @copydoc count(const K& key) const
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type count(const K& key, std::size_t precalculated_hash) const { return m_ht.count(key, precalculated_hash); }
|
||||
|
||||
|
||||
|
||||
|
||||
iterator find(const Key& key) { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
iterator find(const Key& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); }
|
||||
|
||||
const_iterator find(const Key& key) const { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const Key& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
const_iterator find(const Key& key, std::size_t precalculated_hash) const { return m_ht.find(key, precalculated_hash); }
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
iterator find(const K& key) { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
iterator find(const K& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const K& key)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
const_iterator find(const K& key) const { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
const_iterator find(const K& key, std::size_t precalculated_hash) const { return m_ht.find(key, precalculated_hash); }
|
||||
|
||||
|
||||
|
||||
|
||||
bool contains(const Key& key) const { return m_ht.contains(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
bool contains(const Key& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.contains(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
bool contains(const K& key) const { return m_ht.contains(key); }
|
||||
|
||||
/**
|
||||
* @copydoc contains(const K& key) const
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
bool contains(const K& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.contains(key, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
std::pair<iterator, iterator> equal_range(const Key& key) { return m_ht.equal_range(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
std::pair<iterator, iterator> equal_range(const Key& key, std::size_t precalculated_hash) {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(const Key& key) const { return m_ht.equal_range(key); }
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const Key& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
std::pair<const_iterator, const_iterator> equal_range(const Key& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
std::pair<iterator, iterator> equal_range(const K& key) { return m_ht.equal_range(key); }
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
std::pair<iterator, iterator> equal_range(const K& key, std::size_t precalculated_hash) {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const K& key)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
std::pair<const_iterator, const_iterator> equal_range(const K& key) const { return m_ht.equal_range(key); }
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const K& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
std::pair<const_iterator, const_iterator> equal_range(const K& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Bucket interface
|
||||
*/
|
||||
size_type bucket_count() const { return m_ht.bucket_count(); }
|
||||
size_type max_bucket_count() const { return m_ht.max_bucket_count(); }
|
||||
|
||||
|
||||
/*
|
||||
* Hash policy
|
||||
*/
|
||||
float load_factor() const { return m_ht.load_factor(); }
|
||||
|
||||
float min_load_factor() const { return m_ht.min_load_factor(); }
|
||||
float max_load_factor() const { return m_ht.max_load_factor(); }
|
||||
|
||||
/**
|
||||
* Set the `min_load_factor` to `ml`. When the `load_factor` of the set goes
|
||||
* below `min_load_factor` after some erase operations, the set will be
|
||||
* shrunk when an insertion occurs. The erase method itself never shrinks
|
||||
* the set.
|
||||
*
|
||||
* The default value of `min_load_factor` is 0.0f, the set never shrinks by default.
|
||||
*/
|
||||
void min_load_factor(float ml) { m_ht.min_load_factor(ml); }
|
||||
void max_load_factor(float ml) { m_ht.max_load_factor(ml); }
|
||||
|
||||
void rehash(size_type count) { m_ht.rehash(count); }
|
||||
void reserve(size_type count) { m_ht.reserve(count); }
|
||||
|
||||
|
||||
/*
|
||||
* Observers
|
||||
*/
|
||||
hasher hash_function() const { return m_ht.hash_function(); }
|
||||
key_equal key_eq() const { return m_ht.key_eq(); }
|
||||
|
||||
|
||||
/*
|
||||
* Other
|
||||
*/
|
||||
|
||||
/**
|
||||
* Convert a const_iterator to an iterator.
|
||||
*/
|
||||
iterator mutable_iterator(const_iterator pos) {
|
||||
return m_ht.mutable_iterator(pos);
|
||||
}
|
||||
|
||||
friend bool operator==(const robin_set& lhs, const robin_set& rhs) {
|
||||
if(lhs.size() != rhs.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for(const auto& element_lhs: lhs) {
|
||||
const auto it_element_rhs = rhs.find(element_lhs);
|
||||
if(it_element_rhs == rhs.cend()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
friend bool operator!=(const robin_set& lhs, const robin_set& rhs) {
|
||||
return !operator==(lhs, rhs);
|
||||
}
|
||||
|
||||
friend void swap(robin_set& lhs, robin_set& rhs) {
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
|
||||
private:
|
||||
ht m_ht;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Same as `tsl::robin_set<Key, Hash, KeyEqual, Allocator, StoreHash, tsl::rh::prime_growth_policy>`.
|
||||
*/
|
||||
template<class Key,
|
||||
class Hash = std::hash<Key>,
|
||||
class KeyEqual = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<Key>,
|
||||
bool StoreHash = false>
|
||||
using robin_pg_set = robin_set<Key, Hash, KeyEqual, Allocator, StoreHash, tsl::rh::prime_growth_policy>;
|
||||
|
||||
} // end namespace tsl
|
||||
|
||||
#endif
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue