+ ex result = G_transform(pendint, a2, scale, gsyms)*
+ G_transform(empty, a1, scale, gsyms);
+
+ result -= shuffle_G(empty, a1, a2, pendint, a, scale, gsyms);
+ 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, gsyms);
+ if (pendint.size() > 0) {
+ result *= trailing_zeros_G(convert_pending_integrals_G(pendint),
+ pendint.front(), gsyms);
+ }
+
+ // 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(), gsyms)*
+ G_transform(empty, new_a, scale, gsyms);
+ int buffer = *changeit;
+ *changeit = *min_it;
+ result += G_transform(new_pendint, new_a, scale, gsyms);
+ *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(), gsyms)*
+ G_transform(empty, new_a, scale, gsyms);
+ *changeit = *min_it;
+ result -= G_transform(new_pendint, new_a, scale, gsyms);
+ } else {
+ // smallest at the front
+ new_pendint.push_back(scale);
+ result += trailing_zeros_G(convert_pending_integrals_G(new_pendint),
+ new_pendint.front(), gsyms)*
+ G_transform(empty, new_a, scale, gsyms);
+ new_pendint.back() = *changeit;
+ result -= trailing_zeros_G(convert_pending_integrals_G(new_pendint),
+ new_pendint.front(), gsyms)*
+ G_transform(empty, new_a, scale, gsyms);
+ *changeit = *min_it;
+ result += G_transform(new_pendint, new_a, scale, gsyms);
+ }
+ 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,
+ const exvector& gsyms)
+{
+ 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, gsyms);
+ }
+
+ 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, gsyms);
+ }
+
+ 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, gsyms);
+ }
+
+ 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, gsyms)
+ + shuffle_G(a02, a1, a2_removed, pendint, a_old, scale, gsyms);
+}
+
+// handles the transformations and the numerical evaluation of G
+// the parameter x, s and y must only contain numerics
+static cln::cl_N
+G_numeric(const std::vector<cln::cl_N>& x, const std::vector<int>& s,
+ const cln::cl_N& y);
+
+// do acceleration transformation (hoelder convolution [BBB])
+// the parameter x, s and y must only contain numerics
+static cln::cl_N
+G_do_hoelder(std::vector<cln::cl_N> x, /* yes, it's passed by value */
+ const std::vector<int>& s, const cln::cl_N& y)
+{
+ cln::cl_N result;
+ const std::size_t size = x.size();
+ for (std::size_t i = 0; i < size; ++i)
+ x[i] = x[i]/y;
+
+ for (std::size_t r = 0; r <= size; ++r) {
+ cln::cl_N buffer(1 & r ? -1 : 1);
+ cln::cl_RA p(2);
+ bool adjustp;
+ do {
+ adjustp = false;
+ for (std::size_t i = 0; i < size; ++i) {
+ if (x[i] == cln::cl_RA(1)/p) {
+ p = p/2 + cln::cl_RA(3)/2;
+ adjustp = true;
+ continue;
+ }
+ }
+ } while (adjustp);
+ cln::cl_RA q = p/(p-1);
+ std::vector<cln::cl_N> qlstx;
+ std::vector<int> qlsts;
+ for (std::size_t j = r; j >= 1; --j) {
+ qlstx.push_back(cln::cl_N(1) - x[j-1]);
+ if (instanceof(x[j-1], cln::cl_R_ring) &&
+ realpart(x[j-1]) > 1 && realpart(x[j-1]) <= 2) {
+ qlsts.push_back(s[j-1]);
+ } else {
+ qlsts.push_back(-s[j-1]);
+ }
+ }
+ if (qlstx.size() > 0) {
+ buffer = buffer*G_numeric(qlstx, qlsts, 1/q);
+ }
+ std::vector<cln::cl_N> plstx;
+ std::vector<int> plsts;
+ for (std::size_t j = r+1; j <= size; ++j) {
+ plstx.push_back(x[j-1]);
+ plsts.push_back(s[j-1]);
+ }
+ if (plstx.size() > 0) {
+ buffer = buffer*G_numeric(plstx, plsts, 1/p);
+ }
+ result = result + buffer;
+ }
+ return result;
+}
+
+// convergence transformation, used for numerical evaluation of G function.
+// the parameter x, s and y must only contain numerics
+static cln::cl_N
+G_do_trafo(const std::vector<cln::cl_N>& x, const std::vector<int>& s,
+ const cln::cl_N& y)
+{
+ // sort (|x|<->position) to determine indices
+ typedef std::multimap<cln::cl_R, std::size_t> sortmap_t;
+ sortmap_t sortmap;
+ std::size_t size = 0;
+ for (std::size_t i = 0; i < x.size(); ++i) {
+ if (!zerop(x[i])) {
+ sortmap.insert(std::make_pair(abs(x[i]), i));
+ ++size;
+ }
+ }
+ // include upper limit (scale)
+ sortmap.insert(std::make_pair(abs(y), x.size()));
+
+ // generate missing dummy-symbols
+ int i = 1;
+ // holding dummy-symbols for the G/Li transformations
+ exvector gsyms;
+ gsyms.push_back(symbol("GSYMS_ERROR"));
+ cln::cl_N lastentry(0);
+ for (sortmap_t::const_iterator it = sortmap.begin(); it != sortmap.end(); ++it) {
+ if (it != sortmap.begin()) {
+ if (it->second < x.size()) {
+ 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.size()) {
+ lastentry = x[it->second];
+ } else {
+ lastentry = y;
+ }
+ }
+
+ // fill position data according to sorted indices and prepare substitution list
+ Gparameter a(x.size());
+ exmap subslst;
+ std::size_t pos = 1;
+ int scale;
+ for (sortmap_t::const_iterator it = sortmap.begin(); it != sortmap.end(); ++it) {
+ if (it->second < x.size()) {
+ if (s[it->second] > 0) {
+ a[it->second] = pos;
+ } else {
+ a[it->second] = -int(pos);
+ }
+ subslst[gsyms[pos]] = numeric(x[it->second]);
+ } else {
+ scale = pos;
+ subslst[gsyms[pos]] = numeric(y);
+ }
+ ++pos;
+ }
+
+ // do transformation
+ Gparameter pendint;
+ ex result = G_transform(pendint, a, scale, gsyms);
+ // replace dummy symbols with their values
+ result = result.eval().expand();
+ result = result.subs(subslst).evalf();
+ if (!is_a<numeric>(result))
+ throw std::logic_error("G_do_trafo: G_transform returned non-numeric result");
+
+ cln::cl_N ret = ex_to<numeric>(result).to_cl_N();
+ return ret;
+}
+
+// handles the transformations and the numerical evaluation of G
+// the parameter x, s and y must only contain numerics
+static cln::cl_N
+G_numeric(const std::vector<cln::cl_N>& x, const std::vector<int>& s,
+ const cln::cl_N& y)
+{
+ // check for convergence and necessary accelerations
+ bool need_trafo = false;
+ bool need_hoelder = false;
+ std::size_t depth = 0;
+ for (std::size_t i = 0; i < x.size(); ++i) {
+ if (!zerop(x[i])) {
+ ++depth;
+ const cln::cl_N x_y = abs(x[i]) - y;
+ if (instanceof(x_y, cln::cl_R_ring) &&
+ realpart(x_y) < cln::least_negative_float(cln::float_format(Digits - 2)))
+ need_trafo = true;
+
+ if (abs(abs(x[i]/y) - 1) < 0.01)
+ need_hoelder = true;
+ }
+ }
+ if (zerop(x[x.size() - 1]))
+ need_trafo = true;
+
+ if (depth == 1 && x.size() == 2 && !need_trafo)
+ return - Li_projection(2, y/x[1], cln::float_format(Digits));
+
+ // do acceleration transformation (hoelder convolution [BBB])
+ if (need_hoelder)
+ return G_do_hoelder(x, s, y);
+
+ // convergence transformation
+ if (need_trafo)
+ return G_do_trafo(x, s, y);
+
+ // do summation
+ std::vector<cln::cl_N> newx;
+ newx.reserve(x.size());
+ std::vector<int> m;
+ m.reserve(x.size());
+ int mcount = 1;
+ int sign = 1;
+ cln::cl_N factor = y;
+ for (std::size_t i = 0; i < x.size(); ++i) {
+ if (zerop(x[i])) {
+ ++mcount;
+ } else {
+ newx.push_back(factor/x[i]);
+ factor = x[i];
+ m.push_back(mcount);
+ mcount = 1;
+ sign = -sign;
+ }
+ }
+
+ return sign*multipleLi_do_sum(m, newx);
+}
+
+
+ex mLi_numeric(const lst& m, const lst& x)
+{
+ // let G_numeric do the transformation
+ std::vector<cln::cl_N> newx;
+ newx.reserve(x.nops());
+ std::vector<int> s;
+ s.reserve(x.nops());
+ cln::cl_N 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.push_back(cln::cl_N(0));
+ s.push_back(1);
+ }
+ const cln::cl_N xi = ex_to<numeric>(*itx).to_cl_N();
+ newx.push_back(factor/xi);
+ factor = factor/xi;
+ s.push_back(1);
+ }
+ return numeric(cln::cl_N(1 & m.nops() ? - 1 : 1)*G_numeric(newx, s, cln::cl_N(1)));
+}
+
+
+} // 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();
+ }
+ std::vector<int> s;
+ s.reserve(x.nops());
+ 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;
+ }
+ if ( !ex_to<numeric>(*it).is_real() && ex_to<numeric>(*it).imag() < 0 ) {
+ s.push_back(-1);
+ }
+ else {
+ s.push_back(1);
+ }
+ }
+ if (all_zero) {
+ return pow(log(y), x.nops()) / factorial(x.nops());
+ }
+ std::vector<cln::cl_N> xv;
+ xv.reserve(x.nops());
+ for (lst::const_iterator it = x.begin(); it != x.end(); ++it)
+ xv.push_back(ex_to<numeric>(*it).to_cl_N());
+ cln::cl_N result = G_numeric(xv, s, ex_to<numeric>(y).to_cl_N());
+ return numeric(result);
+}
+
+
+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();
+ }
+ std::vector<int> s;
+ s.reserve(x.nops());
+ 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;
+ }
+ if ( !ex_to<numeric>(*it).is_real() && ex_to<numeric>(*it).imag() < 0 ) {
+ s.push_back(-1);
+ }
+ else {
+ s.push_back(+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();
+ }
+ std::vector<cln::cl_N> xv;
+ xv.reserve(x.nops());
+ for (lst::const_iterator it = x.begin(); it != x.end(); ++it)
+ xv.push_back(ex_to<numeric>(*it).to_cl_N());
+ cln::cl_N result = G_numeric(xv, s, ex_to<numeric>(y).to_cl_N());
+ return numeric(result);
+}
+
+
+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();
+ }
+ std::vector<int> sn;
+ sn.reserve(s.nops());
+ 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 ( ex_to<numeric>(*itx).is_real() ) {
+ if ( *its >= 0 ) {
+ sn.push_back(1);
+ }
+ else {
+ sn.push_back(-1);
+ }
+ }
+ else {
+ if ( ex_to<numeric>(*itx).imag() > 0 ) {
+ sn.push_back(1);
+ }
+ else {
+ sn.push_back(-1);
+ }
+ }
+ }
+ if (all_zero) {
+ return pow(log(y), x.nops()) / factorial(x.nops());
+ }
+ std::vector<cln::cl_N> xn;
+ xn.reserve(x.nops());
+ for (lst::const_iterator it = x.begin(); it != x.end(); ++it)
+ xn.push_back(ex_to<numeric>(*it).to_cl_N());
+ cln::cl_N result = G_numeric(xn, sn, ex_to<numeric>(y).to_cl_N());
+ return numeric(result);
+}
+
+
+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();
+ }
+ std::vector<int> sn;
+ sn.reserve(s.nops());
+ 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 ( ex_to<numeric>(*itx).is_real() ) {
+ if ( *its >= 0 ) {
+ sn.push_back(1);
+ }
+ else {
+ sn.push_back(-1);
+ }
+ }
+ else {
+ if ( ex_to<numeric>(*itx).imag() > 0 ) {
+ sn.push_back(1);
+ }
+ else {
+ sn.push_back(-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();
+ }
+ std::vector<cln::cl_N> xn;
+ xn.reserve(x.nops());
+ for (lst::const_iterator it = x.begin(); it != x.end(); ++it)
+ xn.push_back(ex_to<numeric>(*it).to_cl_N());
+ cln::cl_N result = G_numeric(xn, sn, ex_to<numeric>(y).to_cl_N());
+ return numeric(result);
+}
+
+
+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)) {
+ int m__ = ex_to<numeric>(m_).to_int();
+ const cln::cl_N x__ = ex_to<numeric>(x_).to_cl_N();
+ const cln::cl_N result = Lin_numeric(m__, x__);
+ return numeric(result);
+ } else {
+ // try to numerically evaluate second argument
+ ex x_val = x_.evalf();
+ if (x_val.info(info_flags::numeric)) {
+ int m__ = ex_to<numeric>(m_).to_int();
+ const cln::cl_N x__ = ex_to<numeric>(x_val).to_cl_N();
+ const cln::cl_N result = Lin_numeric(m__, x__);
+ return numeric(result);
+ }
+ }
+ }
+ // 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)) {
+ int m__ = ex_to<numeric>(m_).to_int();
+ const cln::cl_N x__ = ex_to<numeric>(x_).to_cl_N();
+ const cln::cl_N result = Lin_numeric(m__, x__);
+ return numeric(result);
+ }
+
+ return Li(m_, x_).hold();
+}
+
+
+static ex Li_series(const ex& m, const ex& x, const relational& rel, int order, unsigned options)
+{
+ if (is_a<lst>(m) || is_a<lst>(x)) {
+ // multiple polylog
+ epvector seq;
+ seq.push_back(expair(Li(m, x), 0));
+ return pseries(rel, seq);
+ }
+
+ // classical polylog
+ const ex x_pt = x.subs(rel, subs_options::no_pattern);
+ if (m.info(info_flags::numeric) && x_pt.info(info_flags::numeric)) {
+ // First special case: x==0 (derivatives have poles)
+ if (x_pt.is_zero()) {
+ const symbol s;
+ ex ser;
+ // manually construct the primitive expansion
+ for (int i=1; i<order; ++i)
+ ser += pow(s,i) / pow(numeric(i), m);
+ // substitute the argument's series expansion
+ ser = ser.subs(s==x.series(rel, order), subs_options::no_pattern);
+ // maybe that was terminating, so add a proper order term
+ epvector nseq;
+ nseq.push_back(expair(Order(_ex1), order));
+ ser += pseries(rel, nseq);
+ // reexpanding it will collapse the series again
+ return ser.series(rel, order);
+ }
+ // TODO special cases: x==1 (branch point) and x real, >=1 (branch cut)
+ throw std::runtime_error("Li_series: don't know how to do the series expansion at this point!");
+ }
+ // all other cases should be safe, by now:
+ throw do_taylor(); // caught by function::series()
+}
+
+
+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) / cln::factorial(2*j);
+ }
+ else {
+ result = result + 2 * cln::expt(cln::pi(),2*j) * S_num(n-2*j,p,1) / 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)
+ / (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)
+ / (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)
+ / (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)
+ / (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)
+{
+ static cln::float_format_t oldprec = cln::default_float_format;
+
+ if (p==1) {
+ return Li_projection(n+1, x, prec);
+ }
+
+ // precision has changed, we need to clear lookup table Yn
+ if ( oldprec != prec ) {
+ Yn.clear();
+ ynsize = 0;
+ ynlength = 100;
+ oldprec = 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) - res2) / cln::factorial(s);
+ }
+
+ return result;
+ }
+
+ return S_do_sum(n, p, x, prec);
+}
+
+
+// helper function for S(n,p,x)
+const cln::cl_N S_num(int n, int p, const cln::cl_N& 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
+ cln::float_format_t prec = cln::default_float_format;
+ const cln::cl_N value = x;
+ // second guess: the argument's format
+ if (!instanceof(realpart(value), cln::cl_RA_ring))
+ prec = cln::float_format(cln::the<cln::cl_F>(cln::realpart(value)));
+ else if (!instanceof(imagpart(value), cln::cl_RA_ring))
+ prec = cln::float_format(cln::the<cln::cl_F>(cln::imagpart(value)));
+
+ // [Kol] (5.3)
+ if ((cln::realpart(value) < -0.5) || (n == 0) || ((cln::abs(value) <= 1) && (cln::abs(value) > 0.95))) {