distribution/log_polynomial.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "terraingen/distribution/log_polynomial.h" | ||
| 2 | |||
| 3 | #include "terraingen/util/checks.h" | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <cmath> | ||
| 7 | #include <stdexcept> | ||
| 8 | #include <utility> | ||
| 9 | |||
| 10 | namespace terraingen { | ||
| 11 | |||
| 12 | 810 | LogPolynomialSfd::LogPolynomialSfd(std::vector<double> coefficients, DiameterRange range, | |
| 13 | 810 | SfdKind kind, double diameterUnit, double areaUnit) | |
| 14 | 810 | : SizeFrequencyDistribution(kind, range), coefficients_(std::move(coefficients)), | |
| 15 | 810 | diameterUnit_(diameterUnit), areaUnit_(areaUnit) { | |
| 16 | 810 | if (coefficients_.empty() || !std::all_of(coefficients_.begin(), coefficients_.end(), | |
| 17 | 9589 | [](double value) { return std::isfinite(value); })) { | |
| 18 | 2 | throw std::invalid_argument("A log polynomial needs finite coefficients"); | |
| 19 | } | ||
| 20 | 808 | if (!IsPositive(diameterUnit_) || !IsPositive(areaUnit_)) { | |
| 21 | 2 | throw std::invalid_argument("Diameter and area units must be positive and finite"); | |
| 22 | } | ||
| 23 | 806 | if (!(range.min > 0.0 && std::isfinite(range.max))) { | |
| 24 | throw std::invalid_argument( | ||
| 25 | 2 | "A log polynomial needs a finite range with a positive minimum"); | |
| 26 | } | ||
| 27 | 804 | constexpr int kSamples = 4096; | |
| 28 | 804 | const double low = std::log10(range.min / diameterUnit_); | |
| 29 | 804 | const double high = std::log10(range.max / diameterUnit_); | |
| 30 | 3286598 | for (int i = 0; i <= kSamples; ++i) { | |
| 31 | 3285796 | if (Derivative(low + (high - low) * i / kSamples) > 0.0) { | |
| 32 | 2 | throw std::invalid_argument("A cumulative distribution cannot increase with diameter"); | |
| 33 | } | ||
| 34 | } | ||
| 35 | 818 | } | |
| 36 | |||
| 37 | 6774404 | double LogPolynomialSfd::Polynomial(double x) const { | |
| 38 | 6774404 | double sum = 0.0; | |
| 39 | 88063291 | for (auto it = coefficients_.rbegin(); it != coefficients_.rend(); ++it) { | |
| 40 | 81288887 | sum = sum * x + *it; | |
| 41 | } | ||
| 42 | 6774404 | return sum; | |
| 43 | } | ||
| 44 | |||
| 45 | 4019357 | double LogPolynomialSfd::Derivative(double x) const { | |
| 46 | 4019357 | double sum = 0.0; | |
| 47 | 48027375 | for (std::size_t i = coefficients_.size() - 1; i > 0; --i) { | |
| 48 | 44008018 | sum = sum * x + static_cast<double>(i) * coefficients_[i]; | |
| 49 | } | ||
| 50 | 4019357 | return sum; | |
| 51 | } | ||
| 52 | |||
| 53 | 6774404 | double LogPolynomialSfd::CumulativeAt(double diameter) const { | |
| 54 | 6774404 | return std::pow(10.0, Polynomial(std::log10(diameter / diameterUnit_))) / areaUnit_; | |
| 55 | } | ||
| 56 | |||
| 57 | 733561 | double LogPolynomialSfd::IncrementalAt(double diameter) const { | |
| 58 | 733561 | return -CumulativeAt(diameter) * Derivative(std::log10(diameter / diameterUnit_)) / diameter; | |
| 59 | } | ||
| 60 | |||
| 61 | } // namespace terraingen | ||
| 62 |