Remove unused datastructures

Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2639
This commit is contained in:
Matej Ferencevic 2020-01-24 10:22:34 +01:00
parent c425624ba6
commit 0c7313bb5f
6 changed files with 0 additions and 519 deletions

View File

@ -1,99 +0,0 @@
#pragma once
#include <algorithm>
#include <bitset>
#include <iostream>
#include <string>
#include "glog/logging.h"
/**
* Bitset data structure with a number of bits provided in constructor.
* @tparam TStore type of underlying bit storage: int32, int64, char, etc.
*/
template <typename TStore>
class Bitset {
public:
/**
* Create bitset.
* @param sz size of bitset
*/
Bitset(size_t sz) : block_size_(8 * sizeof(TStore)) {
if (sz % block_size_ != 0) sz += block_size_;
blocks_.resize(sz / block_size_);
}
/**
* Set bit to one.
* @param idx position of bit.
*/
void Set(int idx) {
DCHECK(idx >= 0) << "Invalid bit location.";
DCHECK(idx < static_cast<int64_t>(blocks_.size()) * block_size_)
<< "Invalid bit location.";
int bucket = idx / block_size_;
blocks_[bucket] |= TStore(1) << idx % block_size_;
}
/**
* Return bit at position.
* @param idx position of bit.
* @return 1/0.
*/
bool At(int idx) const {
DCHECK(idx >= 0) << "Invalid bit location.";
DCHECK(idx < static_cast<int64_t>(blocks_.size()) * block_size_)
<< "Invalid bit location.";
int bucket = idx / block_size_;
return (blocks_[bucket] >> (idx % block_size_)) & 1;
}
/**
* Intersect two bitsets
* @param other bitset.
* @return intersection.
*/
Bitset<TStore> Intersect(const Bitset<TStore> &other) const {
DCHECK(this->blocks_.size() == other.blocks_.size())
<< "Bitsets are not of equal size.";
Bitset<TStore> ret(this->blocks_.size() * this->block_size_);
for (int i = 0; i < (int)this->blocks_.size(); ++i) {
ret.blocks_[i] = this->blocks_[i] & other.blocks_[i];
continue;
}
return ret;
}
/**
* Positions of bits set to 1.
* @return positions of bits set to 1.
*/
std::vector<int> Ones() const {
std::vector<int> ret;
int ret_idx = 0;
for (auto x : blocks_) {
while (x) {
auto pos = CountTrailingZeroes(x & -x);
x -= x & -x;
ret.push_back(ret_idx + pos);
}
ret_idx += block_size_;
}
return ret;
}
private:
/**
* Calculate number of trailing zeroes in a binary number, usually a power
* of two.
* @return number of trailing zeroes.
*/
size_t CountTrailingZeroes(TStore v) const {
size_t ret = 0;
while (v >> 32) ret += 32, v >>= 32;
if (v >> 16) ret += 16, v >>= 16;
if (v >> 8) ret += 8, v >>= 8;
if (v >> 4) ret += 4, v >>= 4;
if (v >> 2) ret += 2, v >>= 2;
if (v >> 1) ret += 1, v >>= 1;
return ret;
}
std::vector<TStore> blocks_;
const size_t block_size_;
};

View File

@ -1,90 +0,0 @@
#pragma once
#include <atomic>
#include <condition_variable>
#include <cstdint>
#include <iostream>
#include <mutex>
#include <optional>
#include <queue>
#include <thread>
#include "glog/logging.h"
// Thread safe queue. Probably doesn't perform very well, but it works.
template <typename T>
class Queue {
public:
Queue() = default;
Queue(const Queue &) = delete;
Queue &operator=(const Queue &) = delete;
Queue(Queue &&) = delete;
Queue &operator=(Queue &&) = delete;
void Push(T x) {
std::unique_lock<std::mutex> guard(mutex_);
queue_.emplace(std::move(x));
guard.unlock();
cvar_.notify_one();
}
template <typename... Args>
void Emplace(Args &&... args) {
std::unique_lock<std::mutex> guard(mutex_);
queue_.emplace(std::forward<Args>(args)...);
guard.unlock();
cvar_.notify_one();
}
int64_t size() const {
std::unique_lock<std::mutex> guard(mutex_);
return queue_.size();
}
bool empty() const {
std::unique_lock<std::mutex> guard(mutex_);
return queue_.empty();
}
// Block until there is an element in the queue and then pop it from the queue
// and return it. Function can return nullopt if Queue is signaled via
// Shutdown function or if there is no element to pop after timeout elapses.
std::optional<T> AwaitPop(std::chrono::system_clock::duration timeout =
std::chrono::system_clock::duration::max()) {
std::unique_lock<std::mutex> guard(mutex_);
auto now = std::chrono::system_clock::now();
auto until = std::chrono::system_clock::time_point::max() - timeout > now
? now + timeout
: std::chrono::system_clock::time_point::max();
cvar_.wait_until(guard, until,
[this] { return !queue_.empty() || !alive_; });
if (queue_.empty() || !alive_) return std::nullopt;
std::optional<T> x(std::move(queue_.front()));
queue_.pop();
return x;
}
// Nonblocking version of above function.
std::optional<T> MaybePop() {
std::unique_lock<std::mutex> guard(mutex_);
if (queue_.empty()) return std::nullopt;
std::optional<T> x(std::move(queue_.front()));
queue_.pop();
return x;
}
// Notify all threads waiting on conditional variable to stop waiting. New
// threads that try to Await will not block.
void Shutdown() {
std::unique_lock<std::mutex> guard(mutex_);
alive_ = false;
guard.unlock();
cvar_.notify_all();
}
private:
bool alive_ = true;
std::queue<T> queue_;
std::condition_variable cvar_;
mutable std::mutex mutex_;
};

