1 /* 2 * Copyright 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef ANDROID_AUDIO_UTILS_STATISTICS_H 18 #define ANDROID_AUDIO_UTILS_STATISTICS_H 19 20 #ifdef __cplusplus 21 22 #include "variadic_utils.h" 23 24 // variadic_utils already contains stl headers; in addition: 25 #include <deque> // for ReferenceStatistics implementation 26 #include <sstream> 27 28 namespace android { 29 namespace audio_utils { 30 31 /** 32 * Compensated summation is used to accumulate a sequence of floating point 33 * values, with "compensation" information to help preserve precision lost 34 * due to catastrophic cancellation, e.g. (BIG) + (SMALL) - (BIG) = 0. 35 * 36 * We provide two forms of compensated summation: 37 * the Kahan variant (which has better properties if the sum is generally 38 * larger than the data added; and the Neumaier variant which is better if 39 * the sum or delta may alternatively be larger. 40 * 41 * https://en.wikipedia.org/wiki/Kahan_summation_algorithm 42 * 43 * Alternative approaches include divide-and-conquer summation 44 * which provides increased accuracy with log n stack depth (recursion). 45 * 46 * https://en.wikipedia.org/wiki/Pairwise_summation 47 */ 48 49 template <typename T> 50 struct KahanSum { 51 T mSum{}; 52 T mCorrection{}; // negative low order bits of mSum. 53 54 constexpr KahanSum<T>() = default; 55 KahanSumKahanSum56 explicit constexpr KahanSum<T>(const T& value) 57 : mSum{value} 58 { } 59 60 // takes T not KahanSum<T> 61 friend constexpr KahanSum<T> operator+(KahanSum<T> lhs, const T& rhs) { 62 const T y = rhs - lhs.mCorrection; 63 const T t = lhs.mSum + y; 64 65 #ifdef __FAST_MATH__ 66 #warning "fast math enabled, could optimize out KahanSum correction" 67 #endif 68 69 lhs.mCorrection = (t - lhs.mSum) - y; // compiler, please do not optimize with /fp:fast 70 lhs.mSum = t; 71 return lhs; 72 } 73 74 constexpr KahanSum<T>& operator+=(const T& rhs) { // takes T not KahanSum<T> 75 *this = *this + rhs; 76 return *this; 77 } 78 TKahanSum79 constexpr operator T() const { 80 return mSum; 81 } 82 resetKahanSum83 constexpr void reset() { 84 mSum = {}; 85 mCorrection = {}; 86 } 87 }; 88 89 // A more robust version of Kahan summation for input greater than sum. 90 // TODO: investigate variants that reincorporate mCorrection bits into mSum if possible. 91 template <typename T> 92 struct NeumaierSum { 93 T mSum{}; 94 T mCorrection{}; // low order bits of mSum. 95 96 constexpr NeumaierSum<T>() = default; 97 NeumaierSumNeumaierSum98 explicit constexpr NeumaierSum<T>(const T& value) 99 : mSum{value} 100 { } 101 102 friend constexpr NeumaierSum<T> operator+(NeumaierSum<T> lhs, const T& rhs) { 103 const T t = lhs.mSum + rhs; 104 105 if (const_abs(lhs.mSum) >= const_abs(rhs)) { 106 lhs.mCorrection += (lhs.mSum - t) + rhs; 107 } else { 108 lhs.mCorrection += (rhs - t) + lhs.mSum; 109 } 110 lhs.mSum = t; 111 return lhs; 112 } 113 114 constexpr NeumaierSum<T>& operator+=(const T& rhs) { // takes T not NeumaierSum<T> 115 *this = *this + rhs; 116 return *this; 117 } 118 const_absNeumaierSum119 static constexpr T const_abs(T x) { 120 return x < T{} ? -x : x; 121 } 122 TNeumaierSum123 constexpr operator T() const { 124 return mSum + mCorrection; 125 } 126 resetNeumaierSum127 constexpr void reset() { 128 mSum = {}; 129 mCorrection = {}; 130 } 131 }; 132 133 //------------------------------------------------------------------- 134 // Constants and limits 135 136 template <typename T, typename T2=void> struct StatisticsConstants; 137 138 template <typename T> 139 struct StatisticsConstants<T, std::enable_if_t<std::is_arithmetic<T>::value>> { 140 // value closest to negative infinity for type T 141 static constexpr T negativeInfinity() { 142 return std::numeric_limits<T>::has_infinity ? 143 -std::numeric_limits<T>::infinity() : std::numeric_limits<T>::min(); 144 }; 145 146 static constexpr T mNegativeInfinity = negativeInfinity(); 147 148 // value closest to positive infinity for type T 149 static constexpr T positiveInfinity() { 150 return std::numeric_limits<T>::has_infinity ? 151 std::numeric_limits<T>::infinity() : std::numeric_limits<T>::max(); 152 } 153 154 static constexpr T mPositiveInfinity = positiveInfinity(); 155 }; 156 157 // specialize for tuple and pair 158 template <typename T> 159 struct StatisticsConstants<T, std::enable_if_t<!std::is_arithmetic<T>::value>> { 160 private: 161 template <std::size_t... I > 162 static constexpr auto negativeInfinity(std::index_sequence<I...>) { 163 return T{StatisticsConstants< 164 typename std::tuple_element<I, T>::type>::mNegativeInfinity...}; 165 } 166 template <std::size_t... I > 167 static constexpr auto positiveInfinity(std::index_sequence<I...>) { 168 return T{StatisticsConstants< 169 typename std::tuple_element<I, T>::type>::mPositiveInfinity...}; 170 } 171 public: 172 static constexpr auto negativeInfinity() { 173 return negativeInfinity(std::make_index_sequence<std::tuple_size<T>::value>()); 174 } 175 static constexpr auto mNegativeInfinity = 176 negativeInfinity(std::make_index_sequence<std::tuple_size<T>::value>()); 177 static constexpr auto positiveInfinity() { 178 return positiveInfinity(std::make_index_sequence<std::tuple_size<T>::value>()); 179 } 180 static constexpr auto mPositiveInfinity = 181 positiveInfinity(std::make_index_sequence<std::tuple_size<T>::value>()); 182 }; 183 184 /** 185 * Statistics provides a running weighted average, variance, and standard deviation of 186 * a sample stream. It is more numerically stable for floating point computation than a 187 * naive sum of values, sum of values squared. 188 * 189 * The weighting is like an IIR filter, with the most recent sample weighted as 1, and decaying 190 * by alpha (between 0 and 1). With alpha == 1. this is rectangular weighting, reducing to 191 * Welford's algorithm. 192 * 193 * The IIR filter weighting emphasizes more recent samples, has low overhead updates, 194 * constant storage, and constant computation (per update or variance read). 195 * 196 * This is a variant of the weighted mean and variance algorithms described here: 197 * https://en.wikipedia.org/wiki/Moving_average 198 * https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Online_algorithm 199 * https://en.wikipedia.org/wiki/Weighted_arithmetic_mean 200 * 201 * weight = sum_{i=1}^n \alpha^{n-i} 202 * mean = 1/weight * sum_{i=1}^n \alpha^{n-i}x_i 203 * var = 1/weight * sum_{i=1}^n alpha^{n-i}(x_i- mean)^2 204 * 205 * The Statistics class is safe to call from a SCHED_FIFO thread with the exception of 206 * the toString() method, which uses std::stringstream to format data for printing. 207 * 208 * Long term data accumulation and constant alpha: 209 * If the alpha weight is 1 (or not specified) then statistics objects with float 210 * summation types (D, S) should NOT add more than the mantissa-bits elements 211 * without reset to prevent variance increases due to weight precision underflow. 212 * This is 1 << 23 elements for float and 1 << 52 elements for double. 213 * 214 * Setting alpha less than 1 avoids this underflow problem. 215 * Alpha < 1 - (epsilon * 32), where epsilon is std::numeric_limits<D>::epsilon() 216 * is recommended for continuously running statistics (alpha <= 0.999996 217 * for float summation precision). 218 * 219 * Alpha may also change on-the-fly, based on the reliability of 220 * new information. In that case, alpha may be set temporarily greater 221 * than 1. 222 * 223 * https://en.wikipedia.org/wiki/Weighted_arithmetic_mean#Reliability_weights_2 224 * 225 * Statistics may also be collected on variadic "vector" object instead of 226 * scalars, where the variance may be computed as an inner product radial squared 227 * distance from the mean; or as an outer product where the variance returned 228 * is a covariance matrix. 229 * 230 * TODO: 231 * 1) Alternative versions of Kahan/Neumaier sum that better preserve precision. 232 * 2) Add binary math ops to corrected sum classes for better precision in lieu of long double. 233 * 3) Add Cholesky decomposition to ensure positive definite covariance matrices if 234 * the input is a variadic object. 235 */ 236 237 /** 238 * Mean may have catastrophic cancellation of positive and negative sample values, 239 * so we use Kahan summation in the algorithms below (or substitute "D" if not needed). 240 * 241 * https://en.wikipedia.org/wiki/Kahan_summation_algorithm 242 */ 243 244 template < 245 typename T, // input data type 246 typename D = double, // output mean data type 247 typename S = KahanSum<D>, // compensated mean summation type, if any 248 typename A = double, // weight type 249 typename D2 = double, // output variance "D^2" type 250 typename PRODUCT = std::multiplies<D> // how the output variance is computed 251 > 252 class Statistics { 253 public: 254 /** alpha is the weight (if alpha == 1. we use a rectangular window) */ 255 explicit constexpr Statistics(A alpha = A(1.)) 256 : mAlpha(alpha) 257 { } 258 259 template <size_t N> 260 explicit constexpr Statistics(const T (&a)[N], A alpha = A(1.)) 261 : mAlpha(alpha) 262 { 263 for (const auto &data : a) { 264 add(data); 265 } 266 } 267 268 constexpr void setAlpha(A alpha) { 269 mAlpha = alpha; 270 } 271 272 constexpr void add(const T &value) { 273 // Note: fastest implementation uses fmin fminf but would not be constexpr 274 275 mMax = audio_utils::max(mMax, value); // order important: reject NaN 276 mMin = audio_utils::min(mMin, value); // order important: reject NaN 277 ++mN; 278 const D delta = value - mMean; 279 /* if (mAlpha == 1.) we have Welford's algorithm 280 ++mN; 281 mMean += delta / mN; 282 mM2 += delta * (value - mMean); 283 284 Note delta * (value - mMean) should be non-negative. 285 */ 286 mWeight = A(1.) + mAlpha * mWeight; 287 mWeight2 = A(1.) + mAlpha * mAlpha * mWeight2; 288 D meanDelta = delta / mWeight; 289 mMean += meanDelta; 290 mM2 = mAlpha * mM2 + PRODUCT()(delta, (value - mMean)); 291 292 /* 293 Alternate variant related to: 294 http://mathworld.wolfram.com/SampleVarianceComputation.html 295 296 const double sweight = mAlpha * mWeight; 297 mWeight = 1. + sweight; 298 const double dmean = delta / mWeight; 299 mMean += dmean; 300 mM2 = mAlpha * mM2 + mWeight * sweight * dmean * dmean; 301 302 The update is slightly different than Welford's algorithm 303 showing a by-construction non-negative update to M2. 304 */ 305 } 306 307 constexpr int64_t getN() const { 308 return mN; 309 } 310 311 constexpr void reset() { 312 mMin = StatisticsConstants<T>::positiveInfinity(); 313 mMax = StatisticsConstants<T>::negativeInfinity(); 314 mN = 0; 315 mWeight = {}; 316 mWeight2 = {}; 317 mMean = {}; 318 mM2 = {}; 319 } 320 321 constexpr A getWeight() const { 322 return mWeight; 323 } 324 325 constexpr D getMean() const { 326 return mMean; 327 } 328 329 constexpr D2 getVariance() const { 330 if (mN < 2) { 331 // must have 2 samples for sample variance. 332 return {}; 333 } else { 334 return mM2 / getSampleWeight(); 335 } 336 } 337 338 constexpr D2 getPopVariance() const { 339 if (mN < 1) { 340 return {}; 341 } else { 342 return mM2 / mWeight; 343 } 344 } 345 346 // explicitly use sqrt_constexpr if you need a constexpr version 347 D2 getStdDev() const { 348 return android::audio_utils::sqrt(getVariance()); 349 } 350 351 D2 getPopStdDev() const { 352 return android::audio_utils::sqrt(getPopVariance()); 353 } 354 355 constexpr T getMin() const { 356 return mMin; 357 } 358 359 constexpr T getMax() const { 360 return mMax; 361 } 362 363 std::string toString() const { 364 const int64_t N = getN(); 365 if (N == 0) return "unavail"; 366 367 std::stringstream ss; 368 ss << "ave=" << getMean(); 369 if (N > 1) { 370 // we use the sample standard deviation (not entirely unbiased, 371 // though the sample variance is unbiased). 372 ss << " std=" << getStdDev(); 373 } 374 ss << " min=" << getMin(); 375 ss << " max=" << getMax(); 376 return ss.str(); 377 } 378 379 private: 380 A mAlpha; 381 T mMin{StatisticsConstants<T>::positiveInfinity()}; 382 T mMax{StatisticsConstants<T>::negativeInfinity()}; 383 384 int64_t mN = 0; // running count of samples. 385 A mWeight{}; // sum of weights. 386 A mWeight2{}; // sum of weights squared. 387 S mMean{}; // running mean. 388 D2 mM2{}; // running unnormalized variance. 389 390 // Reliability correction for unbiasing variance, since mean is estimated 391 // from same sample stream as variance. 392 // if mAlpha == 1 this is mWeight - 1; 393 // 394 // TODO: consider exposing the correction factor. 395 constexpr A getSampleWeight() const { 396 // if mAlpha is constant then the mWeight2 member variable is not 397 // needed, one can use instead: 398 // return (mWeight - D(1.)) * D(2.) / (D(1.) + mAlpha); 399 400 return mWeight - mWeight2 / mWeight; 401 } 402 }; 403 404 /** 405 * ReferenceStatistics is a naive implementation of the weighted running variance, 406 * which consumes more space and is slower than Statistics. It is provided for 407 * comparison and testing purposes. Do not call from a SCHED_FIFO thread! 408 * 409 * Note: Common code not combined for implementation clarity. 410 * We don't invoke Kahan summation or other tricks. 411 */ 412 template < 413 typename T, // input data type 414 typename D = double // output mean/variance data type 415 > 416 class ReferenceStatistics { 417 public: 418 /** alpha is the weight (alpha == 1. is rectangular window) */ 419 explicit ReferenceStatistics(D alpha = D(1.)) 420 : mAlpha(alpha) 421 { } 422 423 constexpr void setAlpha(D alpha) { 424 mAlpha = alpha; 425 } 426 427 // For independent testing, have intentionally slightly different behavior 428 // of min and max than Statistics with respect to Nan. 429 constexpr void add(const T &value) { 430 if (getN() == 0) { 431 mMax = value; 432 mMin = value; 433 } else if (value > mMax) { 434 mMax = value; 435 } else if (value < mMin) { 436 mMin = value; 437 } 438 439 mData.push_front(value); 440 mAlphaList.push_front(mAlpha); 441 } 442 443 int64_t getN() const { 444 return mData.size(); 445 } 446 447 void reset() { 448 mMin = {}; 449 mMax = {}; 450 mData.clear(); 451 mAlphaList.clear(); 452 } 453 454 D getWeight() const { 455 D weight{}; 456 D alpha_i(1.); 457 for (size_t i = 0; i < mData.size(); ++i) { 458 weight += alpha_i; 459 alpha_i *= mAlphaList[i]; 460 } 461 return weight; 462 } 463 464 D getWeight2() const { 465 D weight2{}; 466 D alpha2_i(1.); 467 for (size_t i = 0; i < mData.size(); ++i) { 468 weight2 += alpha2_i; 469 alpha2_i *= mAlphaList[i] * mAlphaList[i]; 470 } 471 return weight2; 472 } 473 474 D getMean() const { 475 D wsum{}; 476 D alpha_i(1.); 477 for (size_t i = 0; i < mData.size(); ++i) { 478 wsum += alpha_i * mData[i]; 479 alpha_i *= mAlphaList[i]; 480 } 481 return wsum / getWeight(); 482 } 483 484 // Should always return a positive value. 485 D getVariance() const { 486 return getUnweightedVariance() / (getWeight() - getWeight2() / getWeight()); 487 } 488 489 // Should always return a positive value. 490 D getPopVariance() const { 491 return getUnweightedVariance() / getWeight(); 492 } 493 494 D getStdDev() const { 495 return sqrt(getVariance()); 496 } 497 498 D getPopStdDev() const { 499 return sqrt(getPopVariance()); 500 } 501 502 T getMin() const { 503 return mMin; 504 } 505 506 T getMax() const { 507 return mMax; 508 } 509 510 std::string toString() const { 511 const auto N = getN(); 512 if (N == 0) return "unavail"; 513 514 std::stringstream ss; 515 ss << "ave=" << getMean(); 516 if (N > 1) { 517 // we use the sample standard deviation (not entirely unbiased, 518 // though the sample variance is unbiased). 519 ss << " std=" << getStdDev(); 520 } 521 ss << " min=" << getMin(); 522 ss << " max=" << getMax(); 523 return ss.str(); 524 } 525 526 private: 527 T mMin{}; 528 T mMax{}; 529 530 D mAlpha; // current alpha value 531 std::deque<T> mData; // store all the data for exact summation, mData[0] most recent. 532 std::deque<D> mAlphaList; // alpha value for the data added. 533 534 D getUnweightedVariance() const { 535 const D mean = getMean(); 536 D wsum{}; 537 D alpha_i(1.); 538 for (size_t i = 0; i < mData.size(); ++i) { 539 const D diff = mData[i] - mean; 540 wsum += alpha_i * diff * diff; 541 alpha_i *= mAlphaList[i]; 542 } 543 return wsum; 544 } 545 }; 546 547 /** 548 * Least squares fitting of a 2D straight line based on the covariance matrix. 549 * 550 * See formula from: 551 * http://mathworld.wolfram.com/LeastSquaresFitting.html 552 * 553 * y = a + b*x 554 * 555 * returns a: y intercept 556 * b: slope 557 * r2: correlation coefficient (1.0 means great fit, 0.0 means no fit.) 558 * 559 * For better numerical stability, it is suggested to use the slope b only: 560 * as the least squares fit line intersects the mean. 561 * 562 * (y - mean_y) = b * (x - mean_x). 563 * 564 */ 565 template <typename T> 566 constexpr void computeYLineFromStatistics( 567 T &a, T& b, T &r2, 568 const T& mean_x, 569 const T& mean_y, 570 const T& var_x, 571 const T& cov_xy, 572 const T& var_y) { 573 574 // Dimensionally r2 is unitless. If there is no correlation 575 // then r2 is clearly 0 as cov_xy == 0. If x and y are identical up to a scale 576 // and shift, then r2 is 1. 577 r2 = cov_xy * cov_xy / (var_x * var_y); 578 579 // The least squares solution to the overconstrained matrix equation requires 580 // the pseudo-inverse. In 2D, the best-fit slope is the mean removed 581 // (via covariance and variance) dy/dx derived from the joint expectation 582 // (this is also dimensionally correct). 583 b = cov_xy / var_x; 584 585 // The best fit line goes through the mean, and can be used to find the y intercept. 586 a = mean_y - b * mean_x; 587 } 588 589 /** 590 * LinearLeastSquaresFit<> class is derived from the Statistics<> class, with a 2 element array. 591 * Arrays are preferred over tuples or pairs because copy assignment is constexpr and 592 * arrays are trivially copyable. 593 */ 594 template <typename T> 595 class LinearLeastSquaresFit : public 596 Statistics<std::array<T, 2>, // input 597 std::array<T, 2>, // mean data output 598 std::array<T, 2>, // compensated mean sum 599 T, // weight type 600 std::array<T, 3>, // covariance_ut 601 audio_utils::outerProduct_UT_array<std::array<T, 2>>> 602 { 603 public: 604 constexpr explicit LinearLeastSquaresFit(const T &alpha = T(1.)) 605 : Statistics<std::array<T, 2>, 606 std::array<T, 2>, 607 std::array<T, 2>, 608 T, 609 std::array<T, 3>, // covariance_ut 610 audio_utils::outerProduct_UT_array<std::array<T, 2>>>(alpha) { } 611 612 /* Note: base class method: add(value) 613 614 constexpr void add(const std::array<T, 2>& value); 615 616 use: 617 add({1., 2.}); 618 or 619 add(to_array(myTuple)); 620 */ 621 622 /** 623 * y = a + b*x 624 * 625 * returns a: y intercept 626 * b: y slope (dy / dx) 627 * r2: correlation coefficient (1.0 means great fit, 0.0 means no fit.) 628 */ 629 constexpr void computeYLine(T &a, T &b, T &r2) const { 630 computeYLineFromStatistics(a, b, r2, 631 std::get<0>(this->getMean()), /* mean_x */ 632 std::get<1>(this->getMean()), /* mean_y */ 633 std::get<0>(this->getPopVariance()), /* var_x */ 634 std::get<1>(this->getPopVariance()), /* cov_xy */ 635 std::get<2>(this->getPopVariance())); /* var_y */ 636 } 637 638 /** 639 * x = a + b*y 640 * 641 * returns a: x intercept 642 * b: x slope (dx / dy) 643 * r2: correlation coefficient (1.0 means great fit, 0.0 means no fit.) 644 */ 645 constexpr void computeXLine(T &a, T &b, T &r2) const { 646 // reverse x and y for X line computation 647 computeYLineFromStatistics(a, b, r2, 648 std::get<1>(this->getMean()), /* mean_x */ 649 std::get<0>(this->getMean()), /* mean_y */ 650 std::get<2>(this->getPopVariance()), /* var_x */ 651 std::get<1>(this->getPopVariance()), /* cov_xy */ 652 std::get<0>(this->getPopVariance())); /* var_y */ 653 } 654 655 /** 656 * this returns the estimate of y from a given x 657 */ 658 constexpr T getYFromX(const T &x) const { 659 const T var_x = std::get<0>(this->getPopVariance()); 660 const T cov_xy = std::get<1>(this->getPopVariance()); 661 const T b = cov_xy / var_x; // dy / dx 662 663 const T mean_x = std::get<0>(this->getMean()); 664 const T mean_y = std::get<1>(this->getMean()); 665 return /* y = */ b * (x - mean_x) + mean_y; 666 } 667 668 /** 669 * this returns the estimate of x from a given y 670 */ 671 constexpr T getXFromY(const T &y) const { 672 const T cov_xy = std::get<1>(this->getPopVariance()); 673 const T var_y = std::get<2>(this->getPopVariance()); 674 const T b = cov_xy / var_y; // dx / dy 675 676 const T mean_x = std::get<0>(this->getMean()); 677 const T mean_y = std::get<1>(this->getMean()); 678 return /* x = */ b * (y - mean_y) + mean_x; 679 } 680 681 constexpr T getR2() const { 682 const T var_x = std::get<0>(this->getPopVariance()); 683 const T cov_xy = std::get<1>(this->getPopVariance()); 684 const T var_y = std::get<2>(this->getPopVariance()); 685 return cov_xy * cov_xy / (var_x * var_y); 686 } 687 }; 688 689 /** 690 * constexpr statistics functions of form: 691 * algorithm(forward_iterator begin, forward_iterator end) 692 * 693 * These check that the input looks like an iterator, but doesn't 694 * check if __is_forward_iterator<>. 695 * 696 * divide-and-conquer pairwise summation forms will require 697 * __is_random_access_iterator<>. 698 */ 699 700 // returns max of elements, or if no elements negative infinity. 701 template <typename T, 702 std::enable_if_t<is_iterator<T>::value, int> = 0> 703 constexpr auto max(T begin, T end) { 704 using S = std::remove_cv_t<std::remove_reference_t< 705 decltype(*begin)>>; 706 S maxValue = StatisticsConstants<S>::mNegativeInfinity; 707 for (auto it = begin; it != end; ++it) { 708 maxValue = std::max(maxValue, *it); 709 } 710 return maxValue; 711 } 712 713 // returns min of elements, or if no elements positive infinity. 714 template <typename T, 715 std::enable_if_t<is_iterator<T>::value, int> = 0> 716 constexpr auto min(T begin, T end) { 717 using S = std::remove_cv_t<std::remove_reference_t< 718 decltype(*begin)>>; 719 S minValue = StatisticsConstants<S>::mPositiveInfinity; 720 for (auto it = begin; it != end; ++it) { 721 minValue = std::min(minValue, *it); 722 } 723 return minValue; 724 } 725 726 template <typename D = double, typename S = KahanSum<D>, typename T, 727 std::enable_if_t<is_iterator<T>::value, int> = 0> 728 constexpr auto sum(T begin, T end) { 729 S sum{}; 730 for (auto it = begin; it != end; ++it) { 731 sum += D(*it); 732 } 733 return sum; 734 } 735 736 template <typename D = double, typename S = KahanSum<D>, typename T, 737 std::enable_if_t<is_iterator<T>::value, int> = 0> 738 constexpr auto sumSqDiff(T begin, T end, D x = {}) { 739 S sum{}; 740 for (auto it = begin; it != end; ++it) { 741 const D diff = *it - x; 742 sum += diff * diff; 743 } 744 return sum; 745 } 746 747 // Form: algorithm(array[]), where array size is known to the compiler. 748 template <typename T, size_t N> 749 constexpr T max(const T (&a)[N]) { 750 return max(&a[0], &a[N]); 751 } 752 753 template <typename T, size_t N> 754 constexpr T min(const T (&a)[N]) { 755 return min(&a[0], &a[N]); 756 } 757 758 template <typename D = double, typename S = KahanSum<D>, typename T, size_t N> 759 constexpr D sum(const T (&a)[N]) { 760 return sum<D, S>(&a[0], &a[N]); 761 } 762 763 template <typename D = double, typename S = KahanSum<D>, typename T, size_t N> 764 constexpr D sumSqDiff(const T (&a)[N], D x = {}) { 765 return sumSqDiff<D, S>(&a[0], &a[N], x); 766 } 767 768 // TODO: remove when std::isnan is constexpr 769 template <typename T> 770 constexpr T isnan(T x) { 771 return __builtin_isnan(x); 772 } 773 774 // constexpr sqrt computed by the Babylonian (Newton's) method. 775 // Please use math libraries for non-constexpr cases. 776 // TODO: remove when there is some std::sqrt which is constexpr. 777 // 778 // https://en.wikipedia.org/wiki/Methods_of_computing_square_roots 779 780 // watch out using the unchecked version, use the checked version below. 781 template <typename T> 782 constexpr T sqrt_constexpr_unchecked(T x, T prev) { 783 static_assert(std::is_floating_point<T>::value, "must be floating point type"); 784 const T next = T(0.5) * (prev + x / prev); 785 return next == prev ? next : sqrt_constexpr_unchecked(x, next); 786 } 787 788 // checked sqrt 789 template <typename T> 790 constexpr T sqrt_constexpr(T x) { 791 static_assert(std::is_floating_point<T>::value, "must be floating point type"); 792 if (x < T{}) { // negative values return nan 793 return std::numeric_limits<T>::quiet_NaN(); 794 } else if (isnan(x) 795 || x == std::numeric_limits<T>::infinity() 796 || x == T{}) { 797 return x; 798 } else { // good to go. 799 return sqrt_constexpr_unchecked(x, T(1.)); 800 } 801 } 802 803 } // namespace audio_utils 804 } // namespace android 805 806 #endif // __cplusplus 807 808 /** \cond */ 809 __BEGIN_DECLS 810 /** \endcond */ 811 812 /** Simple stats structure for low overhead statistics gathering. 813 * Designed to be accessed by C (with no functional getters). 814 * Zero initialize {} to clear or reset. 815 */ 816 typedef struct { 817 int64_t n; 818 double min; 819 double max; 820 double last; 821 double mean; 822 } simple_stats_t; 823 824 /** logs new value to the simple_stats_t */ 825 static inline void simple_stats_log(simple_stats_t *stats, double value) { 826 if (++stats->n == 1) { 827 stats->min = stats->max = stats->last = stats->mean = value; 828 } else { 829 stats->last = value; 830 if (value < stats->min) { 831 stats->min = value; 832 } else if (value > stats->max) { 833 stats->max = value; 834 } 835 // Welford's algorithm for mean 836 const double delta = value - stats->mean; 837 stats->mean += delta / stats->n; 838 } 839 } 840 841 /** dumps statistics to a string, returns the length of string excluding null termination. */ 842 static inline size_t simple_stats_to_string(simple_stats_t *stats, char *buffer, size_t size) { 843 if (size == 0) { 844 return 0; 845 } else if (stats->n == 0) { 846 return snprintf(buffer, size, "none"); 847 } else { 848 return snprintf(buffer, size, "(mean: %lf min: %lf max: %lf last: %lf n: %lld)", 849 stats->mean, stats->min, stats->max, stats->last, (long long)stats->n); 850 } 851 } 852 853 /** \cond */ 854 __END_DECLS 855 /** \endcond */ 856 857 #endif // !ANDROID_AUDIO_UTILS_STATISTICS_H 858