@@ -32,17 +32,26 @@ namespace boost::math::tools {
3232template <typename Real, typename Z = int64_t >
3333class simple_continued_fraction {
3434public:
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
145196private:
146- const Real x_;
147- std::vector<Z> b_;
197+ std::vector<Z> b_{};
148198};
149199
150200
151201template <typename Real, typename Z2>
152202std::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