Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-06-02 08:17:09

0001 //
0002 //  lisk.ipp
0003 //  LiSK
0004 //
0005 //  Created by Sebastian on 08/03/16.
0006 //  Copyright © 2016 Sebastian Kirchner. All rights reserved.
0007 //
0008 //  This file is part of LiSK.
0009 //
0010 //  LiSK is free software: you can redistribute it and/or modify
0011 //  it under the terms of the GNU General Public License as published by
0012 //  the Free Software Foundation, either version 3 of the License, or
0013 //  (at your option) any later version.
0014 //
0015 //  LiSK is distributed in the hope that it will be useful,
0016 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
0017 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0018 //  GNU General Public License for more details.
0019 //
0020 //  You should have received a copy of the GNU General Public License
0021 //  along with LiSK.  If not, see <http://www.gnu.org/licenses/>.
0022 
0023 #include "lisk.hpp"
0024 #include <string>
0025 #include <cmath>
0026 #include <cassert>
0027 #include <type_traits>
0028 
0029 
0030 /************************************************************************
0031 *                                                                       *
0032 *                   Implementation of LiSK                              *
0033 *                                                                       *
0034 *************************************************************************/
0035 /*******************************************
0036  *  C'stors                                *
0037  *******************************************/
0038 template <typename numtype>
0039 LiSK<numtype>::LiSK(const size_t n, const size_t prec) :
0040         _prec{prec<17 ? 17:prec},
0041         _constants_clR{n},
0042         _constants{n},
0043         _iep_d{0.,std::pow(10,-(17-_offset))},
0044         _iep_clN{cln::complex(0,cln::cl_F(("1.e-"+std::to_string(prec-_offset)+"_"+std::to_string(prec)).c_str()))}
0045 {
0046     
0047         // Check for supported argument type
0048     if (!std::is_same<numtype, cp_d>::value && !std::is_same<numtype, cln::cl_N>::value) {
0049         throw std::runtime_error("Momentarily LiSK only supports 'std::complex<double>' and 'cln::cl_N' as types!");
0050     }
0051     
0052         // Initilise constants by respecting user choice and the following default minimum
0053     std::unordered_map<std::string, size_t> default_max_values = {
0054             {"nHarmNum",3},{"nPosZeta",4},{"nNegZeta",100},{"nFactorials",100},{"nBernNum",100},{"nLiCij",100},{"nLiBn_eq59",50}};
0055     
0056     for (auto x : _user_max_values) {
0057         default_max_values[x.first] = std::max(default_max_values[x.first], x.second);
0058     }
0059     
0060     _init(default_max_values);
0061     
0062 }
0063 
0064 template <typename numtype>
0065 template <typename T>
0066 LiSK<numtype>::constants<T>::constants(const size_t Li_n) {
0067     
0068     max_values["nweight"] = Li_n;
0069     
0070         // Adapt vector size of expected weights n for Li_n
0071         // LiBn_eq59 is specialised to n<=4, where n=1 does not
0072         // contribute
0073     LiCij.resize(Li_n);
0074     LiBn_eq59.resize(3);
0075 }
0076 
0077 
0078 /************************************************************************
0079  *                   Public Wrappers                                    *
0080  ************************************************************************/
0081 /********************************************
0082  * Li(n,x)                                  *
0083  ********************************************/
0084 template <typename numtype>
0085 numtype LiSK<numtype>::Li(const size_t n, const numtype x) {
0086     
0087     assert(n>0);
0088     switch (n) {
0089         case 1:
0090             return _Li1(_add_iep(x));
0091             
0092         case 2:
0093             return _Li2(_add_iep(x));
0094             
0095         case 3:
0096             return _Li3(_add_iep(x));
0097             
0098         case 4:
0099             return _Li4(_add_iep(x));
0100             
0101         default:
0102             return _Lin_basis(n, _add_iep(x));
0103     }
0104 }
0105 
0106 template <typename numtype> inline
0107 numtype LiSK<numtype>::Li1(const numtype x) {
0108     return _Li1(_add_iep(x));
0109 }
0110 
0111 template <typename numtype> inline
0112 numtype LiSK<numtype>::Li2(const numtype x) {
0113     return _Li2(_add_iep(x));
0114 }
0115 
0116 template <typename numtype> inline
0117 numtype LiSK<numtype>::Li3(const numtype x) {
0118     return _Li3(_add_iep(x));
0119 }
0120 
0121 template <typename numtype> inline
0122 numtype LiSK<numtype>::Li4(const numtype x) {
0123     return _Li4(_add_iep(x));
0124 }
0125 
0126 /********************************************
0127  * Li22(x,y)                                *
0128  ********************************************/
0129 template <typename numtype>
0130 numtype LiSK<numtype>::Li22(const numtype x, const numtype y) {
0131     
0132     const auto one = Re((numtype)1);
0133     const numtype tx = _add_iep(x), ty = _add_iep(y);
0134     
0135         //Special cases from (A.1)-(A.3) of 1601.02649
0136     if (is_zero(x) || is_zero(y)) return 0;
0137     
0138     if (is_zero(x-one) && is_zero(y-one)) return (((numtype)3)*_constants.PosZeta[4])/((numtype)4);
0139     if (is_zero(x+one) && is_zero(y+one)) return -(((numtype)3)*_constants.PosZeta[4])/((numtype)16);
0140     
0141     if (is_zero(one/x-y)) {
0142         
0143             // Check if inside of radius of convergence or if stuffle
0144             // relation is needed
0145         if (abs(x)<=1) {
0146             const numtype lmty = log(-ty), Li2 = _Li2(ty), &zeta2 = _constants.PosZeta[2];
0147             return ((numtype)3)*_Li4(ty) - Li2*(Li2 + lmty*lmty + ((numtype)6)*zeta2)/((numtype)2) - (((numtype)2)*zeta2*zeta2)/((numtype)5);
0148             
0149         } else {
0150                 // Li_22(y,x)
0151             const numtype lmtx = log(-tx), Li2x = _Li2(tx), &zeta2 = _constants.PosZeta[2];
0152             const numtype Li22_yx = ((numtype)3)*_Li4(tx) - Li2x*(Li2x + lmtx*lmtx + ((numtype)6)*zeta2)/((numtype)2)
0153                     - (((numtype)2)*zeta2*zeta2)/((numtype)5);
0154             
0155             return -Li22_yx - _Li4(tx*ty) + Li2x*_Li2(ty);
0156         }
0157     }
0158     
0159     return _Li22(tx, ty);
0160 }
0161 
0162 
0163 /************************************************************************
0164  *                   Initialisation                                     *
0165  ************************************************************************/
0166 /************************************************
0167  * Auxillary functions for Init                 *
0168  ************************************************/
0169     // Max value from vector
0170 template <typename numtype>
0171 template <typename T>
0172 T LiSK<numtype>::_vec_max(const std::vector<T> v) const {
0173     
0174     T tmp = (v.size()==0 ? T() : v[0]);
0175     for(auto i : v) tmp = std::max(tmp, i);
0176     return tmp;
0177 }
0178 
0179     // Harmonic numbers
0180 template <typename numtype>
0181 void LiSK<numtype>::_HarmNum_extend(){
0182     
0183         // nstart and nend define the start and end index of H_n
0184     auto &v = _constants_clR.HarmNum;
0185     const size_t nstart = v.size();
0186     const size_t nend   = _vec_max<size_t>({_constants_clR.max_values["nHarmNum"]+1,nstart});
0187     
0188     for(size_t n=nstart; n<nend; ++n){
0189         cln::cl_RA tmp = 0;
0190         if(n>0){
0191             for(size_t k=1;k<=n; ++k){
0192                 tmp += cln::cl_R("1")/k;
0193             }
0194         }
0195         v.push_back(tmp);
0196     }
0197 }
0198 
0199     // Positve zeta values
0200 template <typename numtype>
0201 void LiSK<numtype>::_PosZeta_extend() {
0202     
0203         // nstart and nend define the start and end index of zeta(n)
0204     auto &v = _constants_clR.PosZeta;
0205     const size_t nstart = v.size();
0206     const size_t nend   = _vec_max<size_t>({_constants_clR.max_values["nPosZeta"]+1,nstart});
0207     
0208     for (size_t n=nstart; n<nend; ++n) {
0209         if(n==0) v.push_back(-cln::cl_R("1/2"));
0210         else if (n==1) v.push_back(0);
0211         else v.push_back(cln::zeta((int)n, cln::float_format(_prec)));
0212     }
0213 }
0214 
0215     // Factorials
0216 template <typename numtype>
0217 void LiSK<numtype>::_Factorials_extend() {
0218     
0219         // nstart and nend define the start and end index of n!
0220     auto &v = _constants_clR.Factorials;
0221     const size_t nstart = v.size();
0222     const size_t nend   = _vec_max<size_t>({_constants_clR.max_values["nFactorials"]+1,nstart});
0223     
0224     for (size_t n=nstart; n<nend; ++n) {
0225         v.push_back(cln::factorial((uintL)n));
0226     }
0227 }
0228 
0229     // Bernoulli numbers. Also zeros are stored for now.
0230 template <typename numtype>
0231 void LiSK<numtype>::_BernNum_extend() {
0232     
0233         // nstart and nend define the start and end index of B_n
0234     auto &v = _constants_clR.BernNum;
0235     const size_t nstart = v.size();
0236     const size_t nend   = _vec_max<size_t>({_constants_clR.max_values["nBernNum"]+1,nstart});
0237     
0238     for (size_t n=nstart; n<nend; ++n) {
0239         if (n==0) v.push_back(cln::cl_I(1));
0240         else{
0241             cln::cl_R tmp = 0;
0242             for (size_t k=0; k<n; ++k) {
0243                 tmp += cln::binomial((uintL)n+1, (uintL)k)*v[k];
0244             }
0245             v.push_back(-tmp/(n+1));
0246         }
0247     }
0248 }
0249 
0250     // Negative zeta values
0251 template <typename numtype>
0252 void LiSK<numtype>::_NegZeta_extend() {
0253     
0254     auto &v = _constants_clR.NegZeta;
0255     const size_t nmax = _constants_clR.max_values["nNegZeta"];
0256     if (nmax==0) throw std::runtime_error("nmax==0 not allowed in NegZeta. Set nmax>0!");
0257     
0258         // nstart and nend define the start and end index of zeta(-n)
0259     const size_t nstart = v.size()+1;
0260     const size_t nend   = _vec_max<size_t>({nmax+1,nstart});
0261     
0262     for (size_t n=nstart; n<nend; ++n) {
0263         v.push_back(-_constants_clR.BernNum[n+1]/(n+1));
0264     }
0265 }
0266 
0267     // Li constants Cij
0268 template <typename numtype>
0269 void LiSK<numtype>::_LiCij_extend() {
0270     
0271         // If requested weight is higher than previous one -> adapt size
0272     auto &v = _constants_clR.LiCij;
0273     const size_t nweight = _constants_clR.max_values["nweight"];
0274     if (nweight>v.size()){
0275         _constants_clR.LiCij.resize(nweight);
0276         _constants.LiCij.resize(nweight);
0277     }
0278     
0279         // i is vector index, i.e. i=n-1 for Li_n
0280     for (size_t i=0; i<nweight; ++i) {
0281         
0282             // nstart and nend define the start and end index of j in C_ij
0283         const size_t nstart = v[i].size();
0284         const size_t nend   = _vec_max<size_t>({_constants_clR.max_values["nLiCij"]+1,nstart});
0285         
0286         for (size_t j=nstart; j<nend; ++j) {
0287             if (!i) (j==0 ? v[0].push_back(cln::cl_I(1)) : v[0].push_back(0));
0288             else {
0289                 
0290                 cln::cl_R tmp = 0;
0291                 for (size_t k=0; k<=j; ++k) {
0292                         // recursion formula from eq.(5.11). Multiply again by (k+1)! since (j+1)! is included in LiCij vector definition
0293                     tmp += cln::binomial((uintL)j, (uintL)k)*_constants_clR.BernNum[j-k]*v[i-1][k]/(k+1)*_constants_clR.Factorials[k+1];
0294                 }
0295                 v[i].push_back(tmp/_constants_clR.Factorials[j+1]);
0296             }
0297         }
0298     }
0299 }
0300 
0301     // Li constants LiBn_eq59. Specialised for 2<=n<=4
0302 template <typename numtype>
0303 void LiSK<numtype>::_LiBn_eq59_extend() {
0304     
0305         // m = n-2 for each Li_n, n>=2
0306     auto &v = _constants_clR.LiBn_eq59;
0307     const size_t nmax = _constants_clR.max_values["nLiBn_eq59"];
0308     for (size_t m=0; m<3; ++m) {
0309             // nstart and nend define start and end index n in LiBn_eq59
0310         const size_t nstart = v[m].size()+1;
0311         const size_t nend   = _vec_max<size_t>({nmax+1,nstart});
0312         
0313         for (size_t n=nstart; n<nend ; ++n) {
0314             v[m].push_back(_constants_clR.BernNum[2*n]/(2*n)/_constants_clR.Factorials[2*n+m+1]);
0315         }
0316     }
0317 }
0318 
0319 /********************************************
0320  *  Init                                    *
0321  ********************************************/
0322 template <typename numtype>
0323 void LiSK<numtype>::_init(const std::unordered_map<std::string, size_t> max_values) {
0324     
0325         // Set input values and choose the correct limits
0326     std::unordered_map<std::string, size_t> &mv = _constants_clR.max_values;
0327     for (auto x : max_values) {
0328         if (mv.count(x.first)>0) mv[x.first] = x.second;
0329         else mv.insert(x);
0330     }
0331     
0332     mv["nBernNum"]      = _vec_max<size_t>({mv["nBernNum"],mv["nLiCij"],mv["nNegZeta"]+1,2*mv["nLiBn_eq59"]});
0333     mv["nFactorials"]   = _vec_max<size_t>({mv["nFactorials"],mv["nLiCij"]+1,2*mv["nLiBn_eq59"]+3,mv["nweight"]});
0334     mv["nPosZeta"]      = _vec_max<size_t>({mv["nPosZeta"],mv["nweight"]});
0335     mv["nHarmNum"]      = _vec_max<size_t>({mv["nHarmNum"],mv["nweight"]-1});
0336         
0337         // Extend constants vectors
0338     _HarmNum_extend(); _PosZeta_extend(); _Factorials_extend(); _BernNum_extend();
0339     _NegZeta_extend(); _LiCij_extend(); _LiBn_eq59_extend();
0340     
0341         // Convert CLN constants to requested and used type
0342     _constants.max_values = _constants_clR.max_values;
0343     
0344     for (size_t i=_constants.HarmNum.size(); i<_constants_clR.HarmNum.size(); ++i) {
0345         _constants.HarmNum.push_back(convert(_constants_clR.HarmNum[i]));
0346     }
0347     for (size_t i=_constants.PosZeta.size(); i<_constants_clR.PosZeta.size(); ++i) {
0348         _constants.PosZeta.push_back(convert(_constants_clR.PosZeta[i]));
0349     }
0350     for (size_t i=_constants.Factorials.size(); i<_constants_clR.Factorials.size(); ++i) {
0351         _constants.Factorials.push_back(convert(_constants_clR.Factorials[i]));
0352     }
0353     for (size_t i=_constants.BernNum.size(); i<_constants_clR.BernNum.size(); ++i) {
0354         _constants.BernNum.push_back(convert(_constants_clR.BernNum[i]));
0355     }
0356     for (size_t i=_constants.NegZeta.size(); i<_constants_clR.NegZeta.size(); ++i) {
0357         _constants.NegZeta.push_back(convert(_constants_clR.NegZeta[i]));
0358     }
0359     for (size_t i=0; i<_constants_clR.LiCij.size(); ++i) {
0360         for (size_t j=_constants.LiCij[i].size(); j<_constants_clR.LiCij[i].size(); ++j) {
0361             _constants.LiCij[i].push_back(convert(_constants_clR.LiCij[i][j]));
0362         }
0363     }
0364     for (size_t i=0; i<_constants_clR.LiBn_eq59.size(); ++i) {
0365         for (size_t j=_constants.LiBn_eq59[i].size(); j<_constants_clR.LiBn_eq59[i].size(); ++j) {
0366             _constants.LiBn_eq59[i].push_back(convert(_constants_clR.LiBn_eq59[i][j]));
0367         }
0368     }
0369 }
0370 
0371 
0372 /************************************************************************
0373  *                   Implementation Li(n,x)                             *
0374  ************************************************************************/
0375 /********************************************
0376  *  Li(n,x)                                 *
0377  ********************************************/
0378 template <typename numtype> inline
0379 numtype LiSK<numtype>::_Li1(const numtype& x){
0380     return -log(((numtype)1)-x);
0381 }
0382 
0383 template <typename numtype>
0384 numtype LiSK<numtype>::_Li2(const numtype& x){
0385     
0386     const auto one = Re((numtype)1);
0387     
0388     if (Re(x)<=one/2 && abs(x)<=one) {
0389         const numtype al = -log(one-x), res = al - al*al/((numtype)4);
0390         return _Li_sumBn(res, al, 2,true);
0391     }
0392     else if (Re(x)>one/2 && abs(x-one)<=one){
0393         const numtype al = -log(x), res = _constants.PosZeta[2]-al-al*al/((numtype)4)+al*log(al);
0394         return _Li_sumBn(res, al, 2);
0395     }
0396     else{
0397             // If this case is reached use inversion relation. Li(1/t) is then
0398             // always evaluated by the first if-condition
0399         return -Li2(one/x) - log(-x)*log(-x)/((numtype)2)-_constants.PosZeta[2];
0400     }
0401 }
0402 
0403 template <typename numtype>
0404 numtype LiSK<numtype>::_Li3(const numtype& x){
0405     
0406     const auto one = Re((numtype)1);
0407     const numtype two = 2, three = 3, four = 4;
0408     
0409     if (Re(x)<=one/2 && abs(x)<=one) {
0410         return _Lin_1mexpal(3, x);
0411     }
0412     else if (Re(x)>one/2 && abs(x-one)<=one){
0413         const numtype al  = -log(x), alsq(al*al);
0414         const numtype res = _constants.PosZeta[3]-_constants.PosZeta[2]*al+(three*alsq)/four+alsq*al/(three*four)-alsq/two*log(al);
0415         return _Li_sumBn(res, al, 3,false,true);
0416     }
0417     else{
0418             // If this case is reached use inversion relation. Li(1/t) is then
0419             // always evaluated by the first if-condition
0420         const numtype lx = log(-x);
0421         return Li3(one/x) - Pow(lx, 3)/(two*three) - _constants.PosZeta[2]*lx;
0422     }
0423 }
0424 
0425 template <typename numtype>
0426 numtype LiSK<numtype>::_Li4(const numtype& x){
0427     
0428     const auto one = Re((numtype)1);
0429     const numtype two = 2, four = 4, six = 6, eight = 8, eleven = 11;
0430     
0431     if (Re(x)<=one/2 && abs(x)<=one) {
0432         return _Lin_1mexpal(4, x);
0433     }
0434     else if (Re(x)>one/2 && abs(x-one)<=one){
0435         const numtype al  = -log(x), alsq(al*al);
0436         const numtype res = _constants.PosZeta[4] - _constants.PosZeta[3]*al + _constants.PosZeta[2]/two*alsq - (eleven*alsq*al)/(six*six)
0437                 - alsq*alsq/(six*eight) + alsq*al/six*log(al);
0438         return _Li_sumBn(res, al, 4);
0439     }
0440     else{
0441             // If this case is reached use inversion relation. Li(1/t) is then
0442             // always evaluated by the first if-condition
0443         const numtype lxsq = log(-x)*log(-x), &zeta2 = _constants.PosZeta[2];
0444         return -Li4(one/x) - lxsq*lxsq/(four*six) - zeta2/two*lxsq - (((numtype)7)*zeta2*zeta2)/((numtype)10);
0445     }
0446 }
0447 
0448 template <typename numtype>
0449 numtype LiSK<numtype>::_Lin_basis(const size_t n, const numtype& x){
0450     
0451     const auto one = Re((numtype)1);
0452     
0453     if (Re(x)<=one/2 && abs(x)<=one) {
0454         return _Lin_1mexpal(n, x);
0455     }
0456     else if (Re(x)>one/2 && abs(x-one)<=one){
0457         return _Lin_expal(n, x);
0458     }
0459     else{
0460             // If this case is reached use inversion relation. Li(1/t) is then
0461             // always evaluated by the first if-condition
0462         return _Lin_inverse(n, x);
0463     }
0464 }
0465 
0466 /********************************************
0467  *  Li(n,x=1-exp(-alpha)), eq.(5.10)        *
0468  ********************************************/
0469 template <typename numtype>
0470 numtype LiSK<numtype>::_Lin_1mexpal(const size_t n, const numtype& x){
0471     
0472     assert(n>0); // Li_n -> n>0
0473     const auto one = Re((numtype)1);
0474     const numtype al = -log(one-x);
0475     numtype alpow(al), res = 0, tmp = 0, prev_res = 0;
0476     size_t j = 0;
0477     
0478         // Adapt constants if necessary
0479     if (n>_constants_clR.max_values["nweight"]) {
0480         _constants_clR.max_values["nweight"] = n;
0481         _init(_constants_clR.max_values);
0482     }
0483     
0484     do {
0485             // Compute more constants if necessary
0486         if (j>_constants_clR.max_values["nLiCij"]){
0487             _constants_clR.max_values["nLiCij"] = j + _step;
0488             _init(_constants_clR.max_values);
0489         }
0490         prev_res = res;
0491         tmp      = _constants.LiCij[n-1][j]*alpow;
0492         res      += tmp;
0493         alpow    *= al;
0494         j++;
0495     } while ( _ErrorEstimate(res, prev_res, tmp) );
0496     
0497     return res;
0498 }
0499 
0500 /********************************************
0501  *  Li(n,x=exp(-alpha)), eq.(5.8)           *
0502  ********************************************/
0503 template <typename numtype>
0504 numtype LiSK<numtype>::_Lin_expal(const size_t n, const numtype& x){
0505     
0506     assert(n>0); // Li_n -> n>0
0507     const numtype al = -log(x);
0508     numtype alpow = 1, tmp = 0, res = 0, prev_res = 0;
0509     numtype sgn = 1; size_t m = 0;
0510     auto &mv = _constants_clR.max_values;
0511     
0512         // Adapt constants if necessary
0513     if (n-1>mv["nHarmNum"] || n-1>mv["nFactorials"] || n>mv["nPosZeta"]) {
0514         mv["nHarmNum"] = mv["nFactorials"] = n-1;
0515         mv["nPosZeta"] = n;
0516         _init(mv);
0517     }
0518     
0519     res = (Pow(-((numtype)1), (int)n-1)/_constants.Factorials[n-1])*Pow(al, (int)n-1)*(_constants.HarmNum[n-1]-log(al));
0520     
0521     do {
0522             // Adapt constants if necessary
0523         if (m>mv["nFactorials"] || std::abs((int)(n-m))>mv["nNegZeta"]){
0524             mv["nFactorials"]   = m + _step;
0525             mv["nNegZeta"]      = std::abs((int)(n-m)) + _step;
0526             _init(mv);
0527         }
0528         
0529         prev_res = res;
0530         const long ndiff = n-m;
0531         if (m==n-1) {
0532             tmp = 0;
0533         } else {
0534             tmp = (ndiff>=0 ? _constants.PosZeta[ndiff] : _constants.NegZeta[-ndiff-1])/_constants.Factorials[m]*sgn*alpow;
0535         }
0536         res     += tmp;
0537         alpow   *= al;
0538         sgn     *= -1;
0539         m++;
0540     } while ( _ErrorEstimate(res, prev_res, tmp) );
0541     
0542     return res;
0543 }
0544 
0545 /********************************************************
0546  *  Inversion formula for Li(n,x), eq.(5.6)             *
0547  ********************************************************/
0548 template <typename numtype>
0549 numtype LiSK<numtype>::_Lin_inverse(const size_t n, const numtype &x) {
0550     
0551     assert(n>=2); // n=1 is trivial
0552     const auto one = Re((numtype)1), two = Re((numtype)2);
0553     const numtype sgn = ((n-1)%2) ? -1 : 1;
0554     const numtype lx = log(-x);
0555     numtype res = 0;
0556     
0557         // Adapt constants if necessary
0558     if (n>_constants_clR.max_values["nweight"]) {
0559         _constants_clR.max_values["nweight"] = n;
0560         _init(_constants_clR.max_values);
0561     }
0562     
0563     for (int r=1; r<=n/2; ++r) {
0564         res += ((Pow(((numtype)2), 1-2*r)-one)*_constants.PosZeta[2*r]/_constants.Factorials[n-2*r])*Pow(lx, (int)n-2*r);
0565     }
0566     
0567         // Eq.(5.10) is always the correct one to call
0568     return sgn*_Lin_1mexpal(n, one/x) - Pow(lx, (int)n)/_constants.Factorials[n] + two*res;
0569 }
0570 
0571 /********************************************************
0572  *  sum B_{2m}/(2m(2m+n-1)!)*al^{2m*n-1} from eq.(5.9)  *
0573  ********************************************************/
0574 template <typename numtype>
0575 numtype LiSK<numtype>::_Li_sumBn(const numtype &precal, const numtype &al, const size_t n, const bool fac_2n, const bool sign){
0576     
0577     assert(n>=2 && n<=4); // only for Li_n, n=2,3,4
0578     const auto one = Re((numtype)1), two = Re((numtype)2);
0579     const numtype alsq(al*al), alfac(Pow(al,(int)n-1));
0580     numtype alpow(alsq), tmp = 0, res(precal), prev_res = 0;
0581     size_t m = 1;
0582     
0583     do {
0584             // Compute more constants if necessary
0585         if (m>_constants_clR.max_values["nLiBn_eq59"]){
0586             _constants_clR.max_values["nLiBn_eq59"] = m + _step;
0587             _init(_constants_clR.max_values);
0588         }
0589         
0590         prev_res = res;
0591         tmp      = _constants.LiBn_eq59[n-2][m-1];
0592         if (fac_2n) tmp *= two*m;
0593         if (sign)   tmp *= -one;
0594         tmp     *= alpow*alfac;
0595         res     += tmp;
0596         alpow   *= alsq;
0597         m++;
0598     } while (_ErrorEstimate(res, prev_res, tmp));
0599     
0600     return res;
0601 }
0602 
0603 
0604 /************************************************************************
0605  *                   Implementation Li22(x,y)                           *
0606  ************************************************************************/
0607 /********************************************
0608  *  Li22(x,y)                               *
0609  ********************************************/
0610 template <typename numtype>
0611 numtype LiSK<numtype>::_Li22(const numtype& x, const numtype& y) {
0612     
0613     const auto one = Re((numtype)1);
0614     const auto ax = abs(x), axy = abs(x*y);
0615     
0616     if (ax<one && axy<one) {
0617         return _Li22_orig(x, y);
0618     }
0619     else if (ax>=one && axy>=one){
0620         return _Li22_inversion(x, y);
0621     }
0622     else{
0623         return _Li22_stuffle(x, y);
0624     }
0625 }
0626 
0627 /********************************************
0628  *  Original definition of Li22(x,y)        *
0629  *  from eq.(6.1)                           *
0630  ********************************************/
0631 template <typename numtype>
0632 numtype LiSK<numtype>::_Li22_orig(const numtype &x, const numtype &y) const {
0633     
0634     // Initilize by performing first step (i=2)
0635     numtype xpow = x*x, ypow = y, res = xpow*ypow/((numtype)4), tmp = 0, prev_res = 0;
0636     // log the sum over j
0637     numtype prev_y = ypow;
0638     size_t i = 3;
0639     
0640     do {
0641         xpow *= x;
0642         ypow *= y;
0643         prev_y += ypow/((numtype)((i-1)*(i-1)));
0644         
0645         // tmp and prev_res are only needed for the precision check
0646         tmp  = xpow*prev_y/((numtype)(i*i));
0647         prev_res = res;
0648         res  += tmp;
0649         i++;
0650         
0651         } while (_ErrorEstimate(res, prev_res, tmp));
0652     
0653     return res;
0654 }
0655 
0656 /********************************************
0657  *  Stuffle relation for Li22(x,y)          *
0658  *  from eq.(6.3)                           *
0659  ********************************************/
0660 template <typename numtype> inline
0661 numtype LiSK<numtype>::_Li22_stuffle(const numtype &x, const numtype &y) {
0662     return -_Li22(y, x) - _Li4(x*y) + _Li2(x)*_Li2(y);
0663 }
0664 
0665 /********************************************
0666  *  Inversion relation for Li22(x,y)        *
0667  *  from eq.(6.4)                           *
0668  ********************************************/
0669 template <typename numtype>
0670 numtype LiSK<numtype>::_Li22_inversion(const numtype &x, const numtype &y) {
0671     
0672     const auto one = Re((numtype)1);
0673     const numtype &Pisq_6 = _constants.PosZeta[2], mxy = -x*y, invx = one/x, lmxy = log(mxy), lmxy2 = lmxy*lmxy, lx2 = Pow(log(-x),2);
0674     
0675     return _Li22(invx, one/y) - _Li4(-mxy) + ((numtype)3)*(_Li4(invx)+_Li4(y)) + ((numtype)2)*(_Li3(invx)-_Li3(y))*lmxy
0676             + _Li2(invx)*(Pisq_6+lmxy2/((numtype)2)) + _Li2(y)*(lmxy2-lx2)/((numtype)2);
0677 }
0678 
0679 
0680 /************************************************************************
0681  *                   Specialised auxillary functions                    *
0682  ************************************************************************/
0683 /*************************************************************************
0684  *                                                                       *
0685  *            Double Precision Specific Implementation                   *
0686  *                                                                       *
0687  *************************************************************************/
0688 /********************************************
0689  *  Convert to double                       *
0690  ********************************************/
0691 template <> inline
0692 cp_d LiSK<cp_d>::convert(const cln::cl_R &x) const {
0693     return {cln::double_approx(x),0};
0694 }
0695     
0696 /********************************************
0697  *  Add imaginary part                      *
0698  ********************************************/
0699 template <> inline
0700 cp_d LiSK<cp_d>::_add_iep(const cp_d &x) const {
0701     return x - _iep_d;
0702 }
0703 
0704 
0705 /********************************************
0706  *  ErrorEstimate                           *
0707  ********************************************/
0708 template <> inline
0709 bool LiSK<cp_d>::_ErrorEstimate(const cp_d& res, const cp_d& prev_res, const cp_d& current) const{
0710     return res!=prev_res || cln::zerop((cln::cl_R)abs(current));
0711 }
0712     
0713 /********************************************
0714  *  Power                                   *
0715  ********************************************/
0716 template <> inline
0717 cp_d LiSK<cp_d>::Pow(const cp_d &x, const int n) const{
0718     return std::pow(x,n);
0719 }
0720     
0721 /********************************************
0722  *  is_zero                                 *
0723  ********************************************/
0724 template <> inline
0725 bool LiSK<cp_d>::is_zero(const cp_d &x) const{
0726     return cln::zerop(cln::complex(x.real(), x.imag()));
0727 }
0728 
0729 /*************************************************************************
0730  *                                                                       *
0731  *            Arbitrary Precision Specific Implementation                *
0732  *                                                                       *
0733  *************************************************************************/
0734 /********************************************
0735  *  Convert to CLN                          *
0736  ********************************************/
0737 template <> inline
0738 cln::cl_N LiSK<cln::cl_N>::convert(const cln::cl_R &x) const {
0739     return _cln_tofloat ? cln::cl_float(x, cln::float_format(_prec)) : x;
0740 }
0741     
0742 /********************************************
0743  *  Adjust digits                           *
0744  ********************************************/
0745 template<> inline
0746 cln::cl_N LiSK<cln::cl_N>::_adjustDigits(const cln::cl_N &x) const{
0747     
0748     const auto prec   = cln::float_format(_prec);
0749     const cln::cl_F r = cln::cl_float(cln::realpart(x), prec);
0750     const cln::cl_F i = cln::cl_float(cln::imagpart(x), prec);
0751     
0752     return cln::complex(r, i);
0753 }
0754 
0755 /********************************************
0756  *  Add imaginary part                      *
0757  ********************************************/
0758 template <> inline
0759 cln::cl_N LiSK<cln::cl_N>::_add_iep(const cln::cl_N &x) const {
0760     return _adjustDigits(x) - _iep_clN;
0761 }
0762     
0763 /********************************************
0764  *  is_zero                                 *
0765  ********************************************/
0766 template <> inline
0767 bool LiSK<cln::cl_N>::is_zero(const cln::cl_N &x) const{
0768     return cln::zerop(x);
0769 }
0770     
0771 /********************************************
0772  *  ErrorEstimate                           *
0773  ********************************************/    
0774 template <> inline
0775 bool LiSK<cln::cl_N>::_ErrorEstimate(const cln::cl_N& res, const cln::cl_N& prev_res, const cln::cl_N& current) const{
0776     return res!=prev_res || is_zero(current);
0777 }
0778     
0779 /********************************************
0780  *  Power                                   *
0781  ********************************************/
0782 template <> inline
0783 cln::cl_N LiSK<cln::cl_N>::Pow(const cln::cl_N &x, const int n) const{
0784     return cln::expt(x, (cln::cl_I)n);
0785 }