+ // do recursion
+ // int_0^y1 ds1/(s1-b1) ... int dsr/(sr-br) G(a1,...,sr,...,aw,y2)
+ // = int_0^y1 ds1/(s1-b1) ... int dsr/(sr-br) G(a1,...,0,...,aw,y2)
+ // + int_0^y1 ds1/(s1-b1) ... int dsr/(sr-br) int_0^{sr} ds_{r+1} d/ds_{r+1} G(a1,...,s_{r+1},...,aw,y2)
+
+ // smallest element in last place
+ if (min_it + 1 == a.end()) {
+ do { --min_it; } while (*min_it == 0);
+ Gparameter empty;
+ Gparameter a1(a.begin(),min_it+1);
+ Gparameter a2(min_it+1,a.end());
+
+ ex result = G_transform(pendint,a2,scale)*G_transform(empty,a1,scale);
+
+ result -= shuffle_G(empty,a1,a2,pendint,a,scale);
+ return result;
+ }
+
+ Gparameter empty;
+ Gparameter::iterator changeit;
+
+ // first term G(a_1,..,0,...,a_w;a_0)
+ Gparameter new_pendint = prepare_pending_integrals(pendint, a[min_it_pos]);
+ Gparameter new_a = a;
+ new_a[min_it_pos] = 0;
+ ex result = G_transform(empty, new_a, scale);
+ if (pendint.size() > 0) {
+ result *= trailing_zeros_G(convert_pending_integrals_G(pendint), pendint.front());
+ }
+
+ // other terms
+ changeit = new_a.begin() + min_it_pos;
+ changeit = new_a.erase(changeit);
+ if (changeit != new_a.begin()) {
+ // smallest in the middle
+ new_pendint.push_back(*changeit);
+ result -= trailing_zeros_G(convert_pending_integrals_G(new_pendint), new_pendint.front())
+ * G_transform(empty, new_a, scale);
+ int buffer = *changeit;
+ *changeit = *min_it;
+ result += G_transform(new_pendint, new_a, scale);
+ *changeit = buffer;
+ new_pendint.pop_back();
+ --changeit;
+ new_pendint.push_back(*changeit);
+ result += trailing_zeros_G(convert_pending_integrals_G(new_pendint), new_pendint.front())
+ * G_transform(empty, new_a, scale);
+ *changeit = *min_it;
+ result -= G_transform(new_pendint, new_a, scale);
+ } else {
+ // smallest at the front
+ new_pendint.push_back(scale);
+ result += trailing_zeros_G(convert_pending_integrals_G(new_pendint), new_pendint.front())
+ * G_transform(empty, new_a, scale);
+ new_pendint.back() = *changeit;
+ result -= trailing_zeros_G(convert_pending_integrals_G(new_pendint), new_pendint.front())
+ * G_transform(empty, new_a, scale);
+ *changeit = *min_it;
+ result += G_transform(new_pendint, new_a, scale);
+ }
+ return result;
+}
+
+
+// shuffles the two parameter list a1 and a2 and calls G_transform for every term except
+// for the one that is equal to a_old
+ex shuffle_G(const Gparameter & a0, const Gparameter & a1, const Gparameter & a2,
+ const Gparameter& pendint, const Gparameter& a_old, int scale)
+{
+ if (a1.size()==0 && a2.size()==0) {
+ // veto the one configuration we don't want
+ if ( a0 == a_old ) return 0;
+
+ return G_transform(pendint,a0,scale);
+ }
+
+ if (a2.size()==0) {
+ Gparameter empty;
+ Gparameter aa0 = a0;
+ aa0.insert(aa0.end(),a1.begin(),a1.end());
+ return shuffle_G(aa0,empty,empty,pendint,a_old,scale);
+ }
+
+ if (a1.size()==0) {
+ Gparameter empty;
+ Gparameter aa0 = a0;
+ aa0.insert(aa0.end(),a2.begin(),a2.end());
+ return shuffle_G(aa0,empty,empty,pendint,a_old,scale);
+ }
+
+ Gparameter a1_removed(a1.begin()+1,a1.end());
+ Gparameter a2_removed(a2.begin()+1,a2.end());
+
+ Gparameter a01 = a0;
+ Gparameter a02 = a0;
+
+ a01.push_back( a1[0] );
+ a02.push_back( a2[0] );
+
+ return shuffle_G(a01,a1_removed,a2,pendint,a_old,scale)
+ + shuffle_G(a02,a1,a2_removed,pendint,a_old,scale);
+}
+
+
+// handles the transformations and the numerical evaluation of G
+// the parameter x, s and y must only contain numerics
+ex G_numeric(const lst& x, const lst& s, const ex& y)
+{
+ // check for convergence and necessary accelerations
+ bool need_trafo = false;
+ bool need_hoelder = false;
+ int depth = 0;
+ for (lst::const_iterator it = x.begin(); it != x.end(); ++it) {
+ if (!(*it).is_zero()) {
+ ++depth;
+ if (abs(*it) - y < -pow(10,-Digits+2)) {
+ need_trafo = true;
+ break;
+ }
+ if (abs((abs(*it) - y)/y) < 0.01) {
+ need_hoelder = true;
+ }
+ }
+ }
+ if (x.op(x.nops()-1).is_zero()) {
+ need_trafo = true;
+ }
+ if (depth == 1 && !need_trafo) {
+ return -Li(x.nops(), y / x.op(x.nops()-1)).evalf();
+ }
+
+ // convergence transformation
+ if (need_trafo) {
+
+ // sort (|x|<->position) to determine indices
+ std::multimap<ex,int> sortmap;
+ int size = 0;
+ for (int i=0; i<x.nops(); ++i) {
+ if (!x[i].is_zero()) {
+ sortmap.insert(std::pair<ex,int>(abs(x[i]), i));
+ ++size;
+ }
+ }
+ // include upper limit (scale)
+ sortmap.insert(std::pair<ex,int>(abs(y), x.nops()));
+
+ // generate missing dummy-symbols
+ int i = 1;
+ gsyms.clear();
+ gsyms.push_back(symbol("GSYMS_ERROR"));
+ ex lastentry;
+ for (std::multimap<ex,int>::const_iterator it = sortmap.begin(); it != sortmap.end(); ++it) {
+ if (it != sortmap.begin()) {
+ if (it->second < x.nops()) {
+ if (x[it->second] == lastentry) {
+ gsyms.push_back(gsyms.back());
+ continue;
+ }
+ } else {
+ if (y == lastentry) {
+ gsyms.push_back(gsyms.back());
+ continue;
+ }
+ }
+ }
+ std::ostringstream os;
+ os << "a" << i;
+ gsyms.push_back(symbol(os.str()));
+ ++i;
+ if (it->second < x.nops()) {
+ lastentry = x[it->second];
+ } else {
+ lastentry = y;
+ }
+ }
+
+ // fill position data according to sorted indices and prepare substitution list
+ Gparameter a(x.nops());
+ lst subslst;
+ int pos = 1;
+ int scale;
+ for (std::multimap<ex,int>::const_iterator it = sortmap.begin(); it != sortmap.end(); ++it) {
+ if (it->second < x.nops()) {
+ if (s[it->second] > 0) {
+ a[it->second] = pos;
+ } else {
+ a[it->second] = -pos;
+ }
+ subslst.append(gsyms[pos] == x[it->second]);
+ } else {
+ scale = pos;
+ subslst.append(gsyms[pos] == y);
+ }
+ ++pos;
+ }
+
+ // do transformation
+ Gparameter pendint;
+ ex result = G_transform(pendint, a, scale);
+ // replace dummy symbols with their values
+ result = result.eval().expand();
+ result = result.subs(subslst).evalf();
+
+ return result;
+ }
+
+ // do acceleration transformation (hoelder convolution [BBB])
+ if (need_hoelder) {
+
+ ex result;
+ const int size = x.nops();
+ lst newx;
+ for (lst::const_iterator it = x.begin(); it != x.end(); ++it) {
+ newx.append(*it / y);
+ }
+
+ for (int r=0; r<=size; ++r) {
+ ex buffer = pow(-1, r);
+ ex p = 2;
+ bool adjustp;
+ do {
+ adjustp = false;
+ for (lst::const_iterator it = newx.begin(); it != newx.end(); ++it) {
+ if (*it == 1/p) {
+ p += (3-p)/2;
+ adjustp = true;
+ continue;
+ }
+ }
+ } while (adjustp);
+ ex q = p / (p-1);
+ lst qlstx;
+ lst qlsts;
+ for (int j=r; j>=1; --j) {
+ qlstx.append(1-newx.op(j-1));
+ if (newx.op(j-1).info(info_flags::real) && newx.op(j-1) > 1 && newx.op(j-1) <= 2) {
+ qlsts.append( s.op(j-1));
+ } else {
+ qlsts.append( -s.op(j-1));
+ }
+ }
+ if (qlstx.nops() > 0) {
+ buffer *= G_numeric(qlstx, qlsts, 1/q);
+ }
+ lst plstx;
+ lst plsts;
+ for (int j=r+1; j<=size; ++j) {
+ plstx.append(newx.op(j-1));
+ plsts.append(s.op(j-1));
+ }
+ if (plstx.nops() > 0) {
+ buffer *= G_numeric(plstx, plsts, 1/p);
+ }
+ result += buffer;
+ }
+ return result;
+ }
+
+ // do summation
+ lst newx;
+ lst m;
+ int mcount = 1;
+ ex sign = 1;
+ ex factor = y;
+ for (lst::const_iterator it = x.begin(); it != x.end(); ++it) {
+ if ((*it).is_zero()) {
+ ++mcount;
+ } else {
+ newx.append(factor / (*it));
+ factor = *it;
+ m.append(mcount);
+ mcount = 1;
+ sign = -sign;
+ }
+ }
+
+ return sign * numeric(mLi_do_summation(m, newx));
+}
+
+
+ex mLi_numeric(const lst& m, const lst& x)
+{
+ // let G_numeric do the transformation
+ lst newx;
+ lst s;
+ ex factor = 1;
+ for (lst::const_iterator itm = m.begin(), itx = x.begin(); itm != m.end(); ++itm, ++itx) {
+ for (int i = 1; i < *itm; ++i) {
+ newx.append(0);
+ s.append(1);
+ }
+ newx.append(factor / *itx);
+ factor /= *itx;
+ s.append(1);
+ }
+ return pow(-1, m.nops()) * G_numeric(newx, s, _ex1);
+}
+
+
+} // end of anonymous namespace
+
+
+//////////////////////////////////////////////////////////////////////
+//
+// Generalized multiple polylogarithm G(x, y) and G(x, s, y)
+//
+// GiNaC function
+//
+//////////////////////////////////////////////////////////////////////
+
+
+static ex G2_evalf(const ex& x_, const ex& y)
+{
+ if (!y.info(info_flags::positive)) {
+ return G(x_, y).hold();
+ }
+ lst x = is_a<lst>(x_) ? ex_to<lst>(x_) : lst(x_);
+ if (x.nops() == 0) {
+ return _ex1;
+ }
+ if (x.op(0) == y) {
+ return G(x_, y).hold();
+ }
+ lst s;
+ bool all_zero = true;
+ for (lst::const_iterator it = x.begin(); it != x.end(); ++it) {
+ if (!(*it).info(info_flags::numeric)) {
+ return G(x_, y).hold();
+ }
+ if (*it != _ex0) {
+ all_zero = false;
+ }
+ s.append(+1);
+ }
+ if (all_zero) {
+ return pow(log(y), x.nops()) / factorial(x.nops());
+ }
+ return G_numeric(x, s, y);
+}
+
+
+static ex G2_eval(const ex& x_, const ex& y)
+{
+ //TODO eval to MZV or H or S or Lin
+
+ if (!y.info(info_flags::positive)) {
+ return G(x_, y).hold();
+ }
+ lst x = is_a<lst>(x_) ? ex_to<lst>(x_) : lst(x_);
+ if (x.nops() == 0) {
+ return _ex1;
+ }
+ if (x.op(0) == y) {
+ return G(x_, y).hold();
+ }
+ lst s;
+ bool all_zero = true;
+ bool crational = true;
+ for (lst::const_iterator it = x.begin(); it != x.end(); ++it) {
+ if (!(*it).info(info_flags::numeric)) {
+ return G(x_, y).hold();
+ }
+ if (!(*it).info(info_flags::crational)) {
+ crational = false;
+ }
+ if (*it != _ex0) {
+ all_zero = false;
+ }
+ s.append(+1);
+ }
+ if (all_zero) {
+ return pow(log(y), x.nops()) / factorial(x.nops());
+ }
+ if (!y.info(info_flags::crational)) {
+ crational = false;
+ }
+ if (crational) {
+ return G(x_, y).hold();
+ }
+ return G_numeric(x, s, y);
+}
+
+
+unsigned G2_SERIAL::serial = function::register_new(function_options("G", 2).
+ evalf_func(G2_evalf).
+ eval_func(G2_eval).
+ do_not_evalf_params().
+ overloaded(2));
+//TODO
+// derivative_func(G2_deriv).
+// print_func<print_latex>(G2_print_latex).
+
+
+static ex G3_evalf(const ex& x_, const ex& s_, const ex& y)
+{
+ if (!y.info(info_flags::positive)) {
+ return G(x_, s_, y).hold();
+ }
+ lst x = is_a<lst>(x_) ? ex_to<lst>(x_) : lst(x_);
+ lst s = is_a<lst>(s_) ? ex_to<lst>(s_) : lst(s_);
+ if (x.nops() != s.nops()) {
+ return G(x_, s_, y).hold();
+ }
+ if (x.nops() == 0) {
+ return _ex1;
+ }
+ if (x.op(0) == y) {
+ return G(x_, s_, y).hold();
+ }
+ lst sn;
+ bool all_zero = true;
+ for (lst::const_iterator itx = x.begin(), its = s.begin(); itx != x.end(); ++itx, ++its) {
+ if (!(*itx).info(info_flags::numeric)) {
+ return G(x_, y).hold();
+ }
+ if (!(*its).info(info_flags::real)) {
+ return G(x_, y).hold();
+ }
+ if (*itx != _ex0) {
+ all_zero = false;
+ }
+ if (*its >= 0) {
+ sn.append(+1);
+ } else {
+ sn.append(-1);
+ }
+ }
+ if (all_zero) {
+ return pow(log(y), x.nops()) / factorial(x.nops());
+ }
+ return G_numeric(x, sn, y);
+}
+
+
+static ex G3_eval(const ex& x_, const ex& s_, const ex& y)
+{
+ //TODO eval to MZV or H or S or Lin
+
+ if (!y.info(info_flags::positive)) {
+ return G(x_, s_, y).hold();
+ }
+ lst x = is_a<lst>(x_) ? ex_to<lst>(x_) : lst(x_);
+ lst s = is_a<lst>(s_) ? ex_to<lst>(s_) : lst(s_);
+ if (x.nops() != s.nops()) {
+ return G(x_, s_, y).hold();
+ }
+ if (x.nops() == 0) {
+ return _ex1;
+ }
+ if (x.op(0) == y) {
+ return G(x_, s_, y).hold();
+ }
+ lst sn;
+ bool all_zero = true;
+ bool crational = true;
+ for (lst::const_iterator itx = x.begin(), its = s.begin(); itx != x.end(); ++itx, ++its) {
+ if (!(*itx).info(info_flags::numeric)) {
+ return G(x_, s_, y).hold();
+ }
+ if (!(*its).info(info_flags::real)) {
+ return G(x_, s_, y).hold();
+ }
+ if (!(*itx).info(info_flags::crational)) {
+ crational = false;
+ }
+ if (*itx != _ex0) {
+ all_zero = false;
+ }
+ if (*its >= 0) {
+ sn.append(+1);
+ } else {
+ sn.append(-1);
+ }
+ }
+ if (all_zero) {
+ return pow(log(y), x.nops()) / factorial(x.nops());
+ }
+ if (!y.info(info_flags::crational)) {
+ crational = false;
+ }
+ if (crational) {
+ return G(x_, s_, y).hold();
+ }
+ return G_numeric(x, sn, y);
+}
+
+
+unsigned G3_SERIAL::serial = function::register_new(function_options("G", 3).
+ evalf_func(G3_evalf).
+ eval_func(G3_eval).
+ do_not_evalf_params().
+ overloaded(2));
+//TODO
+// derivative_func(G3_deriv).
+// print_func<print_latex>(G3_print_latex).
+
+
+//////////////////////////////////////////////////////////////////////
+//
+// Classical polylogarithm and multiple polylogarithm Li(m,x)
+//
+// GiNaC function
+//
+//////////////////////////////////////////////////////////////////////
+
+
+static ex Li_evalf(const ex& m_, const ex& x_)
+{
+ // classical polylogs
+ if (m_.info(info_flags::posint)) {
+ if (x_.info(info_flags::numeric)) {
+ return Lin_numeric(ex_to<numeric>(m_).to_int(), ex_to<numeric>(x_));
+ } else {
+ // try to numerically evaluate second argument
+ ex x_val = x_.evalf();
+ if (x_val.info(info_flags::numeric)) {
+ return Lin_numeric(ex_to<numeric>(m_).to_int(), ex_to<numeric>(x_val));
+ }
+ }
+ }
+ // multiple polylogs
+ if (is_a<lst>(m_) && is_a<lst>(x_)) {
+
+ const lst& m = ex_to<lst>(m_);
+ const lst& x = ex_to<lst>(x_);
+ if (m.nops() != x.nops()) {
+ return Li(m_,x_).hold();
+ }
+ if (x.nops() == 0) {
+ return _ex1;
+ }
+ if ((m.op(0) == _ex1) && (x.op(0) == _ex1)) {
+ return Li(m_,x_).hold();
+ }
+
+ for (lst::const_iterator itm = m.begin(), itx = x.begin(); itm != m.end(); ++itm, ++itx) {
+ if (!(*itm).info(info_flags::posint)) {
+ return Li(m_, x_).hold();
+ }
+ if (!(*itx).info(info_flags::numeric)) {
+ return Li(m_, x_).hold();
+ }
+ if (*itx == _ex0) {
+ return _ex0;
+ }
+ }
+
+ return mLi_numeric(m, x);
+ }
+
+ return Li(m_,x_).hold();
+}
+
+
+static ex Li_eval(const ex& m_, const ex& x_)
+{
+ if (is_a<lst>(m_)) {
+ if (is_a<lst>(x_)) {
+ // multiple polylogs
+ const lst& m = ex_to<lst>(m_);
+ const lst& x = ex_to<lst>(x_);
+ if (m.nops() != x.nops()) {
+ return Li(m_,x_).hold();
+ }
+ if (x.nops() == 0) {
+ return _ex1;
+ }
+ bool is_H = true;
+ bool is_zeta = true;
+ bool do_evalf = true;
+ bool crational = true;
+ for (lst::const_iterator itm = m.begin(), itx = x.begin(); itm != m.end(); ++itm, ++itx) {
+ if (!(*itm).info(info_flags::posint)) {
+ return Li(m_,x_).hold();
+ }
+ if ((*itx != _ex1) && (*itx != _ex_1)) {
+ if (itx != x.begin()) {
+ is_H = false;
+ }
+ is_zeta = false;
+ }
+ if (*itx == _ex0) {
+ return _ex0;
+ }
+ if (!(*itx).info(info_flags::numeric)) {
+ do_evalf = false;
+ }
+ if (!(*itx).info(info_flags::crational)) {
+ crational = false;
+ }
+ }
+ if (is_zeta) {
+ return zeta(m_,x_);
+ }
+ if (is_H) {
+ ex prefactor;
+ lst newm = convert_parameter_Li_to_H(m, x, prefactor);
+ return prefactor * H(newm, x[0]);
+ }
+ if (do_evalf && !crational) {
+ return mLi_numeric(m,x);
+ }
+ }
+ return Li(m_, x_).hold();
+ } else if (is_a<lst>(x_)) {
+ return Li(m_, x_).hold();
+ }
+
+ // classical polylogs
+ if (x_ == _ex0) {
+ return _ex0;
+ }
+ if (x_ == _ex1) {
+ return zeta(m_);
+ }
+ if (x_ == _ex_1) {
+ return (pow(2,1-m_)-1) * zeta(m_);
+ }
+ if (m_ == _ex1) {
+ return -log(1-x_);
+ }
+ if (m_ == _ex2) {
+ if (x_.is_equal(I)) {
+ return power(Pi,_ex2)/_ex_48 + Catalan*I;
+ }
+ if (x_.is_equal(-I)) {
+ return power(Pi,_ex2)/_ex_48 - Catalan*I;
+ }
+ }
+ if (m_.info(info_flags::posint) && x_.info(info_flags::numeric) && !x_.info(info_flags::crational)) {
+ return Lin_numeric(ex_to<numeric>(m_).to_int(), ex_to<numeric>(x_));
+ }
+
+ return Li(m_, x_).hold();
+}
+
+
+static ex Li_series(const ex& m, const ex& x, const relational& rel, int order, unsigned options)
+{
+ epvector seq;
+ seq.push_back(expair(Li(m, x), 0));
+ return pseries(rel, seq);
+}
+
+
+static ex Li_deriv(const ex& m_, const ex& x_, unsigned deriv_param)
+{
+ GINAC_ASSERT(deriv_param < 2);
+ if (deriv_param == 0) {
+ return _ex0;
+ }
+ if (m_.nops() > 1) {
+ throw std::runtime_error("don't know how to derivate multiple polylogarithm!");
+ }
+ ex m;
+ if (is_a<lst>(m_)) {
+ m = m_.op(0);
+ } else {
+ m = m_;
+ }
+ ex x;
+ if (is_a<lst>(x_)) {
+ x = x_.op(0);
+ } else {
+ x = x_;
+ }
+ if (m > 0) {
+ return Li(m-1, x) / x;
+ } else {
+ return 1/(1-x);
+ }
+}
+
+
+static void Li_print_latex(const ex& m_, const ex& x_, const print_context& c)
+{
+ lst m;
+ if (is_a<lst>(m_)) {
+ m = ex_to<lst>(m_);
+ } else {
+ m = lst(m_);
+ }
+ lst x;
+ if (is_a<lst>(x_)) {
+ x = ex_to<lst>(x_);
+ } else {
+ x = lst(x_);
+ }
+ c.s << "\\mbox{Li}_{";
+ lst::const_iterator itm = m.begin();
+ (*itm).print(c);
+ itm++;
+ for (; itm != m.end(); itm++) {
+ c.s << ",";
+ (*itm).print(c);
+ }
+ c.s << "}(";
+ lst::const_iterator itx = x.begin();
+ (*itx).print(c);
+ itx++;
+ for (; itx != x.end(); itx++) {
+ c.s << ",";
+ (*itx).print(c);
+ }
+ c.s << ")";
+}
+
+
+REGISTER_FUNCTION(Li,
+ evalf_func(Li_evalf).
+ eval_func(Li_eval).
+ series_func(Li_series).
+ derivative_func(Li_deriv).
+ print_func<print_latex>(Li_print_latex).
+ do_not_evalf_params());
+
+
+//////////////////////////////////////////////////////////////////////
+//
+// Nielsen's generalized polylogarithm S(n,p,x)
+//
+// helper functions
+//
+//////////////////////////////////////////////////////////////////////
+
+
+// anonymous namespace for helper functions
+namespace {
+
+
+// lookup table for special Euler-Zagier-Sums (used for S_n,p(x))
+// see fill_Yn()
+std::vector<std::vector<cln::cl_N> > Yn;
+int ynsize = 0; // number of Yn[]
+int ynlength = 100; // initial length of all Yn[i]
+
+
+// This function calculates the Y_n. The Y_n are needed for the evaluation of S_{n,p}(x).
+// The Y_n are basically Euler-Zagier sums with all m_i=1. They are subsums in the Z-sum
+// representing S_{n,p}(x).
+// The first index in Y_n corresponds to the parameter p minus one, i.e. the depth of the
+// equivalent Z-sum.
+// The second index in Y_n corresponds to the running index of the outermost sum in the full Z-sum
+// representing S_{n,p}(x).
+// The calculation of Y_n uses the values from Y_{n-1}.
+void fill_Yn(int n, const cln::float_format_t& prec)
+{
+ const int initsize = ynlength;
+ //const int initsize = initsize_Yn;
+ cln::cl_N one = cln::cl_float(1, prec);
+
+ if (n) {
+ std::vector<cln::cl_N> buf(initsize);
+ std::vector<cln::cl_N>::iterator it = buf.begin();
+ std::vector<cln::cl_N>::iterator itprev = Yn[n-1].begin();
+ *it = (*itprev) / cln::cl_N(n+1) * one;
+ it++;
+ itprev++;
+ // sums with an index smaller than the depth are zero and need not to be calculated.
+ // calculation starts with depth, which is n+2)
+ for (int i=n+2; i<=initsize+n; i++) {
+ *it = *(it-1) + (*itprev) / cln::cl_N(i) * one;
+ it++;
+ itprev++;
+ }
+ Yn.push_back(buf);
+ } else {
+ std::vector<cln::cl_N> buf(initsize);
+ std::vector<cln::cl_N>::iterator it = buf.begin();
+ *it = 1 * one;
+ it++;
+ for (int i=2; i<=initsize; i++) {
+ *it = *(it-1) + 1 / cln::cl_N(i) * one;
+ it++;
+ }
+ Yn.push_back(buf);
+ }
+ ynsize++;
+}
+
+
+// make Yn longer ...
+void make_Yn_longer(int newsize, const cln::float_format_t& prec)
+{
+
+ cln::cl_N one = cln::cl_float(1, prec);
+
+ Yn[0].resize(newsize);
+ std::vector<cln::cl_N>::iterator it = Yn[0].begin();
+ it += ynlength;
+ for (int i=ynlength+1; i<=newsize; i++) {
+ *it = *(it-1) + 1 / cln::cl_N(i) * one;
+ it++;
+ }
+
+ for (int n=1; n<ynsize; n++) {
+ Yn[n].resize(newsize);
+ std::vector<cln::cl_N>::iterator it = Yn[n].begin();
+ std::vector<cln::cl_N>::iterator itprev = Yn[n-1].begin();
+ it += ynlength;
+ itprev += ynlength;
+ for (int i=ynlength+n+1; i<=newsize+n; i++) {
+ *it = *(it-1) + (*itprev) / cln::cl_N(i) * one;
+ it++;
+ itprev++;
+ }
+ }
+
+ ynlength = newsize;
+}
+
+
+// helper function for S(n,p,x)
+// [Kol] (7.2)
+cln::cl_N C(int n, int p)
+{
+ cln::cl_N result;
+
+ for (int k=0; k<p; k++) {
+ for (int j=0; j<=(n+k-1)/2; j++) {
+ if (k == 0) {
+ if (n & 1) {
+ if (j & 1) {
+ result = result - 2 * cln::expt(cln::pi(),2*j) * S_num(n-2*j,p,1).to_cl_N() / cln::factorial(2*j);
+ }
+ else {
+ result = result + 2 * cln::expt(cln::pi(),2*j) * S_num(n-2*j,p,1).to_cl_N() / cln::factorial(2*j);
+ }
+ }
+ }
+ else {
+ if (k & 1) {
+ if (j & 1) {
+ result = result + cln::factorial(n+k-1)
+ * cln::expt(cln::pi(),2*j) * S_num(n+k-2*j,p-k,1).to_cl_N()
+ / (cln::factorial(k) * cln::factorial(n-1) * cln::factorial(2*j));
+ }
+ else {
+ result = result - cln::factorial(n+k-1)
+ * cln::expt(cln::pi(),2*j) * S_num(n+k-2*j,p-k,1).to_cl_N()
+ / (cln::factorial(k) * cln::factorial(n-1) * cln::factorial(2*j));
+ }
+ }
+ else {
+ if (j & 1) {
+ result = result - cln::factorial(n+k-1) * cln::expt(cln::pi(),2*j) * S_num(n+k-2*j,p-k,1).to_cl_N()
+ / (cln::factorial(k) * cln::factorial(n-1) * cln::factorial(2*j));
+ }
+ else {
+ result = result + cln::factorial(n+k-1)
+ * cln::expt(cln::pi(),2*j) * S_num(n+k-2*j,p-k,1).to_cl_N()
+ / (cln::factorial(k) * cln::factorial(n-1) * cln::factorial(2*j));
+ }
+ }
+ }
+ }
+ }
+ int np = n+p;
+ if ((np-1) & 1) {
+ if (((np)/2+n) & 1) {
+ result = -result - cln::expt(cln::pi(),np) / (np * cln::factorial(n-1) * cln::factorial(p));
+ }
+ else {
+ result = -result + cln::expt(cln::pi(),np) / (np * cln::factorial(n-1) * cln::factorial(p));
+ }
+ }
+
+ return result;
+}
+
+
+// helper function for S(n,p,x)
+// [Kol] remark to (9.1)
+cln::cl_N a_k(int k)
+{
+ cln::cl_N result;
+
+ if (k == 0) {
+ return 1;
+ }
+
+ result = result;
+ for (int m=2; m<=k; m++) {
+ result = result + cln::expt(cln::cl_N(-1),m) * cln::zeta(m) * a_k(k-m);
+ }
+
+ return -result / k;
+}
+
+
+// helper function for S(n,p,x)
+// [Kol] remark to (9.1)
+cln::cl_N b_k(int k)
+{
+ cln::cl_N result;
+
+ if (k == 0) {
+ return 1;
+ }
+
+ result = result;
+ for (int m=2; m<=k; m++) {
+ result = result + cln::expt(cln::cl_N(-1),m) * cln::zeta(m) * b_k(k-m);
+ }
+
+ return result / k;
+}
+
+
+// helper function for S(n,p,x)
+cln::cl_N S_do_sum(int n, int p, const cln::cl_N& x, const cln::float_format_t& prec)
+{
+ if (p==1) {
+ return Li_projection(n+1, x, prec);
+ }
+
+ // check if precalculated values are sufficient
+ if (p > ynsize+1) {
+ for (int i=ynsize; i<p-1; i++) {
+ fill_Yn(i, prec);
+ }
+ }
+
+ // should be done otherwise
+ cln::cl_F one = cln::cl_float(1, cln::float_format(Digits));
+ cln::cl_N xf = x * one;
+ //cln::cl_N xf = x * cln::cl_float(1, prec);
+
+ cln::cl_N res;
+ cln::cl_N resbuf;
+ cln::cl_N factor = cln::expt(xf, p);
+ int i = p;
+ do {
+ resbuf = res;
+ if (i-p >= ynlength) {
+ // make Yn longer
+ make_Yn_longer(ynlength*2, prec);
+ }
+ res = res + factor / cln::expt(cln::cl_I(i),n+1) * Yn[p-2][i-p]; // should we check it? or rely on magic number? ...
+ //res = res + factor / cln::expt(cln::cl_I(i),n+1) * (*it); // should we check it? or rely on magic number? ...
+ factor = factor * xf;
+ i++;
+ } while (res != resbuf);
+
+ return res;
+}
+
+
+// helper function for S(n,p,x)
+cln::cl_N S_projection(int n, int p, const cln::cl_N& x, const cln::float_format_t& prec)
+{
+ // [Kol] (5.3)
+ if (cln::abs(cln::realpart(x)) > cln::cl_F("0.5")) {
+
+ cln::cl_N result = cln::expt(cln::cl_I(-1),p) * cln::expt(cln::log(x),n)
+ * cln::expt(cln::log(1-x),p) / cln::factorial(n) / cln::factorial(p);
+
+ for (int s=0; s<n; s++) {
+ cln::cl_N res2;
+ for (int r=0; r<p; r++) {
+ res2 = res2 + cln::expt(cln::cl_I(-1),r) * cln::expt(cln::log(1-x),r)
+ * S_do_sum(p-r,n-s,1-x,prec) / cln::factorial(r);
+ }
+ result = result + cln::expt(cln::log(x),s) * (S_num(n-s,p,1).to_cl_N() - res2) / cln::factorial(s);
+ }
+
+ return result;
+ }
+
+ return S_do_sum(n, p, x, prec);
+}
+
+
+// helper function for S(n,p,x)
+numeric S_num(int n, int p, const numeric& x)
+{
+ if (x == 1) {
+ if (n == 1) {
+ // [Kol] (2.22) with (2.21)
+ return cln::zeta(p+1);
+ }
+
+ if (p == 1) {
+ // [Kol] (2.22)
+ return cln::zeta(n+1);
+ }
+
+ // [Kol] (9.1)
+ cln::cl_N result;
+ for (int nu=0; nu<n; nu++) {
+ for (int rho=0; rho<=p; rho++) {
+ result = result + b_k(n-nu-1) * b_k(p-rho) * a_k(nu+rho+1)
+ * cln::factorial(nu+rho+1) / cln::factorial(rho) / cln::factorial(nu+1);
+ }
+ }
+ result = result * cln::expt(cln::cl_I(-1),n+p-1);
+
+ return result;
+ }
+ else if (x == -1) {
+ // [Kol] (2.22)
+ if (p == 1) {
+ return -(1-cln::expt(cln::cl_I(2),-n)) * cln::zeta(n+1);
+ }
+// throw std::runtime_error("don't know how to evaluate this function!");
+ }
+
+ // what is the desired float format?
+ // first guess: default format