View File

@ -1,90 +0,0 @@
#pragma once
#include <memory>
#include <vector>
template <class uintXX_t = uint32_t>
/**
* UnionFind data structure. Provides means of connectivity
* setting and checking in O(alpha(n)) amortized complexity. Memory
* complexity is linear.
*/
class UnionFind {
public:
/**
* Constructor, creates a UnionFind structure of fixed size.
*
* @param n Number of elements in the data structure.
*/
explicit UnionFind(uintXX_t n) : set_count_(n), rank_(n), parent_(n) {
for (auto i = 0; i < n; ++i) rank_[i] = 0, parent_[i] = i;
}
/**
* Connects two elements (and thereby the sets they belong
* to). If they are already connected the function has no effect.
*
* Has O(alpha(n)) amortized time complexity.
*
* @param p First element.
* @param q Second element.
*/
void Connect(uintXX_t p, uintXX_t q) {
auto rp = Root(p);
auto rq = Root(q);
// if roots are equal, we don't have to do anything
if (rp == rq) return;
// merge the subtree with the smaller rank to the root of the subtree with
// the larger rank
if (rank_[rp] < rank_[rq])
parent_[rp] = rq;
else if (rank_[rp] > rank_[rq])
parent_[rq] = rp;
else
parent_[rq] = rp, rank_[rp] += 1;
// update the number of groups
set_count_--;
}
/**
* Indicates if two elements are connected. Has amortized O(alpha(n)) time
* complexity.
*
* @param p First element.
* @param q Second element.
* @return See above.
*/
bool Find(uintXX_t p, uintXX_t q) { return Root(p) == Root(q); }
/**
* Returns the number of disjoint sets in this UnionFind.
*
* @return See above.
*/
uintXX_t Size() const { return set_count_; }
private:
uintXX_t set_count_;
// array of subtree ranks
std::vector<uintXX_t> rank_;
// array of tree indices
std::vector<uintXX_t> parent_;
uintXX_t Root(uintXX_t p) {
auto r = p;
auto newp = p;
// find the node connected to itself, that's the root
while (parent_[r] != r) r = parent_[r];
// do some path compression to enable faster searches
while (p != r) newp = parent_[p], parent_[p] = r, p = newp;
return r;
}
};

View File

@ -24,9 +24,6 @@ target_link_libraries(${test_prefix}bolt_encoder mg-communication mg-query)
add_unit_test(commit_log_v2.cpp)
target_link_libraries(${test_prefix}commit_log_v2 glog gflags)
add_unit_test(datastructure_union_find.cpp)
target_link_libraries(${test_prefix}datastructure_union_find glog gflags)
add_unit_test(kvstore.cpp)
target_link_libraries(${test_prefix}kvstore kvstore_lib glog)
@ -112,9 +109,6 @@ target_link_libraries(${test_prefix}typed_value mg-query)
# END mg-query
add_unit_test(queue.cpp)
target_link_libraries(${test_prefix}queue glog gflags)
add_unit_test(skip_list.cpp)
target_link_libraries(${test_prefix}skip_list mg-utils)

View File

