aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp/histogram/adaptive/adaptive_histogram.cpp
diff options
context:
space:
mode:
authorDevtools Arcadia <arcadia-devtools@yandex-team.ru>2022-02-07 18:08:42 +0300
committerDevtools Arcadia <arcadia-devtools@mous.vla.yp-c.yandex.net>2022-02-07 18:08:42 +0300
commit1110808a9d39d4b808aef724c861a2e1a38d2a69 (patch)
treee26c9fed0de5d9873cce7e00bc214573dc2195b7 /library/cpp/histogram/adaptive/adaptive_histogram.cpp
downloadydb-1110808a9d39d4b808aef724c861a2e1a38d2a69.tar.gz
intermediate changes
ref:cde9a383711a11544ce7e107a78147fb96cc4029
Diffstat (limited to 'library/cpp/histogram/adaptive/adaptive_histogram.cpp')
-rw-r--r--library/cpp/histogram/adaptive/adaptive_histogram.cpp637
1 files changed, 637 insertions, 0 deletions
diff --git a/library/cpp/histogram/adaptive/adaptive_histogram.cpp b/library/cpp/histogram/adaptive/adaptive_histogram.cpp
new file mode 100644
index 0000000000..cbfc494021
--- /dev/null
+++ b/library/cpp/histogram/adaptive/adaptive_histogram.cpp
@@ -0,0 +1,637 @@
+#include "adaptive_histogram.h"
+
+#include <util/generic/algorithm.h>
+#include <util/generic/yexception.h>
+#include <util/generic/ymath.h>
+#include <util/string/printf.h>
+
+#include <util/system/backtrace.h>
+
+namespace NKiwiAggr {
+ TAdaptiveHistogram::TAdaptiveHistogram(size_t intervals, ui64 id, TQualityFunction qualityFunc)
+ : Id(id)
+ , Sum(0.0)
+ , Intervals(intervals)
+ , CalcQuality(qualityFunc)
+ {
+ }
+
+ TAdaptiveHistogram::TAdaptiveHistogram(const THistogram& histo, size_t defaultIntervals, ui64 defaultId, TQualityFunction qualityFunc)
+ : TAdaptiveHistogram(defaultIntervals, defaultId, qualityFunc)
+ {
+ FromProto(histo);
+ }
+
+ TAdaptiveHistogram::TAdaptiveHistogram(IHistogram* histo, size_t defaultIntervals, ui64 defaultId, TQualityFunction qualityFunc)
+ : TAdaptiveHistogram(defaultIntervals, defaultId, qualityFunc)
+ {
+ TAdaptiveHistogram* adaptiveHisto = dynamic_cast<TAdaptiveHistogram*>(histo);
+ if (!adaptiveHisto) {
+ FromIHistogram(histo);
+ return;
+ }
+ Id = adaptiveHisto->Id;
+ MinValue = adaptiveHisto->MinValue;
+ MaxValue = adaptiveHisto->MaxValue;
+ Sum = adaptiveHisto->Sum;
+ Intervals = adaptiveHisto->Intervals;
+ Bins = adaptiveHisto->Bins;
+ BinsByQuality = adaptiveHisto->BinsByQuality;
+ if (CalcQuality == nullptr) {
+ CalcQuality = adaptiveHisto->CalcQuality;
+ }
+ }
+
+ TQualityFunction TAdaptiveHistogram::GetQualityFunc() {
+ return CalcQuality;
+ }
+
+ void TAdaptiveHistogram::Clear() {
+ Sum = 0.0;
+ Bins.clear();
+ BinsByQuality.clear();
+ }
+
+ void TAdaptiveHistogram::Add(const THistoRec& histoRec) {
+ if (!histoRec.HasId() || histoRec.GetId() == Id) {
+ Add(histoRec.GetValue(), histoRec.GetWeight());
+ }
+ }
+
+ void TAdaptiveHistogram::Add(double value, double weight) {
+ if (!IsValidFloat(value) || !IsValidFloat(weight)) {
+ ythrow yexception() << Sprintf("Histogram id %lu: bad value %f weight %f", Id, value, weight);
+ }
+ TWeightedValue weightedValue(value, weight);
+ Add(weightedValue, true);
+ PrecomputedBins.clear();
+ }
+
+ void TAdaptiveHistogram::Merge(const THistogram& histo, double multiplier) {
+ if (!IsValidFloat(histo.GetMinValue()) || !IsValidFloat(histo.GetMaxValue())) {
+ fprintf(stderr, "Merging in histogram id %lu: skip bad histo with minvalue %f maxvalue %f\n", Id, histo.GetMinValue(), histo.GetMaxValue());
+ return;
+ }
+ if (histo.FreqSize() == 0) {
+ return; // skip empty histos
+ }
+ if (histo.GetType() == HT_ADAPTIVE_DISTANCE_HISTOGRAM ||
+ histo.GetType() == HT_ADAPTIVE_WEIGHT_HISTOGRAM ||
+ histo.GetType() == HT_ADAPTIVE_WARD_HISTOGRAM ||
+ histo.GetType() == HT_ADAPTIVE_HISTOGRAM)
+ {
+ Y_VERIFY(histo.FreqSize() == histo.PositionSize(), "Corrupted histo");
+ for (size_t j = 0; j < histo.FreqSize(); ++j) {
+ double value = histo.GetPosition(j);
+ double weight = histo.GetFreq(j);
+ if (!IsValidFloat(value) || !IsValidFloat(weight)) {
+ fprintf(stderr, "Merging in histogram id %lu: skip bad value %f weight %f\n", Id, value, weight);
+ continue;
+ }
+ Add(value, weight * multiplier);
+ }
+
+ MinValue = Min(MinValue, histo.GetMinValue());
+ MaxValue = Max(MaxValue, histo.GetMaxValue());
+ } else if (histo.GetType() == HT_FIXED_BIN_HISTOGRAM) {
+ double pos = histo.GetMinValue() + histo.GetBinRange() / 2.0;
+ for (size_t j = 0; j < histo.FreqSize(); ++j) {
+ double weight = histo.GetFreq(j);
+ if (!IsValidFloat(pos) || !IsValidFloat(weight)) {
+ fprintf(stderr, "Merging in histogram id %lu: skip bad value %f weight %f\n", Id, pos, weight);
+ pos += histo.GetBinRange();
+ continue;
+ }
+ Add(pos, weight * multiplier);
+ pos += histo.GetBinRange();
+ }
+
+ MinValue = Min(MinValue, histo.GetMinValue());
+ MaxValue = Max(MaxValue, histo.GetMaxValue());
+ } else {
+ ythrow yexception() << "Unknown THistogram type";
+ }
+ }
+
+ void TAdaptiveHistogram::Merge(const TVector<THistogram>& histogramsToMerge) {
+ for (size_t i = 0; i < histogramsToMerge.size(); ++i) {
+ Merge(histogramsToMerge[i], 1.0);
+ }
+ }
+
+ void TAdaptiveHistogram::Merge(TVector<IHistogramPtr> histogramsToMerge) {
+ TVector<IHistogramPtr> histogramsToMergeRepacked(0);
+ TVector<TAdaptiveHistogram*> histograms(0);
+ for (size_t i = 0; i < histogramsToMerge.size(); ++i) {
+ if (!histogramsToMerge[i] || histogramsToMerge[i]->Empty()) {
+ continue;
+ }
+ TAdaptiveHistogram* adaptiveHisto = dynamic_cast<TAdaptiveHistogram*>(histogramsToMerge[i].Get());
+ if (adaptiveHisto) {
+ histogramsToMergeRepacked.push_back(histogramsToMerge[i]);
+ } else {
+ histogramsToMergeRepacked.push_back(IHistogramPtr(new TAdaptiveHistogram(histogramsToMerge[i].Get(), Intervals, Id, CalcQuality))); // Convert histograms that are not of TFixedBinHistogram type
+ }
+ if (histogramsToMergeRepacked.back()->Empty()) {
+ continue;
+ }
+ histograms.push_back(dynamic_cast<TAdaptiveHistogram*>(histogramsToMergeRepacked.back().Get()));
+ }
+
+ if (histograms.size() == 0) {
+ return;
+ }
+
+ for (size_t histoIndex = 0; histoIndex < histograms.size(); ++histoIndex) {
+ TAdaptiveHistogram* histo = histograms[histoIndex];
+ for (TPairSet::const_iterator it = histo->Bins.begin(); it != histo->Bins.end(); ++it) {
+ Add(*it, true);
+ }
+ }
+
+ for (size_t i = 0; i < histograms.size(); ++i) {
+ MinValue = Min(MinValue, histograms[i]->MinValue);
+ MaxValue = Max(MaxValue, histograms[i]->MaxValue);
+ }
+ }
+
+ void TAdaptiveHistogram::Multiply(double factor) {
+ if (!IsValidFloat(factor) || factor <= 0) {
+ ythrow yexception() << "Not valid factor in IHistogram::Multiply(): " << factor;
+ }
+ Sum *= factor;
+
+ TPairSet newBins;
+ for (TPairSet::iterator it = Bins.begin(); it != Bins.end(); ++it) {
+ newBins.insert(TWeightedValue(it->first, it->second * factor));
+ }
+ Bins = newBins;
+
+ TPairSet newBinsByQuality;
+ for (TPairSet::iterator it = Bins.begin(); it != Bins.end(); ++it) {
+ TPairSet::iterator rightBin = it;
+ ++rightBin;
+ if (rightBin == Bins.end()) {
+ break;
+ }
+ newBinsByQuality.insert(CalcQuality(*it, *rightBin));
+ }
+ BinsByQuality = newBinsByQuality;
+ }
+
+ void TAdaptiveHistogram::FromProto(const THistogram& histo) {
+ Y_VERIFY(histo.HasType(), "Attempt to parse TAdaptiveHistogram from THistogram protobuf with no Type field set");
+ ;
+ switch (histo.GetType()) { // check that histogram type could be deduced
+ case HT_ADAPTIVE_DISTANCE_HISTOGRAM:
+ case HT_ADAPTIVE_WEIGHT_HISTOGRAM:
+ case HT_ADAPTIVE_WARD_HISTOGRAM:
+ break; // ok
+ case HT_ADAPTIVE_HISTOGRAM:
+ if (CalcQuality != nullptr)
+ break; // ok
+ [[fallthrough]];
+ default: // not ok
+ ythrow yexception() << "Attempt to parse TAdaptiveHistogram from THistogram protobuf record of type = " << (ui32)histo.GetType();
+ }
+
+ if (histo.FreqSize() != histo.PositionSize()) {
+ ythrow yexception() << "Attempt to parse TAdaptiveHistogram from THistogram protobuf record where FreqSize != PositionSize. FreqSize == " << (ui32)histo.FreqSize() << ", PositionSize == " << (ui32)histo.PositionSize();
+ }
+ if (CalcQuality == nullptr) {
+ if (histo.GetType() == HT_ADAPTIVE_DISTANCE_HISTOGRAM) {
+ CalcQuality = CalcDistanceQuality;
+ } else if (histo.GetType() == HT_ADAPTIVE_WEIGHT_HISTOGRAM) {
+ CalcQuality = CalcWeightQuality;
+ } else if (histo.GetType() == HT_ADAPTIVE_WARD_HISTOGRAM) {
+ CalcQuality = CalcWardQuality;
+ } else {
+ ythrow yexception() << "Attempt to parse an HT_ADAPTIVE_HISTOGRAM without default quality function";
+ }
+ }
+ Id = histo.GetId();
+ Sum = 0.0;
+ Intervals = Max(Intervals, histo.FreqSize());
+ for (size_t i = 0; i < histo.FreqSize(); ++i) {
+ double value = histo.GetPosition(i);
+ double weight = histo.GetFreq(i);
+ if (!IsValidFloat(value) || !IsValidFloat(weight)) {
+ fprintf(stderr, "FromProto in histogram id %lu: skip bad value %f weight %f\n", Id, value, weight);
+ continue;
+ }
+ Add(value, weight);
+ }
+
+ if (!IsValidFloat(histo.GetMinValue()) || !IsValidFloat(histo.GetMaxValue())) {
+ ythrow yexception() << Sprintf("FromProto in histogram id %lu: skip bad histo with minvalue %f maxvalue %f", Id, histo.GetMinValue(), histo.GetMaxValue());
+ }
+
+ MinValue = histo.GetMinValue();
+ MaxValue = histo.GetMaxValue();
+ }
+
+ void TAdaptiveHistogram::ToProto(THistogram& histo) {
+ histo.Clear();
+ if (CalcQuality == CalcDistanceQuality) {
+ histo.SetType(HT_ADAPTIVE_DISTANCE_HISTOGRAM);
+ } else if (CalcQuality == CalcWeightQuality) {
+ histo.SetType(HT_ADAPTIVE_WEIGHT_HISTOGRAM);
+ } else if (CalcQuality == CalcWardQuality) {
+ histo.SetType(HT_ADAPTIVE_WARD_HISTOGRAM);
+ } else {
+ histo.SetType(HT_ADAPTIVE_HISTOGRAM);
+ }
+ histo.SetId(Id);
+ if (Empty()) {
+ return;
+ }
+ histo.SetMinValue(MinValue);
+ histo.SetMaxValue(MaxValue);
+ for (TPairSet::const_iterator it = Bins.begin(); it != Bins.end(); ++it) {
+ histo.AddFreq(it->second);
+ histo.AddPosition(it->first);
+ }
+ }
+
+ void TAdaptiveHistogram::SetId(ui64 id) {
+ Id = id;
+ }
+
+ ui64 TAdaptiveHistogram::GetId() {
+ return Id;
+ }
+
+ bool TAdaptiveHistogram::Empty() {
+ return Bins.size() == 0;
+ }
+
+ double TAdaptiveHistogram::GetMinValue() {
+ return MinValue;
+ }
+
+ double TAdaptiveHistogram::GetMaxValue() {
+ return MaxValue;
+ }
+
+ double TAdaptiveHistogram::GetSum() {
+ return Sum;
+ }
+
+ double TAdaptiveHistogram::GetSumInRange(double leftBound, double rightBound) {
+ if (leftBound > rightBound) {
+ return 0.0;
+ }
+ return GetSumAboveBound(leftBound) + GetSumBelowBound(rightBound) - Sum;
+ }
+
+ double TAdaptiveHistogram::GetSumAboveBound(double bound) {
+ if (Empty()) {
+ return 0.0;
+ }
+ if (bound < MinValue) {
+ return Sum;
+ }
+ if (bound > MaxValue) {
+ return 0.0;
+ }
+
+ if (!PrecomputedBins.empty()) {
+ return GetSumAboveBoundImpl(
+ bound,
+ PrecomputedBins,
+ LowerBound(PrecomputedBins.begin(), PrecomputedBins.end(), TFastBin{bound, -1.0, 0, 0}),
+ [](const auto& it) { return it->SumAbove; });
+ } else {
+ return GetSumAboveBoundImpl(
+ bound,
+ Bins,
+ Bins.lower_bound(TWeightedValue(bound, -1.0)),
+ [this](TPairSet::const_iterator rightBin) {
+ ++rightBin;
+ double sum = 0;
+ for (TPairSet::const_iterator it = rightBin; it != Bins.end(); ++it) {
+ sum += it->second;
+ }
+ return sum;
+ });
+ }
+ }
+
+ double TAdaptiveHistogram::GetSumBelowBound(double bound) {
+ if (Empty()) {
+ return 0.0;
+ }
+ if (bound < MinValue) {
+ return 0.0;
+ }
+ if (bound > MaxValue) {
+ return Sum;
+ }
+
+ if (!PrecomputedBins.empty()) {
+ return GetSumBelowBoundImpl(
+ bound,
+ PrecomputedBins,
+ LowerBound(PrecomputedBins.begin(), PrecomputedBins.end(), TFastBin{bound, -1.0, 0, 0}),
+ [](const auto& it) { return it->SumBelow; });
+ } else {
+ return GetSumBelowBoundImpl(
+ bound,
+ Bins,
+ Bins.lower_bound(TWeightedValue(bound, -1.0)),
+ [this](TPairSet::const_iterator rightBin) {
+ double sum = 0;
+ for (TPairSet::iterator it = Bins.begin(); it != rightBin; ++it) {
+ sum += it->second;
+ }
+ return sum;
+ });
+ }
+ }
+
+ double TAdaptiveHistogram::CalcUpperBound(double sum) {
+ Y_VERIFY(sum >= 0, "Sum must be >= 0");
+ if (sum == 0.0) {
+ return MinValue;
+ }
+ if (Empty()) {
+ return MaxValue;
+ }
+ TPairSet::iterator current = Bins.begin();
+ double gatheredSum = 0.0;
+ while (current != Bins.end() && gatheredSum < sum) {
+ gatheredSum += current->second;
+ ++current;
+ }
+ --current;
+ if (gatheredSum < sum) {
+ return MaxValue;
+ }
+ TWeightedValue left(MinValue, 0.0);
+ TWeightedValue right(MaxValue, 0.0);
+ if (current != Bins.begin()) {
+ TPairSet::iterator leftBin = current;
+ --leftBin;
+ left = *leftBin;
+ }
+ {
+ TPairSet::iterator rightBin = current;
+ ++rightBin;
+ if (rightBin != Bins.end()) {
+ right = *rightBin;
+ }
+ }
+ double sumToAdd = sum - (gatheredSum - current->second - left.second / 2);
+ if (sumToAdd <= ((current->second + left.second) / 2)) {
+ return left.first + 2 * sumToAdd * (current->first - left.first) / (current->second + left.second);
+ } else {
+ sumToAdd -= (current->second + left.second) / 2;
+ return current->first + 2 * sumToAdd * (right.first - current->first) / (right.second + current->second);
+ }
+ }
+
+ double TAdaptiveHistogram::CalcLowerBound(double sum) {
+ Y_VERIFY(sum >= 0, "Sum must be >= 0");
+ if (sum == 0.0) {
+ return MaxValue;
+ }
+ if (Empty()) {
+ return MinValue;
+ }
+ TPairSet::iterator current = Bins.end();
+ double gatheredSum = 0.0;
+ while (current != Bins.begin() && gatheredSum < sum) {
+ --current;
+ gatheredSum += current->second;
+ }
+ if (gatheredSum < sum) {
+ return MinValue;
+ }
+ TWeightedValue left(MinValue, 0.0);
+ TWeightedValue right(MaxValue, 0.0);
+ if (current != Bins.begin()) {
+ TPairSet::iterator leftBin = current;
+ --leftBin;
+ left = *leftBin;
+ }
+ {
+ TPairSet::iterator rightBin = current;
+ ++rightBin;
+ if (rightBin != Bins.end()) {
+ right = *rightBin;
+ }
+ }
+ double sumToAdd = sum - (gatheredSum - current->second - right.second / 2);
+ if (sumToAdd <= ((current->second + right.second) / 2)) {
+ return right.first - 2 * sumToAdd * (right.first - current->first) / (current->second + right.second);
+ } else {
+ sumToAdd -= (current->second + right.second) / 2;
+ return current->first - 2 * sumToAdd * (current->first - left.first) / (left.second + current->second);
+ }
+ }
+
+ double TAdaptiveHistogram::CalcUpperBoundSafe(double sum) {
+ if (!Empty()) {
+ sum = Max(Bins.begin()->second, sum);
+ }
+ return CalcUpperBound(sum);
+ }
+
+ double TAdaptiveHistogram::CalcLowerBoundSafe(double sum) {
+ if (!Empty()) {
+ sum = Max(Bins.rbegin()->second, sum);
+ }
+ return CalcLowerBound(sum);
+ }
+
+ void TAdaptiveHistogram::FromIHistogram(IHistogram* histo) {
+ if (!histo) {
+ ythrow yexception() << "Attempt to create TAdaptiveHistogram from a NULL pointer";
+ }
+ if (CalcQuality == CalcWardQuality) {
+ ythrow yexception() << "Not implemented";
+ } else if (CalcQuality != CalcDistanceQuality && CalcQuality != CalcWeightQuality) {
+ ythrow yexception() << "Attempt to create TAdaptiveHistogram from a pointer without default CalcQuality";
+ }
+ Id = histo->GetId();
+ if (histo->Empty()) {
+ return;
+ }
+ double sum = histo->GetSum();
+ double minValue = histo->GetMinValue();
+ double maxValue = histo->GetMaxValue();
+ if (minValue == maxValue) {
+ Add(minValue, sum);
+ return;
+ }
+ if (CalcQuality == CalcDistanceQuality) {
+ double binRange = (maxValue - minValue) / (Intervals);
+ for (size_t i = 0; i < Intervals; ++i) {
+ Add(minValue + binRange * (i + 0.5), histo->GetSumInRange(minValue + binRange * i, minValue + binRange * (i + 1)));
+ }
+ } else if (CalcQuality == CalcWeightQuality && sum != 0.0) {
+ double slab = sum / Intervals;
+ double prevBound = minValue;
+ for (size_t i = 0; i < Intervals; ++i) {
+ double bound = histo->CalcUpperBound(slab * (i + 1));
+ Add((bound + prevBound) / 2, slab);
+ prevBound = bound;
+ }
+ }
+ MinValue = minValue;
+ MaxValue = maxValue;
+ }
+
+ void TAdaptiveHistogram::Add(const TWeightedValue& weightedValue, bool initial) {
+ const double& value = weightedValue.first;
+ const double& weight = weightedValue.second;
+ if (weight <= 0.0) {
+ return; // all zero-weighted values should be skipped because they don't affect the distribution, negative weights are forbidden
+ }
+ if (initial) {
+ Sum += weight;
+ }
+ if (Bins.size() == 0) {
+ MinValue = value;
+ MaxValue = value;
+ Bins.insert(weightedValue);
+ return;
+ }
+ if (value < MinValue) {
+ MinValue = value;
+ }
+ if (value > MaxValue) {
+ MaxValue = value;
+ }
+ TPairSet::iterator rightBin = Bins.lower_bound(TWeightedValue(value, -1.0));
+ if (rightBin != Bins.end() && rightBin->first == value) {
+ TPairSet::iterator currentBin = rightBin;
+ ++rightBin;
+ TWeightedValue newBin(value, weight + currentBin->second);
+ if (rightBin != Bins.end()) {
+ Y_VERIFY(BinsByQuality.erase(CalcQuality(*currentBin, *rightBin)) == 1, "Erase failed");
+ BinsByQuality.insert(CalcQuality(newBin, *rightBin));
+ }
+ if (currentBin != Bins.begin()) {
+ TPairSet::iterator leftBin = currentBin;
+ --leftBin;
+ Y_VERIFY(BinsByQuality.erase(CalcQuality(*leftBin, *currentBin)) == 1, "Erase failed");
+ BinsByQuality.insert(CalcQuality(*leftBin, newBin));
+ }
+ Bins.erase(currentBin);
+ Bins.insert(newBin);
+ return;
+ }
+ if (rightBin == Bins.begin()) {
+ BinsByQuality.insert(CalcQuality(weightedValue, *rightBin));
+ } else {
+ TPairSet::iterator leftBin = rightBin;
+ --leftBin;
+ if (rightBin == Bins.end()) {
+ BinsByQuality.insert(CalcQuality(*leftBin, weightedValue));
+ } else {
+ Y_VERIFY(BinsByQuality.erase(CalcQuality(*leftBin, *rightBin)) == 1, "Erase failed");
+ BinsByQuality.insert(CalcQuality(*leftBin, weightedValue));
+ BinsByQuality.insert(CalcQuality(weightedValue, *rightBin));
+ }
+ }
+ Bins.insert(weightedValue);
+ if (Bins.size() > Intervals) {
+ Shrink();
+ }
+ }
+
+ void TAdaptiveHistogram::Erase(double value) {
+ TPairSet::iterator currentBin = Bins.lower_bound(TWeightedValue(value, -1.0));
+ Y_VERIFY(currentBin != Bins.end() && currentBin->first == value, "Can't find bin that should be erased");
+ TPairSet::iterator rightBin = currentBin;
+ ++rightBin;
+ if (currentBin == Bins.begin()) {
+ Y_VERIFY(rightBin != Bins.end(), "No right bin for the first bin");
+ Y_VERIFY(BinsByQuality.erase(CalcQuality(*currentBin, *rightBin)) != 0, "Erase failed");
+ } else {
+ TPairSet::iterator leftBin = currentBin;
+ --leftBin;
+ if (rightBin == Bins.end()) {
+ Y_VERIFY(BinsByQuality.erase(CalcQuality(*leftBin, *currentBin)) != 0, "Erase failed");
+ } else {
+ Y_VERIFY(BinsByQuality.erase(CalcQuality(*leftBin, *currentBin)) != 0, "Erase failed");
+ Y_VERIFY(BinsByQuality.erase(CalcQuality(*currentBin, *rightBin)) != 0, "Erase failed");
+ BinsByQuality.insert(CalcQuality(*leftBin, *rightBin));
+ }
+ }
+ Bins.erase(currentBin);
+ }
+
+ void TAdaptiveHistogram::Shrink() {
+ TPairSet::iterator worstBin = BinsByQuality.begin();
+ Y_VERIFY(worstBin != BinsByQuality.end(), "No right bin for the first bin");
+ TPairSet::iterator leftBin = Bins.lower_bound(TWeightedValue(worstBin->second, -1.0));
+ Y_VERIFY(leftBin != Bins.end() && leftBin->first == worstBin->second, "Can't find worst bin");
+ TPairSet::iterator rightBin = leftBin;
+ ++rightBin;
+ Y_VERIFY(rightBin != Bins.end(), "Can't find right bin");
+
+ TWeightedValue newBin((leftBin->first * leftBin->second + rightBin->first * rightBin->second) / (leftBin->second + rightBin->second), leftBin->second + rightBin->second);
+ if (Bins.size() > 2) {
+ Erase(leftBin->first);
+ Erase(rightBin->first);
+ } else {
+ Bins.clear();
+ BinsByQuality.clear();
+ }
+ Add(newBin, false);
+ }
+
+ void TAdaptiveHistogram::PrecomputePartialSums() {
+ PrecomputedBins.clear();
+ PrecomputedBins.reserve(Bins.size());
+ double currentSum = 0;
+ for (const auto& bin : Bins) {
+ PrecomputedBins.emplace_back(bin.first, bin.second, currentSum, Sum - currentSum - bin.second);
+ currentSum += bin.second;
+ }
+ }
+
+ template <typename TBins, typename TGetSumAbove>
+ double TAdaptiveHistogram::GetSumAboveBoundImpl(double bound, const TBins& bins, typename TBins::const_iterator rightBin, const TGetSumAbove& getSumAbove) const {
+ typename TBins::value_type left(MinValue, 0.0);
+ typename TBins::value_type right(MaxValue, 0.0);
+ if (rightBin != bins.end()) {
+ right = *rightBin;
+ }
+ if (rightBin != bins.begin()) {
+ typename TBins::const_iterator leftBin = rightBin;
+ --leftBin;
+ left = *leftBin;
+ }
+ double sum = (right.second / 2) + ((right.first == left.first) ? ((left.second + right.second) / 2) : (((left.second + right.second) / 2) * (right.first - bound) / (right.first - left.first)));
+ if (rightBin == bins.end()) {
+ return sum;
+ }
+ sum += getSumAbove(rightBin);
+ return sum;
+ }
+
+ template <typename TBins, typename TGetSumBelow>
+ double TAdaptiveHistogram::GetSumBelowBoundImpl(double bound, const TBins& bins, typename TBins::const_iterator rightBin, const TGetSumBelow& getSumBelow) const {
+ typename TBins::value_type left(MinValue, 0.0);
+ typename TBins::value_type right(MaxValue, 0.0);
+ if (rightBin != bins.end()) {
+ right = *rightBin;
+ }
+ if (rightBin != bins.begin()) {
+ typename TBins::const_iterator leftBin = rightBin;
+ --leftBin;
+ left = *leftBin;
+ }
+ double sum = (left.second / 2) + ((right.first == left.first) ? ((left.second + right.second) / 2) : (((left.second + right.second) / 2) * (bound - left.first) / (right.first - left.first)));
+ if (rightBin == bins.begin()) {
+ return sum;
+ }
+ --rightBin;
+ sum += getSumBelow(rightBin);
+ return sum;
+ }
+
+}