From 78e553b7249cbf683183e29eefaee7997137407e Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Tue, 2 May 2023 17:31:56 +0300 Subject: [PATCH] Added usage of segment tree in Data class for statistics. --- Telegram/SourceFiles/data/data_statistics.cpp | 141 ++++++++++++++++++ Telegram/SourceFiles/data/data_statistics.h | 49 ++++++ 2 files changed, 190 insertions(+) diff --git a/Telegram/SourceFiles/data/data_statistics.cpp b/Telegram/SourceFiles/data/data_statistics.cpp index 604797551e..d663530267 100644 --- a/Telegram/SourceFiles/data/data_statistics.cpp +++ b/Telegram/SourceFiles/data/data_statistics.cpp @@ -11,4 +11,145 @@ namespace Data { namespace { } // namespace +void StatisticalChart::measure() { + if (x.empty()) { + return; + } + const auto n = x.size(); + const auto start = x.front(); + const auto end = x.back(); + + xPercentage.clear(); + xPercentage.resize(n); + if (n == 1) { + xPercentage[0] = 1; + } else { + for (auto i = 0; i < n; i++) { + xPercentage[i] = (x[i] - start) / float64(end - start); + } + } + + for (auto i = 0; i < lines.size(); i++) { + if (lines[i].maxValue > maxValue) { + maxValue = lines[i].maxValue; + } + if (lines[i].minValue < minValue) { + minValue = lines[i].minValue; + } + lines[i].segmentTree = Statistic::SegmentTree(lines[i].y); + } + + daysLookup.clear(); + const auto dateCount = int((end - start) / timeStep) + 10; + daysLookup.reserve(dateCount); + constexpr auto kOneDay = 3600 * 24 * 1000; + const auto formatter = u"MMM d"_q; + for (auto i = 0; i < dateCount; i++) { + const auto r = (start + (i * timeStep)) / 1000; + const auto dateTime = QDateTime::fromSecsSinceEpoch(r); + if (timeStep == 1) { + daysLookup.push_back( + QString(((i < 10) ? u"0%1:00"_q : u"%1:00"_q).arg(i))); + } else if (timeStep < kOneDay) { + daysLookup.push_back(u"%1:%2"_q + .arg(dateTime.time().hour(), 2, 10, QChar('0')) + .arg(dateTime.time().minute(), 2, 10, QChar('0'))); + } else { + const auto date = dateTime.date(); + daysLookup.push_back(QLocale().toString(date, formatter)); + } + } + + oneDayPercentage = timeStep / float64(end - start); +} + +QString StatisticalChart::getDayString(int i) const { + return daysLookup[int((x[i] - x[0]) / timeStep)]; +} + +int StatisticalChart::findStartIndex(float v) const { + if (v == 0) { + return 0; + } + const auto n = int(xPercentage.size()); + + if (n < 2) { + return 0; + } + auto left = 0; + auto right = n - 1; + + while (left <= right) { + const auto middle = (right + left) >> 1; + if (v < xPercentage[middle] + && (middle == 0 || v > xPercentage[middle - 1])) { + return middle; + } + if (v == xPercentage[middle]) { + return middle; + } + if (v < xPercentage[middle]) { + right = middle - 1; + } else if (v > xPercentage[middle]) { + left = middle + 1; + } + } + return left; +} + +int StatisticalChart::findEndIndex(int left, float v) const { + const auto n = int(xPercentage.size()); + if (v == 1.) { + return n - 1; + } + auto right = n - 1; + + while (left <= right) { + const auto middle = (right + left) >> 1; + if (v > xPercentage[middle] + && (middle == n - 1 || v < xPercentage[middle + 1])) { + return middle; + } + if (v == xPercentage[middle]) { + return middle; + } + if (v < xPercentage[middle]) { + right = middle - 1; + } else if (v > xPercentage[middle]) { + left = middle + 1; + } + } + return right; +} + + +int StatisticalChart::findIndex(int left, int right, float v) const { + const auto n = int(xPercentage.size()); + + if (v <= xPercentage[left]) { + return left; + } + if (v >= xPercentage[right]) { + return right; + } + + while (left <= right) { + const auto middle = (right + left) >> 1; + if (v > xPercentage[middle] + && (middle == n - 1 || v < xPercentage[middle + 1])) { + return middle; + } + + if (v == xPercentage[middle]) { + return middle; + } + if (v < xPercentage[middle]) { + right = middle - 1; + } else if (v > xPercentage[middle]) { + left = middle + 1; + } + } + return right; +} + } // namespace Data diff --git a/Telegram/SourceFiles/data/data_statistics.h b/Telegram/SourceFiles/data/data_statistics.h index dd0c640719..fbfe0f9139 100644 --- a/Telegram/SourceFiles/data/data_statistics.h +++ b/Telegram/SourceFiles/data/data_statistics.h @@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +#include "statistics/segment_tree.h" + namespace Data { struct StatisticsMessageInteractionInfo final { @@ -33,7 +35,54 @@ struct StatisticsInviterInfo final { int addedMemberCount = 0; }; +struct StatisticalChart { + StatisticalChart() = default; + + [[nodiscard]] bool empty() const { + return lines.empty(); + } + [[nodiscard]] explicit operator bool() const { + return !empty(); + } + + void measure(); + + [[nodiscard]] QString getDayString(int i) const; + + [[nodiscard]] int findStartIndex(float v) const; + [[nodiscard]] int findEndIndex(int left, float v) const; + [[nodiscard]] int findIndex(int left, int right, float v) const; + + struct Line final { + std::vector y; + + Statistic::SegmentTree segmentTree; + QString id; + QString name; + int maxValue = 0; + int minValue = std::numeric_limits::max(); + int colorKey = 0; + QColor color; + QColor colorDark; + }; + + std::vector x; + std::vector xPercentage; + std::vector daysLookup; + + std::vector lines; + + int maxValue = 0; + int minValue = std::numeric_limits::max(); + + float64 oneDayPercentage = 0.; + + float64 timeStep = 0.; + +}; + struct StatisticalGraph final { + StatisticalChart chart; }; struct StatisticalValue final {