2929
3030namespace boost ::math::tools {
3131
32- template <typename Real, typename Z = int64_t >
32+ template <typename Real, typename I = int64_t >
3333class simple_continued_fraction {
3434public:
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+ }
145162private:
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