Skip to content

Commit 5090fd1

Browse files
author
Tomáš Kolárik
committed
simple_continued_fraction: added public functions to access and modify the coefficients
1 parent 5376689 commit 5090fd1

File tree

1 file changed

+85
-38
lines changed

1 file changed

+85
-38
lines changed

include/boost/math/tools/simple_continued_fraction.hpp

+85-38
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,26 @@ namespace boost::math::tools {
3232
template<typename Real, typename Z = int64_t>
3333
class simple_continued_fraction {
3434
public:
35-
simple_continued_fraction(Real x) : x_{x} {
35+
using int_type = Z;
36+
37+
simple_continued_fraction() = default;
38+
~simple_continued_fraction() = default;
39+
simple_continued_fraction(const simple_continued_fraction&) = default;
40+
simple_continued_fraction& operator =(const simple_continued_fraction&) = default;
41+
simple_continued_fraction(simple_continued_fraction&&) = default;
42+
simple_continued_fraction& operator =(simple_continued_fraction&&) = default;
43+
simple_continued_fraction(Real x) {
3644
using std::floor;
3745
using std::abs;
3846
using std::sqrt;
3947
using std::isfinite;
48+
const Real orig_x = x;
4049
if (!isfinite(x)) {
41-
throw std::domain_error("Cannot convert non-finites into continued fractions.");
50+
throw std::domain_error("Cannot convert non-finites into continued fractions.");
4251
}
43-
b_.reserve(50);
52+
reserve(50);
4453
Real bj = floor(x);
45-
b_.push_back(static_cast<Z>(bj));
54+
push_back(bj);
4655
if (bj == x) {
4756
b_.shrink_to_fit();
4857
return;
@@ -54,14 +63,13 @@ class simple_continued_fraction {
5463
}
5564
Real C = f;
5665
Real D = 0;
57-
int i = 0;
58-
// the "1 + i++" lets the error bound grow slowly with the number of convergents.
66+
// the "1 + i" lets the error bound grow slowly with the number of convergents.
5967
// I have not worked out the error propagation of the Modified Lentz's method to see if it does indeed grow at this rate.
6068
// 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-
{
69+
const Real eps_abs_orig_x = std::numeric_limits<Real>::epsilon()*abs(orig_x);
70+
for (int i = 0; abs(f - orig_x) >= (1 + i)*eps_abs_orig_x; ++i) {
6371
bj = floor(x);
64-
b_.push_back(static_cast<Z>(bj));
72+
push_back(bj);
6573
x = 1/(x-bj);
6674
D += bj;
6775
if (D == 0) {
@@ -77,13 +85,14 @@ class simple_continued_fraction {
7785
// Deal with non-uniqueness of continued fractions: [a0; a1, ..., an, 1] = a0; a1, ..., an + 1].
7886
// The shorter representation is considered the canonical representation,
7987
// so if we compute a non-canonical representation, change it to canonical:
80-
if (b_.size() > 2 && b_.back() == 1) {
81-
b_[b_.size() - 2] += 1;
82-
b_.resize(b_.size() - 1);
88+
if (size() > 2 && back() == 1) {
89+
pop_back();
90+
back() += 1;
8391
}
8492
b_.shrink_to_fit();
85-
86-
for (size_t i = 1; i < b_.size(); ++i) {
93+
94+
const int size_ = size();
95+
for (int i = 1; i < size_; ++i) {
8796
if (b_[i] <= 0) {
8897
std::ostringstream oss;
8998
oss << "Found a negative partial denominator: b[" << i << "] = " << b_[i] << "."
@@ -98,9 +107,10 @@ class simple_continued_fraction {
98107
}
99108
}
100109
}
101-
110+
102111
Real khinchin_geometric_mean() const {
103-
if (b_.size() == 1) {
112+
const int size_ = size();
113+
if (size_ == 1) {
104114
return std::numeric_limits<Real>::quiet_NaN();
105115
}
106116
using std::log;
@@ -110,7 +120,7 @@ class simple_continued_fraction {
110120
// A random partial denominator has ~80% chance of being in this table:
111121
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))};
112122
Real log_prod = 0;
113-
for (size_t i = 1; i < b_.size(); ++i) {
123+
for (int i = 1; i < size_; ++i) {
114124
if (b_[i] < static_cast<Z>(logs.size())) {
115125
log_prod += logs[b_[i]];
116126
}
@@ -119,53 +129,90 @@ class simple_continued_fraction {
119129
log_prod += log(static_cast<Real>(b_[i]));
120130
}
121131
}
122-
log_prod /= (b_.size()-1);
132+
log_prod /= (size_-1);
123133
return exp(log_prod);
124134
}
125-
135+
126136
Real khinchin_harmonic_mean() const {
127-
if (b_.size() == 1) {
137+
const int size_ = size();
138+
if (size_ == 1) {
128139
return std::numeric_limits<Real>::quiet_NaN();
129140
}
130-
Real n = b_.size() - 1;
141+
Real n = size_ - 1;
131142
Real denom = 0;
132-
for (size_t i = 1; i < b_.size(); ++i) {
143+
for (int i = 1; i < size_; ++i) {
133144
denom += 1/static_cast<Real>(b_[i]);
134145
}
135146
return n/denom;
136147
}
137-
138-
const std::vector<Z>& partial_denominators() const {
139-
return b_;
148+
149+
size_t size() const noexcept {
150+
return b_.size();
151+
}
152+
int_type operator [](int idx) const {
153+
return b_[idx];
154+
}
155+
int_type& operator [](int idx) {
156+
return b_[idx];
157+
}
158+
int_type front() const {
159+
return b_.front();
160+
}
161+
int_type& front() {
162+
return b_.front();
163+
}
164+
int_type back() const {
165+
return b_.back();
166+
}
167+
int_type& back() {
168+
return b_.back();
140169
}
141-
170+
auto begin() const noexcept {
171+
return b_.begin();
172+
}
173+
auto begin() noexcept {
174+
return b_.begin();
175+
}
176+
auto end() const noexcept {
177+
return b_.end();
178+
}
179+
auto end() noexcept {
180+
return b_.end();
181+
}
182+
183+
void reserve(size_t size_) {
184+
b_.reserve(size_);
185+
}
186+
void push_back(int_type c) {
187+
b_.push_back(c);
188+
}
189+
void pop_back() {
190+
b_.pop_back();
191+
}
192+
142193
template<typename T, typename Z2>
143194
friend std::ostream& operator<<(std::ostream& out, simple_continued_fraction<T, Z2>& scf);
144195

145196
private:
146-
const Real x_;
147-
std::vector<Z> b_;
197+
std::vector<Z> b_{};
148198
};
149199

150200

151201
template<typename Real, typename Z2>
152202
std::ostream& operator<<(std::ostream& out, simple_continued_fraction<Real, Z2>& scf) {
153203
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-
160-
out << "[" << scf.b_.front();
161-
if (scf.b_.size() > 1)
204+
static_assert(p != 2147483647, "numeric_limits<Real>::max_digits10 == 2147483647");
205+
out << std::setprecision(p);
206+
207+
out << "[" << scf.front();
208+
if (scf.size() > 1)
162209
{
163210
out << "; ";
164-
for (size_t i = 1; i < scf.b_.size() -1; ++i)
211+
for (size_t i = 1; i < scf.size() -1; ++i)
165212
{
166213
out << scf.b_[i] << ", ";
167214
}
168-
out << scf.b_.back();
215+
out << scf.back();
169216
}
170217
out << "]";
171218
return out;

0 commit comments

Comments
 (0)