File indexing completed on 2025-10-31 08:44:08
0001 
0002 
0003 
0004 
0005 
0006 
0007 #ifndef BOOST_HYPERGEOMETRIC_PFQ_SERIES_HPP_
0008 #define BOOST_HYPERGEOMETRIC_PFQ_SERIES_HPP_
0009 
0010 #ifndef BOOST_MATH_PFQ_MAX_B_TERMS
0011 #  define BOOST_MATH_PFQ_MAX_B_TERMS 5
0012 #endif
0013 
0014 #include <array>
0015 #include <cstdint>
0016 #include <boost/math/special_functions/gamma.hpp>
0017 #include <boost/math/special_functions/expm1.hpp>
0018 #include <boost/math/special_functions/detail/hypergeometric_series.hpp>
0019 
0020   namespace boost { namespace math { namespace detail {
0021 
0022      template <class Seq, class Real>
0023      unsigned set_crossover_locations(const Seq& aj, const Seq& bj, const Real& z, unsigned int* crossover_locations)
0024      {
0025         BOOST_MATH_STD_USING
0026         unsigned N_terms = 0;
0027 
0028         if(aj.size() == 1 && bj.size() == 1)
0029         {
0030            
0031            
0032            
0033            
0034            
0035            
0036            
0037            
0038            
0039            
0040            
0041            
0042            
0043            
0044            
0045            
0046            
0047            
0048            
0049            
0050            Real a = *aj.begin();
0051            Real b = *bj.begin();
0052            Real sq = 4 * a * z + b * b - 2 * b * z + z * z;
0053            if (sq >= 0)
0054            {
0055               Real t = (-sqrt(sq) - b + z) / 2;
0056               if (t >= 0)
0057               {
0058                  crossover_locations[N_terms] = itrunc(t);
0059                  ++N_terms;
0060               }
0061               t = (sqrt(sq) - b + z) / 2;
0062               if (t >= 0)
0063               {
0064                  crossover_locations[N_terms] = itrunc(t);
0065                  ++N_terms;
0066               }
0067            }
0068            sq = -4 * a * z + b * b + 2 * b * z + z * z;
0069            if (sq >= 0)
0070            {
0071               Real t = (-sqrt(sq) - b - z) / 2;
0072               if (t >= 0)
0073               {
0074                  crossover_locations[N_terms] = itrunc(t);
0075                  ++N_terms;
0076               }
0077               t = (sqrt(sq) - b - z) / 2;
0078               if (t >= 0)
0079               {
0080                  crossover_locations[N_terms] = itrunc(t);
0081                  ++N_terms;
0082               }
0083            }
0084            std::sort(crossover_locations, crossover_locations + N_terms, std::less<Real>());
0085            
0086            
0087            
0088            switch (N_terms)
0089            {
0090            case 0:
0091            case 1:
0092               break;
0093            case 2:
0094               crossover_locations[0] = crossover_locations[1];
0095               --N_terms;
0096               break;
0097            case 3:
0098               crossover_locations[1] = crossover_locations[2];
0099               --N_terms;
0100               break;
0101            case 4:
0102               crossover_locations[0] = crossover_locations[1];
0103               crossover_locations[1] = crossover_locations[3];
0104               N_terms -= 2;
0105               break;
0106            }
0107         }
0108         else
0109         {
0110            unsigned n = 0;
0111            for (auto bi = bj.begin(); bi != bj.end(); ++bi, ++n)
0112            {
0113               crossover_locations[n] = *bi >= 0 ? 0 : itrunc(-*bi) + 1;
0114            }
0115            std::sort(crossover_locations, crossover_locations + bj.size(), std::less<Real>());
0116            N_terms = (unsigned)bj.size();
0117         }
0118         return N_terms;
0119      }
0120 
0121      template <class Seq, class Real, class Policy, class Terminal>
0122      std::pair<Real, Real> hypergeometric_pFq_checked_series_impl(const Seq& aj, const Seq& bj, const Real& z, const Policy& pol, const Terminal& termination, long long& log_scale)
0123      {
0124         BOOST_MATH_STD_USING
0125         Real result = 1;
0126         Real abs_result = 1;
0127         Real term = 1;
0128         Real term0 = 0;
0129         Real tol = boost::math::policies::get_epsilon<Real, Policy>();
0130         std::uintmax_t k = 0;
0131         Real upper_limit(sqrt(boost::math::tools::max_value<Real>())), diff;
0132         Real lower_limit(1 / upper_limit);
0133         long long log_scaling_factor = lltrunc(boost::math::tools::log_max_value<Real>()) - 2;
0134         Real scaling_factor = exp(Real(log_scaling_factor));
0135         Real term_m1;
0136         long long local_scaling = 0;
0137         bool have_no_correct_bits = false;
0138 
0139         if ((aj.size() == 1) && (bj.size() == 0))
0140         {
0141            if (fabs(z) > 1)
0142            {
0143               if ((z > 0) && (floor(*aj.begin()) != *aj.begin()))
0144               {
0145                  Real r = policies::raise_domain_error("boost::math::hypergeometric_pFq", "Got p == 1 and q == 0 and |z| > 1, result is imaginary", z, pol);
0146                  return std::make_pair(r, r);
0147               }
0148               std::pair<Real, Real> r = hypergeometric_pFq_checked_series_impl(aj, bj, Real(1 / z), pol, termination, log_scale);
0149               
0150               #if (defined(__GNUC__) && __GNUC__ == 13)
0151               Real mul = pow(-z, Real(-*aj.begin()));
0152               #else
0153               Real mul = pow(-z, -*aj.begin());
0154               #endif
0155               
0156               r.first *= mul;
0157               r.second *= mul;
0158               return r;
0159            }
0160         }
0161 
0162         if (aj.size() > bj.size())
0163         {
0164            if (aj.size() == bj.size() + 1)
0165            {
0166               if (fabs(z) > 1)
0167               {
0168                  Real r = policies::raise_domain_error("boost::math::hypergeometric_pFq", "Got p == q+1 and |z| > 1, series does not converge", z, pol);
0169                  return std::make_pair(r, r);
0170               }
0171               if (fabs(z) == 1)
0172               {
0173                  Real s = 0;
0174                  for (auto i = bj.begin(); i != bj.end(); ++i)
0175                     s += *i;
0176                  for (auto i = aj.begin(); i != aj.end(); ++i)
0177                     s -= *i;
0178                  if ((z == 1) && (s <= 0))
0179                  {
0180                     Real r = policies::raise_domain_error("boost::math::hypergeometric_pFq", "Got p == q+1 and |z| == 1, in a situation where the series does not converge", z, pol);
0181                     return std::make_pair(r, r);
0182                  }
0183                  if ((z == -1) && (s <= -1))
0184                  {
0185                     Real r = policies::raise_domain_error("boost::math::hypergeometric_pFq", "Got p == q+1 and |z| == 1, in a situation where the series does not converge", z, pol);
0186                     return std::make_pair(r, r);
0187                  }
0188               }
0189            }
0190            else
0191            {
0192               Real r = policies::raise_domain_error("boost::math::hypergeometric_pFq", "Got p > q+1, series does not converge", z, pol);
0193               return std::make_pair(r, r);
0194            }
0195         }
0196 
0197         while (!termination(k))
0198         {
0199            for (auto ai = aj.begin(); ai != aj.end(); ++ai)
0200            {
0201               term *= *ai + k;
0202            }
0203            if (term == 0)
0204            {
0205               
0206               return std::make_pair(result, abs_result);
0207            }
0208            for (auto bi = bj.begin(); bi != bj.end(); ++bi)
0209            {
0210               if (*bi + k == 0)
0211               {
0212                  
0213                  result = boost::math::policies::raise_domain_error("boost::math::hypergeometric_pFq<%1%>", "One of the b values was the negative integer %1%", *bi, pol);
0214                  return std::make_pair(result, result);
0215               }
0216               term /= *bi + k;
0217            }
0218            term *= z;
0219            ++k;
0220            term /= k;
0221            
0222            result += term;
0223            abs_result += abs(term);
0224            
0225 
0226            
0227            
0228            
0229            if (fabs(abs_result) >= upper_limit)
0230            {
0231               abs_result /= scaling_factor;
0232               result /= scaling_factor;
0233               term /= scaling_factor;
0234               log_scale += log_scaling_factor;
0235               local_scaling += log_scaling_factor;
0236            }
0237            if (fabs(abs_result) < lower_limit)
0238            {
0239               abs_result *= scaling_factor;
0240               result *= scaling_factor;
0241               term *= scaling_factor;
0242               log_scale -= log_scaling_factor;
0243               local_scaling -= log_scaling_factor;
0244            }
0245 
0246            if ((abs(result * tol) > abs(term)) && (abs(term0) > abs(term)))
0247               break;
0248            if (abs_result * tol > abs(result))
0249            {
0250               
0251               
0252               
0253               if (have_no_correct_bits)
0254               {
0255                  
0256                  result = boost::math::policies::raise_evaluation_error("boost::math::hypergeometric_pFq<%1%>", "Cancellation is so severe that no bits in the result are correct, last result was %1%", Real(result * exp(Real(log_scale))), pol);
0257                  return std::make_pair(result, result);
0258               }
0259               else
0260                  have_no_correct_bits = true;
0261            }
0262            else
0263               have_no_correct_bits = false;
0264            term0 = term;
0265         }
0266         
0267         
0268         
0269         
0270         
0271         
0272         if(bj.size() > BOOST_MATH_PFQ_MAX_B_TERMS)
0273            policies::raise_domain_error<Real>("boost::math::hypergeometric_pFq<%1%>(Seq, Seq, %1%)",
0274               "The number of b terms must be less than the value of BOOST_MATH_PFQ_MAX_B_TERMS (" BOOST_MATH_STRINGIZE(BOOST_MATH_PFQ_MAX_B_TERMS)  "), but got %1%.",
0275               Real(bj.size()), pol);
0276 
0277         unsigned crossover_locations[BOOST_MATH_PFQ_MAX_B_TERMS];
0278 
0279         unsigned N_crossovers = set_crossover_locations(aj, bj, z, crossover_locations);
0280 
0281         bool terminate = false;   
0282 
0283         for (unsigned n = 0; n < N_crossovers; ++n)
0284         {
0285            if (k < crossover_locations[n])
0286            {
0287               for (auto ai = aj.begin(); ai != aj.end(); ++ai)
0288               {
0289                  if ((*ai < 0) && (floor(*ai) == *ai) && (*ai > static_cast<decltype(*ai)>(crossover_locations[n])))
0290                     return std::make_pair(result, abs_result);  
0291               }
0292               
0293               
0294               
0295               Real loop_result = 0;
0296               Real loop_abs_result = 0;
0297               long long loop_scale = 0;
0298               
0299               
0300               
0301               
0302               
0303               Real loop_error_scale = 0;
0304               
0305               
0306               
0307               
0308               
0309               unsigned s = crossover_locations[n];
0310               std::uintmax_t backstop = k;
0311               long long s1(1), s2(1);
0312               term = 0;
0313               for (auto ai = aj.begin(); ai != aj.end(); ++ai)
0314               {
0315                  if ((floor(*ai) == *ai) && (*ai < 0) && (-*ai <= static_cast<decltype(*ai)>(s)))
0316                  {
0317                     
0318                     terminate = true;
0319                     break;
0320                  }
0321                  else
0322                  {
0323                     int ls = 1;
0324                     Real p = log_pochhammer(*ai, s, pol, &ls);
0325                     s1 *= ls;
0326                     term += p;
0327                     loop_error_scale = (std::max)(p, loop_error_scale);
0328                     
0329                  }
0330               }
0331               
0332               if (terminate)
0333                  break;
0334               for (auto bi = bj.begin(); bi != bj.end(); ++bi)
0335               {
0336                  int ls = 1;
0337                  Real p = log_pochhammer(*bi, s, pol, &ls);
0338                  s2 *= ls;
0339                  term -= p;
0340                  loop_error_scale = (std::max)(p, loop_error_scale);
0341                  
0342               }
0343               
0344               Real p = lgamma(Real(s + 1), pol);
0345               term -= p;
0346               loop_error_scale = (std::max)(p, loop_error_scale);
0347               
0348               p = s * log(fabs(z));
0349               term += p;
0350               loop_error_scale = (std::max)(p, loop_error_scale);
0351               
0352               
0353               
0354               
0355               
0356               
0357               
0358               loop_error_scale *= tools::epsilon<Real>();
0359               
0360               
0361               
0362               loop_error_scale = fabs(expm1(loop_error_scale, pol));
0363               
0364               
0365               
0366               loop_error_scale /= tools::epsilon<Real>();
0367 
0368               if (z < 0)
0369                  s1 *= (s & 1 ? -1 : 1);
0370 
0371               if (term <= tools::log_min_value<Real>())
0372               {
0373                  
0374                  long long scale = lltrunc(floor(term - tools::log_min_value<Real>()) - 2);
0375                  term -= scale;
0376                  loop_scale += scale;
0377               }
0378                if (term > 10)
0379                {
0380                   int scale = itrunc(floor(term));
0381                   term -= scale;
0382                   loop_scale += scale;
0383                }
0384                
0385                term = s1 * s2 * exp(term);
0386                
0387                
0388                k = s;
0389                term0 = term;
0390                long long saved_loop_scale = loop_scale;
0391                bool terms_are_growing = true;
0392                bool trivial_small_series_check = false;
0393                do
0394                {
0395                   loop_result += term;
0396                   loop_abs_result += fabs(term);
0397                   
0398                   if (fabs(loop_result) >= upper_limit)
0399                   {
0400                      loop_result /= scaling_factor;
0401                      loop_abs_result /= scaling_factor;
0402                      term /= scaling_factor;
0403                      loop_scale += log_scaling_factor;
0404                   }
0405                   if (fabs(loop_result) < lower_limit)
0406                   {
0407                      loop_result *= scaling_factor;
0408                      loop_abs_result *= scaling_factor;
0409                      term *= scaling_factor;
0410                      loop_scale -= log_scaling_factor;
0411                   }
0412                   term_m1 = term;
0413                   for (auto ai = aj.begin(); ai != aj.end(); ++ai)
0414                   {
0415                      term *= *ai + k;
0416                   }
0417                   if (term == 0)
0418                   {
0419                      
0420                      return std::make_pair(result, abs_result);
0421                   }
0422                   for (auto bi = bj.begin(); bi != bj.end(); ++bi)
0423                   {
0424                      if (*bi + k == 0)
0425                      {
0426                         
0427                         result = boost::math::policies::raise_domain_error("boost::math::hypergeometric_pFq<%1%>", "One of the b values was the negative integer %1%", *bi, pol);
0428                         return std::make_pair(result, result);
0429                      }
0430                      term /= *bi + k;
0431                   }
0432                   term *= z / (k + 1);
0433 
0434                   ++k;
0435                   diff = fabs(term / loop_result);
0436                   terms_are_growing = fabs(term) > fabs(term_m1);
0437                   if (!trivial_small_series_check && !terms_are_growing)
0438                   {
0439                      
0440                      
0441                      
0442                      
0443                      
0444                      trivial_small_series_check = true;
0445                      Real d;
0446                      if (loop_scale > local_scaling)
0447                      {
0448                         long long rescale = local_scaling - loop_scale;
0449                         if (rescale < tools::log_min_value<Real>())
0450                            d = 1;  
0451                         else
0452                            d = fabs(term / (result * exp(Real(rescale))));
0453                      }
0454                      else
0455                      {
0456                         long long rescale = loop_scale - local_scaling;
0457                         if (rescale < tools::log_min_value<Real>())
0458                            d = 0;  
0459                         else
0460                            d = fabs(term * exp(Real(rescale)) / result);
0461                      }
0462                      if (d < boost::math::policies::get_epsilon<Real, Policy>())
0463                         break;
0464                   }
0465                } while (!termination(k - s) && ((diff > boost::math::policies::get_epsilon<Real, Policy>()) || terms_are_growing));
0466 
0467                
0468                
0469                
0470                
0471                
0472                
0473                std::uintmax_t next_backstop = k;
0474                loop_abs_result += loop_error_scale * fabs(loop_result);
0475                if (loop_scale > local_scaling)
0476                {
0477                   
0478                   
0479                   
0480                   long long rescale = local_scaling - loop_scale;
0481                   local_scaling = loop_scale;
0482                   log_scale -= rescale;
0483                   Real ex = exp(Real(rescale));
0484                   result *= ex;
0485                   abs_result *= ex;
0486                   result += loop_result;
0487                   abs_result += loop_abs_result;
0488                }
0489                else if (local_scaling > loop_scale)
0490                {
0491                   
0492                   
0493                   
0494                   long long rescale = loop_scale - local_scaling;
0495                   Real ex = exp(Real(rescale));
0496                   loop_result *= ex;
0497                   loop_abs_result *= ex;
0498                   result += loop_result;
0499                   abs_result += loop_abs_result;
0500                }
0501                else
0502                {
0503                   result += loop_result;
0504                   abs_result += loop_abs_result;
0505                }
0506                
0507                
0508                
0509                k = s;
0510                term = term0;
0511                loop_result = 0;
0512                loop_abs_result = 0;
0513                loop_scale = saved_loop_scale;
0514                trivial_small_series_check = false;
0515                do
0516                {
0517                   --k;
0518                   if (k == backstop)
0519                      break;
0520                   term_m1 = term;
0521                   for (auto ai = aj.begin(); ai != aj.end(); ++ai)
0522                   {
0523                      term /= *ai + k;
0524                   }
0525                   for (auto bi = bj.begin(); bi != bj.end(); ++bi)
0526                   {
0527                      if (*bi + k == 0)
0528                      {
0529                         
0530                         result = boost::math::policies::raise_domain_error("boost::math::hypergeometric_pFq<%1%>", "One of the b values was the negative integer %1%", *bi, pol);
0531                         return std::make_pair(result, result);
0532                      }
0533                      term *= *bi + k;
0534                   }
0535                   term *= (k + 1) / z;
0536                   loop_result += term;
0537                   loop_abs_result += fabs(term);
0538 
0539                   if (!trivial_small_series_check && (fabs(term) < fabs(term_m1)))
0540                   {
0541                      
0542                      
0543                      
0544                      
0545                      
0546                      trivial_small_series_check = true;
0547                      Real d;
0548                      if (loop_scale > local_scaling)
0549                      {
0550                         long long rescale = local_scaling - loop_scale;
0551                         if (rescale < tools::log_min_value<Real>())
0552                            d = 1;  
0553                         else
0554                            d = fabs(term / (result * exp(Real(rescale))));
0555                      }
0556                      else
0557                      {
0558                         long long rescale = loop_scale - local_scaling;
0559                         if (rescale < tools::log_min_value<Real>())
0560                            d = 0;  
0561                         else
0562                            d = fabs(term * exp(Real(rescale)) / result);
0563                      }
0564                      if (d < boost::math::policies::get_epsilon<Real, Policy>())
0565                         break;
0566                   }
0567 
0568                   
0569                   if (fabs(loop_result) >= upper_limit)
0570                   {
0571                      loop_result /= scaling_factor;
0572                      loop_abs_result /= scaling_factor;
0573                      term /= scaling_factor;
0574                      loop_scale += log_scaling_factor;
0575                   }
0576                   if (fabs(loop_result) < lower_limit)
0577                   {
0578                      loop_result *= scaling_factor;
0579                      loop_abs_result *= scaling_factor;
0580                      term *= scaling_factor;
0581                      loop_scale -= log_scaling_factor;
0582                   }
0583                   diff = fabs(term / loop_result);
0584                } while (!termination(s - k) && ((diff > boost::math::policies::get_epsilon<Real, Policy>()) || (fabs(term) > fabs(term_m1))));
0585 
0586                
0587                
0588                
0589                
0590                
0591                
0592                loop_abs_result += loop_error_scale * fabs(loop_result);
0593                
0594                if (loop_scale > local_scaling)
0595                {
0596                   
0597                   
0598                   
0599                   long long rescale = local_scaling - loop_scale;
0600                   local_scaling = loop_scale;
0601                   log_scale -= rescale;
0602                   Real ex = exp(Real(rescale));
0603                   result *= ex;
0604                   abs_result *= ex;
0605                   result += loop_result;
0606                   abs_result += loop_abs_result;
0607                }
0608                else if (local_scaling > loop_scale)
0609                {
0610                   
0611                   
0612                   
0613                   long long rescale = loop_scale - local_scaling;
0614                   Real ex = exp(Real(rescale));
0615                   loop_result *= ex;
0616                   loop_abs_result *= ex;
0617                   result += loop_result;
0618                   abs_result += loop_abs_result;
0619                }
0620                else
0621                {
0622                   result += loop_result;
0623                   abs_result += loop_abs_result;
0624                }
0625                
0626                
0627                
0628                k = next_backstop;
0629            }
0630         }
0631 
0632         return std::make_pair(result, abs_result);
0633      }
0634 
0635      struct iteration_terminator
0636      {
0637         iteration_terminator(std::uintmax_t i) : m(i) {}
0638 
0639         bool operator()(std::uintmax_t v) const { return v >= m; }
0640 
0641         std::uintmax_t m;
0642      };
0643 
0644      template <class Seq, class Real, class Policy>
0645      Real hypergeometric_pFq_checked_series_impl(const Seq& aj, const Seq& bj, const Real& z, const Policy& pol, long long& log_scale)
0646      {
0647         BOOST_MATH_STD_USING
0648         iteration_terminator term(boost::math::policies::get_max_series_iterations<Policy>());
0649         std::pair<Real, Real> result = hypergeometric_pFq_checked_series_impl(aj, bj, z, pol, term, log_scale);
0650         
0651         
0652         
0653         
0654         if (result.second * sqrt(boost::math::policies::get_epsilon<Real, Policy>()) > abs(result.first))
0655         {
0656            return boost::math::policies::raise_evaluation_error("boost::math::hypergeometric_pFq<%1%>", "Cancellation is so severe that fewer than half the bits in the result are correct, last result was %1%", Real(result.first * exp(Real(log_scale))), pol);
0657         }
0658         return result.first;
0659      }
0660 
0661      template <class Real, class Policy>
0662      inline Real hypergeometric_1F1_checked_series_impl(const Real& a, const Real& b, const Real& z, const Policy& pol, long long& log_scale)
0663      {
0664         std::array<Real, 1> aj = { a };
0665         std::array<Real, 1> bj = { b };
0666         return hypergeometric_pFq_checked_series_impl(aj, bj, z, pol, log_scale);
0667      }
0668 
0669   } } } 
0670 
0671 #endif