Skip to content
This repository was archived by the owner on Jul 21, 2022. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
cmake_minimum_required (VERSION 2.8)

set(CMAKE_VERBOSE_MAKEFILE on)

add_subdirectory (src)
6 changes: 3 additions & 3 deletions src/tdigest/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ add_library (tdigest
tdigest.cpp
)

list(APPEND CMAKE_CXX_FLAGS "-std=c++11 -O0 -g -fprofile-arcs -ftest-coverage -DNDEBUG ${CMAKE_CXX_FLAGS}")
list(APPEND CMAKE_C_FLAGS " -O0 -g -fprofile-arcs -ftest-coverage -DNDEBUG ${CMAKE_C_FLAGS}")
list(APPEND CMAKE_EXE_LINKER_FLAGS "-O0 -g -fprofile-arcs -ftest-coverage -DNDEBUG ${CMAKE_EXE_LINKER_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -O0 -g -fprofile-arcs -ftest-coverage -DNDEBUG")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0 -g -fprofile-arcs -ftest-coverage -DNDEBUG")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -O0 -g -fprofile-arcs -ftest-coverage -DNDEBUG")

add_executable (demo main.cpp)
target_link_libraries (demo tdigest)
Expand Down
68 changes: 68 additions & 0 deletions src/tdigest/avltree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -254,3 +254,71 @@ void AvlTree::rotateRight(int node) {
updateAggregates(node);
updateAggregates(parentNode(node));
}


struct SerializedTreeNode
{
int parent;
int left;
int right;
char depth;
int count;
double values;
int aggregatedCount;
};

struct SerializedTree
{
int root;
int n;
SerializedTreeNode nodes[0];
};

void AvlTree::save(std::string& out) {

size_t len = (sizeof(SerializedTree) +
((_n + 1) * sizeof(SerializedTreeNode)));
char* buf = (char*)malloc(len);

SerializedTree* header = (SerializedTree*)buf;
header->n = _n;
header->root = _root;

for (int idx = 0; idx <= _n; idx ++)
{
header->nodes[idx].parent = _parent[idx];
header->nodes[idx].left = _left[idx];
header->nodes[idx].right = _right[idx];
header->nodes[idx].depth = _depth[idx];
header->nodes[idx].count = _count[idx];
header->nodes[idx].values = _values[idx];
header->nodes[idx].aggregatedCount = _aggregatedCount[idx];
}

out.append(buf, len);
free(buf);
}

void AvlTree::load(const std::string& in) {
SerializedTree* header = (SerializedTree*)in.data();

assert(header->n > 0);
assert(header->n < MAXNODE);

_root = header->root;
_n = header->n;

SerializedTreeNode* nodes = (SerializedTreeNode*)
(in.data() + sizeof(SerializedTree));

for (int idx = 0; idx <= _n; idx ++)
{
_parent[idx] = nodes[idx].parent;
_left[idx] = nodes[idx].left;
_right[idx] = nodes[idx].right;
_depth[idx] = nodes[idx].depth;
_count[idx] = nodes[idx].count;
_values[idx] = nodes[idx].values;
_aggregatedCount[idx] = nodes[idx].aggregatedCount;
}
}
26 changes: 15 additions & 11 deletions src/tdigest/avltree.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#ifndef HEADER_AVLTREE
#define HEADER_AVLTREE
#pragma once

#include <cassert>
#include <cmath>
Expand All @@ -12,21 +11,24 @@ using namespace std;
class AvlTree {

private:

static constexpr size_t MAXNODE = 1000;

int _root;
int _n = 0;
// TODO We should reallocate tables (ie allow dynamic arrays)
int _parent[1000];
int _left[1000];
int _right[1000];
char _depth[1000];
int _count[1000];
double _values[1000];
int _aggregatedCount[1000];
int _parent[MAXNODE];
int _left[MAXNODE];
int _right[MAXNODE];
char _depth[MAXNODE];
int _count[MAXNODE];
double _values[MAXNODE];
int _aggregatedCount[MAXNODE];

public:
static const int NIL = 0;

AvlTree();
explicit AvlTree();

//
// Node comparison
Expand Down Expand Up @@ -252,6 +254,8 @@ class AvlTree {
print(_root);
}

void save(std::string& out);

void load(const std::string& in);
};

#endif
26 changes: 26 additions & 0 deletions src/tdigest/tdigest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,29 @@ double TDigest::quantile(double q) {

}

struct SerializedDigest
{
double compression;
double count;
};

void TDigest::save(std::string& out)
{
SerializedDigest d;
d.compression = _compression;
d.count = _count;

out.append((char*)&d, sizeof(d));

_centroids->save(out);
}