@ -1,80 +0,0 @@
#include <stdlib.h>
#include <iostream>
#include "gtest/gtest.h"
#include "data_structures/union_find.hpp"
void ExpectFully(UnionFind<> &uf, bool connected, int from = 0, int to = -1) {
if (to == -1) to = uf.Size();
for (int i = from; i < to; i++)
for (int j = from; j < to; j++)
if (i != j) EXPECT_EQ(uf.Find(i, j), connected);
}
TEST(UnionFindTest, InitialSizeTest) {
for (int i = 0; i < 10; i++) {
UnionFind<> uf(i);
EXPECT_EQ(i, uf.Size());
}
}
TEST(UnionFindTest, ModifiedSizeTest) {
UnionFind<> uf(10);
EXPECT_EQ(10, uf.Size());
uf.Connect(0, 0);
EXPECT_EQ(10, uf.Size());
uf.Connect(0, 1);
EXPECT_EQ(9, uf.Size());
uf.Connect(2, 3);
EXPECT_EQ(8, uf.Size());
uf.Connect(0, 2);
EXPECT_EQ(7, uf.Size());
uf.Connect(1, 3);
EXPECT_EQ(7, uf.Size());
}
TEST(UnionFindTest, Disconectivity) {
UnionFind<> uf(10);
ExpectFully(uf, false);
}
TEST(UnionFindTest, ConnectivityAlongChain) {
UnionFind<> uf(10);
for (unsigned int i = 1; i < uf.Size(); i++) uf.Connect(i - 1, i);
ExpectFully(uf, true);
}
TEST(UnionFindTest, ConnectivityOnTree) {
UnionFind<> uf(10);
ExpectFully(uf, false);
uf.Connect(0, 1);
uf.Connect(0, 2);
ExpectFully(uf, true, 0, 3);
ExpectFully(uf, false, 2);
uf.Connect(2, 3);
ExpectFully(uf, true, 0, 4);
ExpectFully(uf, false, 3);
}
TEST(UnionFindTest, DisjointChains) {
UnionFind<> uf(30);
for (int i = 0; i < 30; i++) uf.Connect(i, i % 10 == 0 ? i : i - 1);
for (int i = 0; i < 30; i++)
for (int j = 0; j < 30; j++)
EXPECT_EQ(uf.Find(i, j), (j - (j % 10)) == (i - (i % 10)));
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@ -1,154 +0,0 @@
#include <atomic>
#include <chrono>
#include <optional>
#include <string>
#include <thread>
#include <utility>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "data_structures/queue.hpp"
namespace {
using namespace std::literals::chrono_literals;
TEST(Queue, PushMaybePop) {
Queue<int> q;
q.Push(1);
EXPECT_EQ(*q.MaybePop(), 1);
EXPECT_EQ(q.MaybePop(), std::nullopt);
q.Push(2);
q.Push(3);
EXPECT_EQ(*q.MaybePop(), 2);
q.Push(4);
q.Push(5);
EXPECT_EQ(*q.MaybePop(), 3);
EXPECT_EQ(*q.MaybePop(), 4);
EXPECT_EQ(*q.MaybePop(), 5);
EXPECT_EQ(q.MaybePop(), std::nullopt);
}
TEST(Queue, Emplace) {
Queue<std::pair<std::string, int>> q;
q.Emplace("abc", 123);
EXPECT_THAT(*q.MaybePop(), testing::Pair("abc", 123));
}
TEST(Queue, Size) {
Queue<int> q;
EXPECT_EQ(q.size(), 0);
q.Push(1);
EXPECT_EQ(q.size(), 1);
q.Push(1);
EXPECT_EQ(q.size(), 2);
q.MaybePop();
EXPECT_EQ(q.size(), 1);
q.MaybePop();
EXPECT_EQ(q.size(), 0);
q.MaybePop();
EXPECT_EQ(q.size(), 0);
}
TEST(Queue, Empty) {
Queue<int> q;
EXPECT_TRUE(q.empty());
q.Push(1);
EXPECT_FALSE(q.empty());
q.MaybePop();
EXPECT_TRUE(q.empty());
}
TEST(Queue, AwaitPop) {
Queue<int> q;
std::thread t([&] {
q.Push(1);
q.Push(2);
std::this_thread::sleep_for(200ms);
q.Push(3);
q.Push(4);
});
EXPECT_EQ(*q.AwaitPop(), 1);
std::this_thread::sleep_for(1000ms);
EXPECT_EQ(*q.AwaitPop(), 2);
EXPECT_EQ(*q.AwaitPop(), 3);
EXPECT_EQ(*q.AwaitPop(), 4);
t.join();
std::thread t2([&] {
std::this_thread::sleep_for(100ms);
q.Shutdown();
});
std::this_thread::sleep_for(200ms);
EXPECT_EQ(q.AwaitPop(), std::nullopt);
t2.join();
}
TEST(Queue, AwaitPopTimeout) {
std::this_thread::sleep_for(1000ms);
Queue<int> q;
EXPECT_EQ(q.AwaitPop(100ms), std::nullopt);
}
TEST(Queue, Concurrent) {
Queue<int> q;
const int kNumProducers = 10;
const int kNumConsumers = 10;
const int kNumElementsPerProducer = 300000;
std::vector<std::thread> producers;
std::atomic<int> next{0};
for (int i = 0; i < kNumProducers; ++i) {
producers.emplace_back([&] {
for (int i = 0; i < kNumElementsPerProducer; ++i) {
q.Push(next++);
}
});
}
std::vector<std::thread> consumers;
std::vector<int> retrieved[kNumConsumers];
std::atomic<int> num_retrieved{0};
for (int i = 0; i < kNumConsumers; ++i) {
consumers.emplace_back(
[&](int thread_id) {
while (true) {
int count = num_retrieved++;
if (count >= kNumProducers * kNumElementsPerProducer) break;
retrieved[thread_id].push_back(*q.AwaitPop());
}
},
i);
}
for (auto &t : consumers) {
t.join();
}
for (auto &t : producers) {
t.join();
}
EXPECT_EQ(q.MaybePop(), std::nullopt);
std::set<int> all_elements;
for (auto &r : retrieved) {
all_elements.insert(r.begin(), r.end());
}
EXPECT_EQ(all_elements.size(), kNumProducers * kNumElementsPerProducer);
EXPECT_EQ(*all_elements.begin(), 0);
EXPECT_EQ(*all_elements.rbegin(),
kNumProducers * kNumElementsPerProducer - 1);
}
} // namespace