Skip to content

Commit 345f53e

Browse files
author
Tomáš Kolárik
committed
simple_continued_fraction: added protected non-const accessor to partial denominators
1 parent 5376689 commit 345f53e

File tree

1 file changed

+52
-39
lines changed

1 file changed

+52
-39
lines changed

include/boost/math/tools/simple_continued_fraction.hpp

Lines changed: 52 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -29,20 +29,28 @@
2929

3030
namespace boost::math::tools {
3131

32-
template<typename Real, typename Z = int64_t>
32+
template<typename Real, typename I = int64_t>
3333
class simple_continued_fraction {
3434
public:
35-
simple_continued_fraction(Real x) : x_{x} {
35+
typedef I int_type;
36+
37+
~simple_continued_fraction() = default;
38+
simple_continued_fraction(const simple_continued_fraction&) = default;
39+
simple_continued_fraction& operator =(const simple_continued_fraction&) = default;
40+
simple_continued_fraction(simple_continued_fraction&&) = default;
41+
simple_continued_fraction& operator =(simple_continued_fraction&&) = default;
42+
simple_continued_fraction(Real x) {
3643
using std::floor;
3744
using std::abs;
3845
using std::sqrt;
3946
using std::isfinite;
47+
const Real orig_x = x;
4048
if (!isfinite(x)) {
41-
throw std::domain_error("Cannot convert non-finites into continued fractions.");
49+
throw std::domain_error("Cannot convert non-finites into continued fractions.");
4250
}
4351
b_.reserve(50);
4452
Real bj = floor(x);
45-
b_.push_back(static_cast<Z>(bj));
53+
b_.push_back(static_cast<int_type>(bj));
4654
if (bj == x) {
4755
b_.shrink_to_fit();
4856
return;
@@ -54,14 +62,13 @@ class simple_continued_fraction {
5462
}
5563
Real C = f;
5664
Real D = 0;
57-
int i = 0;
58-
// the "1 + i++" lets the error bound grow slowly with the number of convergents.
65+
// the "1 + i" lets the error bound grow slowly with the number of convergents.
5966
// I have not worked out the error propagation of the Modified Lentz's method to see if it does indeed grow at this rate.
6067
// Numerical Recipes claims that no one has worked out the error analysis of the modified Lentz's method.
61-
while (abs(f - x_) >= (1 + i++)*std::numeric_limits<Real>::epsilon()*abs(x_))
62-
{
68+
const Real eps_abs_orig_x = std::numeric_limits<Real>::epsilon()*abs(orig_x);
69+
for (int i = 0; abs(f - orig_x) >= (1 + i)*eps_abs_orig_x; ++i) {
6370
bj = floor(x);
64-
b_.push_back(static_cast<Z>(bj));
71+
b_.push_back(static_cast<int_type>(bj));
6572
x = 1/(x-bj);
6673
D += bj;
6774
if (D == 0) {
@@ -78,29 +85,31 @@ class simple_continued_fraction {
7885
// The shorter representation is considered the canonical representation,
7986
// so if we compute a non-canonical representation, change it to canonical:
8087
if (b_.size() > 2 && b_.back() == 1) {
81-
b_[b_.size() - 2] += 1;
82-
b_.resize(b_.size() - 1);
88+
b_.pop_back();
89+
b_.back() += 1;
8390
}
8491
b_.shrink_to_fit();
85-
86-
for (size_t i = 1; i < b_.size(); ++i) {
92+
93+
const int size_ = b_.size();
94+
for (int i = 1; i < size_; ++i) {
8795
if (b_[i] <= 0) {
8896
std::ostringstream oss;
8997
oss << "Found a negative partial denominator: b[" << i << "] = " << b_[i] << "."
9098
#ifndef BOOST_MATH_STANDALONE
91-
<< " This means the integer type '" << boost::core::demangle(typeid(Z).name())
99+
<< " This means the integer type '" << boost::core::demangle(typeid(int_type).name())
92100
#else
93-
<< " This means the integer type '" << typeid(Z).name()
101+
<< " This means the integer type '" << typeid(int_type).name()
94102
#endif
95103
<< "' has overflowed and you need to use a wider type,"
96104
<< " or there is a bug.";
97105
throw std::overflow_error(oss.str());
98106
}
99107
}
100108
}
101-
109+
102110
Real khinchin_geometric_mean() const {
103-
if (b_.size() == 1) {
111+
const int size_ = b_.size();
112+
if (size_ == 1) {
104113
return std::numeric_limits<Real>::quiet_NaN();
105114
}
106115
using std::log;
@@ -110,53 +119,57 @@ class simple_continued_fraction {
110119
// A random partial denominator has ~80% chance of being in this table:
111120
const std::array<Real, 7> logs{std::numeric_limits<Real>::quiet_NaN(), Real(0), log(static_cast<Real>(2)), log(static_cast<Real>(3)), log(static_cast<Real>(4)), log(static_cast<Real>(5)), log(static_cast<Real>(6))};
112121
Real log_prod = 0;
113-
for (size_t i = 1; i < b_.size(); ++i) {
114-
if (b_[i] < static_cast<Z>(logs.size())) {
122+
for (int i = 1; i < size_; ++i) {
123+
if (b_[i] < static_cast<int_type>(logs.size())) {
115124
log_prod += logs[b_[i]];
116125
}
117126
else
118127
{
119128
log_prod += log(static_cast<Real>(b_[i]));
120129
}
121130
}
122-
log_prod /= (b_.size()-1);
131+
log_prod /= (size_-1);
123132
return exp(log_prod);
124133
}
125-
134+
126135
Real khinchin_harmonic_mean() const {
127-
if (b_.size() == 1) {
136+
const int size_ = b_.size();
137+
if (size_ == 1) {
128138
return std::numeric_limits<Real>::quiet_NaN();
129139
}
130-
Real n = b_.size() - 1;
140+
Real n = size_ - 1;
131141
Real denom = 0;
132-
for (size_t i = 1; i < b_.size(); ++i) {
142+
for (int i = 1; i < size_; ++i) {
133143
denom += 1/static_cast<Real>(b_[i]);
134144
}
135145
return n/denom;
136146
}
137-
138-
const std::vector<Z>& partial_denominators() const {
147+
148+
// Note that this also includes the integer part (i.e. all the coefficients)
149+
const std::vector<int_type>& partial_denominators() const {
139150
return b_;
140151
}
141-
142-
template<typename T, typename Z2>
143-
friend std::ostream& operator<<(std::ostream& out, simple_continued_fraction<T, Z2>& scf);
144152

153+
template<typename T, typename I2>
154+
friend std::ostream& operator<<(std::ostream& out, simple_continued_fraction<T, I2>& scf);
155+
protected:
156+
simple_continued_fraction() = default;
157+
158+
// Note that non-const operations may result in invalid simple continued fraction
159+
std::vector<int_type>& partial_denominators() {
160+
return b_;
161+
}
145162
private:
146-
const Real x_;
147-
std::vector<Z> b_;
163+
std::vector<int_type> b_{};
148164
};
149165

150166

151-
template<typename Real, typename Z2>
152-
std::ostream& operator<<(std::ostream& out, simple_continued_fraction<Real, Z2>& scf) {
167+
template<typename Real, typename I2>
168+
std::ostream& operator<<(std::ostream& out, simple_continued_fraction<Real, I2>& scf) {
153169
constexpr const int p = std::numeric_limits<Real>::max_digits10;
154-
if constexpr (p == 2147483647) {
155-
out << std::setprecision(scf.x_.backend().precision());
156-
} else {
157-
out << std::setprecision(p);
158-
}
159-
170+
static_assert(p != 2147483647, "numeric_limits<Real>::max_digits10 == 2147483647");
171+
out << std::setprecision(p);
172+
160173
out << "[" << scf.b_.front();
161174
if (scf.b_.size() > 1)
162175
{

0 commit comments

Comments
 (0)