MySQL 9.6.0
Source Code Documentation
summation.h
Go to the documentation of this file.
1// Copyright (c) 2025, Oracle and/or its affiliates.
2//
3// This program is free software; you can redistribute it and/or modify
4// it under the terms of the GNU General Public License, version 2.0,
5// as published by the Free Software Foundation.
6//
7// This program is designed to work with certain software (including
8// but not limited to OpenSSL) that is licensed under separate terms,
9// as designated in a particular file or component or in included license
10// documentation. The authors of MySQL hereby grant you an additional
11// permission to link the program and your derivative works with the
12// separately licensed software that they have either included with
13// the program or referenced in the documentation.
14//
15// This program is distributed in the hope that it will be useful,
16// but WITHOUT ANY WARRANTY; without even the implied warranty of
17// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18// GNU General Public License, version 2.0, for more details.
19//
20// You should have received a copy of the GNU General Public License
21// along with this program; if not, write to the Free Software
22// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23
24#ifndef MYSQL_MATH_SUMMATION_H
25#define MYSQL_MATH_SUMMATION_H
26
27/// @file
28/// Experimental API header
29
30#include <iterator> // sentinel_for
31#include <numeric> // accumulate
32#include <type_traits> // is_arithmetic_v
33
34/// @addtogroup GroupLibsMysqlMath
35/// @{
36
37namespace mysql::math {
38
39/// Tracks the state of the Kahan summation algorithm, which produces a sum over
40/// a sequence of floating point numbers with very low numeric error, using an
41/// internal error compensation term.
42///
43/// @tparam Value_tp The numeric type used in internal computations, and to
44/// store the result.
45template <class Value_tp = double>
46class Kahan_sum {
47 public:
48 using Value_t = Value_tp;
49
50 explicit Kahan_sum(Value_t value = 0) : m_sum(value) {}
51
52 /// Return the current approximated sum.
53 [[nodiscard]] explicit operator Value_t() const { return m_sum; }
54
55 /// In-place add the given value to this object.
57 Value_t compensated_value = value - m_compensation;
58 Value_t new_sum = m_sum + compensated_value;
59 m_compensation = (new_sum - m_sum) - compensated_value;
60 m_sum = new_sum;
61 return *this;
62 }
63
64 /// In-place subtract the given value from this object.
65 Kahan_sum &operator-=(const Value_t &value) { return *this += -value; }
66
67 /// Return a new object holding the sum of this object and the given value.
68 [[nodiscard]] Kahan_sum operator+(const Value_t &value) const {
69 Kahan_sum ret = *this;
70 ret += value;
71 return ret;
72 }
73
74 /// Return a new object holding this object minus the given value.
75 [[nodiscard]] Kahan_sum operator-(const Value_t &value) const {
76 Kahan_sum ret = *this;
77 ret -= value;
78 return ret;
79 }
80
81 private:
84}; // class Kahan_sum
85
86/// Compute the sum of values in the given range, with very low numeric error.
87template <class Value_t = double, std::input_iterator Iterator_t>
88[[nodiscard]] Value_t kahan_sum(const Iterator_t &first,
89 const std::sentinel_for<Iterator_t> auto &last,
90 Value_t init = 0) {
91 return (Value_t)std::accumulate(first, last, Kahan_sum(init));
92}
93
94/// Compute the sum of values in the first sequence, minus the sum of values in
95/// the second sequence. When the return type is floating point, the result is
96/// exact if it is at most `mysql::math::max_exact_int<Return_t>`.
97///
98/// This uses an algorithm that avoids overflow in intermediate computations
99/// as long as the exact result is small, and uses a summation algorithm that
100/// minmizes floating point errors if the result is large.
101///
102/// @tparam Result_t Numeric type for the result.
103///
104/// @tparam Iterator1_t Type of iterator to first sequence.
105///
106/// @tparam Iterator2_t Type of iterator to second sequence.
107///
108/// @param begin1 Iterator to beginning of first sequence.
109///
110/// @param end1 Sentinel of first sequence.
111///
112/// @param begin2 Iterator to beginning of second sequence.
113///
114/// @param end2 Sentinel of second sequence.
115///
116/// @param init Initial value. Defaults to 0.
117///
118/// @return Return_t The sum of all values in the range from begin1 to end1,
119/// minus the sum of all values in the range from begin2 to end2.
120template <class Result_t = long double, std::input_iterator Iterator1_t,
121 std::input_iterator Iterator2_t>
122 requires std::is_arithmetic_v<Result_t> &&
123 std::unsigned_integral<decltype(*std::declval<Iterator1_t>())> &&
124 std::unsigned_integral<decltype(*std::declval<Iterator2_t>())>
125[[nodiscard]] Result_t sequence_sum_difference(
126 const Iterator1_t &begin1, const std::sentinel_for<Iterator1_t> auto &end1,
127 const Iterator2_t &begin2, const std::sentinel_for<Iterator2_t> auto &end2,
128 uint64_t init = 0) {
129 uint64_t sum{init};
130
131 // Read and step the iterator. Subtract from sum. If the result becomes
132 // negative, negate and return true; otherwise, return false.
133 auto step = [&](auto &it) {
134 uint64_t value = *it;
135 ++it;
136 if (value > sum) {
137 sum = value - sum;
138 return false;
139 }
140 sum -= value;
141 return true;
142 };
143
144 // Add sum(it..last) to the sum and return the result. Do all computations in
145 // double since they may overflow.
146 auto sum_tail = [&](auto &it, auto last) {
147 return kahan_sum(it, last, Result_t(sum));
148 };
149
150 // Subtract *it1 from sum until sum becomes negative.
151 // Then negate, and subtract *it2 from sum until sum becomes negative.
152 // Then negate and start over.
153 auto it1{begin1};
154 auto it2{begin2};
155 while (true) {
156 do {
157 // Invariant: sum == sum(begin1..it1) - sum(begin2..it2) >= 0
158 if (it2 == end2) {
159 // Add sum(it1..end1) and return the result.
160 return sum_tail(it1, end1);
161 }
162 } while (step(it2));
163 do {
164 // Invariant: sum == sum(begin2..it2) - sum(begin1..it1) >= 0
165 if (it1 == end1) {
166 // Add sum(it2..end2) and return the negated result.
167 auto ret = sum_tail(it2, end2);
168 // Don't negate 0.0, to avoid returning -0.0
169 if (ret > 0) ret = -ret;
170 return ret;
171 }
172 } while (step(it1));
173 }
174}
175
176} // namespace mysql::math
177
178// addtogroup GroupLibsMysqlMath
179/// @}
180
181#endif // ifndef MYSQL_MATH_SUMMATION_H
static mysql_service_status_t init()
Component initialization.
Definition: audit_api_message_emit.cc:566
Tracks the state of the Kahan summation algorithm, which produces a sum over a sequence of floating p...
Definition: summation.h:46
Kahan_sum(Value_t value=0)
Definition: summation.h:50
Kahan_sum operator-(const Value_t &value) const
Return a new object holding this object minus the given value.
Definition: summation.h:75
Value_t m_sum
Definition: summation.h:82
Kahan_sum & operator-=(const Value_t &value)
In-place subtract the given value from this object.
Definition: summation.h:65
Value_tp Value_t
Definition: summation.h:48
Value_t m_compensation
Definition: summation.h:83
Kahan_sum operator+(const Value_t &value) const
Return a new object holding the sum of this object and the given value.
Definition: summation.h:68
Kahan_sum & operator+=(const Value_t &value)
In-place add the given value to this object.
Definition: summation.h:56
ValueType value(const std::optional< ValueType > &v)
Definition: gtid.h:83
Definition: bounded_arithmetic.h:34
Result_t sequence_sum_difference(const Iterator1_t &begin1, const std::sentinel_for< Iterator1_t > auto &end1, const Iterator2_t &begin2, const std::sentinel_for< Iterator2_t > auto &end2, uint64_t init=0)
Compute the sum of values in the first sequence, minus the sum of values in the second sequence.
Definition: summation.h:125
Value_t kahan_sum(const Iterator_t &first, const std::sentinel_for< Iterator_t > auto &last, Value_t init=0)
Compute the sum of values in the given range, with very low numeric error.
Definition: summation.h:88