File indexing completed on 2025-01-18 09:40:40
0001
0002
0003
0004
0005
0006 #ifndef BOOST_MATH_TOOLS_ENGEL_EXPANSION_HPP
0007 #define BOOST_MATH_TOOLS_ENGEL_EXPANSION_HPP
0008
0009 #include <cmath>
0010 #include <cstdint>
0011 #include <vector>
0012 #include <ostream>
0013 #include <iomanip>
0014 #include <limits>
0015 #include <stdexcept>
0016
0017 #include <boost/math/tools/is_standalone.hpp>
0018 #ifndef BOOST_MATH_STANDALONE
0019 #include <boost/config.hpp>
0020 #ifdef BOOST_NO_CXX17_IF_CONSTEXPR
0021 #error "The header <boost/math/norms.hpp> can only be used in C++17 and later."
0022 #endif
0023 #endif
0024
0025 namespace boost::math::tools {
0026
0027 template<typename Real, typename Z = int64_t>
0028 class engel_expansion {
0029 public:
0030 engel_expansion(Real x) : x_{x}
0031 {
0032 using std::floor;
0033 using std::abs;
0034 using std::sqrt;
0035 using std::isfinite;
0036 if (!isfinite(x))
0037 {
0038 throw std::domain_error("Cannot convert non-finites into an Engel expansion.");
0039 }
0040
0041 if(x==0)
0042 {
0043 throw std::domain_error("Zero does not have an Engel expansion.");
0044 }
0045 a_.reserve(64);
0046
0047
0048
0049 Real i = 1;
0050 Real computed = 0;
0051 Real term = 1;
0052 Real scale = std::numeric_limits<Real>::epsilon()*abs(x_)/2;
0053 Real u = x;
0054 while (abs(x_ - computed) > (i++)*scale)
0055 {
0056 Real recip = 1/u;
0057 Real ak = ceil(recip);
0058 a_.push_back(static_cast<Z>(ak));
0059 u = u*ak - 1;
0060 if (u==0)
0061 {
0062 break;
0063 }
0064 term /= ak;
0065 computed += term;
0066 }
0067
0068 for (size_t j = 1; j < a_.size(); ++j)
0069 {
0070
0071 if (a_[j] < a_[j-1])
0072 {
0073 throw std::domain_error("The digits of an Engel expansion must form a non-decreasing sequence; consider increasing the wide of the integer type.");
0074 }
0075
0076 if (a_[j] == (std::numeric_limits<Z>::max)())
0077 {
0078 throw std::domain_error("The integer type Z does not have enough width to hold the terms of the Engel expansion; please widen the type.");
0079 }
0080 }
0081 a_.shrink_to_fit();
0082 }
0083
0084
0085 const std::vector<Z>& digits() const
0086 {
0087 return a_;
0088 }
0089
0090 template<typename T, typename Z2>
0091 friend std::ostream& operator<<(std::ostream& out, engel_expansion<T, Z2>& eng);
0092
0093 private:
0094 Real x_;
0095 std::vector<Z> a_;
0096 };
0097
0098
0099 template<typename Real, typename Z2>
0100 std::ostream& operator<<(std::ostream& out, engel_expansion<Real, Z2>& engel)
0101 {
0102 constexpr const int p = std::numeric_limits<Real>::max_digits10;
0103 if constexpr (p == 2147483647)
0104 {
0105 out << std::setprecision(engel.x_.backend().precision());
0106 }
0107 else
0108 {
0109 out << std::setprecision(p);
0110 }
0111
0112 out << "{";
0113 for (size_t i = 0; i < engel.a_.size() - 1; ++i)
0114 {
0115 out << engel.a_[i] << ", ";
0116 }
0117 out << engel.a_.back();
0118 out << "}";
0119 return out;
0120 }
0121
0122
0123 }
0124 #endif