void TDigest::load(const std::string& in)
{
SerializedDigest* d = (SerializedDigest*)in.data();
_compression = d->compression;
_count = d->count;

_centroids->load(in.substr(sizeof(*d), in.size() - sizeof(*d)));
}

18 changes: 12 additions & 6 deletions src/tdigest/tdigest.hpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#ifndef HEADER_TDIGEST
#define HEADER_TDIGEST
#pragma once

#include <cfloat>
#include <memory>

#include "avltree.hpp"

Expand All @@ -14,10 +14,14 @@ class TDigest {
private:
double _compression = 100;
double _count = 0;
AvlTree* _centroids = new AvlTree();
std::unique_ptr<AvlTree> _centroids;

public:
TDigest (double compression): _compression(compression) {}
TDigest (double compression)
: _compression(compression)
{
_centroids = std::unique_ptr<AvlTree>(new AvlTree());
}

inline long size() const {
return _count;
Expand Down Expand Up @@ -104,7 +108,7 @@ class TDigest {
}

inline AvlTree* centroids() const {
return _centroids;
return _centroids.get();
}

inline void merge(TDigest* digest) {
Expand All @@ -117,6 +121,8 @@ class TDigest {
void compress();
double quantile(double q);

void save(std::string& out);

void load(const std::string& in);
};

#endif
19 changes: 15 additions & 4 deletions src/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
project(cpptdigest-tests)

enable_testing()
list(APPEND CMAKE_CXX_FLAGS "-std=c++11 -O0 -g -fprofile-arcs -ftest-coverage -DNDEBUG ${CMAKE_CXX_FLAGS}")
list(APPEND CMAKE_C_FLAGS " -O0 -g -fprofile-arcs -ftest-coverage -DNDEBUG ${CMAKE_C_FLAGS}")
list(APPEND CMAKE_EXE_LINKER_FLAGS "-O0 -g -fprofile-arcs -ftest-coverage -DNDEBUG ${CMAKE_EXE_LINKER_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -O0 -g -fprofile-arcs -ftest-coverage -DNDEBUG")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0 -g -fprofile-arcs -ftest-coverage -DNDEBUG ")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -O0 -g -fprofile-arcs -ftest-coverage -DNDEBUG")

find_package(GTest REQUIRED)
include_directories(${GTEST_INCLUDE_DIRS})
Expand All @@ -13,6 +13,17 @@ add_executable (AvlTreeTest avltree.cpp)
target_link_libraries (AvlTreeTest
tdigest
${GTEST_BOTH_LIBRARIES}
pthread
)

add_test(TestAvlTree AvlTreeTest)
add_executable (TDigestTest tdigest_test.cpp)

target_link_libraries (TDigestTest
tdigest
${GTEST_BOTH_LIBRARIES}
pthread
)

add_test(TestAvlTree
AvlTreeTest
TDigestTest)
31 changes: 31 additions & 0 deletions src/tests/avltree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,34 @@ TEST(AvlTreeTest, GenericTest) {
ASSERT_EQ(tree->checkAggregates(), true);
ASSERT_EQ(tree->checkIntegrity(), true);
}

TEST(AvlTreeTest, LoadSave) {
AvlTree* tree = new AvlTree();

ASSERT_EQ(tree->root(), 0);
ASSERT_EQ(tree->size(), 0);

tree->add(5., 3);
tree->add(8., 1);
tree->add(9., 2);
tree->add(4., 2);
tree->add(7., 2);
tree->add(6., 2);
tree->add(2., 2);
tree->add(1., 6);
tree->add(3., 6);

std::string out_string;
tree->save(out_string);

AvlTree* newtree = new AvlTree();
newtree->load(out_string);

EXPECT_EQ(tree->root(), newtree->root());
EXPECT_EQ(tree->size(), newtree->size());

ASSERT_EQ(newtree->checkBalance(), true);
ASSERT_EQ(newtree->checkAggregates(), true);
ASSERT_EQ(newtree->checkIntegrity(), true);

}
21 changes: 21 additions & 0 deletions src/tests/tdigest_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#include "../tdigest/tdigest.hpp"

#include <gtest/gtest.h>

TEST(TDigestTest, LoadSave) {
TDigest* tdigest = new TDigest(100);

for (int i = 0; i < 1000; i++)
{
tdigest->add(rand() % 1001);
}

std::string str;
tdigest->save(str);

TDigest* new_digest = new TDigest(100);
new_digest->load(str);

EXPECT_EQ(new_digest->quantile(0.5), tdigest->quantile(0.5));
}