diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index ca2fccad8..7be00c5e5 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -1282,6 +1282,8 @@ PRIVATE settings/settings_type.h settings/settings_websites.cpp settings/settings_websites.h + statistics/segment_tree.cpp + statistics/segment_tree.h statistics/statistics_box.cpp statistics/statistics_box.h storage/details/storage_file_utilities.cpp diff --git a/Telegram/SourceFiles/statistics/segment_tree.cpp b/Telegram/SourceFiles/statistics/segment_tree.cpp new file mode 100644 index 000000000..385560964 --- /dev/null +++ b/Telegram/SourceFiles/statistics/segment_tree.cpp @@ -0,0 +1,151 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "statistics/segment_tree.h" + +namespace Statistic { +namespace { + +constexpr auto kMinArraySize = size_t(30); + +} // namespace + +SegmentTree::SegmentTree(std::vector array) +: _array(std::move(array)) { + if (_array.size() < kMinArraySize) { + return; + } + + // The max size of this array is about 2 * 2 ^ log2(n) + 1. + const auto size = 2 * std::pow( + 2., + std::floor((std::logf(_array.size()) / std::logf(2.)) + 1)); + _heap.resize(int(size)); + build(1, 0, _array.size()); +} + +void SegmentTree::build(int v, int from, int size) { + _heap[v].from = from; + _heap[v].to = (from + size - 1); + + if (size == 1) { + _heap[v].sum = _array[from]; + _heap[v].max = _array[from]; + _heap[v].min = _array[from]; + } else { + // Build childs. + build(2 * v, from, size / 2); + build(2 * v + 1, from + size / 2, size - size / 2); + + _heap[v].sum = _heap[2 * v].sum + _heap[2 * v + 1].sum; + // max = max of the children. + _heap[v].max = std::max(_heap[2 * v].max, _heap[2 * v + 1].max); + _heap[v].min = std::min(_heap[2 * v].min, _heap[2 * v + 1].min); + } +} + +int SegmentTree::rMaxQ(int from, int to) { + if (_array.size() < kMinArraySize) { + auto max = std::numeric_limits::min(); + from = std::max(from, 0); + to = std::min(to, int(_array.size() - 1)); + for (auto i = from; i <= to; i++) { + max = std::max(max, _array[i]); + } + return max; + } + return rMaxQ(1, from, to); +} + +int SegmentTree::rMaxQ(int v, int from, int to) { + const auto &n = _heap[v]; + // If you did a range update that contained this node, + // you can infer the Min value without going down the tree. + if (n.pendingVal && contains(n.from, n.to, from, to)) { + return n.pendingVal.value; + } + + if (contains(from, to, n.from, n.to)) { + return _heap[v].max; + } + + if (intersects(from, to, n.from, n.to)) { + propagate(v); + const auto leftMin = rMaxQ(2 * v, from, to); + const auto rightMin = rMaxQ(2 * v + 1, from, to); + + return std::max(leftMin, rightMin); + } + + return 0; +} + +int SegmentTree::rMinQ(int from, int to) { + if (_array.size() < kMinArraySize) { + auto min = std::numeric_limits::max(); + from = std::max(from, 0); + to = std::min(to, int(_array.size() - 1)); + for (auto i = from; i <= to; i++) { + min = std::min(min, _array[i]); + } + return min; + } + return rMinQ(1, from, to); +} + +int SegmentTree::rMinQ(int v, int from, int to) { + const auto &n = _heap[v]; + // If you did a range update that contained this node, + // you can infer the Min value without going down the tree. + if (n.pendingVal && contains(n.from, n.to, from, to)) { + return n.pendingVal.value; + } + + if (contains(from, to, n.from, n.to)) { + return _heap[v].min; + } + + if (intersects(from, to, n.from, n.to)) { + propagate(v); + const auto leftMin = rMinQ(2 * v, from, to); + const auto rightMin = rMinQ(2 * v + 1, from, to); + + return std::min(leftMin, rightMin); + } + + return std::numeric_limits::max(); +} + +void SegmentTree::propagate(int v) { + auto &n = _heap[v]; + + if (n.pendingVal) { + const auto value = n.pendingVal.value; + n.pendingVal = {}; + change(_heap[2 * v], value); + change(_heap[2 * v + 1], value); + } +} + +void SegmentTree::change(SegmentTree::Node &n, int value) { + n.pendingVal = { value, true }; + n.sum = n.size() * value; + n.max = value; + n.min = value; + _array[n.from] = value; +} + +bool SegmentTree::contains(int from1, int to1, int from2, int to2) const { + return (from2 >= from1) && (to2 <= to1); +} + +bool SegmentTree::intersects(int from1, int to1, int from2, int to2) const { + return ((from1 <= from2) && (to1 >= from2)) // (.[..)..] or (.[...]..) + || ((from1 >= from2) && (from1 <= to2)); // [.(..]..) or [..(..).. +} + +} // namespace Statistic diff --git a/Telegram/SourceFiles/statistics/segment_tree.h b/Telegram/SourceFiles/statistics/segment_tree.h new file mode 100644 index 000000000..e67adc76a --- /dev/null +++ b/Telegram/SourceFiles/statistics/segment_tree.h @@ -0,0 +1,69 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +namespace Statistic { + +class SegmentTree final { +public: + SegmentTree() = default; + SegmentTree(std::vector array); + + [[nodiscard]] bool empty() const { + return _array.empty(); + } + [[nodiscard]] explicit operator bool() const { + return !empty(); + } + + [[nodiscard]] int rMaxQ(int from, int to); + [[nodiscard]] int rMinQ(int from, int to); + +private: + struct Node final { + int sum = 0; + int max = 0; + int min = 0; + + struct PendingVal { + [[nodiscard]] explicit operator bool() const { + return available; + } + int value = 0; + bool available = false; + }; + PendingVal pendingVal; + + int from = 0; + int to = 0; + + [[nodiscard]] int size() { + return to - from + 1; + } + }; + + void build(int v, int from, int size); + void propagate(int v); + void change(Node &n, int value); + + [[nodiscard]] int rMaxQ(int v, int from, int to); + [[nodiscard]] int rMinQ(int v, int from, int to); + + [[nodiscard]] bool contains(int from1, int to1, int from2, int to2) const; + [[nodiscard]] bool intersects( + int from1, + int to1, + int from2, + int to2) const; + + std::vector _array; + std::vector _heap; + +}; + +} // namespace Statistic