1
0
Fork 0
arangodb/lib/Basics/Traverser.h

487 lines
17 KiB
C++

////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Michael Hackstein
////////////////////////////////////////////////////////////////////////////////
#ifndef ARANGODB_BASICS_TRAVERSER_H
#define ARANGODB_BASICS_TRAVERSER_H 1
#include "Basics/Common.h"
#include "Basics/Exceptions.h"
#include "Basics/Mutex.h"
#include "Basics/MutexLocker.h"
#include <deque>
#include <stack>
namespace arangodb {
namespace basics {
template <typename edgeIdentifier, typename vertexIdentifier>
struct EnumeratedPath {
std::vector<edgeIdentifier> edges;
std::vector<vertexIdentifier> vertices;
EnumeratedPath() {}
};
template <typename edgeIdentifier, typename vertexIdentifier>
struct VertexGetter {
VertexGetter() = default;
virtual ~VertexGetter() = default;
virtual bool getVertex(edgeIdentifier const&, vertexIdentifier const&, size_t,
vertexIdentifier&) = 0;
virtual void setStartVertex(std::string const&) {}
};
template <typename edgeIdentifier, typename vertexIdentifier, typename edgeItem>
struct EdgeGetter {
EdgeGetter() = default;
virtual ~EdgeGetter() = default;
virtual void getEdge(vertexIdentifier const&, std::vector<edgeIdentifier>&,
edgeItem*&, size_t&) = 0;
virtual void getAllEdges(vertexIdentifier const&,
std::vector<edgeIdentifier>&, size_t) = 0;
};
template <typename edgeIdentifier, typename vertexIdentifier, typename edgeItem>
class PathEnumerator {
protected:
//////////////////////////////////////////////////////////////////////////////
/// @brief Function to get the next edge from index.
//////////////////////////////////////////////////////////////////////////////
EdgeGetter<edgeIdentifier, vertexIdentifier, edgeItem>* _edgeGetter;
//////////////////////////////////////////////////////////////////////////////
/// @brief Function to get the connected vertex from index.
/// Returns false if the vertex does not match the filter
//////////////////////////////////////////////////////////////////////////////
VertexGetter<edgeIdentifier, vertexIdentifier>* _vertexGetter;
//////////////////////////////////////////////////////////////////////////////
/// @brief Indicates if we issue next() the first time.
/// It shall return an empty path in this case.
//////////////////////////////////////////////////////////////////////////////
bool _isFirst;
//////////////////////////////////////////////////////////////////////////////
/// @brief Maximal path length which should be enumerated.
//////////////////////////////////////////////////////////////////////////////
size_t _maxDepth;
//////////////////////////////////////////////////////////////////////////////
/// @brief List of the last path is used to
//////////////////////////////////////////////////////////////////////////////
EnumeratedPath<edgeIdentifier, vertexIdentifier> _enumeratedPath;
public:
PathEnumerator(
EdgeGetter<edgeIdentifier, vertexIdentifier, edgeItem>* edgeGetter,
VertexGetter<edgeIdentifier, vertexIdentifier>* vertexGetter,
vertexIdentifier const& startVertex, size_t maxDepth)
: _edgeGetter(edgeGetter), _vertexGetter(vertexGetter), _isFirst(true), _maxDepth(maxDepth) {
_enumeratedPath.vertices.push_back(startVertex);
TRI_ASSERT(_enumeratedPath.vertices.size() == 1);
}
virtual ~PathEnumerator() {}
//////////////////////////////////////////////////////////////////////////////
/// @brief Get the next Path element from the traversal.
//////////////////////////////////////////////////////////////////////////////
virtual const EnumeratedPath<edgeIdentifier, vertexIdentifier>& next() = 0;
//////////////////////////////////////////////////////////////////////////////
/// @brief Prunes the current path prefix, the next function should not return
/// any path having this prefix anymore.
//////////////////////////////////////////////////////////////////////////////
virtual void prune() = 0;
};
template <typename edgeIdentifier, typename vertexIdentifier, typename edgeItem>
class DepthFirstEnumerator : public PathEnumerator<edgeIdentifier, vertexIdentifier, edgeItem> {
private:
//////////////////////////////////////////////////////////////////////////////
/// @brief The pointers returned for edge indexes on this path. Used to
/// continue
/// the search on respective levels.
//////////////////////////////////////////////////////////////////////////////
std::stack<edgeItem*> _lastEdges;
//////////////////////////////////////////////////////////////////////////////
/// @brief An internal index for the edge collection used at each depth level
//////////////////////////////////////////////////////////////////////////////
std::stack<size_t> _lastEdgesIdx;
public:
DepthFirstEnumerator(
EdgeGetter<edgeIdentifier, vertexIdentifier, edgeItem>* edgeGetter,
VertexGetter<edgeIdentifier, vertexIdentifier>* vertexGetter,
vertexIdentifier const& startVertex, size_t maxDepth)
: PathEnumerator<edgeIdentifier, vertexIdentifier, edgeItem>(edgeGetter, vertexGetter, startVertex, maxDepth) {
_lastEdges.push(nullptr);
_lastEdgesIdx.push(0);
TRI_ASSERT(_lastEdges.size() == 1);
}
~DepthFirstEnumerator() {}
//////////////////////////////////////////////////////////////////////////////
/// @brief Get the next Path element from the traversal.
//////////////////////////////////////////////////////////////////////////////
EnumeratedPath<edgeIdentifier, vertexIdentifier> const& next() override {
if (this->_isFirst) {
this->_isFirst = false;
return this->_enumeratedPath;
}
if (this->_enumeratedPath.edges.size() == this->_maxDepth) {
// we have reached the maximal search depth.
// We can prune this path and go to the next.
prune();
}
// Avoid tail recusion. May crash on high search depth
while (true) {
if (_lastEdges.empty()) {
this->_enumeratedPath.edges.clear();
this->_enumeratedPath.vertices.clear();
return this->_enumeratedPath;
}
this->_edgeGetter->getEdge(this->_enumeratedPath.vertices.back(),
this->_enumeratedPath.edges, _lastEdges.top(),
_lastEdgesIdx.top());
if (_lastEdges.top() != nullptr) {
// Could continue the path in the next depth.
_lastEdges.push(nullptr);
_lastEdgesIdx.push(0);
vertexIdentifier v;
bool isValid = this->_vertexGetter->getVertex(
this->_enumeratedPath.edges.back(), this->_enumeratedPath.vertices.back(),
this->_enumeratedPath.vertices.size(), v);
this->_enumeratedPath.vertices.push_back(v);
TRI_ASSERT(this->_enumeratedPath.vertices.size() ==
this->_enumeratedPath.edges.size() + 1);
if (isValid) {
return this->_enumeratedPath;
}
} else {
if (this->_enumeratedPath.edges.empty()) {
// We are done with enumerating paths
this->_enumeratedPath.edges.clear();
this->_enumeratedPath.vertices.clear();
return this->_enumeratedPath;
}
}
// This either modifies the stack or _lastEdges is empty.
// This will return in next depth
prune();
}
}
//////////////////////////////////////////////////////////////////////////////
/// @brief Prunes the current path prefix, the next function should not return
/// any path having this prefix anymore.
//////////////////////////////////////////////////////////////////////////////
void prune() override {
if (!_lastEdges.empty()) {
_lastEdges.pop();
_lastEdgesIdx.pop();
if (!this->_enumeratedPath.edges.empty()) {
this->_enumeratedPath.edges.pop_back();
this->_enumeratedPath.vertices.pop_back();
}
}
}
};
template <typename edgeIdentifier, typename vertexIdentifier, typename edgeItem>
class BreadthFirstEnumerator : public PathEnumerator<edgeIdentifier, vertexIdentifier, edgeItem> {
private:
//////////////////////////////////////////////////////////////////////////////
/// @brief One entry in the schreier vector
//////////////////////////////////////////////////////////////////////////////
struct PathStep {
size_t sourceIdx;
edgeIdentifier edge;
vertexIdentifier vertex;
private:
PathStep() {}
public:
PathStep(vertexIdentifier const& vertex) : sourceIdx(0), vertex(vertex) {}
PathStep(size_t sourceIdx, edgeIdentifier const& edge,
vertexIdentifier const& vertex)
: sourceIdx(sourceIdx), edge(edge), vertex(vertex) {}
};
//////////////////////////////////////////////////////////////////////////////
/// @brief Struct to hold all information required to get the list of
/// connected edges
//////////////////////////////////////////////////////////////////////////////
struct NextStep {
size_t sourceIdx;
vertexIdentifier vertex;
private:
NextStep() {}
public:
NextStep(size_t sourceIdx, vertexIdentifier const& vertex)
: sourceIdx(sourceIdx), vertex(vertex) {}
};
//////////////////////////////////////////////////////////////////////////////
/// @brief schreier vector to store the visited vertices
//////////////////////////////////////////////////////////////////////////////
std::vector<PathStep*> _schreier;
//////////////////////////////////////////////////////////////////////////////
/// @brief Next free index in schreier vector.
//////////////////////////////////////////////////////////////////////////////
size_t _schreierIndex;
//////////////////////////////////////////////////////////////////////////////
/// @brief Position of the last returned value in the schreier vector
//////////////////////////////////////////////////////////////////////////////
size_t _lastReturned;
//////////////////////////////////////////////////////////////////////////////
/// @brief Vector to store where to continue search on next depth
//////////////////////////////////////////////////////////////////////////////
std::vector<NextStep*> _nextDepth;
//////////////////////////////////////////////////////////////////////////////
/// @brief Vector storing the position at current search depth
//////////////////////////////////////////////////////////////////////////////
std::vector<NextStep*> _toSearch;
//////////////////////////////////////////////////////////////////////////////
/// @brief Vector storing the position at current search depth
//////////////////////////////////////////////////////////////////////////////
std::vector<edgeIdentifier> _tmpEdges;
//////////////////////////////////////////////////////////////////////////////
/// @brief Marker for the search depth. Used to abort searching.
//////////////////////////////////////////////////////////////////////////////
size_t _currentDepth;
//////////////////////////////////////////////////////////////////////////////
/// @brief position in _toSerach. If this is >= _toSearch.size() we are done
/// with this depth.
//////////////////////////////////////////////////////////////////////////////
size_t _toSearchPos;
public:
BreadthFirstEnumerator(
EdgeGetter<edgeIdentifier, vertexIdentifier, edgeItem>* edgeGetter,
VertexGetter<edgeIdentifier, vertexIdentifier>* vertexGetter,
vertexIdentifier const& startVertex, size_t maxDepth)
: PathEnumerator<edgeIdentifier, vertexIdentifier, edgeItem>(
edgeGetter, vertexGetter, startVertex, maxDepth),
_schreierIndex(1),
_lastReturned(0),
_currentDepth(0),
_toSearchPos(0) {
auto step = std::make_unique<PathStep>(startVertex);
_schreier.emplace_back(step.get());
step.release();
auto next = std::make_unique<NextStep>(0, startVertex);
_toSearch.emplace_back(next.get());
next.release();
if (this->_maxDepth > 0) {
// We build the search values
// only for one depth less
this->_maxDepth--;
}
}
~BreadthFirstEnumerator() {
for (auto& it : _schreier) {
delete it;
}
for (auto& it : _toSearch) {
delete it;
}
for (auto& it : _nextDepth) {
delete it;
}
}
//////////////////////////////////////////////////////////////////////////////
/// @brief Get the next Path element from the traversal.
//////////////////////////////////////////////////////////////////////////////
EnumeratedPath<edgeIdentifier, vertexIdentifier> const& next() override {
if (_lastReturned < _schreierIndex) {
// We still have something on our stack.
// Paths have been read but not returned.
computeEnumeratedPath(_lastReturned++);
return this->_enumeratedPath;
}
// Avoid large call stacks.
// Loop will be left if we are either finished
// with searching.
// Or we found vertices in the next depth for
// a vertex.
while (true) {
if (_toSearchPos >= _toSearch.size()) {
// This depth is done. GoTo next
if (_nextDepth.empty()) {
// That's it. we are done.
this->_enumeratedPath.edges.clear();
this->_enumeratedPath.vertices.clear();
return this->_enumeratedPath;
}
// Save copies:
// We clear current
// we swap current and next.
// So now current is filled
// and next is empty.
for (auto& it : _toSearch) {
delete it;
}
_toSearch.clear();
_toSearchPos = 0;
_toSearch.swap(_nextDepth);
_currentDepth++;
TRI_ASSERT(_toSearchPos < _toSearch.size());
TRI_ASSERT(_nextDepth.empty());
TRI_ASSERT(_currentDepth <= this->_maxDepth);
}
// This access is always safe.
// If not it should have bailed out before.
TRI_ASSERT(_toSearchPos < _toSearch.size());
_tmpEdges.clear();
auto next = _toSearch[_toSearchPos++];
TRI_ASSERT(next != nullptr);
this->_edgeGetter->getAllEdges(next->vertex, _tmpEdges, _currentDepth);
if (!_tmpEdges.empty()) {
bool didInsert = false;
for (auto const& e : _tmpEdges) {
vertexIdentifier v;
bool valid =
this->_vertexGetter->getVertex(e, next->vertex, _currentDepth, v);
if (valid) {
auto step = std::make_unique<PathStep>(next->sourceIdx, e, v);
_schreier.emplace_back(step.get());
step.release();
if (_currentDepth < this->_maxDepth) {
auto nextSearch = std::make_unique<NextStep>(_schreierIndex, v);
_nextDepth.emplace_back(nextSearch.get());
nextSearch.release();
}
_schreierIndex++;
didInsert = true;
}
}
if (didInsert) {
// We exit the loop here.
// _schreierIndex is moved forward
break;
}
}
// Nothing found for this vertex.
// _toSearchPos is increased so
// we are not stuck in an endless loop
}
// _lastReturned points to the last used
// entry. We compute the path to it
// and increase the schreierIndex to point
// to the next free position.
computeEnumeratedPath(_lastReturned++);
return this->_enumeratedPath;
}
//////////////////////////////////////////////////////////////////////////////
/// @brief Prunes the current path prefix, the next function should not return
/// any path having this prefix anymore.
//////////////////////////////////////////////////////////////////////////////
void prune() override {
if (!_nextDepth.empty()) {
_nextDepth.pop_back();
}
}
private:
//////////////////////////////////////////////////////////////////////////////
/// @brief Build the enumerated path for the given index in the schreier
/// vector.
//////////////////////////////////////////////////////////////////////////////
void computeEnumeratedPath(size_t index) {
TRI_ASSERT(index < _schreier.size());
std::vector<edgeIdentifier> edges;
std::vector<vertexIdentifier> vertices;
PathStep* current = nullptr;
while (index != 0) {
current = _schreier[index];
vertices.push_back(current->vertex);
edges.push_back(current->edge);
index = current->sourceIdx;
}
current = _schreier[0];
vertices.push_back(current->vertex);
// Computed path. Insert it into the path enumerator.
this->_enumeratedPath.edges.clear();
this->_enumeratedPath.vertices.clear();
std::copy(vertices.rbegin(), vertices.rend(), std::back_inserter(this->_enumeratedPath.vertices));
std::copy(edges.rbegin(), edges.rend(), std::back_inserter(this->_enumeratedPath.edges));
}
};
}
}
#endif