+// holding dummy-symbols for the G/Li transformations
+std::vector<ex> gsyms;
+
+
+// type used by the transformation functions for G
+typedef std::vector<int> Gparameter;
+
+
+// G_eval1-function for G transformations
+ex G_eval1(int a, int scale)
+{
+ if (a != 0) {
+ const ex& scs = gsyms[std::abs(scale)];
+ const ex& as = gsyms[std::abs(a)];
+ if (as != scs) {
+ return -log(1 - scs/as);
+ } else {
+ return -zeta(1);
+ }
+ } else {
+ return log(gsyms[std::abs(scale)]);
+ }
+}
+
+
+// G_eval-function for G transformations
+ex G_eval(const Gparameter& a, int scale)
+{
+ // check for properties of G
+ ex sc = gsyms[std::abs(scale)];
+ lst newa;
+ bool all_zero = true;
+ bool all_ones = true;
+ int count_ones = 0;
+ for (Gparameter::const_iterator it = a.begin(); it != a.end(); ++it) {
+ if (*it != 0) {
+ const ex sym = gsyms[std::abs(*it)];
+ newa.append(sym);
+ all_zero = false;
+ if (sym != sc) {
+ all_ones = false;
+ }
+ if (all_ones) {
+ ++count_ones;
+ }
+ } else {
+ all_ones = false;
+ }
+ }
+
+ // care about divergent G: shuffle to separate divergencies that will be canceled
+ // later on in the transformation
+ if (newa.nops() > 1 && newa.op(0) == sc && !all_ones && a.front()!=0) {
+ // do shuffle
+ Gparameter short_a;
+ Gparameter::const_iterator it = a.begin();
+ ++it;
+ for (; it != a.end(); ++it) {
+ short_a.push_back(*it);
+ }
+ ex result = G_eval1(a.front(), scale) * G_eval(short_a, scale);
+ it = short_a.begin();
+ for (int i=1; i<count_ones; ++i) {
+ ++it;
+ }
+ for (; it != short_a.end(); ++it) {
+
+ Gparameter newa;
+ Gparameter::const_iterator it2 = short_a.begin();
+ for (--it2; it2 != it;) {
+ ++it2;
+ newa.push_back(*it2);
+ }
+ newa.push_back(a[0]);
+ ++it2;
+ for (; it2 != short_a.end(); ++it2) {
+ newa.push_back(*it2);
+ }
+ result -= G_eval(newa, scale);
+ }
+ return result / count_ones;
+ }
+
+ // G({1,...,1};y) -> G({1};y)^k / k!
+ if (all_ones && a.size() > 1) {
+ return pow(G_eval1(a.front(),scale), count_ones) / factorial(count_ones);
+ }
+
+ // G({0,...,0};y) -> log(y)^k / k!
+ if (all_zero) {
+ return pow(log(gsyms[std::abs(scale)]), a.size()) / factorial(a.size());
+ }
+
+ // no special cases anymore -> convert it into Li
+ lst m;
+ lst x;
+ ex argbuf = gsyms[std::abs(scale)];
+ ex mval = _ex1;
+ for (Gparameter::const_iterator it=a.begin(); it!=a.end(); ++it) {
+ if (*it != 0) {
+ const ex& sym = gsyms[std::abs(*it)];
+ x.append(argbuf / sym);
+ m.append(mval);
+ mval = _ex1;
+ argbuf = sym;
+ } else {
+ ++mval;
+ }
+ }
+ return pow(-1, x.nops()) * Li(m, x);
+}
+
+
+// converts data for G: pending_integrals -> a
+Gparameter convert_pending_integrals_G(const Gparameter& pending_integrals)
+{
+ GINAC_ASSERT(pending_integrals.size() != 1);
+
+ if (pending_integrals.size() > 0) {
+ // get rid of the first element, which would stand for the new upper limit
+ Gparameter new_a(pending_integrals.begin()+1, pending_integrals.end());
+ return new_a;
+ } else {
+ // just return empty parameter list
+ Gparameter new_a;
+ return new_a;
+ }
+}
+
+
+// check the parameters a and scale for G and return information about convergence, depth, etc.
+// convergent : true if G(a,scale) is convergent
+// depth : depth of G(a,scale)
+// trailing_zeros : number of trailing zeros of a
+// min_it : iterator of a pointing on the smallest element in a
+Gparameter::const_iterator check_parameter_G(const Gparameter& a, int scale,
+ bool& convergent, int& depth, int& trailing_zeros, Gparameter::const_iterator& min_it)
+{
+ convergent = true;
+ depth = 0;
+ trailing_zeros = 0;
+ min_it = a.end();
+ Gparameter::const_iterator lastnonzero = a.end();
+ for (Gparameter::const_iterator it = a.begin(); it != a.end(); ++it) {
+ if (std::abs(*it) > 0) {
+ ++depth;
+ trailing_zeros = 0;
+ lastnonzero = it;
+ if (std::abs(*it) < scale) {
+ convergent = false;
+ if ((min_it == a.end()) || (std::abs(*it) < std::abs(*min_it))) {
+ min_it = it;
+ }
+ }
+ } else {
+ ++trailing_zeros;
+ }
+ }
+ return ++lastnonzero;
+}
+
+
+// add scale to pending_integrals if pending_integrals is empty
+Gparameter prepare_pending_integrals(const Gparameter& pending_integrals, int scale)
+{
+ GINAC_ASSERT(pending_integrals.size() != 1);
+
+ if (pending_integrals.size() > 0) {
+ return pending_integrals;
+ } else {
+ Gparameter new_pending_integrals;
+ new_pending_integrals.push_back(scale);
+ return new_pending_integrals;
+ }
+}
+
+
+// handles trailing zeroes for an otherwise convergent integral
+ex trailing_zeros_G(const Gparameter& a, int scale)
+{
+ bool convergent;
+ int depth, trailing_zeros;
+ Gparameter::const_iterator last, dummyit;
+ last = check_parameter_G(a, scale, convergent, depth, trailing_zeros, dummyit);
+
+ GINAC_ASSERT(convergent);
+
+ if ((trailing_zeros > 0) && (depth > 0)) {
+ ex result;
+ Gparameter new_a(a.begin(), a.end()-1);
+ result += G_eval1(0, scale) * trailing_zeros_G(new_a, scale);
+ for (Gparameter::const_iterator it = a.begin(); it != last; ++it) {
+ Gparameter new_a(a.begin(), it);
+ new_a.push_back(0);
+ new_a.insert(new_a.end(), it, a.end()-1);
+ result -= trailing_zeros_G(new_a, scale);
+ }
+
+ return result / trailing_zeros;
+ } else {
+ return G_eval(a, scale);
+ }
+}
+
+
+// G transformation [VSW] (57),(58)
+ex depth_one_trafo_G(const Gparameter& pending_integrals, const Gparameter& a, int scale)
+{
+ // pendint = ( y1, b1, ..., br )
+ // a = ( 0, ..., 0, amin )
+ // scale = y2
+ //
+ // int_0^y1 ds1/(s1-b1) ... int dsr/(sr-br) G(0, ..., 0, sr; y2)
+ // where sr replaces amin
+
+ GINAC_ASSERT(a.back() != 0);
+ GINAC_ASSERT(a.size() > 0);
+
+ ex result;
+ Gparameter new_pending_integrals = prepare_pending_integrals(pending_integrals, std::abs(a.back()));
+ const int psize = pending_integrals.size();
+
+ // length == 1
+ // G(sr_{+-}; y2 ) = G(y2_{-+}; sr) - G(0; sr) + ln(-y2_{-+})
+
+ if (a.size() == 1) {
+
+ // ln(-y2_{-+})
+ result += log(gsyms[ex_to<numeric>(scale).to_int()]);
+ if (a.back() > 0) {
+ new_pending_integrals.push_back(-scale);
+ result += I*Pi;
+ } else {
+ new_pending_integrals.push_back(scale);
+ result -= I*Pi;
+ }
+ if (psize) {
+ result *= trailing_zeros_G(convert_pending_integrals_G(pending_integrals), pending_integrals.front());
+ }
+
+ // G(y2_{-+}; sr)
+ result += trailing_zeros_G(convert_pending_integrals_G(new_pending_integrals), new_pending_integrals.front());
+
+ // G(0; sr)
+ new_pending_integrals.back() = 0;
+ result -= trailing_zeros_G(convert_pending_integrals_G(new_pending_integrals), new_pending_integrals.front());
+
+ return result;
+ }
+
+ // length > 1
+ // G_m(sr_{+-}; y2) = -zeta_m + int_0^y2 dt/t G_{m-1}( (1/y2)_{+-}; 1/t )
+ // - int_0^sr dt/t G_{m-1}( (1/y2)_{+-}; 1/t )
+
+ //term zeta_m
+ result -= zeta(a.size());
+ if (psize) {
+ result *= trailing_zeros_G(convert_pending_integrals_G(pending_integrals), pending_integrals.front());
+ }
+
+ // term int_0^sr dt/t G_{m-1}( (1/y2)_{+-}; 1/t )
+ // = int_0^sr dt/t G_{m-1}( t_{+-}; y2 )
+ Gparameter new_a(a.begin()+1, a.end());
+ new_pending_integrals.push_back(0);
+ result -= depth_one_trafo_G(new_pending_integrals, new_a, scale);
+
+ // term int_0^y2 dt/t G_{m-1}( (1/y2)_{+-}; 1/t )
+ // = int_0^y2 dt/t G_{m-1}( t_{+-}; y2 )
+ Gparameter new_pending_integrals_2;
+ new_pending_integrals_2.push_back(scale);
+ new_pending_integrals_2.push_back(0);
+ if (psize) {
+ result += trailing_zeros_G(convert_pending_integrals_G(pending_integrals), pending_integrals.front())
+ * depth_one_trafo_G(new_pending_integrals_2, new_a, scale);
+ } else {
+ result += depth_one_trafo_G(new_pending_integrals_2, new_a, scale);
+ }
+
+ return result;
+}
+
+
+// forward declaration
+ex shuffle_G(const Gparameter & a0, const Gparameter & a1, const Gparameter & a2,
+ const Gparameter& pendint, const Gparameter& a_old, int scale);
+
+
+// G transformation [VSW]
+ex G_transform(const Gparameter& pendint, const Gparameter& a, int scale)
+{
+ // main recursion routine
+ //
+ // pendint = ( y1, b1, ..., br )
+ // a = ( a1, ..., amin, ..., aw )
+ // scale = y2
+ //
+ // int_0^y1 ds1/(s1-b1) ... int dsr/(sr-br) G(a1,...,sr,...,aw,y2)
+ // where sr replaces amin
+
+ // find smallest alpha, determine depth and trailing zeros, and check for convergence
+ bool convergent;
+ int depth, trailing_zeros;
+ Gparameter::const_iterator min_it;
+ Gparameter::const_iterator firstzero =
+ check_parameter_G(a, scale, convergent, depth, trailing_zeros, min_it);
+ int min_it_pos = min_it - a.begin();
+
+ // special case: all a's are zero
+ if (depth == 0) {
+ ex result;
+
+ if (a.size() == 0) {
+ result = 1;
+ } else {
+ result = G_eval(a, scale);
+ }
+ if (pendint.size() > 0) {
+ result *= trailing_zeros_G(convert_pending_integrals_G(pendint), pendint.front());
+ }
+ return result;
+ }
+
+ // handle trailing zeros
+ if (trailing_zeros > 0) {
+ ex result;
+ Gparameter new_a(a.begin(), a.end()-1);
+ result += G_eval1(0, scale) * G_transform(pendint, new_a, scale);
+ for (Gparameter::const_iterator it = a.begin(); it != firstzero; ++it) {
+ Gparameter new_a(a.begin(), it);
+ new_a.push_back(0);
+ new_a.insert(new_a.end(), it, a.end()-1);
+ result -= G_transform(pendint, new_a, scale);
+ }
+ return result / trailing_zeros;
+ }
+
+ // convergence case
+ if (convergent) {
+ if (pendint.size() > 0) {
+ return G_eval(convert_pending_integrals_G(pendint), pendint.front()) * G_eval(a, scale);
+ } else {
+ return G_eval(a, scale);
+ }
+ }
+
+ // call basic transformation for depth equal one
+ if (depth == 1) {
+ return depth_one_trafo_G(pendint, a, scale);
+ }
+
+ // 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);
+}
+
+