Fix warnings all over the codebase.

Reviewers: florijan, buda

Reviewed By: buda

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D146
This commit is contained in:
Dominik Gleich 2017-03-21 13:48:35 +01:00
parent 88a96c2ebd
commit 6be2399ad1
14 changed files with 506 additions and 463 deletions

View File

@ -37,9 +37,9 @@ class Server
public:
Server(Socket &&socket, Dbms &dbms, QueryEngine<OutputStream> &query_engine)
: socket_(std::forward<Socket>(socket)),
dbms_(dbms),
: dbms_(dbms),
query_engine_(query_engine),
socket_(std::forward<Socket>(socket)),
logger_(logging::log->logger("communication::Server")) {
event_.data.fd = socket_;

View File

@ -74,7 +74,7 @@ class ConcurrentList {
IteratorBase(const IteratorBase &) = delete;
IteratorBase(IteratorBase &&other)
: list(other.list), curr(other.curr), prev(other.prev) {
: list(other.list), prev(other.prev), curr(other.curr) {
other.list = nullptr;
other.curr = nullptr;
other.prev = nullptr;

View File

@ -771,7 +771,7 @@ class SkipList : private Lockable<lock_t> {
}
Node *preds[H];
auto level = find_path(this, H - 1, item, preds);
find_path(this, H - 1, item, preds);
return std::make_pair(ReverseIterator(this, preds[0], preds), true);
}

View File

@ -78,7 +78,7 @@ class RhBase {
}
}
IteratorBase(const RhBase *map, size_t start)
: map(map), index(start), advanced(0) {}
: map(map), advanced(0), index(start) {}
const RhBase *map;

View File

@ -1,16 +1,20 @@
#include <functional>
#include "data_structures/map/rh_common.hpp"
#include "utils/assert.hpp"
#include "utils/crtp.hpp"
#include "utils/option_ptr.hpp"
// HashMap with RobinHood collision resolution policy.
// Single threaded.
// Entrys are saved as pointers alligned to 8B.
// Entrys must know thers key.
// D must have method const K & get_key()
// K must be comparable with ==.
// HashMap behaves as if it isn't owner of entrys.
/**
* HashMap with RobinHood collision resolution policy.
* Single threaded.
* Entries are saved as pointers alligned to 8B.
* Entries must know thers key.
* D must have method const K & get_key()
* K must be comparable with ==.
* HashMap behaves as if it isn't owner of entries.
* BE CAREFUL - this structure assumes that the pointer to Data is 8-alligned!
*/
template <class K, class D, size_t init_size_pow2 = 2>
class RhHashMap : public RhBase<K, D, init_size_pow2> {
typedef RhBase<K, D, init_size_pow2> base;
@ -69,6 +73,8 @@ class RhHashMap : public RhBase<K, D, init_size_pow2> {
// Inserts element. Returns true if element wasn't in the map.
bool insert(D *data) {
permanent_assert(!(((uint64_t) static_cast<void *>(data) & 7)),
"Data is not 8-alligned.");
if (count < capacity) {
size_t mask = this->mask();
auto key = std::ref(data->get_key());

View File

@ -4,39 +4,45 @@
#include <functional>
#include "data_structures/map/rh_common.hpp"
#include "utils/assert.hpp"
#include "utils/assert.hpp"
#include "utils/crtp.hpp"
#include "utils/likely.hpp"
#include "utils/option.hpp"
#include "utils/option_ptr.hpp"
// HashMultiMap with RobinHood collision resolution policy.
// Single threaded.
// Entrys are POINTERS alligned to 8B.
// Entrys must know thers key.
// D must have method K& get_key()
// K must be comparable with ==.
// HashMap behaves as if it isn't owner of entrys.
//
// Main idea of this MultiMap is a tweak of logic in RobinHood.
// RobinHood offset from prefered slot is equal to the number of slots between
// [current slot and prefered slot>.
// While in this flavour of "multi RobinHood" offset from prefered slot is equal
// to the number of different keyed elements between his current slot and
// prefered slot.
// In the following examples slots will have keys as caracters. So something
// like this: |a| will mean that in this slot there is data with key 'a'.
// like this: | | will mean empty slot.
// like this: |...| will mean arbitary number of slots.
// like this: |b:a| will mean that a want's to be in slot but b is in't.
//
// Examples:
// |...|a:a|...| => off(a) = 0
// |...|a:a|a|...|a|...| => off(a) = 0
// |...|b:a|a|...| => off(a) = 1
// |...|b:a|b|...|b|a|...| => off(a) = 1
// |...|c:a|b|a|...| => off(a) = 2
// |...|c:a|c|...|c|b|...|b||a|...|a|...| => off(a) = 2
// ...
/**
* HashMultiMap with RobinHood collision resolution policy.
* Single threaded.
* Entries are POINTERS alligned to 8B.
* Entries must know thers key.
* D must have method K& get_key()
* K must be comparable with ==.
* HashMap behaves as if it isn't owner of entries.
* BE CAREFUL - this structure assumes that the pointer to Data is
* 8-alligned!
*
* Main idea of this MultiMap is a tweak of logic in RobinHood.
* RobinHood offset from prefered slot is equal to the number of slots between
* [current slot and prefered slot>.
* While in this flavour of "multi RobinHood" offset from prefered slot is
* equal
* to the number of different keyed elements between his current slot and
* prefered slot.
* In the following examples slots will have keys as caracters. So something
* like this: |a| will mean that in this slot there is data with key 'a'.
* like this: | | will mean empty slot.
* like this: |...| will mean arbitary number of slots.
* like this: |b:a| will mean that a want's to be in slot but b is in't.
*
* Examples:
* |...|a:a|...| => off(a) = 0
* |...|a:a|a|...|a|...| => off(a) = 0
* |...|b:a|a|...| => off(a) = 1
* |...|b:a|b|...|b|a|...| => off(a) = 1
* |...|c:a|b|a|...| => off(a) = 2
* |...|c:a|c|...|c|b|...|b||a|...|a|...| => off(a) = 2
* ...
*/
template <class K, class D, size_t init_size_pow2 = 2>
class RhHashMultiMap : public RhBase<K, D, init_size_pow2> {
typedef RhBase<K, D, init_size_pow2> base;
@ -124,6 +130,8 @@ class RhHashMultiMap : public RhBase<K, D, init_size_pow2> {
// Inserts element with the given key.
void add(const K &key_in, D *data) {
debug_assert(key_in == data->get_key(), "Key doesn't match data key.");
permanent_assert(!((uint64_t)(static_cast<void *>(data)) & 7),
"Data is not 8-alligned.");
if (count < capacity) {
auto key = std::ref(key_in);

View File

@ -75,7 +75,6 @@ static void FindFromRBegin(benchmark::State &state) {
auto accessor = skiplist.access();
auto rbegin = accessor.rbegin();
auto rend = accessor.rend();
state.ResumeTiming();

View File

@ -29,10 +29,9 @@ int main(int argc, char **argv) {
auto tests = dataset["benchmark_queries"].as<std::vector<std::string>>();
for (auto &test : tests) {
auto *benchmark =
benchmark::RegisterBenchmark(test.c_str(), BM_Strip, preprocess, test)
->Range(1, 1)
->Complexity(benchmark::oN);
benchmark::RegisterBenchmark(test.c_str(), BM_Strip, preprocess, test)
->Range(1, 1)
->Complexity(benchmark::oN);
}
benchmark::Initialize(&argc, argv);

View File

@ -25,188 +25,158 @@ constexpr int max_no_threads = 8;
using std::cout;
using std::endl;
using map_t = ConcurrentMap<int, int>;
using set_t = ConcurrentSet<int>;
using map_t = ConcurrentMap<int, int>;
using set_t = ConcurrentSet<int>;
using multiset_t = ConcurrentMultiSet<int>;
using multimap_t = ConcurrentMultiMap<int, int>;
using namespace std::chrono_literals;
// Returns uniform random size_t generator from range [0,n>
auto rand_gen(size_t n)
{
std::default_random_engine generator;
std::uniform_int_distribution<size_t> distribution(0, n - 1);
return std::bind(distribution, generator);
auto rand_gen(size_t n) {
std::default_random_engine generator;
std::uniform_int_distribution<size_t> distribution(0, n - 1);
return std::bind(distribution, generator);
}
// Returns random bool generator with distribution of 1 true for n false.
auto rand_gen_bool(size_t n = 1)
{
auto gen = rand_gen(n + 1);
return [=]() mutable { return gen() == 0; };
auto rand_gen_bool(size_t n = 1) {
auto gen = rand_gen(n + 1);
return [=]() mutable { return gen() == 0; };
}
// Checks for all owned keys if there data is data.
template <typename S>
void check_present_same(typename S::Accessor &acc, size_t data,
std::vector<size_t> &owned)
{
for (auto num : owned)
{
permanent_assert(acc.find(num)->second == data,
"My data is present and my");
}
std::vector<size_t> &owned) {
for (auto num : owned) {
permanent_assert(acc.find(num)->second == data,
"My data is present and my");
}
}
// Checks for all owned.second keys if there data is owned.first.
template <typename S>
void check_present_same(typename S::Accessor &acc,
std::pair<size_t, std::vector<size_t>> &owned)
{
check_present_same<S>(acc, owned.first, owned.second);
std::pair<size_t, std::vector<size_t>> &owned) {
check_present_same<S>(acc, owned.first, owned.second);
}
// Checks if reported size and traversed size are equal to given size.
template <typename S>
void check_size_list(S &acc, long long size)
{
// check size
void check_size_list(S &acc, long long size) {
// check size
permanent_assert(acc.size() == size, "Size should be " << size
<< ", but size is "
<< acc.size());
permanent_assert(acc.size() == size,
"Size should be " << size << ", but size is " << acc.size());
// check count
// check count
size_t iterator_counter = 0;
size_t iterator_counter = 0;
for (auto elem : acc)
{
++iterator_counter;
}
permanent_assert(iterator_counter == size, "Iterator count should be "
<< size << ", but size is "
<< iterator_counter);
for ([[gnu::unused]] auto elem : acc) {
++iterator_counter;
}
permanent_assert(iterator_counter == size, "Iterator count should be "
<< size << ", but size is "
<< iterator_counter);
}
template <typename S>
void check_size(typename S::Accessor &acc, long long size)
{
// check size
void check_size(typename S::Accessor &acc, long long size) {
// check size
permanent_assert(acc.size() == size, "Size should be " << size
<< ", but size is "
<< acc.size());
permanent_assert(acc.size() == size,
"Size should be " << size << ", but size is " << acc.size());
// check count
// check count
size_t iterator_counter = 0;
size_t iterator_counter = 0;
for (auto elem : acc)
{
++iterator_counter;
}
permanent_assert(iterator_counter == size, "Iterator count should be "
<< size << ", but size is "
<< iterator_counter);
for ([[gnu::unused]] auto elem : acc) {
++iterator_counter;
}
permanent_assert(iterator_counter == size, "Iterator count should be "
<< size << ", but size is "
<< iterator_counter);
}
// Checks if order in list is maintened. It expects map
template <typename S>
void check_order(typename S::Accessor &acc)
{
if (acc.begin() != acc.end())
{
auto last = acc.begin()->first;
for (auto elem : acc)
{
if (!(last <= elem))
std::cout << "Order isn't maintained. Before was: " << last
<< " next is " << elem.first << "\n";
last = elem.first;
}
void check_order(typename S::Accessor &acc) {
if (acc.begin() != acc.end()) {
auto last = acc.begin()->first;
for (auto elem : acc) {
if (!(last <= elem))
std::cout << "Order isn't maintained. Before was: " << last
<< " next is " << elem.first << "\n";
last = elem.first;
}
}
}
void check_zero(size_t key_range, long array[], const char *str)
{
for (int i = 0; i < key_range; i++)
{
permanent_assert(array[i] == 0,
str << " doesn't hold it's guarantees. It has "
<< array[i] << " extra elements.");
}
void check_zero(size_t key_range, long array[], const char *str) {
for (int i = 0; i < key_range; i++) {
permanent_assert(array[i] == 0,
str << " doesn't hold it's guarantees. It has " << array[i]
<< " extra elements.");
}
}
void check_set(DynamicBitset<> &db, std::vector<bool> &set)
{
for (int i = 0; i < set.size(); i++)
{
permanent_assert(!(set[i] ^ db.at(i)),
"Set constraints aren't fullfilled.");
}
void check_set(DynamicBitset<> &db, std::vector<bool> &set) {
for (int i = 0; i < set.size(); i++) {
permanent_assert(!(set[i] ^ db.at(i)),
"Set constraints aren't fullfilled.");
}
}
// Checks multiIterator and iterator guarantees
void check_multi_iterator(multimap_t::Accessor &accessor, size_t key_range,
long set[])
{
for (int i = 0; i < key_range; i++)
{
auto it = accessor.find(i);
auto it_m = accessor.find_multi(i);
permanent_assert(
!(it_m != accessor.end(i) && it == accessor.end()),
"MultiIterator ended before Iterator. Set: " << set[i]);
permanent_assert(
!(it_m == accessor.end(i) && it != accessor.end()),
"Iterator ended before MultiIterator. Set: " << set[i]);
permanent_assert((it_m == accessor.end(i) && it == accessor.end()) ||
it->second == it_m->second,
"MultiIterator didn't found the same "
"first element. Set: "
<< set[i]);
if (set[i] > 0)
{
for (int j = 0; j < set[i]; j++)
{
permanent_assert(
it->second == it_m->second,
"MultiIterator and iterator aren't on the same "
"element.");
permanent_assert(it_m->first == i,
"MultiIterator is showing illegal data") it++;
it_m++;
}
}
permanent_assert(
it_m == accessor.end(i),
"There is more data than it should be in MultiIterator. "
<< it_m->first << "\n");
long set[]) {
for (int i = 0; i < key_range; i++) {
auto it = accessor.find(i);
auto it_m = accessor.find_multi(i);
permanent_assert(!(it_m != accessor.end(i) && it == accessor.end()),
"MultiIterator ended before Iterator. Set: " << set[i]);
permanent_assert(!(it_m == accessor.end(i) && it != accessor.end()),
"Iterator ended before MultiIterator. Set: " << set[i]);
permanent_assert((it_m == accessor.end(i) && it == accessor.end()) ||
it->second == it_m->second,
"MultiIterator didn't found the same "
"first element. Set: "
<< set[i]);
if (set[i] > 0) {
for (int j = 0; j < set[i]; j++) {
permanent_assert(it->second == it_m->second,
"MultiIterator and iterator aren't on the same "
"element.");
permanent_assert(it_m->first == i,
"MultiIterator is showing illegal data") it++;
it_m++;
}
}
permanent_assert(it_m == accessor.end(i),
"There is more data than it should be in MultiIterator. "
<< it_m->first << "\n");
}
}
// Runs given function in threads_no threads and returns vector of futures for
// there
// results.
template <class R, typename S>
std::vector<std::future<std::pair<size_t, R>>>
run(size_t threads_no, S &skiplist,
std::function<R(typename S::Accessor, size_t)> f)
{
std::vector<std::future<std::pair<size_t, R>>> futures;
std::vector<std::future<std::pair<size_t, R>>> run(
size_t threads_no, S &skiplist,
std::function<R(typename S::Accessor, size_t)> f) {
std::vector<std::future<std::pair<size_t, R>>> futures;
for (size_t thread_i = 0; thread_i < threads_no; ++thread_i)
{
std::packaged_task<std::pair<size_t, R>()> task(
[&skiplist, f, thread_i]() {
return std::pair<size_t, R>(thread_i,
f(skiplist.access(), thread_i));
}); // wrap the function
futures.push_back(task.get_future()); // get a future
std::thread(std::move(task)).detach();
}
return futures;
for (size_t thread_i = 0; thread_i < threads_no; ++thread_i) {
std::packaged_task<std::pair<size_t, R>()> task([&skiplist, f, thread_i]() {
return std::pair<size_t, R>(thread_i, f(skiplist.access(), thread_i));
}); // wrap the function
futures.push_back(task.get_future()); // get a future
std::thread(std::move(task)).detach();
}
return futures;
}
// Runs given function in threads_no threads and returns vector of futures for
@ -214,98 +184,87 @@ run(size_t threads_no, S &skiplist,
// results.
template <class R>
std::vector<std::future<std::pair<size_t, R>>> run(size_t threads_no,
std::function<R(size_t)> f)
{
std::vector<std::future<std::pair<size_t, R>>> futures;
std::function<R(size_t)> f) {
std::vector<std::future<std::pair<size_t, R>>> futures;
for (size_t thread_i = 0; thread_i < threads_no; ++thread_i)
{
std::packaged_task<std::pair<size_t, R>()> task([f, thread_i]() {
return std::pair<size_t, R>(thread_i, f(thread_i));
}); // wrap the function
futures.push_back(task.get_future()); // get a future
std::thread(std::move(task)).detach();
}
return futures;
for (size_t thread_i = 0; thread_i < threads_no; ++thread_i) {
std::packaged_task<std::pair<size_t, R>()> task([f, thread_i]() {
return std::pair<size_t, R>(thread_i, f(thread_i));
}); // wrap the function
futures.push_back(task.get_future()); // get a future
std::thread(std::move(task)).detach();
}
return futures;
}
// Collects all data from futures.
template <class R>
auto collect(std::vector<std::future<R>> &collect)
{
std::vector<R> collection;
for (auto &fut : collect)
{
collection.push_back(fut.get());
}
return collection;
auto collect(std::vector<std::future<R>> &collect) {
std::vector<R> collection;
for (auto &fut : collect) {
collection.push_back(fut.get());
}
return collection;
}
std::vector<bool> collect_set(
std::vector<std::future<std::pair<size_t, std::vector<bool>>>> &&futures)
{
std::vector<bool> set;
for (auto &data : collect(futures))
{
set.resize(data.second.size());
for (int i = 0; i < data.second.size(); i++)
{
set[i] = set[i] | data.second[i];
}
std::vector<std::future<std::pair<size_t, std::vector<bool>>>> &&futures) {
std::vector<bool> set;
for (auto &data : collect(futures)) {
set.resize(data.second.size());
for (int i = 0; i < data.second.size(); i++) {
set[i] = set[i] | data.second[i];
}
return set;
}
return set;
}
// Returns object which tracs in owned which (key,data) where added and
// downcounts.
template <class K, class D, class S>
auto insert_try(typename S::Accessor &acc, long long &downcount,
std::vector<K> &owned)
{
return [&](K key, D data) mutable {
if (acc.insert(key, data).second)
{
downcount--;
owned.push_back(key);
}
};
std::vector<K> &owned) {
return [&](K key, D data) mutable {
if (acc.insert(key, data).second) {
downcount--;
owned.push_back(key);
}
};
}
// Performs memory check to determine if memory usage before calling given
// function
// is aproximately equal to memory usage after function. Memory usage is thread
// senstive so no_threads spawned in function is necessary.
void memory_check(size_t no_threads, std::function<void()> f)
{
logging::info("Number of threads: {}", no_threads);
void memory_check(size_t no_threads, std::function<void()> f) {
logging::info("Number of threads: {}", no_threads);
// TODO: replace vm_size with something more appropriate
// the past implementation was teribble wrong
// to that ASAP
// OR
// use custom allocation wrapper
// OR
// user Boost.Test
auto start = vm_size();
logging::info("Memory check (used memory at the beginning): {}", start);
// TODO: replace vm_size with something more appropriate
// the past implementation was teribble wrong
// to that ASAP
// OR
// use custom allocation wrapper
// OR
// user Boost.Test
auto start = vm_size();
logging::info("Memory check (used memory at the beginning): {}", start);
f();
f();
auto end = vm_size();
logging::info("Memory check (used memory at the end): {}", end);
auto end = vm_size();
logging::info("Memory check (used memory at the end): {}", end);
long long delta = end - start;
logging::info("Delta: {}", delta);
long long delta = end - start;
logging::info("Delta: {}", delta);
// TODO: do memory check somehow
// the past implementation was wrong
permanent_assert(true, "Memory leak");
// TODO: do memory check somehow
// the past implementation was wrong
permanent_assert(true, "Memory leak");
}
// TODO: move this inside logging/default
// Initializes loging faccilityes
void init_log()
{
logging::init_async();
logging::log->pipe(std::make_unique<Stdout>());
void init_log() {
logging::init_async();
logging::log->pipe(std::make_unique<Stdout>());
}

View File

@ -17,7 +17,6 @@
#include "io/network/socket.hpp"
#include "query/engine.hpp"
static constexpr const int SIZE = 60000;
static constexpr const int REPLY = 10;
@ -28,9 +27,10 @@ class TestOutputStream {};
class TestSession {
public:
TestSession(socket_t &&socket, Dbms &dbms,
QueryEngine<TestOutputStream> &query_engine)
: socket(std::move(socket)), logger_(logging::log->logger("TestSession")) {
TestSession(socket_t&& socket, Dbms& dbms,
QueryEngine<TestOutputStream>& query_engine)
: logger_(logging::log->logger("TestSession")),
socket(std::move(socket)) {
event.data.ptr = this;
}
@ -38,7 +38,7 @@ class TestSession {
int id() const { return socket.id(); }
void execute(const byte *data, size_t len) {
void execute(const byte* data, size_t len) {
if (size_ == 0) {
size_ = data[0];
size_ <<= 8;
@ -77,7 +77,8 @@ void server_start(void* serverptr, int num) {
((test_server_t*)serverptr)->Start(num);
}
void client_run(int num, const char* interface, const char* port, const unsigned char* data, int lo, int hi) {
void client_run(int num, const char* interface, const char* port,
const unsigned char* data, int lo, int hi) {
std::stringstream name;
name << "Client " << num;
Logger logger = logging::log->logger(name.str());

View File

@ -2,7 +2,6 @@
constexpr size_t THREADS_NO = std::min(max_no_threads, 8);
constexpr size_t elems_per_thread = 100000;
constexpr size_t key_range = elems_per_thread * THREADS_NO * 2;
// TODO: document the test
@ -18,16 +17,17 @@ int main() {
auto futures = run<std::vector<size_t>>(
THREADS_NO, skiplist, [](auto acc, auto index) {
auto rand = rand_gen(key_range);
long long downcount = elems_per_thread;
std::vector<size_t> owned;
auto inserter =
insert_try<size_t, size_t, map_t>(acc, downcount, owned);
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wfor-loop-analysis"
for (int i = 0; downcount > 0; i++) {
inserter(i, index);
}
#pragma GCC diagnostic pop
check_present_same<map_t>(acc, index, owned);
return owned;
});

View File

@ -1,10 +1,10 @@
#include "gtest/gtest.h"
#include <memory>
#include "data_structures/map/rh_hashmap.hpp"
class Data {
private:
size_t data = 0;
int key;
public:
@ -19,14 +19,16 @@ TEST(RobinHoodHashmap, BasicFunctionality) {
RhHashMap<int, Data> map;
ASSERT_EQ(map.size(), 0);
ASSERT_EQ(map.insert(new Data(0)), true);
Data d0(0);
ASSERT_EQ(map.insert(&d0), true);
ASSERT_EQ(map.size(), 1);
}
TEST(RobinHoodHashmap, RemoveFunctionality) {
RhHashMap<int, Data> map;
ASSERT_EQ(map.insert(new Data(0)), true);
Data d0(0);
ASSERT_EQ(map.insert(&d0), true);
ASSERT_EQ(map.remove(0).is_present(), true);
ASSERT_EQ(map.size(), 0);
ASSERT_EQ(!map.find(0).is_present(), true);
@ -36,25 +38,29 @@ TEST(RobinHoodHashmap, InsertGetCheck) {
RhHashMap<int, Data> map;
ASSERT_EQ(!map.find(0).is_present(), true);
auto ptr0 = new Data(0);
ASSERT_EQ(map.insert(ptr0), true);
Data d0(0);
ASSERT_EQ(map.insert(&d0), true);
ASSERT_EQ(map.find(0).is_present(), true);
ASSERT_EQ(map.find(0).get(), ptr0);
ASSERT_EQ(map.find(0).get(), &d0);
}
TEST(RobinHoodHashmap, DoubleInsert) {
RhHashMap<int, Data> map;
ASSERT_EQ(map.insert(new Data(0)), true);
ASSERT_EQ(!map.insert(new Data(0)), true);
Data d0(0);
ASSERT_EQ(map.insert(&d0), true);
ASSERT_EQ(!map.insert(&d0), true);
}
TEST(RobinHoodHashmap, FindInsertFind) {
RhHashMap<int, Data> map;
std::vector<std::unique_ptr<Data>> di;
di.reserve(128);
for (int i = 0; i < 128; ++i) di.emplace_back(std::make_unique<Data>(i));
for (int i = 0; i < 128; i++) {
ASSERT_EQ(!map.find(i).is_present(), true);
ASSERT_EQ(map.insert(new Data(i)), true);
ASSERT_EQ(map.insert(di[i].get()), true);
ASSERT_EQ(map.find(i).is_present(), true);
}
@ -67,9 +73,12 @@ TEST(RobinHoodHashmap, FindInsertFind) {
TEST(RobinHoodHashmap, Iterate) {
RhHashMap<int, Data> map;
std::vector<std::unique_ptr<Data>> di;
di.reserve(128);
for (int i = 0; i < 128; ++i) di.emplace_back(std::make_unique<Data>(i));
for (int i = 0; i < 128; i++) {
ASSERT_EQ(!map.find(i).is_present(), true);
ASSERT_EQ(map.insert(new Data(i)), true);
ASSERT_EQ(map.insert(di[i].get()), true);
ASSERT_EQ(map.find(i).is_present(), true);
}
@ -88,14 +97,21 @@ TEST(RobinHoodHashmap, Checked) {
RhHashMap<int, Data> map;
std::map<int, Data *> s_map;
std::vector<std::unique_ptr<Data>> di;
std::vector<int> key;
di.reserve(128);
key.reserve(128);
for (int i = 0; i < 128; ++i) {
const int curr_key = std::rand();
key.emplace_back(curr_key);
di.emplace_back(std::make_unique<Data>(curr_key));
}
for (int i = 0; i < 128; i++) {
int key = std::rand();
auto data = new Data(key);
if (map.insert(data)) {
ASSERT_EQ(s_map.find(key), s_map.end());
s_map[key] = data;
if (map.insert(di[i].get())) {
ASSERT_EQ(s_map.find(key[i]), s_map.end());
s_map[key[i]] = di[i].get();
} else {
ASSERT_NE(s_map.find(key), s_map.end());
ASSERT_NE(s_map.find(key[i]), s_map.end());
}
}
@ -105,17 +121,24 @@ TEST(RobinHoodHashmap, Checked) {
TEST(RobinHoodHashMap, CheckWithRemove) {
RhHashMap<int, Data> map;
std::map<int, Data *> s_map;
std::vector<std::unique_ptr<Data>> di;
std::vector<int> key;
di.reserve(1280);
key.reserve(1280);
for (int i = 0; i < 1280; ++i) {
const int curr_key = std::rand() % 100;
key.emplace_back(curr_key);
di.emplace_back(std::make_unique<Data>(curr_key));
}
for (int i = 0; i < 1280; i++) {
int key = std::rand() % 100;
auto data = new Data(key);
if (map.insert(data)) {
ASSERT_EQ(s_map.find(key), s_map.end());
s_map[key] = data;
if (map.insert(di[i].get())) {
ASSERT_EQ(s_map.find(key[i]), s_map.end());
s_map[key[i]] = di[i].get();
cross_validate(map, s_map);
} else {
ASSERT_EQ(map.remove(key).is_present(), true);
ASSERT_EQ(s_map.erase(key), 1);
ASSERT_EQ(map.remove(key[i]).is_present(), true);
ASSERT_EQ(s_map.erase(key[i]), 1);
cross_validate(map, s_map);
}
}
@ -123,6 +146,13 @@ TEST(RobinHoodHashMap, CheckWithRemove) {
cross_validate(map, s_map);
}
TEST(RobinhoodHashmmap, AlignmentCheck) {
RhHashMap<int, Data> map;
char *block = static_cast<char *>(std::malloc(20));
++block; // not alligned - offset 1
EXPECT_DEATH(map.insert((Data *)(block)), "not 8-alligned");
}
void cross_validate(RhHashMap<int, Data> &map, std::map<int, Data *> &s_map) {
for (auto e : map) {
ASSERT_NE(s_map.find(e->get_key()), s_map.end());

View File

@ -4,7 +4,6 @@
class Data {
private:
size_t data = 0;
int key;
public:
@ -23,7 +22,8 @@ TEST(RobinHoodHashmultimap, BasicFunctionality) {
RhHashMultiMap<int, Data> map;
ASSERT_EQ(map.size(), 0);
map.add(new Data(0));
Data d0(0);
map.add(&d0);
ASSERT_EQ(map.size(), 1);
}
@ -31,67 +31,72 @@ TEST(RobinHoodHashmultimap, InsertGetCheck) {
RhHashMultiMap<int, Data> map;
ASSERT_EQ(map.find(0), map.end());
auto ptr0 = new Data(0);
map.add(ptr0);
Data d0(0);
map.add(&d0);
ASSERT_NE(map.find(0), map.end());
ASSERT_EQ(*map.find(0), ptr0);
ASSERT_EQ(*map.find(0), &d0);
}
TEST(RobinHoodHashmultimap, ExtremeSameKeyValusFull) {
RhHashMultiMap<int, Data> map;
std::vector<std::unique_ptr<Data>> di;
di.reserve(128);
for (int i = 0; i < 128; i++) {
map.add(new Data(7));
di.emplace_back(std::make_unique<Data>(7));
map.add(di[i].get());
}
ASSERT_EQ(map.size(), 128);
ASSERT_NE(map.find(7), map.end());
ASSERT_EQ(map.find(0), map.end());
auto ptr0 = new Data(0);
map.add(ptr0);
Data d0(0);
map.add(&d0);
ASSERT_NE(map.find(0), map.end());
ASSERT_EQ(*map.find(0), ptr0);
ASSERT_EQ(*map.find(0), &d0);
}
TEST(RobinHoodHashmultimap, ExtremeSameKeyValusFullWithRemove) {
RhHashMultiMap<int, Data> map;
std::vector<std::unique_ptr<Data>> di;
di.reserve(128);
for (int i = 0; i < 127; i++) {
map.add(new Data(7));
di.emplace_back(std::make_unique<Data>(7));
map.add(di[i].get());
}
auto ptr = new Data(7);
map.add(ptr);
Data d7(7);
map.add(&d7);
ASSERT_EQ(map.size(), 128);
ASSERT_EQ(!map.remove(new Data(0)), true);
ASSERT_EQ(map.remove(ptr), true);
Data d0(0);
ASSERT_EQ(!map.remove(&d0), true);
ASSERT_EQ(map.remove(&d7), true);
}
TEST(RobinHoodHasmultihmap, RemoveFunctionality) {
RhHashMultiMap<int, Data> map;
ASSERT_EQ(map.find(0), map.end());
auto ptr0 = new Data(0);
map.add(ptr0);
Data d0(0);
map.add(&d0);
ASSERT_NE(map.find(0), map.end());
ASSERT_EQ(*map.find(0), ptr0);
ASSERT_EQ(map.remove(ptr0), true);
ASSERT_EQ(*map.find(0), &d0);
ASSERT_EQ(map.remove(&d0), true);
ASSERT_EQ(map.find(0), map.end());
}
TEST(RobinHoodHashmultimap, DoubleInsert) {
RhHashMultiMap<int, Data> map;
auto ptr0 = new Data(0);
auto ptr1 = new Data(0);
map.add(ptr0);
map.add(ptr1);
Data d0(0);
Data d01(0);
map.add(&d0);
map.add(&d01);
for (auto e : map) {
if (ptr0 == e) {
ptr0 = nullptr;
if (&d0 == e) {
continue;
}
if (ptr1 == e) {
ptr1 = nullptr;
if (&d01 == e) {
continue;
}
ASSERT_EQ(true, false);
@ -101,9 +106,12 @@ TEST(RobinHoodHashmultimap, DoubleInsert) {
TEST(RobinHoodHashmultimap, FindAddFind) {
RhHashMultiMap<int, Data> map;
std::vector<std::unique_ptr<Data>> di;
di.reserve(128);
for (int i = 0; i < 128; i++) {
ASSERT_EQ(map.find(i), map.end());
map.add(new Data(i));
di.emplace_back(std::make_unique<Data>(i));
map.add(di[i].get());
ASSERT_NE(map.find(i), map.end());
}
@ -116,9 +124,12 @@ TEST(RobinHoodHashmultimap, FindAddFind) {
TEST(RobinHoodHashmultimap, Iterate) {
RhHashMultiMap<int, Data> map;
std::vector<std::unique_ptr<Data>> di;
di.reserve(128);
for (int i = 0; i < 128; i++) {
ASSERT_EQ(map.find(i), map.end());
map.add(new Data(i));
di.emplace_back(std::make_unique<Data>(i));
map.add(di[i].get());
ASSERT_NE(map.find(i), map.end());
}
@ -137,12 +148,18 @@ TEST(RobinHoodHashmultimap, Checked) {
RhHashMultiMap<int, Data> map;
std::multimap<int, Data *> s_map;
for (int i = 0; i < 1638; i++) {
int key = (std::rand() % 100) << 3;
std::vector<std::unique_ptr<Data>> di;
std::vector<int> keys;
di.reserve(1638);
for (int i = 0; i < 1638; ++i) {
const int key = (std::rand() % 100) << 3;
keys.push_back(key);
di.emplace_back(std::make_unique<Data>(key));
}
auto data = new Data(key);
map.add(data);
s_map.insert(std::pair<int, Data *>(key, data));
for (int i = 0; i < 1638; i++) {
map.add(di[i].get());
s_map.insert(std::pair<int, Data *>(keys[i], di[i].get()));
}
cross_validate(map, s_map);
}
@ -151,12 +168,19 @@ TEST(RobinHoodHashmultimap, CheckedRand) {
RhHashMultiMap<int, Data> map;
std::multimap<int, Data *> s_map;
std::srand(std::time(0));
for (int i = 0; i < 164308; i++) {
int key = (std::rand() % 10000) << 3;
auto data = new Data(key);
map.add(data);
s_map.insert(std::pair<int, Data *>(key, data));
std::vector<std::unique_ptr<Data>> di;
std::vector<int> keys;
di.reserve(164308);
for (int i = 0; i < 164308; ++i) {
const int key = (std::rand() % 10000) << 3;
keys.push_back(key);
di.emplace_back(std::make_unique<Data>(key));
}
for (int i = 0; i < 164308; i++) {
map.add(di[i].get());
s_map.insert(std::pair<int, Data *>(keys[i], di[i].get()));
}
cross_validate(map, s_map);
}
@ -166,6 +190,7 @@ TEST(RobinHoodHashmultimap, WithRemoveDataChecked) {
std::multimap<int, Data *> s_map;
std::srand(std::time(0));
std::vector<std::unique_ptr<Data>> di;
for (int i = 0; i < 162638; i++) {
int key = (std::rand() % 10000) << 3;
if ((std::rand() % 2) == 0) {
@ -177,15 +202,22 @@ TEST(RobinHoodHashmultimap, WithRemoveDataChecked) {
ASSERT_EQ(map.remove(it->second), true);
}
} else {
auto data = new Data(key);
map.add(data);
s_map.insert(std::pair<int, Data *>(key, data));
di.emplace_back(std::make_unique<Data>(key));
map.add(di.back().get());
s_map.insert(std::pair<int, Data *>(key, di.back().get()));
}
}
cross_validate(map, s_map);
}
TEST(RobinhoodHashmultimap, AlignmentCheck) {
RhHashMultiMap<int, Data> map;
char *block = static_cast<char *>(std::malloc(20));
++block; // not alligned - offset 1
EXPECT_DEATH(map.add((Data *)(block)), "not 8-alligned");
}
void cross_validate(RhHashMultiMap<int, Data> &map,
std::multimap<int, Data *> &s_map) {
for (auto e : map) {

View File

@ -3,12 +3,12 @@
// Created by Florijan Stamenkovic on 24.01.17..
//
#include <vector>
#include <list>
#include <unordered_set>
#include <iostream>
#include <list>
#include <list>
#include <memory>
#include <unordered_set>
#include <vector>
#include "gtest/gtest.h"
@ -38,14 +38,14 @@ class Edge;
class Vertex {
friend class Edge;
public:
public:
// unique identifier (uniqueness guaranteed by this class)
const int id_;
// vertex set by the user, for testing purposes
const int label_;
Vertex(int label=0) : id_(counter_++), label_(label) {}
Vertex(int label = 0) : id_(counter_++), label_(label) {}
const auto &in() const { return *in_; }
const auto &out() const { return *out_; }
@ -57,15 +57,16 @@ public:
bool operator==(const Vertex &v) const { return this->id_ == v.id_; }
bool operator!=(const Vertex &v) const { return !(*this == v); }
private:
private:
// shared pointers to outgoing and incoming edges are used so that when
// a Vertex is copied it still points to the same connectivity storage
std::shared_ptr<std::vector<Edge>> in_ = std::make_shared<std::vector<Edge>>();
std::shared_ptr<std::vector<Edge>> out_ = std::make_shared<std::vector<Edge>>();
std::shared_ptr<std::vector<Edge>> in_ =
std::make_shared<std::vector<Edge>>();
std::shared_ptr<std::vector<Edge>> out_ =
std::make_shared<std::vector<Edge>>();
// Vertex counter, used for generating IDs
static int counter_;
};
int Vertex::counter_ = 0;
@ -75,15 +76,15 @@ int Vertex::counter_ = 0;
* and an exposed id_ member.
*/
class Edge {
public:
public:
// unique identifier (uniqueness guaranteed by this class)
const int id_;
// vertex set by the user, for testing purposes
const int type_;
Edge(const Vertex &from, const Vertex &to, int type=0) :
id_(counter_++), from_(from), to_(to), type_(type) {
Edge(const Vertex &from, const Vertex &to, int type = 0)
: id_(counter_++), type_(type), from_(from), to_(to) {
from.out_->emplace_back(*this);
to.in_->emplace_back(*this);
}
@ -98,7 +99,7 @@ public:
return stream << "[" << e.id_ << "]";
}
private:
private:
const Vertex &from_;
const Vertex &to_;
@ -115,34 +116,34 @@ using Paths = traversal_template::Paths<Vertex, Edge>;
/**
* Specialization of the traversal_template::Begin function.
*/
template<typename TCollection>
auto Begin(const TCollection &vertices, std::function<bool(const Vertex &)> vertex_filter = {}) {
return traversal_template::Begin<TCollection, Vertex, Edge>(vertices, vertex_filter);
template <typename TCollection>
auto Begin(const TCollection &vertices,
std::function<bool(const Vertex &)> vertex_filter = {}) {
return traversal_template::Begin<TCollection, Vertex, Edge>(vertices,
vertex_filter);
}
/**
* Specialization of the traversal_template::Cartesian function that accepts
* a single argument.
*/
template<typename TVisitable>
template <typename TVisitable>
auto Cartesian(TVisitable &&visitable) {
return traversal_template::Cartesian<TVisitable, Vertex, Edge>(
std::forward<TVisitable>(visitable));
}
/**
* Specialization of the traversal_template::Cartesian function that accepts
* multiple arguments.
*/
template<typename TVisitableFirst, typename... TVisitableOthers>
template <typename TVisitableFirst, typename... TVisitableOthers>
auto Cartesian(TVisitableFirst &&first, TVisitableOthers &&... others) {
return traversal_template::CartesianBinaryType<TVisitableFirst, decltype(Cartesian(
std::forward<TVisitableOthers>(others)...)),
Vertex, Edge>(
std::forward<TVisitableFirst>(first),
Cartesian(std::forward<TVisitableOthers>(others)...)
);
return traversal_template::CartesianBinaryType<
TVisitableFirst,
decltype(Cartesian(std::forward<TVisitableOthers>(others)...)), Vertex,
Edge>(std::forward<TVisitableFirst>(first),
Cartesian(std::forward<TVisitableOthers>(others)...));
}
}
@ -150,27 +151,27 @@ auto Cartesian(TVisitableFirst &&first, TVisitableOthers &&... others) {
* Hash calculations for text vertex, edge, Path and Paths.
* Only for testing purposes.
*/
namespace std{
namespace std {
template <>
class hash<traversal_test::Vertex> {
public:
struct hash<traversal_test::Vertex> {
public:
size_t operator()(const traversal_test::Vertex &vertex) const {
return (size_t) vertex.id_;
return (size_t)vertex.id_;
}
};
template <>
class hash<traversal_test::Edge> {
public:
struct hash<traversal_test::Edge> {
public:
size_t operator()(const traversal_test::Edge &edge) const {
return (size_t) edge.id_;
return (size_t)edge.id_;
}
};
template <>
class hash<traversal_test::Path> {
public:
struct hash<traversal_test::Path> {
public:
std::size_t operator()(const traversal_test::Path &path) const {
std::size_t r_val = 0;
@ -182,8 +183,8 @@ public:
};
template <>
class hash<traversal_test::Paths> {
public:
struct hash<traversal_test::Paths> {
public:
std::size_t operator()(const traversal_test::Paths &paths) const {
std::size_t r_val = 0;
@ -195,14 +196,13 @@ public:
};
}
/**
* Hash for a list of edges, used in tests.
*/
namespace std{
namespace std {
template <>
class hash<std::list<traversal_test::Edge>> {
public:
struct hash<std::list<traversal_test::Edge>> {
public:
std::size_t operator()(const std::list<traversal_test::Edge> &edges) const {
std::size_t r_val = 0;
@ -221,7 +221,8 @@ std::unordered_set<TElement> accumulate_visited(const TVisitable &visitable) {
// we accept const here so we can receive r-values
// we cast the const-ness away because the Visit function is not const
std::unordered_set<TElement> results;
const_cast<TVisitable&>(visitable).Visit([&results](auto e) { results.insert(e); });
const_cast<TVisitable &>(visitable).Visit(
[&results](auto e) { results.insert(e); });
return results;
};
@ -276,8 +277,7 @@ TEST(Traversal, Begin) {
std::vector<Vertex> vertices(3);
std::unordered_set<Path> expected;
for (const auto &vertex : vertices)
expected.insert(Path().Start(vertex));
for (const auto &vertex : vertices) expected.insert(Path().Start(vertex));
EXPECT_EQ(expected.size(), 3);
auto result = accumulate_visited<Path>(Begin(vertices));
@ -291,13 +291,10 @@ TEST(Traversal, BeginExpand) {
Edge e2(v2, v3);
// test traversal from (v0)
auto expected = std::unordered_set<Path>{
Path().Start(v0).Append(e0, v1),
Path().Start(v0).Append(e1, v2)
};
auto result= accumulate_visited<Path>(
Begin(std::vector<Vertex>{v0}).
Expand(Expansion::Back, Direction::Out));
auto expected = std::unordered_set<Path>{Path().Start(v0).Append(e0, v1),
Path().Start(v0).Append(e1, v2)};
auto result = accumulate_visited<Path>(
Begin(std::vector<Vertex>{v0}).Expand(Expansion::Back, Direction::Out));
EXPECT_EQ(expected, result);
}
@ -312,29 +309,23 @@ TEST(Traversal, BeginExpandVariable) {
// test traversal from (v0)
auto expected = std::unordered_set<Path>{
Path().Start(v0).Append(e0, v1),
Path().Start(v0).Append(e1, v2),
Path().Start(v0).Append(e1, v2).Append(e2, v3)
};
auto result= accumulate_visited<Path>(
Begin(std::vector<Vertex>{v0}).
ExpandVariable(Expansion::Back, Direction::Out));
Path().Start(v0).Append(e0, v1), Path().Start(v0).Append(e1, v2),
Path().Start(v0).Append(e1, v2).Append(e2, v3)};
auto result = accumulate_visited<Path>(
Begin(std::vector<Vertex>{v0})
.ExpandVariable(Expansion::Back, Direction::Out));
EXPECT_EQ(expected, result);
}
TEST(Traversal, BeginFilter) {
Vertex v0(1), v1(1), v2(2);
std::unordered_set<Path> expected{
Path().Start(v0),
Path().Start(v1)
};
std::unordered_set<Path> expected{Path().Start(v0), Path().Start(v1)};
auto result = accumulate_visited<Path>(Begin(std::vector<Vertex>{v0, v1, v2},
[](const Vertex &v) { return v.label_ == 1; }));
auto result = accumulate_visited<Path>(
Begin(std::vector<Vertex>{v0, v1, v2},
[](const Vertex &v) { return v.label_ == 1; }));
EXPECT_EQ(expected, result);
}
TEST(Traversal, BeginCurrent) {
@ -344,7 +335,7 @@ TEST(Traversal, BeginCurrent) {
std::vector<Vertex> begin_input{v0, v1, v2};
auto b = Begin(begin_input);
b.Visit([&b, &expected](Path &path){
b.Visit([&b, &expected](Path &path) {
EXPECT_EQ(1, expected.erase(b.CurrentVertex()));
});
@ -365,32 +356,36 @@ TEST(Traversal, ExpandExpand) {
Edge e6(v7, v1);
// test traversal from (v0)
auto expected = std::unordered_set<Path>{
Path().Start(v0).Append(e0, v1).Append(e2, v3),
Path().Start(v0).Append(e1, v2).Append(e3, v4),
Path().Start(v0).Append(e1, v2).Append(e4, v5)
};
auto result= accumulate_visited<Path>(
Begin(std::vector<Vertex>{v0}).
Expand(Expansion::Back, Direction::Out).
Expand(Expansion::Back, Direction::Out));
auto expected =
std::unordered_set<Path>{Path().Start(v0).Append(e0, v1).Append(e2, v3),
Path().Start(v0).Append(e1, v2).Append(e3, v4),
Path().Start(v0).Append(e1, v2).Append(e4, v5)};
auto result =
accumulate_visited<Path>(Begin(std::vector<Vertex>{v0})
.Expand(Expansion::Back, Direction::Out)
.Expand(Expansion::Back, Direction::Out));
EXPECT_EQ(expected, result);
}
TEST(Traversal, ExpandFilter) {
// make a one-level deep tree and 4 children that have {1,2} vertex labels and {3,4} edge types
// make a one-level deep tree and 4 children that have {1,2} vertex labels and
// {3,4} edge types
Vertex root;
Vertex v1(1); Edge e1(root, v1, 3);
Vertex v2(1); Edge e2(root, v2, 4);
Vertex v3(2); Edge e3(root, v3, 3);
Vertex v4(2); Edge e4(root, v4, 4);
Vertex v1(1);
Edge e1(root, v1, 3);
Vertex v2(1);
Edge e2(root, v2, 4);
Vertex v3(2);
Edge e3(root, v3, 3);
Vertex v4(2);
Edge e4(root, v4, 4);
// filter to accept only label 1 and edge type 3, expect exactly one path
auto result = accumulate_visited<Path>(
Begin(std::vector<Vertex>{root}).
Expand(Expansion::Back, Direction::Out,
[](const Vertex &v) { return v.label_ == 1; },
[](const Edge &e) { return e.type_ == 3; }));
Begin(std::vector<Vertex>{root})
.Expand(Expansion::Back, Direction::Out,
[](const Vertex &v) { return v.label_ == 1; },
[](const Edge &e) { return e.type_ == 3; }));
auto expected = std::unordered_set<Path>{Path().Start(root).Append(e1, v1)};
EXPECT_EQ(expected, result);
@ -416,7 +411,7 @@ TEST(Traversal, ExpandCurrent) {
std::unordered_set<Vertex> expected_expand1_vertices{v3, v4, v5, v6};
std::unordered_set<Edge> expected_expand1_edges{e3, e4, e5, e6};
expand1.Visit([&](Path &path){
expand1.Visit([&](Path &path) {
// we can't check that erasure on the first level because it only gets
// erased the first time a path containing first level stuff gets visited
expected_expand0_vertices.erase(expand0.CurrentVertex());
@ -434,10 +429,14 @@ TEST(Traversal, ExpandCurrent) {
TEST(Traversal, ExpandVariableLimits) {
// make a chain
Vertex v0;
Vertex v1; Edge e1(v0, v1);
Vertex v2; Edge e2(v1, v2);
Vertex v3; Edge e3(v2, v3);
Vertex v4; Edge e4(v3, v4);
Vertex v1;
Edge e1(v0, v1);
Vertex v2;
Edge e2(v1, v2);
Vertex v3;
Edge e3(v2, v3);
Vertex v4;
Edge e4(v3, v4);
// all possible results
std::vector<Path> all_possible{Path().Start(v0)};
@ -448,29 +447,36 @@ TEST(Traversal, ExpandVariableLimits) {
// default is one or more traversals
auto result0 = accumulate_visited<Path>(
Begin(std::vector<Vertex>{v0}).ExpandVariable(Expansion::Back, Direction::Out));
EXPECT_EQ(result0, std::unordered_set<Path>(all_possible.begin() + 1, all_possible.end()));
Begin(std::vector<Vertex>{v0})
.ExpandVariable(Expansion::Back, Direction::Out));
EXPECT_EQ(result0, std::unordered_set<Path>(all_possible.begin() + 1,
all_possible.end()));
// zero or more traversals
auto result1 = accumulate_visited<Path>(
Begin(std::vector<Vertex>{v0}).
ExpandVariable(Expansion::Back, Direction::Out, {}, {}, 0));
EXPECT_EQ(result1, std::unordered_set<Path>(all_possible.begin(), all_possible.end()));
Begin(std::vector<Vertex>{v0})
.ExpandVariable(Expansion::Back, Direction::Out, {}, {}, 0));
EXPECT_EQ(result1,
std::unordered_set<Path>(all_possible.begin(), all_possible.end()));
// an exact number of traversals [2, 4)
auto result2 = accumulate_visited<Path>(
Begin(std::vector<Vertex>{v0}).
ExpandVariable(Expansion::Back, Direction::Out, {}, {}, 2, 4));
EXPECT_EQ(result2, std::unordered_set<Path>(all_possible.begin() + 2, all_possible.begin() + 4));
Begin(std::vector<Vertex>{v0})
.ExpandVariable(Expansion::Back, Direction::Out, {}, {}, 2, 4));
EXPECT_EQ(result2, std::unordered_set<Path>(all_possible.begin() + 2,
all_possible.begin() + 4));
}
TEST(Traversal, ExpandVariableFilter) {
// make a chain with vertex labels and edge types with these values
// (0)-[1]->(1)-[2]->(2)-[3]->(3)
Vertex v0(0);
Vertex v1(1); Edge e1(v0, v1, 1);
Vertex v2(2); Edge e2(v1, v2, 2);
Vertex v3(3); Edge e3(v2, v3, 3);
Vertex v1(1);
Edge e1(v0, v1, 1);
Vertex v2(2);
Edge e2(v1, v2, 2);
Vertex v3(3);
Edge e3(v2, v3, 3);
// all possible paths
std::vector<Path> all_possible{Path().Start(v0)};
@ -480,46 +486,49 @@ TEST(Traversal, ExpandVariableFilter) {
// filter on edge type < 3
auto result0 = accumulate_visited<Path>(
Begin(std::vector<Vertex>{v0}).
ExpandVariable(Expansion::Back, Direction::Out, {},
[](const Edge &e) { return e.type_ < 3; }));
EXPECT_EQ(result0, std::unordered_set<Path>(all_possible.begin() + 1, all_possible.begin() + 3));
Begin(std::vector<Vertex>{v0})
.ExpandVariable(Expansion::Back, Direction::Out, {},
[](const Edge &e) { return e.type_ < 3; }));
EXPECT_EQ(result0, std::unordered_set<Path>(all_possible.begin() + 1,
all_possible.begin() + 3));
// filter on edge type > 1, expect nothing because the first edge is 1
auto result1 = accumulate_visited<Path>(
Begin(std::vector<Vertex>{v0}).
ExpandVariable(Expansion::Back, Direction::Out, {},
[](const Edge &e) { return e.type_ > 1; }));
Begin(std::vector<Vertex>{v0})
.ExpandVariable(Expansion::Back, Direction::Out, {},
[](const Edge &e) { return e.type_ > 1; }));
EXPECT_EQ(result1.size(), 0);
// filter on vertex label > 1, expect last 2 results
auto result2 = accumulate_visited<Path>(
Begin(std::vector<Vertex>{v0}).
ExpandVariable(Expansion::Back, Direction::Out,
[](const Vertex &v) { return v.label_ > 1; }, {}));
EXPECT_EQ(result2, std::unordered_set<Path>(all_possible.end() - 2, all_possible.end()));
Begin(std::vector<Vertex>{v0})
.ExpandVariable(Expansion::Back, Direction::Out,
[](const Vertex &v) { return v.label_ > 1; }, {}));
EXPECT_EQ(result2, std::unordered_set<Path>(all_possible.end() - 2,
all_possible.end()));
}
TEST(Traversal, ExpandVariableCurrent) {
// a chain
Vertex v0;
Vertex v1; Edge e1(v0, v1);
Vertex v2; Edge e2(v1, v2);
Vertex v3; Edge e3(v2, v3);
Vertex v1;
Edge e1(v0, v1);
Vertex v2;
Edge e2(v1, v2);
Vertex v3;
Edge e3(v2, v3);
// ensure that expanded vertices and all edge sets get visited as current
std::unordered_set<Vertex> expected_vertices{v1, v2, v3};
std::unordered_set<std::list<Edge>> expected_edge_lists{
std::list<Edge>{e1},
std::list<Edge>{e1, e2},
std::list<Edge>{e1, e2, e3}
};
std::list<Edge>{e1}, std::list<Edge>{e1, e2},
std::list<Edge>{e1, e2, e3}};
std::vector<Vertex> begin_input{v0};
auto begin = Begin(begin_input);
auto expand = begin.ExpandVariable(Expansion::Back, Direction::Out);
expand.Visit([&](Path &path){
expand.Visit([&](Path &path) {
EXPECT_EQ(1, expected_vertices.erase(expand.CurrentVertex()));
EXPECT_EQ(1, expected_edge_lists.erase(expand.CurrentEdges()));
});
@ -531,13 +540,13 @@ TEST(Traversal, EnumExpansion) {
Edge e0(v0, v1);
// Expansion::Back
auto result0= accumulate_visited<Path>(
auto result0 = accumulate_visited<Path>(
Begin(std::vector<Vertex>{v0}).Expand(Expansion::Back, Direction::Out));
auto expected0 = std::unordered_set<Path>{Path().Start(v0).Append(e0, v1)};
EXPECT_EQ(result0, expected0);
// Expansion::Front
auto result1= accumulate_visited<Path>(
auto result1 = accumulate_visited<Path>(
Begin(std::vector<Vertex>{v0}).Expand(Expansion::Front, Direction::Out));
auto expected1 = std::unordered_set<Path>{Path().Start(v0).Prepend(e0, v1)};
EXPECT_EQ(result1, expected1);
@ -549,24 +558,22 @@ TEST(Traversal, EnumDirection) {
Edge e0(v0, v1), e1(v1, v2);
// Direction::Out
auto result0= accumulate_visited<Path>(
auto result0 = accumulate_visited<Path>(
Begin(std::vector<Vertex>{v1}).Expand(Expansion::Back, Direction::Out));
auto expected0 = std::unordered_set<Path>{Path().Start(v1).Append(e1, v2)};
EXPECT_EQ(result0, expected0);
// Direction::In
auto result1= accumulate_visited<Path>(
auto result1 = accumulate_visited<Path>(
Begin(std::vector<Vertex>{v1}).Expand(Expansion::Back, Direction::In));
auto expected1 = std::unordered_set<Path>{Path().Start(v1).Append(e0, v0)};
EXPECT_EQ(result1, expected1);
// Direction::Both
auto result2= accumulate_visited<Path>(
auto result2 = accumulate_visited<Path>(
Begin(std::vector<Vertex>{v1}).Expand(Expansion::Back, Direction::Both));
auto expected2 = std::unordered_set<Path>{
Path().Start(v1).Append(e0, v0),
Path().Start(v1).Append(e1, v2)
};
auto expected2 = std::unordered_set<Path>{Path().Start(v1).Append(e0, v0),
Path().Start(v1).Append(e1, v2)};
EXPECT_EQ(result2, expected2);
}
@ -577,30 +584,28 @@ TEST(Traversal, EnumUniqueness) {
// Uniqueness::Edge
auto result0 = accumulate_visited<Path>(
Begin(std::vector<Vertex>{v0}).
Expand(Expansion::Back, Direction::Out, {}, {}, Uniqueness::Edge).
Expand(Expansion::Back, Direction::Out));
auto expected0 = std::unordered_set<Path>{
Path().Start(v0).Append(e0, v0).Append(e1, v1)
};
Begin(std::vector<Vertex>{v0})
.Expand(Expansion::Back, Direction::Out, {}, {}, Uniqueness::Edge)
.Expand(Expansion::Back, Direction::Out));
auto expected0 =
std::unordered_set<Path>{Path().Start(v0).Append(e0, v0).Append(e1, v1)};
EXPECT_EQ(result0, expected0);
// Uniqueness::Vertex
auto result1 = accumulate_visited<Path>(
Begin(std::vector<Vertex>{v0}).
Expand(Expansion::Back, Direction::Out, {}, {}, Uniqueness::Vertex).
Expand(Expansion::Back, Direction::Out));
Begin(std::vector<Vertex>{v0})
.Expand(Expansion::Back, Direction::Out, {}, {}, Uniqueness::Vertex)
.Expand(Expansion::Back, Direction::Out));
EXPECT_EQ(result1.size(), 0);
// Uniqueness::None
auto result2 = accumulate_visited<Path>(
Begin(std::vector<Vertex>{v0}).
Expand(Expansion::Back, Direction::Out, {}, {}, Uniqueness::None).
Expand(Expansion::Back, Direction::Out));
auto expected2 = std::unordered_set<Path>{
Path().Start(v0).Append(e0, v0).Append(e1, v1),
Path().Start(v0).Append(e0, v0).Append(e0, v0)
};
Begin(std::vector<Vertex>{v0})
.Expand(Expansion::Back, Direction::Out, {}, {}, Uniqueness::None)
.Expand(Expansion::Back, Direction::Out));
auto expected2 =
std::unordered_set<Path>{Path().Start(v0).Append(e0, v0).Append(e1, v1),
Path().Start(v0).Append(e0, v0).Append(e0, v0)};
EXPECT_EQ(result2, expected2);
}
@ -624,11 +629,11 @@ TEST(Traversal, Cartesian) {
std::unordered_set<Paths> expected;
for (auto &path1 : paths0_set)
for (auto &path2 : paths1_set)
for (auto &path3: paths0_set) {
for (auto &path3 : paths0_set) {
Paths paths;
paths.emplace_back(std::ref(const_cast<Path&>(path1)));
paths.emplace_back(std::ref(const_cast<Path&>(path2)));
paths.emplace_back(std::ref(const_cast<Path&>(path3)));
paths.emplace_back(std::ref(const_cast<Path &>(path1)));
paths.emplace_back(std::ref(const_cast<Path &>(path2)));
paths.emplace_back(std::ref(const_cast<Path &>(path3)));
expected.insert(paths);
}
EXPECT_EQ(8, expected.size());
@ -651,7 +656,7 @@ TEST(Traversal, UniquenessSubgroups) {
// counts the number of visits to the visitable
auto visit_counter = [](const auto &visitable) {
auto r_val = 0;
visitable.Visit([&r_val](const auto &x) {r_val++;});
visitable.Visit([&r_val](const auto &x) { r_val++; });
return r_val;
};
@ -666,16 +671,20 @@ TEST(Traversal, UniquenessSubgroups) {
// edge uniqueness sharing
{
auto paths0 = Begin(begin_v).Expand(Expansion::Back, Direction::Out, {}, {}, Uniqueness::Edge);
auto paths1 = Begin(begin_v).Expand(Expansion::Back, Direction::Out, {}, {}, Uniqueness::Edge);
auto paths0 = Begin(begin_v).Expand(Expansion::Back, Direction::Out, {}, {},
Uniqueness::Edge);
auto paths1 = Begin(begin_v).Expand(Expansion::Back, Direction::Out, {}, {},
Uniqueness::Edge);
paths1.UniquenessGroup().Add(paths0.UniquenessGroup());
EXPECT_EQ(2, visit_counter(Cartesian(paths0, paths1)));
}
// vertex uniqueness sharing
{
auto paths0 = Begin(begin_v).Expand(Expansion::Back, Direction::Out, {}, {}, Uniqueness::Vertex);
auto paths1 = Begin(begin_v).Expand(Expansion::Back, Direction::Out, {}, {}, Uniqueness::Vertex);
auto paths0 = Begin(begin_v).Expand(Expansion::Back, Direction::Out, {}, {},
Uniqueness::Vertex);
auto paths1 = Begin(begin_v).Expand(Expansion::Back, Direction::Out, {}, {},
Uniqueness::Vertex);
paths1.UniquenessGroup().Add(paths0.UniquenessGroup());
EXPECT_EQ(0, visit_counter(Cartesian(paths0, paths1)));
}