#ifndef _TDIGEST_HPP_
#define _TDIGEST_HPP_
#include <cstddef>
#include <limits>
#include <type_traits>
#include <vector>
#include "common_defs.hpp"
namespace datasketches {
struct scale_function {
double max(double q, double normalizer) const {
return q * (1 - q) / normalizer;
}
double normalizer(double compression, double n) const {
return compression / z(compression, n);
}
double z(double compression, double n) const {
return 4 * std::log(n / compression) + 24;
}
};
template <typename T, typename Allocator = std::allocator<T>> class tdigest;
using tdigest_float = tdigest<float>;
using tdigest_double = tdigest<double>;
template <typename T, typename Allocator>
class tdigest {
static_assert(std::is_same<T, double>::value || std::is_same<T, float>::value, "Either double or float type expected");
static_assert(std::numeric_limits<T>::is_iec559, "IEEE 754 compatibility required");
public:
using value_type = T;
using allocator_type = Allocator;
static const uint16_t DEFAULT_K = 200;
using W = typename std::conditional<std::is_same<T, double>::value, uint64_t, uint32_t>::type;
class centroid {
public:
centroid(T value, W weight): mean_(value), weight_(weight) {}
void add(const centroid& other) {
weight_ += other.weight_;
mean_ += (other.mean_ - mean_) * other.weight_ / weight_;
}
T get_mean() const { return mean_; }
W get_weight() const { return weight_; }
private:
T mean_;
W weight_;
};
using vector_t = std::vector<T, Allocator>;
using vector_centroid = std::vector<centroid, typename std::allocator_traits<Allocator>::template rebind_alloc<centroid>>;
using vector_bytes = std::vector<uint8_t, typename std::allocator_traits<Allocator>::template rebind_alloc<uint8_t>>;
using vector_double = std::vector<double, typename std::allocator_traits<Allocator>::template rebind_alloc<double>>;
struct centroid_cmp {
centroid_cmp() {}
bool operator()(const centroid& a, const centroid& b) const {
if (a.get_mean() < b.get_mean()) return true;
return false;
}
};
explicit tdigest(uint16_t k = DEFAULT_K, const Allocator& allocator = Allocator());
void update(T value);
void merge(const tdigest& other);
void compress();
bool is_empty() const;
T get_min_value() const;
T get_max_value() const;
uint64_t get_total_weight() const;
Allocator get_allocator() const;
double get_rank(T value) const;
T get_quantile(double rank) const;
vector_double get_PMF(const T* split_points, uint32_t size) const;
vector_double get_CDF(const T* split_points, uint32_t size) const;
uint16_t get_k() const;
string<Allocator> to_string(bool print_centroids = false) const;
size_t get_serialized_size_bytes(bool with_buffer = false) const;
void serialize(std::ostream& os, bool with_buffer = false) const;
vector_bytes serialize(unsigned header_size_bytes = 0, bool with_buffer = false) const;
static tdigest deserialize(std::istream& is, const Allocator& allocator = Allocator());
static tdigest deserialize(const void* bytes, size_t size, const Allocator& allocator = Allocator());
private:
bool reverse_merge_;
uint16_t k_;
uint16_t internal_k_;
T min_;
T max_;
size_t centroids_capacity_;
vector_centroid centroids_;
uint64_t centroids_weight_;
size_t buffer_capacity_;
vector_t buffer_;
static const size_t BUFFER_MULTIPLIER = 4;
static const uint8_t PREAMBLE_LONGS_EMPTY_OR_SINGLE = 1;
static const uint8_t PREAMBLE_LONGS_MULTIPLE = 2;
static const uint8_t SERIAL_VERSION = 1;
static const uint8_t SKETCH_TYPE = 20;
static const uint8_t COMPAT_DOUBLE = 1;
static const uint8_t COMPAT_FLOAT = 2;
enum flags { IS_EMPTY, IS_SINGLE_VALUE, REVERSE_MERGE };
bool is_single_value() const;
uint8_t get_preamble_longs() const;
void merge(vector_centroid& buffer, W weight);
tdigest(bool reverse_merge, uint16_t k, T min, T max, vector_centroid&& centroids, uint64_t total_weight_, vector_t&& buffer);
static double weighted_average(double x1, double w1, double x2, double w2);
static tdigest deserialize_compat(std::istream& is, const Allocator& allocator = Allocator());
static tdigest deserialize_compat(const void* bytes, size_t size, const Allocator& allocator = Allocator());
static inline void check_split_points(const T* values, uint32_t size);
};
}
#include "tdigest_impl.hpp"
#endif