@@ -32,17 +32,26 @@ namespace boost::math::tools {
32
32
template <typename Real, typename Z = int64_t >
33
33
class simple_continued_fraction {
34
34
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) {
36
44
using std::floor ;
37
45
using std::abs ;
38
46
using std::sqrt ;
39
47
using std::isfinite;
48
+ const Real orig_x = x;
40
49
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." );
42
51
}
43
- b_. reserve (50 );
52
+ reserve (50 );
44
53
Real bj = floor (x);
45
- b_. push_back (static_cast <Z>(bj) );
54
+ push_back (bj );
46
55
if (bj == x) {
47
56
b_.shrink_to_fit ();
48
57
return ;
@@ -54,14 +63,13 @@ class simple_continued_fraction {
54
63
}
55
64
Real C = f;
56
65
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.
59
67
// I have not worked out the error propagation of the Modified Lentz's method to see if it does indeed grow at this rate.
60
68
// 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) {
63
71
bj = floor (x);
64
- b_. push_back (static_cast <Z>(bj) );
72
+ push_back (bj );
65
73
x = 1 /(x-bj);
66
74
D += bj;
67
75
if (D == 0 ) {
@@ -77,13 +85,14 @@ class simple_continued_fraction {
77
85
// Deal with non-uniqueness of continued fractions: [a0; a1, ..., an, 1] = a0; a1, ..., an + 1].
78
86
// The shorter representation is considered the canonical representation,
79
87
// 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 ;
83
91
}
84
92
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) {
87
96
if (b_[i] <= 0 ) {
88
97
std::ostringstream oss;
89
98
oss << " Found a negative partial denominator: b[" << i << " ] = " << b_[i] << " ."
@@ -98,9 +107,10 @@ class simple_continued_fraction {
98
107
}
99
108
}
100
109
}
101
-
110
+
102
111
Real khinchin_geometric_mean () const {
103
- if (b_.size () == 1 ) {
112
+ const int size_ = size ();
113
+ if (size_ == 1 ) {
104
114
return std::numeric_limits<Real>::quiet_NaN ();
105
115
}
106
116
using std::log ;
@@ -110,7 +120,7 @@ class simple_continued_fraction {
110
120
// A random partial denominator has ~80% chance of being in this table:
111
121
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 ))};
112
122
Real log_prod = 0 ;
113
- for (size_t i = 1 ; i < b_. size () ; ++i) {
123
+ for (int i = 1 ; i < size_ ; ++i) {
114
124
if (b_[i] < static_cast <Z>(logs.size ())) {
115
125
log_prod += logs[b_[i]];
116
126
}
@@ -119,53 +129,90 @@ class simple_continued_fraction {
119
129
log_prod += log (static_cast <Real>(b_[i]));
120
130
}
121
131
}
122
- log_prod /= (b_. size () -1 );
132
+ log_prod /= (size_ -1 );
123
133
return exp (log_prod);
124
134
}
125
-
135
+
126
136
Real khinchin_harmonic_mean () const {
127
- if (b_.size () == 1 ) {
137
+ const int size_ = size ();
138
+ if (size_ == 1 ) {
128
139
return std::numeric_limits<Real>::quiet_NaN ();
129
140
}
130
- Real n = b_. size () - 1 ;
141
+ Real n = size_ - 1 ;
131
142
Real denom = 0 ;
132
- for (size_t i = 1 ; i < b_. size () ; ++i) {
143
+ for (int i = 1 ; i < size_ ; ++i) {
133
144
denom += 1 /static_cast <Real>(b_[i]);
134
145
}
135
146
return n/denom;
136
147
}
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 ();
140
169
}
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
+
142
193
template <typename T, typename Z2>
143
194
friend std::ostream& operator <<(std::ostream& out, simple_continued_fraction<T, Z2>& scf);
144
195
145
196
private:
146
- const Real x_;
147
- std::vector<Z> b_;
197
+ std::vector<Z> b_{};
148
198
};
149
199
150
200
151
201
template <typename Real, typename Z2>
152
202
std::ostream& operator <<(std::ostream& out, simple_continued_fraction<Real, Z2>& scf) {
153
203
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 )
162
209
{
163
210
out << " ; " ;
164
- for (size_t i = 1 ; i < scf.b_ . size () -1 ; ++i)
211
+ for (size_t i = 1 ; i < scf.size () -1 ; ++i)
165
212
{
166
213
out << scf.b_ [i] << " , " ;
167
214
}
168
- out << scf.b_ . back ();
215
+ out << scf.back ();
169
216
}
170
217
out << " ]" ;
171
218
return out;
0 commit comments