GiNaC 1.8.10
inifcns_nstdsums.cpp
Go to the documentation of this file.
1
49/*
50 * GiNaC Copyright (C) 1999-2026 Johannes Gutenberg University Mainz, Germany
51 *
52 * This program is free software; you can redistribute it and/or modify
53 * it under the terms of the GNU General Public License as published by
54 * the Free Software Foundation; either version 2 of the License, or
55 * (at your option) any later version.
56 *
57 * This program is distributed in the hope that it will be useful,
58 * but WITHOUT ANY WARRANTY; without even the implied warranty of
59 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
60 * GNU General Public License for more details.
61 *
62 * You should have received a copy of the GNU General Public License
63 * along with this program. If not, see <https://www.gnu.org/licenses/>.
64 */
65
66#include "inifcns.h"
67
68#include "add.h"
69#include "constant.h"
70#include "lst.h"
71#include "mul.h"
72#include "numeric.h"
73#include "operators.h"
74#include "power.h"
75#include "pseries.h"
76#include "relational.h"
77#include "symbol.h"
78#include "utils.h"
79#include "wildcard.h"
80
81#include <cln/cln.h>
82#include <sstream>
83#include <stdexcept>
84#include <vector>
85#include <cmath>
86
87namespace GiNaC {
88
89
91//
92// Classical polylogarithm Li(n,x)
93//
94// helper functions
95//
97
98
99// anonymous namespace for helper functions
100namespace {
101
102
103// lookup table for factors built from Bernoulli numbers
104// see fill_Xn()
105std::vector<std::vector<cln::cl_N>> Xn;
106// initial size of Xn that should suffice for 32bit machines (must be even)
107const int xninitsizestep = 26;
108int xninitsize = xninitsizestep;
109int xnsize = 0;
110
111
112// This function calculates the X_n. The X_n are needed for speed up of classical polylogarithms.
113// With these numbers the polylogs can be calculated as follows:
114// Li_p (x) = \sum_{n=0}^\infty X_{p-2}(n) u^{n+1}/(n+1)! with u = -log(1-x)
115// X_0(n) = B_n (Bernoulli numbers)
116// X_p(n) = \sum_{k=0}^n binomial(n,k) B_{n-k} / (k+1) * X_{p-1}(k)
117// The calculation of Xn depends on X0 and X{n-1}.
118// X_0 is special, it holds only the non-zero Bernoulli numbers with index 2 or greater.
119// This results in a slightly more complicated algorithm for the X_n.
120// The first index in Xn corresponds to the index of the polylog minus 2.
121// The second index in Xn corresponds to the index from the actual sum.
122void fill_Xn(int n)
123{
124 if (n>1) {
125 // calculate X_2 and higher (corresponding to Li_4 and higher)
126 std::vector<cln::cl_N> buf(xninitsize);
127 auto it = buf.begin();
128 cln::cl_N result;
129 *it = -(cln::expt(cln::cl_I(2),n+1) - 1) / cln::expt(cln::cl_I(2),n+1); // i == 1
130 it++;
131 for (int i=2; i<=xninitsize; i++) {
132 if (i&1) {
133 result = 0; // k == 0
134 } else {
135 result = Xn[0][i/2-1]; // k == 0
136 }
137 for (int k=1; k<i-1; k++) {
138 if ( !(((i-k) & 1) && ((i-k) > 1)) ) {
139 result = result + cln::binomial(i,k) * Xn[0][(i-k)/2-1] * Xn[n-1][k-1] / (k+1);
140 }
141 }
142 result = result - cln::binomial(i,i-1) * Xn[n-1][i-2] / 2 / i; // k == i-1
143 result = result + Xn[n-1][i-1] / (i+1); // k == i
144
145 *it = result;
146 it++;
147 }
148 Xn.push_back(buf);
149 } else if (n==1) {
150 // special case to handle the X_0 correct
151 std::vector<cln::cl_N> buf(xninitsize);
152 auto it = buf.begin();
153 cln::cl_N result;
154 *it = cln::cl_I(-3)/cln::cl_I(4); // i == 1
155 it++;
156 *it = cln::cl_I(17)/cln::cl_I(36); // i == 2
157 it++;
158 for (int i=3; i<=xninitsize; i++) {
159 if (i & 1) {
160 result = -Xn[0][(i-3)/2]/2;
161 *it = (cln::binomial(i,1)/cln::cl_I(2) + cln::binomial(i,i-1)/cln::cl_I(i))*result;
162 it++;
163 } else {
164 result = Xn[0][i/2-1] + Xn[0][i/2-1]/(i+1);
165 for (int k=1; k<i/2; k++) {
166 result = result + cln::binomial(i,k*2) * Xn[0][k-1] * Xn[0][i/2-k-1] / (k*2+1);
167 }
168 *it = result;
169 it++;
170 }
171 }
172 Xn.push_back(buf);
173 } else {
174 // calculate X_0
175 std::vector<cln::cl_N> buf(xninitsize/2);
176 auto it = buf.begin();
177 for (int i=1; i<=xninitsize/2; i++) {
178 *it = bernoulli(i*2).to_cl_N();
179 it++;
180 }
181 Xn.push_back(buf);
182 }
183
184 xnsize++;
185}
186
187// doubles the number of entries in each Xn[]
188void double_Xn()
189{
190 const int pos0 = xninitsize / 2;
191 // X_0
192 for (int i=1; i<=xninitsizestep/2; ++i) {
193 Xn[0].push_back(bernoulli((i+pos0)*2).to_cl_N());
194 }
195 if (Xn.size() > 1) {
196 int xend = xninitsize + xninitsizestep;
197 cln::cl_N result;
198 // X_1
199 for (int i=xninitsize+1; i<=xend; ++i) {
200 if (i & 1) {
201 result = -Xn[0][(i-3)/2]/2;
202 Xn[1].push_back((cln::binomial(i,1)/cln::cl_I(2) + cln::binomial(i,i-1)/cln::cl_I(i))*result);
203 } else {
204 result = Xn[0][i/2-1] + Xn[0][i/2-1]/(i+1);
205 for (int k=1; k<i/2; k++) {
206 result = result + cln::binomial(i,k*2) * Xn[0][k-1] * Xn[0][i/2-k-1] / (k*2+1);
207 }
208 Xn[1].push_back(result);
209 }
210 }
211 // X_n
212 for (size_t n=2; n<Xn.size(); ++n) {
213 for (int i=xninitsize+1; i<=xend; ++i) {
214 if (i & 1) {
215 result = 0; // k == 0
216 } else {
217 result = Xn[0][i/2-1]; // k == 0
218 }
219 for (int k=1; k<i-1; ++k) {
220 if ( !(((i-k) & 1) && ((i-k) > 1)) ) {
221 result = result + cln::binomial(i,k) * Xn[0][(i-k)/2-1] * Xn[n-1][k-1] / (k+1);
222 }
223 }
224 result = result - cln::binomial(i,i-1) * Xn[n-1][i-2] / 2 / i; // k == i-1
225 result = result + Xn[n-1][i-1] / (i+1); // k == i
226 Xn[n].push_back(result);
227 }
228 }
229 }
230 xninitsize += xninitsizestep;
231}
232
233
234// calculates Li(2,x) without Xn
235cln::cl_N Li2_do_sum(const cln::cl_N& x)
236{
237 cln::cl_N res = x;
238 cln::cl_N resbuf;
239 cln::cl_N num = x * cln::cl_float(1, cln::float_format(Digits));
240 cln::cl_I den = 1; // n^2 = 1
241 unsigned i = 3;
242 do {
243 resbuf = res;
244 num = num * x;
245 den = den + i; // n^2 = 4, 9, 16, ...
246 i += 2;
247 res = res + num / den;
248 } while (res != resbuf);
249 return res;
250}
251
252
253// calculates Li(2,x) with Xn
254cln::cl_N Li2_do_sum_Xn(const cln::cl_N& x)
255{
256 std::vector<cln::cl_N>::const_iterator it = Xn[0].begin();
257 std::vector<cln::cl_N>::const_iterator xend = Xn[0].end();
258 cln::cl_N u = -cln::log(1-x);
259 cln::cl_N factor = u * cln::cl_float(1, cln::float_format(Digits));
260 cln::cl_N uu = cln::square(u);
261 cln::cl_N res = u - uu/4;
262 cln::cl_N resbuf;
263 unsigned i = 1;
264 do {
265 resbuf = res;
266 factor = factor * uu / (2*i * (2*i+1));
267 res = res + (*it) * factor;
268 i++;
269 if (++it == xend) {
270 double_Xn();
271 it = Xn[0].begin() + (i-1);
272 xend = Xn[0].end();
273 }
274 } while (res != resbuf);
275 return res;
276}
277
278
279// calculates Li(n,x), n>2 without Xn
280cln::cl_N Lin_do_sum(int n, const cln::cl_N& x)
281{
282 cln::cl_N factor = x * cln::cl_float(1, cln::float_format(Digits));
283 cln::cl_N res = x;
284 cln::cl_N resbuf;
285 int i=2;
286 do {
287 resbuf = res;
288 factor = factor * x;
289 res = res + factor / cln::expt(cln::cl_I(i),n);
290 i++;
291 } while (res != resbuf);
292 return res;
293}
294
295
296// calculates Li(n,x), n>2 with Xn
297cln::cl_N Lin_do_sum_Xn(int n, const cln::cl_N& x)
298{
299 std::vector<cln::cl_N>::const_iterator it = Xn[n-2].begin();
300 std::vector<cln::cl_N>::const_iterator xend = Xn[n-2].end();
301 cln::cl_N u = -cln::log(1-x);
302 cln::cl_N factor = u * cln::cl_float(1, cln::float_format(Digits));
303 cln::cl_N res = u;
304 cln::cl_N resbuf;
305 unsigned i=2;
306 do {
307 resbuf = res;
308 factor = factor * u / i;
309 res = res + (*it) * factor;
310 i++;
311 if (++it == xend) {
312 double_Xn();
313 it = Xn[n-2].begin() + (i-2);
314 xend = Xn[n-2].end();
315 }
316 } while (res != resbuf);
317 return res;
318}
319
320
321// forward declaration needed by function Li_projection and C below
322const cln::cl_N S_num(int n, int p, const cln::cl_N& x);
323
324
325// helper function for classical polylog Li
326cln::cl_N Li_projection(int n, const cln::cl_N& x, const cln::float_format_t& prec)
327{
328 // treat n=2 as special case
329 if (n == 2) {
330 // check if precalculated X0 exists
331 if (xnsize == 0) {
332 fill_Xn(0);
333 }
334
335 if (cln::realpart(x) < 0.5) {
336 // choose the faster algorithm
337 // the switching point was empirically determined. the optimal point
338 // depends on hardware, Digits, ... so an approx value is okay.
339 // it solves also the problem with precision due to the u=-log(1-x) transformation
340 if (cln::abs(x) < 0.25) {
341 return Li2_do_sum(x);
342 } else {
343 // Li2_do_sum practically doesn't converge near x == ±I
344 return Li2_do_sum_Xn(x);
345 }
346 } else {
347 // choose the faster algorithm
348 if (cln::abs(cln::realpart(x)) > 0.75) {
349 if ( x == 1 ) {
350 return cln::zeta(2);
351 } else {
352 return -Li2_do_sum(1-x) - cln::log(x) * cln::log(1-x) + cln::zeta(2);
353 }
354 } else {
355 return -Li2_do_sum_Xn(1-x) - cln::log(x) * cln::log(1-x) + cln::zeta(2);
356 }
357 }
358 } else {
359 // check if precalculated Xn exist
360 if (n > xnsize+1) {
361 for (int i=xnsize; i<n-1; i++) {
362 fill_Xn(i);
363 }
364 }
365
366 if (cln::realpart(x) < 0.5) {
367 // choose the faster algorithm
368 // with n>=12 the "normal" summation always wins against the method with Xn
369 if ((cln::abs(x) < 0.3) || (n >= 12)) {
370 return Lin_do_sum(n, x);
371 } else {
372 // Li2_do_sum practically doesn't converge near x == ±I
373 return Lin_do_sum_Xn(n, x);
374 }
375 } else {
376 cln::cl_N result = 0;
377 if ( x != 1 ) result = -cln::expt(cln::log(x), n-1) * cln::log(1-x) / cln::factorial(n-1);
378 for (int j=0; j<n-1; j++) {
379 result = result + (S_num(n-j-1, 1, 1) - S_num(1, n-j-1, 1-x))
380 * cln::expt(cln::log(x), j) / cln::factorial(j);
381 }
382 return result;
383 }
384 }
385}
386
387// helper function for classical polylog Li
388const cln::cl_N Lin_numeric(const int n, const cln::cl_N& x)
389{
390 if (n == 1) {
391 // just a log
392 return -cln::log(1-x);
393 }
394 if (zerop(x)) {
395 return 0;
396 }
397 if (x == 1) {
398 // [Kol] (2.22)
399 return cln::zeta(n);
400 }
401 else if (x == -1) {
402 // [Kol] (2.22)
403 return -(1-cln::expt(cln::cl_I(2),1-n)) * cln::zeta(n);
404 }
405 if (cln::abs(realpart(x)) < 0.4 && cln::abs(cln::abs(x)-1) < 0.01) {
406 cln::cl_N result = -cln::expt(cln::log(x), n-1) * cln::log(1-x) / cln::factorial(n-1);
407 for (int j=0; j<n-1; j++) {
408 result = result + (S_num(n-j-1, 1, 1) - S_num(1, n-j-1, 1-x))
409 * cln::expt(cln::log(x), j) / cln::factorial(j);
410 }
411 return result;
412 }
413
414 // what is the desired float format?
415 // first guess: default format
416 cln::float_format_t prec = cln::default_float_format;
417 const cln::cl_N value = x;
418 // second guess: the argument's format
419 if (!instanceof(realpart(x), cln::cl_RA_ring))
420 prec = cln::float_format(cln::the<cln::cl_F>(cln::realpart(value)));
421 else if (!instanceof(imagpart(x), cln::cl_RA_ring))
422 prec = cln::float_format(cln::the<cln::cl_F>(cln::imagpart(value)));
423
424 // [Kol] (5.15)
425 if (cln::abs(value) > 1) {
426 cln::cl_N result = -cln::expt(cln::log(-value),n) / cln::factorial(n);
427 // check if argument is complex. if it is real, the new polylog has to be conjugated.
428 if (cln::zerop(cln::imagpart(value))) {
429 if (n & 1) {
430 result = result + conjugate(Li_projection(n, cln::recip(value), prec));
431 }
432 else {
433 result = result - conjugate(Li_projection(n, cln::recip(value), prec));
434 }
435 }
436 else {
437 if (n & 1) {
438 result = result + Li_projection(n, cln::recip(value), prec);
439 }
440 else {
441 result = result - Li_projection(n, cln::recip(value), prec);
442 }
443 }
444 cln::cl_N add;
445 for (int j=0; j<n-1; j++) {
446 add = add + (1+cln::expt(cln::cl_I(-1),n-j)) * (1-cln::expt(cln::cl_I(2),1-n+j))
447 * Lin_numeric(n-j,1) * cln::expt(cln::log(-value),j) / cln::factorial(j);
448 }
449 result = result - add;
450 return result;
451 }
452 else {
453 return Li_projection(n, value, prec);
454 }
455}
456
457
458} // end of anonymous namespace
459
460
462//
463// Multiple polylogarithm Li(n,x)
464//
465// helper function
466//
468
469
470// anonymous namespace for helper function
471namespace {
472
473
474// performs the actual series summation for multiple polylogarithms
475cln::cl_N multipleLi_do_sum(const std::vector<int>& s, const std::vector<cln::cl_N>& x)
476{
477 // ensure all x <> 0.
478 for (const auto & it : x) {
479 if (it == 0) return cln::cl_float(0, cln::float_format(Digits));
480 }
481
482 const int j = s.size();
483 bool flag_accidental_zero = false;
484
485 std::vector<cln::cl_N> t(j);
486 cln::cl_F one = cln::cl_float(1, cln::float_format(Digits));
487
488 cln::cl_N t0buf;
489 int q = 0;
490 do {
491 t0buf = t[0];
492 q++;
493 t[j-1] = t[j-1] + cln::expt(x[j-1], q) / cln::expt(cln::cl_I(q),s[j-1]) * one;
494 for (int k=j-2; k>=0; k--) {
495 t[k] = t[k] + t[k+1] * cln::expt(x[k], q+j-1-k) / cln::expt(cln::cl_I(q+j-1-k), s[k]);
496 }
497 q++;
498 t[j-1] = t[j-1] + cln::expt(x[j-1], q) / cln::expt(cln::cl_I(q),s[j-1]) * one;
499 for (int k=j-2; k>=0; k--) {
500 flag_accidental_zero = cln::zerop(t[k+1]);
501 t[k] = t[k] + t[k+1] * cln::expt(x[k], q+j-1-k) / cln::expt(cln::cl_I(q+j-1-k), s[k]);
502 }
503 } while ( (t[0] != t0buf) || cln::zerop(t[0]) || flag_accidental_zero );
504
505 return t[0];
506}
507
508
509// forward declaration for Li_eval()
510lst convert_parameter_Li_to_H(const lst& m, const lst& x, ex& pf);
511
512
513// type used by the transformation functions for G
514typedef std::vector<int> Gparameter;
515
516
517// G_eval1-function for G transformations
518ex G_eval1(int a, int scale, const exvector& gsyms)
519{
520 if (a != 0) {
521 const ex& scs = gsyms[std::abs(scale)];
522 const ex& as = gsyms[std::abs(a)];
523 if (as != scs) {
524 return -log(1 - scs/as);
525 } else {
526 return -zeta(1);
527 }
528 } else {
529 return log(gsyms[std::abs(scale)]);
530 }
531}
532
533
534// G_eval-function for G transformations
535ex G_eval(const Gparameter& a, int scale, const exvector& gsyms)
536{
537 // check for properties of G
538 ex sc = gsyms[std::abs(scale)];
539 lst newa;
540 bool all_zero = true;
541 bool all_ones = true;
542 int count_ones = 0;
543 for (const auto & it : a) {
544 if (it != 0) {
545 const ex sym = gsyms[std::abs(it)];
546 newa.append(sym);
547 all_zero = false;
548 if (sym != sc) {
549 all_ones = false;
550 }
551 if (all_ones) {
552 ++count_ones;
553 }
554 } else {
555 all_ones = false;
556 }
557 }
558
559 // care about divergent G: shuffle to separate divergencies that will be canceled
560 // later on in the transformation
561 if (newa.nops() > 1 && newa.op(0) == sc && !all_ones && a.front()!=0) {
562 // do shuffle
563 Gparameter short_a(a.begin()+1, a.end());
564 ex result = G_eval1(a.front(), scale, gsyms) * G_eval(short_a, scale, gsyms);
565
566 auto it = short_a.begin();
567 advance(it, count_ones-1);
568 for (; it != short_a.end(); ++it) {
569
570 Gparameter newa(short_a.begin(), it);
571 newa.push_back(*it);
572 newa.push_back(a[0]);
573 newa.insert(newa.end(), it+1, short_a.end());
574 result -= G_eval(newa, scale, gsyms);
575 }
576 return result / count_ones;
577 }
578
579 // G({1,...,1};y) -> G({1};y)^k / k!
580 if (all_ones && a.size() > 1) {
581 return pow(G_eval1(a.front(),scale, gsyms), count_ones) / factorial(count_ones);
582 }
583
584 // G({0,...,0};y) -> log(y)^k / k!
585 if (all_zero) {
586 return pow(log(gsyms[std::abs(scale)]), a.size()) / factorial(a.size());
587 }
588
589 // no special cases anymore -> convert it into Li
590 lst m;
591 lst x;
592 ex argbuf = gsyms[std::abs(scale)];
593 ex mval = _ex1;
594 for (const auto & it : a) {
595 if (it != 0) {
596 const ex& sym = gsyms[std::abs(it)];
597 x.append(argbuf / sym);
598 m.append(mval);
599 mval = _ex1;
600 argbuf = sym;
601 } else {
602 ++mval;
603 }
604 }
605 return pow(-1, x.nops()) * Li(m, x);
606}
607
608// convert back to standard G-function, keep information on small imaginary parts
609ex G_eval_to_G(const Gparameter& a, int scale, const exvector& gsyms)
610{
611 lst z;
612 lst s;
613 for (const auto & it : a) {
614 if (it != 0) {
615 z.append(gsyms[std::abs(it)]);
616 if ( it<0 ) {
617 s.append(-1);
618 } else {
619 s.append(1);
620 }
621 } else {
622 z.append(0);
623 s.append(1);
624 }
625 }
626 return G(z,s,gsyms[std::abs(scale)]);
627}
628
629
630// converts data for G: pending_integrals -> a
631Gparameter convert_pending_integrals_G(const Gparameter& pending_integrals)
632{
633 GINAC_ASSERT(pending_integrals.size() != 1);
634
635 if (pending_integrals.size() > 0) {
636 // get rid of the first element, which would stand for the new upper limit
637 Gparameter new_a(pending_integrals.begin()+1, pending_integrals.end());
638 return new_a;
639 } else {
640 // just return empty parameter list
641 Gparameter new_a;
642 return new_a;
643 }
644}
645
646
647// check the parameters a and scale for G and return information about convergence, depth, etc.
648// convergent : true if G(a,scale) is convergent
649// depth : depth of G(a,scale)
650// trailing_zeros : number of trailing zeros of a
651// min_it : iterator of a pointing on the smallest element in a
652Gparameter::const_iterator check_parameter_G(const Gparameter& a, int scale,
653 bool& convergent, int& depth, int& trailing_zeros, Gparameter::const_iterator& min_it)
654{
655 convergent = true;
656 depth = 0;
657 trailing_zeros = 0;
658 min_it = a.end();
659 auto lastnonzero = a.end();
660 for (auto it = a.begin(); it != a.end(); ++it) {
661 if (std::abs(*it) > 0) {
662 ++depth;
663 trailing_zeros = 0;
664 lastnonzero = it;
665 if (std::abs(*it) < scale) {
666 convergent = false;
667 if ((min_it == a.end()) || (std::abs(*it) < std::abs(*min_it))) {
668 min_it = it;
669 }
670 }
671 } else {
672 ++trailing_zeros;
673 }
674 }
675 if (lastnonzero == a.end())
676 return a.end();
677 return ++lastnonzero;
678}
679
680
681// add scale to pending_integrals if pending_integrals is empty
682Gparameter prepare_pending_integrals(const Gparameter& pending_integrals, int scale)
683{
684 GINAC_ASSERT(pending_integrals.size() != 1);
685
686 if (pending_integrals.size() > 0) {
687 return pending_integrals;
688 } else {
689 Gparameter new_pending_integrals;
690 new_pending_integrals.push_back(scale);
691 return new_pending_integrals;
692 }
693}
694
695
696// handles trailing zeroes for an otherwise convergent integral
697ex trailing_zeros_G(const Gparameter& a, int scale, const exvector& gsyms)
698{
699 bool convergent;
700 int depth, trailing_zeros;
701 Gparameter::const_iterator last, dummyit;
702 last = check_parameter_G(a, scale, convergent, depth, trailing_zeros, dummyit);
703
704 GINAC_ASSERT(convergent);
705
706 if ((trailing_zeros > 0) && (depth > 0)) {
707 ex result;
708 Gparameter new_a(a.begin(), a.end()-1);
709 result += G_eval1(0, scale, gsyms) * trailing_zeros_G(new_a, scale, gsyms);
710 for (auto it = a.begin(); it != last; ++it) {
711 Gparameter new_a(a.begin(), it);
712 new_a.push_back(0);
713 new_a.insert(new_a.end(), it, a.end()-1);
714 result -= trailing_zeros_G(new_a, scale, gsyms);
715 }
716
717 return result / trailing_zeros;
718 } else {
719 return G_eval(a, scale, gsyms);
720 }
721}
722
723
724// G transformation [VSW] (57),(58)
725ex depth_one_trafo_G(const Gparameter& pending_integrals, const Gparameter& a, int scale, const exvector& gsyms)
726{
727 // pendint = ( y1, b1, ..., br )
728 // a = ( 0, ..., 0, amin )
729 // scale = y2
730 //
731 // int_0^y1 ds1/(s1-b1) ... int dsr/(sr-br) G(0, ..., 0, sr; y2)
732 // where sr replaces amin
733
734 GINAC_ASSERT(a.back() != 0);
735 GINAC_ASSERT(a.size() > 0);
736
737 ex result;
738 Gparameter new_pending_integrals = prepare_pending_integrals(pending_integrals, std::abs(a.back()));
739 const int psize = pending_integrals.size();
740
741 // length == 1
742 // G(sr_{+-}; y2 ) = G(y2_{-+}; sr) - G(0; sr) + ln(-y2_{-+})
743
744 if (a.size() == 1) {
745
746 // ln(-y2_{-+})
747 result += log(gsyms[ex_to<numeric>(scale).to_int()]);
748 if (a.back() > 0) {
749 new_pending_integrals.push_back(-scale);
750 result += I*Pi;
751 } else {
752 new_pending_integrals.push_back(scale);
753 result -= I*Pi;
754 }
755 if (psize) {
756 result *= trailing_zeros_G(convert_pending_integrals_G(pending_integrals),
757 pending_integrals.front(),
758 gsyms);
759 }
760
761 // G(y2_{-+}; sr)
762 result += trailing_zeros_G(convert_pending_integrals_G(new_pending_integrals),
763 new_pending_integrals.front(),
764 gsyms);
765
766 // G(0; sr)
767 new_pending_integrals.back() = 0;
768 result -= trailing_zeros_G(convert_pending_integrals_G(new_pending_integrals),
769 new_pending_integrals.front(),
770 gsyms);
771
772 return result;
773 }
774
775 // length > 1
776 // G_m(sr_{+-}; y2) = -zeta_m + int_0^y2 dt/t G_{m-1}( (1/y2)_{+-}; 1/t )
777 // - int_0^sr dt/t G_{m-1}( (1/y2)_{+-}; 1/t )
778
779 //term zeta_m
780 result -= zeta(a.size());
781 if (psize) {
782 result *= trailing_zeros_G(convert_pending_integrals_G(pending_integrals),
783 pending_integrals.front(),
784 gsyms);
785 }
786
787 // term int_0^sr dt/t G_{m-1}( (1/y2)_{+-}; 1/t )
788 // = int_0^sr dt/t G_{m-1}( t_{+-}; y2 )
789 Gparameter new_a(a.begin()+1, a.end());
790 new_pending_integrals.push_back(0);
791 result -= depth_one_trafo_G(new_pending_integrals, new_a, scale, gsyms);
792
793 // term int_0^y2 dt/t G_{m-1}( (1/y2)_{+-}; 1/t )
794 // = int_0^y2 dt/t G_{m-1}( t_{+-}; y2 )
795 Gparameter new_pending_integrals_2;
796 new_pending_integrals_2.push_back(scale);
797 new_pending_integrals_2.push_back(0);
798 if (psize) {
799 result += trailing_zeros_G(convert_pending_integrals_G(pending_integrals),
800 pending_integrals.front(),
801 gsyms)
802 * depth_one_trafo_G(new_pending_integrals_2, new_a, scale, gsyms);
803 } else {
804 result += depth_one_trafo_G(new_pending_integrals_2, new_a, scale, gsyms);
805 }
806
807 return result;
808}
809
810
811// forward declaration
812ex shuffle_G(const Gparameter & a0, const Gparameter & a1, const Gparameter & a2,
813 const Gparameter& pendint, const Gparameter& a_old, int scale,
814 const exvector& gsyms, bool flag_trailing_zeros_only);
815
816
817// G transformation [VSW]
818ex G_transform(const Gparameter& pendint, const Gparameter& a, int scale,
819 const exvector& gsyms, bool flag_trailing_zeros_only)
820{
821 // main recursion routine
822 //
823 // pendint = ( y1, b1, ..., br )
824 // a = ( a1, ..., amin, ..., aw )
825 // scale = y2
826 //
827 // int_0^y1 ds1/(s1-b1) ... int dsr/(sr-br) G(a1,...,sr,...,aw,y2)
828 // where sr replaces amin
829
830 // find smallest alpha, determine depth and trailing zeros, and check for convergence
831 bool convergent;
832 int depth, trailing_zeros;
833 Gparameter::const_iterator min_it;
834 auto firstzero = check_parameter_G(a, scale, convergent, depth, trailing_zeros, min_it);
835 int min_it_pos = distance(a.begin(), min_it);
836
837 // special case: all a's are zero
838 if (depth == 0) {
839 ex result;
840
841 if (a.size() == 0) {
842 result = 1;
843 } else {
844 result = G_eval(a, scale, gsyms);
845 }
846 if (pendint.size() > 0) {
847 result *= trailing_zeros_G(convert_pending_integrals_G(pendint),
848 pendint.front(),
849 gsyms);
850 }
851 return result;
852 }
853
854 // handle trailing zeros
855 if (trailing_zeros > 0) {
856 ex result;
857 Gparameter new_a(a.begin(), a.end()-1);
858 result += G_eval1(0, scale, gsyms) * G_transform(pendint, new_a, scale, gsyms, flag_trailing_zeros_only);
859 for (auto it = a.begin(); it != firstzero; ++it) {
860 Gparameter new_a(a.begin(), it);
861 new_a.push_back(0);
862 new_a.insert(new_a.end(), it, a.end()-1);
863 result -= G_transform(pendint, new_a, scale, gsyms, flag_trailing_zeros_only);
864 }
865 return result / trailing_zeros;
866 }
867
868 // flag_trailing_zeros_only: in this case we don't have pending integrals
869 if (flag_trailing_zeros_only)
870 return G_eval_to_G(a, scale, gsyms);
871
872 // convergence case
873 if (convergent) {
874 if (pendint.size() > 0) {
875 return G_eval(convert_pending_integrals_G(pendint),
876 pendint.front(), gsyms) *
877 G_eval(a, scale, gsyms);
878 } else {
879 return G_eval(a, scale, gsyms);
880 }
881 }
882
883 // call basic transformation for depth equal one
884 if (depth == 1) {
885 return depth_one_trafo_G(pendint, a, scale, gsyms);
886 }
887
888 // do recursion
889 // int_0^y1 ds1/(s1-b1) ... int dsr/(sr-br) G(a1,...,sr,...,aw,y2)
890 // = int_0^y1 ds1/(s1-b1) ... int dsr/(sr-br) G(a1,...,0,...,aw,y2)
891 // + 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)
892
893 // smallest element in last place
894 if (min_it + 1 == a.end()) {
895 do { --min_it; } while (*min_it == 0);
896 Gparameter empty;
897 Gparameter a1(a.begin(),min_it+1);
898 Gparameter a2(min_it+1,a.end());
899
900 ex result = G_transform(pendint, a2, scale, gsyms, flag_trailing_zeros_only)*
901 G_transform(empty, a1, scale, gsyms, flag_trailing_zeros_only);
902
903 result -= shuffle_G(empty, a1, a2, pendint, a, scale, gsyms, flag_trailing_zeros_only);
904 return result;
905 }
906
907 Gparameter empty;
908 Gparameter::iterator changeit;
909
910 // first term G(a_1,..,0,...,a_w;a_0)
911 Gparameter new_pendint = prepare_pending_integrals(pendint, a[min_it_pos]);
912 Gparameter new_a = a;
913 new_a[min_it_pos] = 0;
914 ex result = G_transform(empty, new_a, scale, gsyms, flag_trailing_zeros_only);
915 if (pendint.size() > 0) {
916 result *= trailing_zeros_G(convert_pending_integrals_G(pendint),
917 pendint.front(), gsyms);
918 }
919
920 // other terms
921 changeit = new_a.begin() + min_it_pos;
922 changeit = new_a.erase(changeit);
923 if (changeit != new_a.begin()) {
924 // smallest in the middle
925 new_pendint.push_back(*changeit);
926 result -= trailing_zeros_G(convert_pending_integrals_G(new_pendint),
927 new_pendint.front(), gsyms)*
928 G_transform(empty, new_a, scale, gsyms, flag_trailing_zeros_only);
929 int buffer = *changeit;
930 *changeit = *min_it;
931 result += G_transform(new_pendint, new_a, scale, gsyms, flag_trailing_zeros_only);
932 *changeit = buffer;
933 new_pendint.pop_back();
934 --changeit;
935 new_pendint.push_back(*changeit);
936 result += trailing_zeros_G(convert_pending_integrals_G(new_pendint),
937 new_pendint.front(), gsyms)*
938 G_transform(empty, new_a, scale, gsyms, flag_trailing_zeros_only);
939 *changeit = *min_it;
940 result -= G_transform(new_pendint, new_a, scale, gsyms, flag_trailing_zeros_only);
941 } else {
942 // smallest at the front
943 new_pendint.push_back(scale);
944 result += trailing_zeros_G(convert_pending_integrals_G(new_pendint),
945 new_pendint.front(), gsyms)*
946 G_transform(empty, new_a, scale, gsyms, flag_trailing_zeros_only);
947 new_pendint.back() = *changeit;
948 result -= trailing_zeros_G(convert_pending_integrals_G(new_pendint),
949 new_pendint.front(), gsyms)*
950 G_transform(empty, new_a, scale, gsyms, flag_trailing_zeros_only);
951 *changeit = *min_it;
952 result += G_transform(new_pendint, new_a, scale, gsyms, flag_trailing_zeros_only);
953 }
954 return result;
955}
956
957
958// shuffles the two parameter list a1 and a2 and calls G_transform for every term except
959// for the one that is equal to a_old
960ex shuffle_G(const Gparameter & a0, const Gparameter & a1, const Gparameter & a2,
961 const Gparameter& pendint, const Gparameter& a_old, int scale,
962 const exvector& gsyms, bool flag_trailing_zeros_only)
963{
964 if (a1.size()==0 && a2.size()==0) {
965 // veto the one configuration we don't want
966 if ( a0 == a_old ) return 0;
967
968 return G_transform(pendint, a0, scale, gsyms, flag_trailing_zeros_only);
969 }
970
971 if (a2.size()==0) {
972 Gparameter empty;
973 Gparameter aa0 = a0;
974 aa0.insert(aa0.end(),a1.begin(),a1.end());
975 return shuffle_G(aa0, empty, empty, pendint, a_old, scale, gsyms, flag_trailing_zeros_only);
976 }
977
978 if (a1.size()==0) {
979 Gparameter empty;
980 Gparameter aa0 = a0;
981 aa0.insert(aa0.end(),a2.begin(),a2.end());
982 return shuffle_G(aa0, empty, empty, pendint, a_old, scale, gsyms, flag_trailing_zeros_only);
983 }
984
985 Gparameter a1_removed(a1.begin()+1,a1.end());
986 Gparameter a2_removed(a2.begin()+1,a2.end());
987
988 Gparameter a01 = a0;
989 Gparameter a02 = a0;
990
991 a01.push_back( a1[0] );
992 a02.push_back( a2[0] );
993
994 return shuffle_G(a01, a1_removed, a2, pendint, a_old, scale, gsyms, flag_trailing_zeros_only)
995 + shuffle_G(a02, a1, a2_removed, pendint, a_old, scale, gsyms, flag_trailing_zeros_only);
996}
997
998// handles the transformations and the numerical evaluation of G
999// the parameter x, s and y must only contain numerics
1000static cln::cl_N
1001G_numeric(const std::vector<cln::cl_N>& x, const std::vector<int>& s,
1002 const cln::cl_N& y);
1003
1004// do acceleration transformation (hoelder convolution [BBB])
1005// the parameter x, s and y must only contain numerics
1006static cln::cl_N
1007G_do_hoelder(std::vector<cln::cl_N> x, /* yes, it's passed by value */
1008 const std::vector<int>& s, const cln::cl_N& y)
1009{
1010 cln::cl_N result;
1011 const std::size_t size = x.size();
1012 for (std::size_t i = 0; i < size; ++i)
1013 x[i] = x[i]/y;
1014
1015 // 24.03.2021: this block can be outside the loop over r
1016 cln::cl_RA p(2);
1017 bool adjustp;
1018 do {
1019 adjustp = false;
1020 for (std::size_t i = 0; i < size; ++i) {
1021 // 24.03.2021: replaced (x[i] == cln::cl_RA(1)/p) by (cln::zerop(x[i] - cln::cl_RA(1)/p)
1022 // in the case where we compare a float with a rational, CLN behaves differently in the two versions
1023 if (cln::zerop(x[i] - cln::cl_RA(1)/p) ) {
1024 p = p/2 + cln::cl_RA(3)/2;
1025 adjustp = true;
1026 continue;
1027 }
1028 }
1029 } while (adjustp);
1030 cln::cl_RA q = p/(p-1);
1031
1032 for (std::size_t r = 0; r <= size; ++r) {
1033 cln::cl_N buffer(1 & r ? -1 : 1);
1034 std::vector<cln::cl_N> qlstx;
1035 std::vector<int> qlsts;
1036 for (std::size_t j = r; j >= 1; --j) {
1037 qlstx.push_back(cln::cl_N(1) - x[j-1]);
1038 if (imagpart(x[j-1])==0 && realpart(x[j-1]) >= 1) {
1039 qlsts.push_back(1);
1040 } else {
1041 qlsts.push_back(-s[j-1]);
1042 }
1043 }
1044 if (qlstx.size() > 0) {
1045 buffer = buffer*G_numeric(qlstx, qlsts, 1/q);
1046 }
1047 std::vector<cln::cl_N> plstx;
1048 std::vector<int> plsts;
1049 for (std::size_t j = r+1; j <= size; ++j) {
1050 plstx.push_back(x[j-1]);
1051 plsts.push_back(s[j-1]);
1052 }
1053 if (plstx.size() > 0) {
1054 buffer = buffer*G_numeric(plstx, plsts, 1/p);
1055 }
1056 result = result + buffer;
1057 }
1058 return result;
1059}
1060
1061class less_object_for_cl_N
1062{
1063public:
1064 bool operator() (const cln::cl_N & a, const cln::cl_N & b) const
1065 {
1066 // absolute value?
1067 if (abs(a) != abs(b))
1068 return (abs(a) < abs(b)) ? true : false;
1069
1070 // complex phase?
1071 if (phase(a) != phase(b))
1072 return (phase(a) < phase(b)) ? true : false;
1073
1074 // equal, therefore "less" is not true
1075 return false;
1076 }
1077};
1078
1079
1080// convergence transformation, used for numerical evaluation of G function.
1081// the parameter x, s and y must only contain numerics
1082static cln::cl_N
1083G_do_trafo(const std::vector<cln::cl_N>& x, const std::vector<int>& s,
1084 const cln::cl_N& y, bool flag_trailing_zeros_only)
1085{
1086 // sort (|x|<->position) to determine indices
1087 typedef std::multimap<cln::cl_N, std::size_t, less_object_for_cl_N> sortmap_t;
1088 sortmap_t sortmap;
1089 std::size_t size = 0;
1090 for (std::size_t i = 0; i < x.size(); ++i) {
1091 if (!zerop(x[i])) {
1092 sortmap.insert(std::make_pair(x[i], i));
1093 ++size;
1094 }
1095 }
1096 // include upper limit (scale)
1097 sortmap.insert(std::make_pair(y, x.size()));
1098
1099 // generate missing dummy-symbols
1100 int i = 1;
1101 // holding dummy-symbols for the G/Li transformations
1102 exvector gsyms;
1103 gsyms.push_back(symbol("GSYMS_ERROR"));
1104 cln::cl_N lastentry(0);
1105 for (sortmap_t::const_iterator it = sortmap.begin(); it != sortmap.end(); ++it) {
1106 if (it != sortmap.begin()) {
1107 if (it->second < x.size()) {
1108 if (x[it->second] == lastentry) {
1109 gsyms.push_back(gsyms.back());
1110 continue;
1111 }
1112 } else {
1113 if (y == lastentry) {
1114 gsyms.push_back(gsyms.back());
1115 continue;
1116 }
1117 }
1118 }
1119 std::ostringstream os;
1120 os << "a" << i;
1121 gsyms.push_back(symbol(os.str()));
1122 ++i;
1123 if (it->second < x.size()) {
1124 lastentry = x[it->second];
1125 } else {
1126 lastentry = y;
1127 }
1128 }
1129
1130 // fill position data according to sorted indices and prepare substitution list
1131 Gparameter a(x.size());
1132 exmap subslst;
1133 std::size_t pos = 1;
1134 int scale = pos;
1135 for (sortmap_t::const_iterator it = sortmap.begin(); it != sortmap.end(); ++it) {
1136 if (it->second < x.size()) {
1137 if (s[it->second] > 0) {
1138 a[it->second] = pos;
1139 } else {
1140 a[it->second] = -int(pos);
1141 }
1142 subslst[gsyms[pos]] = numeric(x[it->second]);
1143 } else {
1144 scale = pos;
1145 subslst[gsyms[pos]] = numeric(y);
1146 }
1147 ++pos;
1148 }
1149
1150 // do transformation
1151 Gparameter pendint;
1152 ex result = G_transform(pendint, a, scale, gsyms, flag_trailing_zeros_only);
1153 // replace dummy symbols with their values
1154 result = result.expand();
1155 result = result.subs(subslst).evalf();
1156 if (!is_a<numeric>(result))
1157 throw std::logic_error("G_do_trafo: G_transform returned non-numeric result");
1158
1159 cln::cl_N ret = ex_to<numeric>(result).to_cl_N();
1160 return ret;
1161}
1162
1163// handles the transformations and the numerical evaluation of G
1164// the parameter x, s and y must only contain numerics
1165static cln::cl_N
1166G_numeric(const std::vector<cln::cl_N>& x, const std::vector<int>& s,
1167 const cln::cl_N& y)
1168{
1169 // check for convergence and necessary accelerations
1170 bool need_trafo = false;
1171 bool need_hoelder = false;
1172 bool have_trailing_zero = false;
1173 std::size_t depth = 0;
1174 for (auto & xi : x) {
1175 if (!zerop(xi)) {
1176 ++depth;
1177 const cln::cl_N x_y = abs(xi) - y;
1178 if (instanceof(x_y, cln::cl_R_ring) &&
1179 realpart(x_y) < cln::least_negative_float(cln::float_format(Digits - 2)))
1180 need_trafo = true;
1181
1182 if (abs(abs(xi/y) - 1) < 0.01)
1183 need_hoelder = true;
1184 }
1185 }
1186 if (zerop(x.back())) {
1187 have_trailing_zero = true;
1188 need_trafo = true;
1189 }
1190
1191 if (depth == 1 && x.size() == 2 && !need_trafo)
1192 return - Li_projection(2, y/x[1], cln::float_format(Digits));
1193
1194 // do acceleration transformation (hoelder convolution [BBB])
1195 if (need_hoelder && !have_trailing_zero)
1196 return G_do_hoelder(x, s, y);
1197
1198 // convergence transformation
1199 if (need_trafo)
1200 return G_do_trafo(x, s, y, have_trailing_zero);
1201
1202 // do summation
1203 std::vector<cln::cl_N> newx;
1204 newx.reserve(x.size());
1205 std::vector<int> m;
1206 m.reserve(x.size());
1207 int mcount = 1;
1208 int sign = 1;
1209 cln::cl_N factor = y;
1210 for (auto & xi : x) {
1211 if (zerop(xi)) {
1212 ++mcount;
1213 } else {
1214 newx.push_back(factor/xi);
1215 factor = xi;
1216 m.push_back(mcount);
1217 mcount = 1;
1218 sign = -sign;
1219 }
1220 }
1221
1222 return sign*multipleLi_do_sum(m, newx);
1223}
1224
1225
1226ex mLi_numeric(const lst& m, const lst& x)
1227{
1228 // let G_numeric do the transformation
1229 std::vector<cln::cl_N> newx;
1230 newx.reserve(x.nops());
1231 std::vector<int> s;
1232 s.reserve(x.nops());
1233 cln::cl_N factor(1);
1234 for (auto itm = m.begin(), itx = x.begin(); itm != m.end(); ++itm, ++itx) {
1235 for (int i = 1; i < *itm; ++i) {
1236 newx.push_back(cln::cl_N(0));
1237 s.push_back(1);
1238 }
1239 const cln::cl_N xi = ex_to<numeric>(*itx).to_cl_N();
1240 factor = factor/xi;
1241 newx.push_back(factor);
1242 if ( !instanceof(factor, cln::cl_R_ring) && imagpart(factor) < 0 ) {
1243 s.push_back(-1);
1244 }
1245 else {
1246 s.push_back(1);
1247 }
1248 }
1249 return numeric(cln::cl_N(1 & m.nops() ? - 1 : 1)*G_numeric(newx, s, cln::cl_N(1)));
1250}
1251
1252
1253} // end of anonymous namespace
1254
1255
1257//
1258// Generalized multiple polylogarithm G(x, y) and G(x, s, y)
1259//
1260// GiNaC function
1261//
1263
1264
1265static ex G2_evalf(const ex& x_, const ex& y)
1266{
1268 return G(x_, y).hold();
1269 }
1270 lst x = is_a<lst>(x_) ? ex_to<lst>(x_) : lst{x_};
1271 if (x.nops() == 0) {
1272 return _ex1;
1273 }
1274 if (x.op(0) == y) {
1275 return G(x_, y).hold();
1276 }
1277 std::vector<int> s;
1278 s.reserve(x.nops());
1279 bool all_zero = true;
1280 for (const auto & it : x) {
1281 if (!it.info(info_flags::numeric)) {
1282 return G(x_, y).hold();
1283 }
1284 if (it != _ex0) {
1285 all_zero = false;
1286 }
1287 if ( !ex_to<numeric>(it).is_real() && ex_to<numeric>(it).imag() < 0 ) {
1288 s.push_back(-1);
1289 }
1290 else {
1291 s.push_back(1);
1292 }
1293 }
1294 if (all_zero) {
1295 return pow(log(y), x.nops()) / factorial(x.nops());
1296 }
1297 std::vector<cln::cl_N> xv;
1298 xv.reserve(x.nops());
1299 for (const auto & it : x)
1300 xv.push_back(ex_to<numeric>(it).to_cl_N());
1301 cln::cl_N result = G_numeric(xv, s, ex_to<numeric>(y).to_cl_N());
1302 return numeric(result);
1303}
1304
1305
1306static ex G2_eval(const ex& x_, const ex& y)
1307{
1308 //TODO eval to MZV or H or S or Lin
1309
1311 return G(x_, y).hold();
1312 }
1313 lst x = is_a<lst>(x_) ? ex_to<lst>(x_) : lst{x_};
1314 if (x.nops() == 0) {
1315 return _ex1;
1316 }
1317 if (x.op(0) == y) {
1318 return G(x_, y).hold();
1319 }
1320 std::vector<int> s;
1321 s.reserve(x.nops());
1322 bool all_zero = true;
1323 bool crational = true;
1324 for (const auto & it : x) {
1325 if (!it.info(info_flags::numeric)) {
1326 return G(x_, y).hold();
1327 }
1328 if (!it.info(info_flags::crational)) {
1329 crational = false;
1330 }
1331 if (it != _ex0) {
1332 all_zero = false;
1333 }
1334 if ( !ex_to<numeric>(it).is_real() && ex_to<numeric>(it).imag() < 0 ) {
1335 s.push_back(-1);
1336 }
1337 else {
1338 s.push_back(+1);
1339 }
1340 }
1341 if (all_zero) {
1342 return pow(log(y), x.nops()) / factorial(x.nops());
1343 }
1344 if (!y.info(info_flags::crational)) {
1345 crational = false;
1346 }
1347 if (crational) {
1348 return G(x_, y).hold();
1349 }
1350 std::vector<cln::cl_N> xv;
1351 xv.reserve(x.nops());
1352 for (const auto & it : x)
1353 xv.push_back(ex_to<numeric>(it).to_cl_N());
1354 cln::cl_N result = G_numeric(xv, s, ex_to<numeric>(y).to_cl_N());
1355 return numeric(result);
1356}
1357
1358
1359// option do_not_evalf_params() removed.
1360unsigned G2_SERIAL::serial = function::register_new(function_options("G", 2).
1361 evalf_func(G2_evalf).
1362 eval_func(G2_eval).
1363 overloaded(2));
1364//TODO
1365// derivative_func(G2_deriv).
1366// print_func<print_latex>(G2_print_latex).
1367
1368
1369static ex G3_evalf(const ex& x_, const ex& s_, const ex& y)
1370{
1372 return G(x_, s_, y).hold();
1373 }
1374 lst x = is_a<lst>(x_) ? ex_to<lst>(x_) : lst{x_};
1375 lst s = is_a<lst>(s_) ? ex_to<lst>(s_) : lst{s_};
1376 if (x.nops() != s.nops()) {
1377 return G(x_, s_, y).hold();
1378 }
1379 if (x.nops() == 0) {
1380 return _ex1;
1381 }
1382 if (x.op(0) == y) {
1383 return G(x_, s_, y).hold();
1384 }
1385 std::vector<int> sn;
1386 sn.reserve(s.nops());
1387 bool all_zero = true;
1388 for (auto itx = x.begin(), its = s.begin(); itx != x.end(); ++itx, ++its) {
1389 if (!(*itx).info(info_flags::numeric)) {
1390 return G(x_, y).hold();
1391 }
1392 if (!(*its).info(info_flags::real)) {
1393 return G(x_, y).hold();
1394 }
1395 if (*itx != _ex0) {
1396 all_zero = false;
1397 }
1398 if ( ex_to<numeric>(*itx).is_real() ) {
1399 if ( ex_to<numeric>(*itx).is_positive() ) {
1400 if ( *its >= 0 ) {
1401 sn.push_back(1);
1402 }
1403 else {
1404 sn.push_back(-1);
1405 }
1406 } else {
1407 sn.push_back(1);
1408 }
1409 }
1410 else {
1411 if ( ex_to<numeric>(*itx).imag() > 0 ) {
1412 sn.push_back(1);
1413 }
1414 else {
1415 sn.push_back(-1);
1416 }
1417 }
1418 }
1419 if (all_zero) {
1420 return pow(log(y), x.nops()) / factorial(x.nops());
1421 }
1422 std::vector<cln::cl_N> xn;
1423 xn.reserve(x.nops());
1424 for (const auto & it : x)
1425 xn.push_back(ex_to<numeric>(it).to_cl_N());
1426 cln::cl_N result = G_numeric(xn, sn, ex_to<numeric>(y).to_cl_N());
1427 return numeric(result);
1428}
1429
1430
1431static ex G3_eval(const ex& x_, const ex& s_, const ex& y)
1432{
1433 //TODO eval to MZV or H or S or Lin
1434
1436 return G(x_, s_, y).hold();
1437 }
1438 lst x = is_a<lst>(x_) ? ex_to<lst>(x_) : lst{x_};
1439 lst s = is_a<lst>(s_) ? ex_to<lst>(s_) : lst{s_};
1440 if (x.nops() != s.nops()) {
1441 return G(x_, s_, y).hold();
1442 }
1443 if (x.nops() == 0) {
1444 return _ex1;
1445 }
1446 if (x.op(0) == y) {
1447 return G(x_, s_, y).hold();
1448 }
1449 std::vector<int> sn;
1450 sn.reserve(s.nops());
1451 bool all_zero = true;
1452 bool crational = true;
1453 for (auto itx = x.begin(), its = s.begin(); itx != x.end(); ++itx, ++its) {
1454 if (!(*itx).info(info_flags::numeric)) {
1455 return G(x_, s_, y).hold();
1456 }
1457 if (!(*its).info(info_flags::real)) {
1458 return G(x_, s_, y).hold();
1459 }
1460 if (!(*itx).info(info_flags::crational)) {
1461 crational = false;
1462 }
1463 if (*itx != _ex0) {
1464 all_zero = false;
1465 }
1466 if ( ex_to<numeric>(*itx).is_real() ) {
1467 if ( ex_to<numeric>(*itx).is_positive() ) {
1468 if ( *its >= 0 ) {
1469 sn.push_back(1);
1470 }
1471 else {
1472 sn.push_back(-1);
1473 }
1474 } else {
1475 sn.push_back(1);
1476 }
1477 }
1478 else {
1479 if ( ex_to<numeric>(*itx).imag() > 0 ) {
1480 sn.push_back(1);
1481 }
1482 else {
1483 sn.push_back(-1);
1484 }
1485 }
1486 }
1487 if (all_zero) {
1488 return pow(log(y), x.nops()) / factorial(x.nops());
1489 }
1490 if (!y.info(info_flags::crational)) {
1491 crational = false;
1492 }
1493 if (crational) {
1494 return G(x_, s_, y).hold();
1495 }
1496 std::vector<cln::cl_N> xn;
1497 xn.reserve(x.nops());
1498 for (const auto & it : x)
1499 xn.push_back(ex_to<numeric>(it).to_cl_N());
1500 cln::cl_N result = G_numeric(xn, sn, ex_to<numeric>(y).to_cl_N());
1501 return numeric(result);
1502}
1503
1504
1505// option do_not_evalf_params() removed.
1506// This is safe: in the code above it only matters if s_ > 0 or s_ < 0,
1507// s_ is allowed to be of floating type.
1508unsigned G3_SERIAL::serial = function::register_new(function_options("G", 3).
1509 evalf_func(G3_evalf).
1510 eval_func(G3_eval).
1511 overloaded(2));
1512//TODO
1513// derivative_func(G3_deriv).
1514// print_func<print_latex>(G3_print_latex).
1515
1516
1518//
1519// Classical polylogarithm and multiple polylogarithm Li(m,x)
1520//
1521// GiNaC function
1522//
1524
1525
1526static ex Li_evalf(const ex& m_, const ex& x_)
1527{
1528 // classical polylogs
1529 if (m_.info(info_flags::posint)) {
1530 if (x_.info(info_flags::numeric)) {
1531 int m__ = ex_to<numeric>(m_).to_int();
1532 const cln::cl_N x__ = ex_to<numeric>(x_).to_cl_N();
1533 const cln::cl_N result = Lin_numeric(m__, x__);
1534 return numeric(result);
1535 } else {
1536 // try to numerically evaluate second argument
1537 ex x_val = x_.evalf();
1538 if (x_val.info(info_flags::numeric)) {
1539 int m__ = ex_to<numeric>(m_).to_int();
1540 const cln::cl_N x__ = ex_to<numeric>(x_val).to_cl_N();
1541 const cln::cl_N result = Lin_numeric(m__, x__);
1542 return numeric(result);
1543 }
1544 }
1545 }
1546 // multiple polylogs
1547 if (is_a<lst>(m_) && is_a<lst>(x_)) {
1548
1549 const lst& m = ex_to<lst>(m_);
1550 const lst& x = ex_to<lst>(x_);
1551 if (m.nops() != x.nops()) {
1552 return Li(m_,x_).hold();
1553 }
1554 if (x.nops() == 0) {
1555 return _ex1;
1556 }
1557 if ((m.op(0) == _ex1) && (x.op(0) == _ex1)) {
1558 return Li(m_,x_).hold();
1559 }
1560
1561 for (auto itm = m.begin(), itx = x.begin(); itm != m.end(); ++itm, ++itx) {
1562 if (!(*itm).info(info_flags::posint)) {
1563 return Li(m_, x_).hold();
1564 }
1565 if (!(*itx).info(info_flags::numeric)) {
1566 return Li(m_, x_).hold();
1567 }
1568 if (*itx == _ex0) {
1569 return _ex0;
1570 }
1571 }
1572
1573 return mLi_numeric(m, x);
1574 }
1575
1576 return Li(m_,x_).hold();
1577}
1578
1579
1580static ex Li_eval(const ex& m_, const ex& x_)
1581{
1582 if (is_a<lst>(m_)) {
1583 if (is_a<lst>(x_)) {
1584 // multiple polylogs
1585 const lst& m = ex_to<lst>(m_);
1586 const lst& x = ex_to<lst>(x_);
1587 if (m.nops() != x.nops()) {
1588 return Li(m_,x_).hold();
1589 }
1590 if (x.nops() == 0) {
1591 return _ex1;
1592 }
1593 bool is_H = true;
1594 bool is_zeta = true;
1595 bool do_evalf = true;
1596 bool crational = true;
1597 for (auto itm = m.begin(), itx = x.begin(); itm != m.end(); ++itm, ++itx) {
1598 if (!(*itm).info(info_flags::posint)) {
1599 return Li(m_,x_).hold();
1600 }
1601 if ((*itx != _ex1) && (*itx != _ex_1)) {
1602 if (itx != x.begin()) {
1603 is_H = false;
1604 }
1605 is_zeta = false;
1606 }
1607 if (*itx == _ex0) {
1608 return _ex0;
1609 }
1610 if (!(*itx).info(info_flags::numeric)) {
1611 do_evalf = false;
1612 }
1613 if (!(*itx).info(info_flags::crational)) {
1614 crational = false;
1615 }
1616 }
1617 if (is_zeta) {
1618 lst newx;
1619 for (const auto & itx : x) {
1620 GINAC_ASSERT((itx == _ex1) || (itx == _ex_1));
1621 // XXX: 1 + 0.0*I is considered equal to 1. However
1622 // the former is a not automatically converted
1623 // to a real number. Do the conversion explicitly
1624 // to avoid the "numeric::operator>(): complex inequality"
1625 // exception (and similar problems).
1626 newx.append(itx != _ex_1 ? _ex1 : _ex_1);
1627 }
1628 return zeta(m_, newx);
1629 }
1630 if (is_H) {
1631 ex prefactor;
1632 lst newm = convert_parameter_Li_to_H(m, x, prefactor);
1633 return prefactor * H(newm, x[0]);
1634 }
1635 if (do_evalf && !crational) {
1636 return mLi_numeric(m,x);
1637 }
1638 }
1639 return Li(m_, x_).hold();
1640 } else if (is_a<lst>(x_)) {
1641 return Li(m_, x_).hold();
1642 }
1643
1644 // classical polylogs
1645 if (x_ == _ex0) {
1646 return _ex0;
1647 }
1648 if (x_ == _ex1) {
1649 return zeta(m_);
1650 }
1651 if (x_ == _ex_1) {
1652 return (pow(2,1-m_)-1) * zeta(m_);
1653 }
1654 if (m_ == _ex1) {
1655 return -log(1-x_);
1656 }
1657 if (m_ == _ex2) {
1658 if (x_.is_equal(I)) {
1659 return power(Pi,_ex2)/_ex_48 + Catalan*I;
1660 }
1661 if (x_.is_equal(-I)) {
1662 return power(Pi,_ex2)/_ex_48 - Catalan*I;
1663 }
1664 }
1666 int m__ = ex_to<numeric>(m_).to_int();
1667 const cln::cl_N x__ = ex_to<numeric>(x_).to_cl_N();
1668 const cln::cl_N result = Lin_numeric(m__, x__);
1669 return numeric(result);
1670 }
1671
1672 return Li(m_, x_).hold();
1673}
1674
1675
1676static ex Li_series(const ex& m, const ex& x, const relational& rel, int order, unsigned options)
1677{
1678 if (is_a<lst>(m) || is_a<lst>(x)) {
1679 // multiple polylog
1680 epvector seq { expair(Li(m, x), 0) };
1681 return pseries(rel, std::move(seq));
1682 }
1683
1684 // classical polylog
1685 const ex x_pt = x.subs(rel, subs_options::no_pattern);
1686 if (m.info(info_flags::numeric) && x_pt.info(info_flags::numeric)) {
1687 // First special case: x==0 (derivatives have poles)
1688 if (x_pt.is_zero()) {
1689 const symbol s;
1690 ex ser;
1691 // manually construct the primitive expansion
1692 for (int i=1; i<order; ++i)
1693 ser += pow(s,i) / pow(numeric(i), m);
1694 // substitute the argument's series expansion
1695 ser = ser.subs(s==x.series(rel, order), subs_options::no_pattern);
1696 // maybe that was terminating, so add a proper order term
1697 epvector nseq { expair(Order(_ex1), order) };
1698 ser += pseries(rel, std::move(nseq));
1699 // reexpanding it will collapse the series again
1700 return ser.series(rel, order);
1701 }
1702 // TODO special cases: x==1 (branch point) and x real, >=1 (branch cut)
1703 throw std::runtime_error("Li_series: don't know how to do the series expansion at this point!");
1704 }
1705 // all other cases should be safe, by now:
1706 throw do_taylor(); // caught by function::series()
1707}
1708
1709
1710static ex Li_deriv(const ex& m_, const ex& x_, unsigned deriv_param)
1711{
1712 GINAC_ASSERT(deriv_param < 2);
1713 if (deriv_param == 0) {
1714 return _ex0;
1715 }
1716 if (m_.nops() > 1) {
1717 throw std::runtime_error("don't know how to derivate multiple polylogarithm!");
1718 }
1719 ex m;
1720 if (is_a<lst>(m_)) {
1721 m = m_.op(0);
1722 } else {
1723 m = m_;
1724 }
1725 ex x;
1726 if (is_a<lst>(x_)) {
1727 x = x_.op(0);
1728 } else {
1729 x = x_;
1730 }
1731 if (m > 0) {
1732 return Li(m-1, x) / x;
1733 } else {
1734 return 1/(1-x);
1735 }
1736}
1737
1738
1739static void Li_print_latex(const ex& m_, const ex& x_, const print_context& c)
1740{
1741 lst m;
1742 if (is_a<lst>(m_)) {
1743 m = ex_to<lst>(m_);
1744 } else {
1745 m = lst{m_};
1746 }
1747 lst x;
1748 if (is_a<lst>(x_)) {
1749 x = ex_to<lst>(x_);
1750 } else {
1751 x = lst{x_};
1752 }
1753 c.s << "\\mathrm{Li}_{";
1754 auto itm = m.begin();
1755 (*itm).print(c);
1756 itm++;
1757 for (; itm != m.end(); itm++) {
1758 c.s << ",";
1759 (*itm).print(c);
1760 }
1761 c.s << "}(";
1762 auto itx = x.begin();
1763 (*itx).print(c);
1764 itx++;
1765 for (; itx != x.end(); itx++) {
1766 c.s << ",";
1767 (*itx).print(c);
1768 }
1769 c.s << ")";
1770}
1771
1772
1774 evalf_func(Li_evalf).
1775 eval_func(Li_eval).
1776 series_func(Li_series).
1777 derivative_func(Li_deriv).
1778 print_func<print_latex>(Li_print_latex).
1779 do_not_evalf_params());
1780
1781
1783//
1784// Nielsen's generalized polylogarithm S(n,p,x)
1785//
1786// helper functions
1787//
1789
1790
1791// anonymous namespace for helper functions
1792namespace {
1793
1794
1795// lookup table for special Euler-Zagier-Sums (used for S_n,p(x))
1796// see fill_Yn()
1797std::vector<std::vector<cln::cl_N>> Yn;
1798int ynsize = 0; // number of Yn[]
1799int ynlength = 100; // initial length of all Yn[i]
1800
1801
1802// This function calculates the Y_n. The Y_n are needed for the evaluation of S_{n,p}(x).
1803// The Y_n are basically Euler-Zagier sums with all m_i=1. They are subsums in the Z-sum
1804// representing S_{n,p}(x).
1805// The first index in Y_n corresponds to the parameter p minus one, i.e. the depth of the
1806// equivalent Z-sum.
1807// The second index in Y_n corresponds to the running index of the outermost sum in the full Z-sum
1808// representing S_{n,p}(x).
1809// The calculation of Y_n uses the values from Y_{n-1}.
1810void fill_Yn(int n, const cln::float_format_t& prec)
1811{
1812 const int initsize = ynlength;
1813 //const int initsize = initsize_Yn;
1814 cln::cl_N one = cln::cl_float(1, prec);
1815
1816 if (n) {
1817 std::vector<cln::cl_N> buf(initsize);
1818 auto it = buf.begin();
1819 auto itprev = Yn[n-1].begin();
1820 *it = (*itprev) / cln::cl_N(n+1) * one;
1821 it++;
1822 itprev++;
1823 // sums with an index smaller than the depth are zero and need not to be calculated.
1824 // calculation starts with depth, which is n+2)
1825 for (int i=n+2; i<=initsize+n; i++) {
1826 *it = *(it-1) + (*itprev) / cln::cl_N(i) * one;
1827 it++;
1828 itprev++;
1829 }
1830 Yn.push_back(buf);
1831 } else {
1832 std::vector<cln::cl_N> buf(initsize);
1833 auto it = buf.begin();
1834 *it = 1 * one;
1835 it++;
1836 for (int i=2; i<=initsize; i++) {
1837 *it = *(it-1) + 1 / cln::cl_N(i) * one;
1838 it++;
1839 }
1840 Yn.push_back(buf);
1841 }
1842 ynsize++;
1843}
1844
1845
1846// make Yn longer ...
1847void make_Yn_longer(int newsize, const cln::float_format_t& prec)
1848{
1849
1850 cln::cl_N one = cln::cl_float(1, prec);
1851
1852 Yn[0].resize(newsize);
1853 auto it = Yn[0].begin();
1854 it += ynlength;
1855 for (int i=ynlength+1; i<=newsize; i++) {
1856 *it = *(it-1) + 1 / cln::cl_N(i) * one;
1857 it++;
1858 }
1859
1860 for (int n=1; n<ynsize; n++) {
1861 Yn[n].resize(newsize);
1862 auto it = Yn[n].begin();
1863 auto itprev = Yn[n-1].begin();
1864 it += ynlength;
1865 itprev += ynlength;
1866 for (int i=ynlength+n+1; i<=newsize+n; i++) {
1867 *it = *(it-1) + (*itprev) / cln::cl_N(i) * one;
1868 it++;
1869 itprev++;
1870 }
1871 }
1872
1873 ynlength = newsize;
1874}
1875
1876
1877// helper function for S(n,p,x)
1878// [Kol] (7.2)
1879cln::cl_N C(int n, int p)
1880{
1881 cln::cl_N result;
1882
1883 for (int k=0; k<p; k++) {
1884 for (int j=0; j<=(n+k-1)/2; j++) {
1885 if (k == 0) {
1886 if (n & 1) {
1887 if (j & 1) {
1888 result = result - 2 * cln::expt(cln::pi(),2*j) * S_num(n-2*j,p,1) / cln::factorial(2*j);
1889 }
1890 else {
1891 result = result + 2 * cln::expt(cln::pi(),2*j) * S_num(n-2*j,p,1) / cln::factorial(2*j);
1892 }
1893 }
1894 }
1895 else {
1896 if (k & 1) {
1897 if (j & 1) {
1898 result = result + cln::factorial(n+k-1)
1899 * cln::expt(cln::pi(),2*j) * S_num(n+k-2*j,p-k,1)
1900 / (cln::factorial(k) * cln::factorial(n-1) * cln::factorial(2*j));
1901 }
1902 else {
1903 result = result - cln::factorial(n+k-1)
1904 * cln::expt(cln::pi(),2*j) * S_num(n+k-2*j,p-k,1)
1905 / (cln::factorial(k) * cln::factorial(n-1) * cln::factorial(2*j));
1906 }
1907 }
1908 else {
1909 if (j & 1) {
1910 result = result - cln::factorial(n+k-1) * cln::expt(cln::pi(),2*j) * S_num(n+k-2*j,p-k,1)
1911 / (cln::factorial(k) * cln::factorial(n-1) * cln::factorial(2*j));
1912 }
1913 else {
1914 result = result + cln::factorial(n+k-1)
1915 * cln::expt(cln::pi(),2*j) * S_num(n+k-2*j,p-k,1)
1916 / (cln::factorial(k) * cln::factorial(n-1) * cln::factorial(2*j));
1917 }
1918 }
1919 }
1920 }
1921 }
1922 int np = n+p;
1923 if ((np-1) & 1) {
1924 if (((np)/2+n) & 1) {
1925 result = -result - cln::expt(cln::pi(),np) / (np * cln::factorial(n-1) * cln::factorial(p));
1926 }
1927 else {
1928 result = -result + cln::expt(cln::pi(),np) / (np * cln::factorial(n-1) * cln::factorial(p));
1929 }
1930 }
1931
1932 return result;
1933}
1934
1935
1936// helper function for S(n,p,x)
1937// [Kol] remark to (9.1)
1938cln::cl_N a_k(int k)
1939{
1940 cln::cl_N result;
1941
1942 if (k == 0) {
1943 return 1;
1944 }
1945
1946 result = result;
1947 for (int m=2; m<=k; m++) {
1948 result = result + cln::expt(cln::cl_N(-1),m) * cln::zeta(m) * a_k(k-m);
1949 }
1950
1951 return -result / k;
1952}
1953
1954
1955// helper function for S(n,p,x)
1956// [Kol] remark to (9.1)
1957cln::cl_N b_k(int k)
1958{
1959 cln::cl_N result;
1960
1961 if (k == 0) {
1962 return 1;
1963 }
1964
1965 result = result;
1966 for (int m=2; m<=k; m++) {
1967 result = result + cln::expt(cln::cl_N(-1),m) * cln::zeta(m) * b_k(k-m);
1968 }
1969
1970 return result / k;
1971}
1972
1973
1974// helper function for S(n,p,x)
1975cln::cl_N S_do_sum(int n, int p, const cln::cl_N& x, const cln::float_format_t& prec)
1976{
1977 static cln::float_format_t oldprec = cln::default_float_format;
1978
1979 if (p==1) {
1980 return Li_projection(n+1, x, prec);
1981 }
1982
1983 // precision has changed, we need to clear lookup table Yn
1984 if ( oldprec != prec ) {
1985 Yn.clear();
1986 ynsize = 0;
1987 ynlength = 100;
1988 oldprec = prec;
1989 }
1990
1991 // check if precalculated values are sufficient
1992 if (p > ynsize+1) {
1993 for (int i=ynsize; i<p-1; i++) {
1994 fill_Yn(i, prec);
1995 }
1996 }
1997
1998 // should be done otherwise
1999 cln::cl_F one = cln::cl_float(1, cln::float_format(Digits));
2000 cln::cl_N xf = x * one;
2001 //cln::cl_N xf = x * cln::cl_float(1, prec);
2002
2003 cln::cl_N res;
2004 cln::cl_N resbuf;
2005 cln::cl_N factor = cln::expt(xf, p);
2006 int i = p;
2007 do {
2008 resbuf = res;
2009 if (i-p >= ynlength) {
2010 // make Yn longer
2011 make_Yn_longer(ynlength*2, prec);
2012 }
2013 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? ...
2014 //res = res + factor / cln::expt(cln::cl_I(i),n+1) * (*it); // should we check it? or rely on magic number? ...
2015 factor = factor * xf;
2016 i++;
2017 } while (res != resbuf);
2018
2019 return res;
2020}
2021
2022
2023// helper function for S(n,p,x)
2024cln::cl_N S_projection(int n, int p, const cln::cl_N& x, const cln::float_format_t& prec)
2025{
2026 // [Kol] (5.3)
2027 if (cln::abs(cln::realpart(x)) > cln::cl_F("0.5")) {
2028
2029 cln::cl_N result = cln::expt(cln::cl_I(-1),p) * cln::expt(cln::log(x),n)
2030 * cln::expt(cln::log(1-x),p) / cln::factorial(n) / cln::factorial(p);
2031
2032 for (int s=0; s<n; s++) {
2033 cln::cl_N res2;
2034 for (int r=0; r<p; r++) {
2035 res2 = res2 + cln::expt(cln::cl_I(-1),r) * cln::expt(cln::log(1-x),r)
2036 * S_do_sum(p-r,n-s,1-x,prec) / cln::factorial(r);
2037 }
2038 result = result + cln::expt(cln::log(x),s) * (S_num(n-s,p,1) - res2) / cln::factorial(s);
2039 }
2040
2041 return result;
2042 }
2043
2044 return S_do_sum(n, p, x, prec);
2045}
2046
2047
2048// helper function for S(n,p,x)
2049const cln::cl_N S_num(int n, int p, const cln::cl_N& x)
2050{
2051 if (x == 1) {
2052 if (n == 1) {
2053 // [Kol] (2.22) with (2.21)
2054 return cln::zeta(p+1);
2055 }
2056
2057 if (p == 1) {
2058 // [Kol] (2.22)
2059 return cln::zeta(n+1);
2060 }
2061
2062 // [Kol] (9.1)
2063 cln::cl_N result;
2064 for (int nu=0; nu<n; nu++) {
2065 for (int rho=0; rho<=p; rho++) {
2066 result = result + b_k(n-nu-1) * b_k(p-rho) * a_k(nu+rho+1)
2067 * cln::factorial(nu+rho+1) / cln::factorial(rho) / cln::factorial(nu+1);
2068 }
2069 }
2070 result = result * cln::expt(cln::cl_I(-1),n+p-1);
2071
2072 return result;
2073 }
2074 else if (x == -1) {
2075 // [Kol] (2.22)
2076 if (p == 1) {
2077 return -(1-cln::expt(cln::cl_I(2),-n)) * cln::zeta(n+1);
2078 }
2079// throw std::runtime_error("don't know how to evaluate this function!");
2080 }
2081
2082 // what is the desired float format?
2083 // first guess: default format
2084 cln::float_format_t prec = cln::default_float_format;
2085 const cln::cl_N value = x;
2086 // second guess: the argument's format
2087 if (!instanceof(realpart(value), cln::cl_RA_ring))
2088 prec = cln::float_format(cln::the<cln::cl_F>(cln::realpart(value)));
2089 else if (!instanceof(imagpart(value), cln::cl_RA_ring))
2090 prec = cln::float_format(cln::the<cln::cl_F>(cln::imagpart(value)));
2091
2092 // [Kol] (5.3)
2093 // the condition abs(1-value)>1 avoids an infinite recursion in the region abs(value)<=1 && abs(value)>0.95 && abs(1-value)<=1 && abs(1-value)>0.95
2094 // we don't care here about abs(value)<1 && real(value)>0.5, this will be taken care of in S_projection
2095 if ((cln::realpart(value) < -0.5) || (n == 0) || ((cln::abs(value) <= 1) && (cln::abs(value) > 0.95) && (cln::abs(1-value) > 1) )) {
2096
2097 cln::cl_N result = cln::expt(cln::cl_I(-1),p) * cln::expt(cln::log(value),n)
2098 * cln::expt(cln::log(1-value),p) / cln::factorial(n) / cln::factorial(p);
2099
2100 for (int s=0; s<n; s++) {
2101 cln::cl_N res2;
2102 for (int r=0; r<p; r++) {
2103 res2 = res2 + cln::expt(cln::cl_I(-1),r) * cln::expt(cln::log(1-value),r)
2104 * S_num(p-r,n-s,1-value) / cln::factorial(r);
2105 }
2106 result = result + cln::expt(cln::log(value),s) * (S_num(n-s,p,1) - res2) / cln::factorial(s);
2107 }
2108
2109 return result;
2110
2111 }
2112 // [Kol] (5.12)
2113 if (cln::abs(value) > 1) {
2114
2115 cln::cl_N result;
2116
2117 for (int s=0; s<p; s++) {
2118 for (int r=0; r<=s; r++) {
2119 result = result + cln::expt(cln::cl_I(-1),s) * cln::expt(cln::log(-value),r) * cln::factorial(n+s-r-1)
2120 / cln::factorial(r) / cln::factorial(s-r) / cln::factorial(n-1)
2121 * S_num(n+s-r,p-s,cln::recip(value));
2122 }
2123 }
2124 result = result * cln::expt(cln::cl_I(-1),n);
2125
2126 cln::cl_N res2;
2127 for (int r=0; r<n; r++) {
2128 res2 = res2 + cln::expt(cln::log(-value),r) * C(n-r,p) / cln::factorial(r);
2129 }
2130 res2 = res2 + cln::expt(cln::log(-value),n+p) / cln::factorial(n+p);
2131
2132 result = result + cln::expt(cln::cl_I(-1),p) * res2;
2133
2134 return result;
2135 }
2136
2137 if ((cln::abs(value) > 0.95) && (cln::abs(value-9.53) < 9.47)) {
2138 lst m;
2139 m.append(n+1);
2140 for (int s=0; s<p-1; s++)
2141 m.append(1);
2142
2143 ex res = H(m,numeric(value)).evalf();
2144 return ex_to<numeric>(res).to_cl_N();
2145 }
2146 else {
2147 return S_projection(n, p, value, prec);
2148 }
2149}
2150
2151
2152} // end of anonymous namespace
2153
2154
2156//
2157// Nielsen's generalized polylogarithm S(n,p,x)
2158//
2159// GiNaC function
2160//
2162
2163
2164static ex S_evalf(const ex& n, const ex& p, const ex& x)
2165{
2166 if (n.info(info_flags::posint) && p.info(info_flags::posint)) {
2167 const int n_ = ex_to<numeric>(n).to_int();
2168 const int p_ = ex_to<numeric>(p).to_int();
2169 if (is_a<numeric>(x)) {
2170 const cln::cl_N x_ = ex_to<numeric>(x).to_cl_N();
2171 const cln::cl_N result = S_num(n_, p_, x_);
2172 return numeric(result);
2173 } else {
2174 ex x_val = x.evalf();
2175 if (is_a<numeric>(x_val)) {
2176 const cln::cl_N x_val_ = ex_to<numeric>(x_val).to_cl_N();
2177 const cln::cl_N result = S_num(n_, p_, x_val_);
2178 return numeric(result);
2179 }
2180 }
2181 }
2182 return S(n, p, x).hold();
2183}
2184
2185
2186static ex S_eval(const ex& n, const ex& p, const ex& x)
2187{
2188 if (n.info(info_flags::posint) && p.info(info_flags::posint)) {
2189 if (x == 0) {
2190 return _ex0;
2191 }
2192 if (x == 1) {
2193 lst m{n+1};
2194 for (int i=ex_to<numeric>(p).to_int()-1; i>0; i--) {
2195 m.append(1);
2196 }
2197 return zeta(m);
2198 }
2199 if (p == 1) {
2200 return Li(n+1, x);
2201 }
2203 int n_ = ex_to<numeric>(n).to_int();
2204 int p_ = ex_to<numeric>(p).to_int();
2205 const cln::cl_N x_ = ex_to<numeric>(x).to_cl_N();
2206 const cln::cl_N result = S_num(n_, p_, x_);
2207 return numeric(result);
2208 }
2209 }
2210 if (n.is_zero()) {
2211 // [Kol] (5.3)
2212 return pow(-log(1-x), p) / factorial(p);
2213 }
2214 return S(n, p, x).hold();
2215}
2216
2217
2218static ex S_series(const ex& n, const ex& p, const ex& x, const relational& rel, int order, unsigned options)
2219{
2220 if (p == _ex1) {
2221 return Li(n+1, x).series(rel, order, options);
2222 }
2223
2224 const ex x_pt = x.subs(rel, subs_options::no_pattern);
2226 // First special case: x==0 (derivatives have poles)
2227 if (x_pt.is_zero()) {
2228 const symbol s;
2229 ex ser;
2230 // manually construct the primitive expansion
2231 // subsum = Euler-Zagier-Sum is needed
2232 // dirty hack (slow ...) calculation of subsum:
2233 std::vector<ex> presubsum, subsum;
2234 subsum.push_back(0);
2235 for (int i=1; i<order-1; ++i) {
2236 subsum.push_back(subsum[i-1] + numeric(1, i));
2237 }
2238 for (int depth=2; depth<p; ++depth) {
2239 presubsum = subsum;
2240 for (int i=1; i<order-1; ++i) {
2241 subsum[i] = subsum[i-1] + numeric(1, i) * presubsum[i-1];
2242 }
2243 }
2244
2245 for (int i=1; i<order; ++i) {
2246 ser += pow(s,i) / pow(numeric(i), n+1) * subsum[i-1];
2247 }
2248 // substitute the argument's series expansion
2249 ser = ser.subs(s==x.series(rel, order), subs_options::no_pattern);
2250 // maybe that was terminating, so add a proper order term
2251 epvector nseq { expair(Order(_ex1), order) };
2252 ser += pseries(rel, std::move(nseq));
2253 // reexpanding it will collapse the series again
2254 return ser.series(rel, order);
2255 }
2256 // TODO special cases: x==1 (branch point) and x real, >=1 (branch cut)
2257 throw std::runtime_error("S_series: don't know how to do the series expansion at this point!");
2258 }
2259 // all other cases should be safe, by now:
2260 throw do_taylor(); // caught by function::series()
2261}
2262
2263
2264static ex S_deriv(const ex& n, const ex& p, const ex& x, unsigned deriv_param)
2265{
2266 GINAC_ASSERT(deriv_param < 3);
2267 if (deriv_param < 2) {
2268 return _ex0;
2269 }
2270 if (n > 0) {
2271 return S(n-1, p, x) / x;
2272 } else {
2273 return S(n, p-1, x) / (1-x);
2274 }
2275}
2276
2277
2278static void S_print_latex(const ex& n, const ex& p, const ex& x, const print_context& c)
2279{
2280 c.s << "\\mathrm{S}_{";
2281 n.print(c);
2282 c.s << ",";
2283 p.print(c);
2284 c.s << "}(";
2285 x.print(c);
2286 c.s << ")";
2287}
2288
2289
2291 evalf_func(S_evalf).
2292 eval_func(S_eval).
2293 series_func(S_series).
2294 derivative_func(S_deriv).
2295 print_func<print_latex>(S_print_latex).
2296 do_not_evalf_params());
2297
2298
2300//
2301// Harmonic polylogarithm H(m,x)
2302//
2303// helper functions
2304//
2306
2307
2308// anonymous namespace for helper functions
2309namespace {
2310
2311
2312// regulates the pole (used by 1/x-transformation)
2313symbol H_polesign("IMSIGN");
2314
2315
2316// convert parameters from H to Li representation
2317// parameters are expected to be in expanded form, i.e. only 0, 1 and -1
2318// returns true if some parameters are negative
2319bool convert_parameter_H_to_Li(const lst& l, lst& m, lst& s, ex& pf)
2320{
2321 // expand parameter list
2322 lst mexp;
2323 for (const auto & it : l) {
2324 if (it > 1) {
2325 for (ex count=it-1; count > 0; count--) {
2326 mexp.append(0);
2327 }
2328 mexp.append(1);
2329 } else if (it < -1) {
2330 for (ex count=it+1; count < 0; count++) {
2331 mexp.append(0);
2332 }
2333 mexp.append(-1);
2334 } else {
2335 mexp.append(it);
2336 }
2337 }
2338
2339 ex signum = 1;
2340 pf = 1;
2341 bool has_negative_parameters = false;
2342 ex acc = 1;
2343 for (const auto & it : mexp) {
2344 if (it == 0) {
2345 acc++;
2346 continue;
2347 }
2348 if (it > 0) {
2349 m.append((it+acc-1) * signum);
2350 } else {
2351 m.append((it-acc+1) * signum);
2352 }
2353 acc = 1;
2354 signum = it;
2355 pf *= it;
2356 if (pf < 0) {
2357 has_negative_parameters = true;
2358 }
2359 }
2360 if (has_negative_parameters) {
2361 for (std::size_t i=0; i<m.nops(); i++) {
2362 if (m.op(i) < 0) {
2363 m.let_op(i) = -m.op(i);
2364 s.append(-1);
2365 } else {
2366 s.append(1);
2367 }
2368 }
2369 }
2370
2371 return has_negative_parameters;
2372}
2373
2374
2375// recursivly transforms H to corresponding multiple polylogarithms
2376struct map_trafo_H_convert_to_Li : public map_function
2377{
2378 ex operator()(const ex& e) override
2379 {
2380 if (is_a<add>(e) || is_a<mul>(e)) {
2381 return e.map(*this);
2382 }
2383 if (is_a<function>(e)) {
2384 std::string name = ex_to<function>(e).get_name();
2385 if (name == "H") {
2386 lst parameter;
2387 if (is_a<lst>(e.op(0))) {
2388 parameter = ex_to<lst>(e.op(0));
2389 } else {
2390 parameter = lst{e.op(0)};
2391 }
2392 ex arg = e.op(1);
2393
2394 lst m;
2395 lst s;
2396 ex pf;
2397 if (convert_parameter_H_to_Li(parameter, m, s, pf)) {
2398 s.let_op(0) = s.op(0) * arg;
2399 return pf * Li(m, s).hold();
2400 } else {
2401 for (std::size_t i=0; i<m.nops(); i++) {
2402 s.append(1);
2403 }
2404 s.let_op(0) = s.op(0) * arg;
2405 return Li(m, s).hold();
2406 }
2407 }
2408 }
2409 return e;
2410 }
2411};
2412
2413
2414// recursivly transforms H to corresponding zetas
2415struct map_trafo_H_convert_to_zeta : public map_function
2416{
2417 ex operator()(const ex& e) override
2418 {
2419 if (is_a<add>(e) || is_a<mul>(e)) {
2420 return e.map(*this);
2421 }
2422 if (is_a<function>(e)) {
2423 std::string name = ex_to<function>(e).get_name();
2424 if (name == "H") {
2425 lst parameter;
2426 if (is_a<lst>(e.op(0))) {
2427 parameter = ex_to<lst>(e.op(0));
2428 } else {
2429 parameter = lst{e.op(0)};
2430 }
2431
2432 lst m;
2433 lst s;
2434 ex pf;
2435 if (convert_parameter_H_to_Li(parameter, m, s, pf)) {
2436 return pf * zeta(m, s);
2437 } else {
2438 return zeta(m);
2439 }
2440 }
2441 }
2442 return e;
2443 }
2444};
2445
2446
2447// remove trailing zeros from H-parameters
2448struct map_trafo_H_reduce_trailing_zeros : public map_function
2449{
2450 ex operator()(const ex& e) override
2451 {
2452 if (is_a<add>(e) || is_a<mul>(e)) {
2453 return e.map(*this);
2454 }
2455 if (is_a<function>(e)) {
2456 std::string name = ex_to<function>(e).get_name();
2457 if (name == "H") {
2458 lst parameter;
2459 if (is_a<lst>(e.op(0))) {
2460 parameter = ex_to<lst>(e.op(0));
2461 } else {
2462 parameter = lst{e.op(0)};
2463 }
2464 ex arg = e.op(1);
2465 if (parameter.op(parameter.nops()-1) == 0) {
2466
2467 //
2468 if (parameter.nops() == 1) {
2469 return log(arg);
2470 }
2471
2472 //
2473 auto it = parameter.begin();
2474 while ((it != parameter.end()) && (*it == 0)) {
2475 it++;
2476 }
2477 if (it == parameter.end()) {
2478 return pow(log(arg),parameter.nops()) / factorial(parameter.nops());
2479 }
2480
2481 //
2482 parameter.remove_last();
2483 std::size_t lastentry = parameter.nops();
2484 while ((lastentry > 0) && (parameter[lastentry-1] == 0)) {
2485 lastentry--;
2486 }
2487
2488 //
2489 ex result = log(arg) * H(parameter,arg).hold();
2490 ex acc = 0;
2491 for (ex i=0; i<lastentry; i++) {
2492 if (parameter[i] > 0) {
2493 parameter[i]++;
2494 result -= (acc + parameter[i]-1) * H(parameter, arg).hold();
2495 parameter[i]--;
2496 acc = 0;
2497 } else if (parameter[i] < 0) {
2498 parameter[i]--;
2499 result -= (acc + abs(parameter[i]+1)) * H(parameter, arg).hold();
2500 parameter[i]++;
2501 acc = 0;
2502 } else {
2503 acc++;
2504 }
2505 }
2506
2507 if (lastentry < parameter.nops()) {
2508 result = result / (parameter.nops()-lastentry+1);
2509 return result.map(*this);
2510 } else {
2511 return result;
2512 }
2513 }
2514 }
2515 }
2516 return e;
2517 }
2518};
2519
2520
2521// returns an expression with zeta functions corresponding to the parameter list for H
2522ex convert_H_to_zeta(const lst& m)
2523{
2524 symbol xtemp("xtemp");
2525 map_trafo_H_reduce_trailing_zeros filter;
2526 map_trafo_H_convert_to_zeta filter2;
2527 return filter2(filter(H(m, xtemp).hold())).subs(xtemp == 1);
2528}
2529
2530
2531// convert signs form Li to H representation
2532lst convert_parameter_Li_to_H(const lst& m, const lst& x, ex& pf)
2533{
2534 lst res;
2535 auto itm = m.begin();
2536 auto itx = ++x.begin();
2537 int signum = 1;
2538 pf = _ex1;
2539 res.append(*itm);
2540 itm++;
2541 while (itx != x.end()) {
2542 GINAC_ASSERT((*itx == _ex1) || (*itx == _ex_1));
2543 // XXX: 1 + 0.0*I is considered equal to 1. However the former
2544 // is not automatically converted to a real number.
2545 // Do the conversion explicitly to avoid the
2546 // "numeric::operator>(): complex inequality" exception.
2547 signum *= (*itx != _ex_1) ? 1 : -1;
2548 pf *= signum;
2549 res.append((*itm) * signum);
2550 itm++;
2551 itx++;
2552 }
2553 return res;
2554}
2555
2556
2557// multiplies an one-dimensional H with another H
2558// [ReV] (18)
2559ex trafo_H_mult(const ex& h1, const ex& h2)
2560{
2561 ex res;
2562 ex hshort;
2563 lst hlong;
2564 ex h1nops = h1.op(0).nops();
2565 ex h2nops = h2.op(0).nops();
2566 if (h1nops > 1) {
2567 hshort = h2.op(0).op(0);
2568 hlong = ex_to<lst>(h1.op(0));
2569 } else {
2570 hshort = h1.op(0).op(0);
2571 if (h2nops > 1) {
2572 hlong = ex_to<lst>(h2.op(0));
2573 } else {
2574 hlong = lst{h2.op(0).op(0)};
2575 }
2576 }
2577 for (std::size_t i=0; i<=hlong.nops(); i++) {
2578 lst newparameter;
2579 std::size_t j=0;
2580 for (; j<i; j++) {
2581 newparameter.append(hlong[j]);
2582 }
2583 newparameter.append(hshort);
2584 for (; j<hlong.nops(); j++) {
2585 newparameter.append(hlong[j]);
2586 }
2587 res += H(newparameter, h1.op(1)).hold();
2588 }
2589 return res;
2590}
2591
2592
2593// applies trafo_H_mult recursively on expressions
2594struct map_trafo_H_mult : public map_function
2595{
2596 ex operator()(const ex& e) override
2597 {
2598 if (is_a<add>(e)) {
2599 return e.map(*this);
2600 }
2601
2602 if (is_a<mul>(e)) {
2603
2604 ex result = 1;
2605 ex firstH;
2606 lst Hlst;
2607 for (std::size_t pos=0; pos<e.nops(); pos++) {
2608 if (is_a<power>(e.op(pos)) && is_a<function>(e.op(pos).op(0))) {
2609 std::string name = ex_to<function>(e.op(pos).op(0)).get_name();
2610 if (name == "H") {
2611 for (ex i=0; i<e.op(pos).op(1); i++) {
2612 Hlst.append(e.op(pos).op(0));
2613 }
2614 continue;
2615 }
2616 } else if (is_a<function>(e.op(pos))) {
2617 std::string name = ex_to<function>(e.op(pos)).get_name();
2618 if (name == "H") {
2619 if (e.op(pos).op(0).nops() > 1) {
2620 firstH = e.op(pos);
2621 } else {
2622 Hlst.append(e.op(pos));
2623 }
2624 continue;
2625 }
2626 }
2627 result *= e.op(pos);
2628 }
2629 if (firstH == 0) {
2630 if (Hlst.nops() > 0) {
2631 firstH = Hlst[Hlst.nops()-1];
2632 Hlst.remove_last();
2633 } else {
2634 return e;
2635 }
2636 }
2637
2638 if (Hlst.nops() > 0) {
2639 ex buffer = trafo_H_mult(firstH, Hlst.op(0));
2640 result *= buffer;
2641 for (std::size_t i=1; i<Hlst.nops(); i++) {
2642 result *= Hlst.op(i);
2643 }
2644 result = result.expand();
2645 map_trafo_H_mult recursion;
2646 return recursion(result);
2647 } else {
2648 return e;
2649 }
2650
2651 }
2652 return e;
2653 }
2654};
2655
2656
2657// do integration [ReV] (55)
2658// put parameter 0 in front of existing parameters
2659ex trafo_H_1tx_prepend_zero(const ex& e, const ex& arg)
2660{
2661 ex h;
2662 std::string name;
2663 if (is_a<function>(e)) {
2664 name = ex_to<function>(e).get_name();
2665 }
2666 if (name == "H") {
2667 h = e;
2668 } else {
2669 for (std::size_t i=0; i<e.nops(); i++) {
2670 if (is_a<function>(e.op(i))) {
2671 std::string name = ex_to<function>(e.op(i)).get_name();
2672 if (name == "H") {
2673 h = e.op(i);
2674 }
2675 }
2676 }
2677 }
2678 if (h != 0) {
2679 lst newparameter = ex_to<lst>(h.op(0));
2680 newparameter.prepend(0);
2681 ex addzeta = convert_H_to_zeta(newparameter);
2682 return e.subs(h == (addzeta-H(newparameter, h.op(1)).hold())).expand();
2683 } else {
2684 return e * (-H(lst{ex(0)},1/arg).hold());
2685 }
2686}
2687
2688
2689// do integration [ReV] (49)
2690// put parameter 1 in front of existing parameters
2691ex trafo_H_prepend_one(const ex& e, const ex& arg)
2692{
2693 ex h;
2694 std::string name;
2695 if (is_a<function>(e)) {
2696 name = ex_to<function>(e).get_name();
2697 }
2698 if (name == "H") {
2699 h = e;
2700 } else {
2701 for (std::size_t i=0; i<e.nops(); i++) {
2702 if (is_a<function>(e.op(i))) {
2703 std::string name = ex_to<function>(e.op(i)).get_name();
2704 if (name == "H") {
2705 h = e.op(i);
2706 }
2707 }
2708 }
2709 }
2710 if (h != 0) {
2711 lst newparameter = ex_to<lst>(h.op(0));
2712 newparameter.prepend(1);
2713 return e.subs(h == H(newparameter, h.op(1)).hold());
2714 } else {
2715 return e * H(lst{ex(1)},1-arg).hold();
2716 }
2717}
2718
2719
2720// do integration [ReV] (55)
2721// put parameter -1 in front of existing parameters
2722ex trafo_H_1tx_prepend_minusone(const ex& e, const ex& arg)
2723{
2724 ex h;
2725 std::string name;
2726 if (is_a<function>(e)) {
2727 name = ex_to<function>(e).get_name();
2728 }
2729 if (name == "H") {
2730 h = e;
2731 } else {
2732 for (std::size_t i=0; i<e.nops(); i++) {
2733 if (is_a<function>(e.op(i))) {
2734 std::string name = ex_to<function>(e.op(i)).get_name();
2735 if (name == "H") {
2736 h = e.op(i);
2737 }
2738 }
2739 }
2740 }
2741 if (h != 0) {
2742 lst newparameter = ex_to<lst>(h.op(0));
2743 newparameter.prepend(-1);
2744 ex addzeta = convert_H_to_zeta(newparameter);
2745 return e.subs(h == (addzeta-H(newparameter, h.op(1)).hold())).expand();
2746 } else {
2747 ex addzeta = convert_H_to_zeta(lst{ex(-1)});
2748 return (e * (addzeta - H(lst{ex(-1)},1/arg).hold())).expand();
2749 }
2750}
2751
2752
2753// do integration [ReV] (55)
2754// put parameter -1 in front of existing parameters
2755ex trafo_H_1mxt1px_prepend_minusone(const ex& e, const ex& arg)
2756{
2757 ex h;
2758 std::string name;
2759 if (is_a<function>(e)) {
2760 name = ex_to<function>(e).get_name();
2761 }
2762 if (name == "H") {
2763 h = e;
2764 } else {
2765 for (std::size_t i = 0; i < e.nops(); i++) {
2766 if (is_a<function>(e.op(i))) {
2767 std::string name = ex_to<function>(e.op(i)).get_name();
2768 if (name == "H") {
2769 h = e.op(i);
2770 }
2771 }
2772 }
2773 }
2774 if (h != 0) {
2775 lst newparameter = ex_to<lst>(h.op(0));
2776 newparameter.prepend(-1);
2777 return e.subs(h == H(newparameter, h.op(1)).hold()).expand();
2778 } else {
2779 return (e * H(lst{ex(-1)},(1-arg)/(1+arg)).hold()).expand();
2780 }
2781}
2782
2783
2784// do integration [ReV] (55)
2785// put parameter 1 in front of existing parameters
2786ex trafo_H_1mxt1px_prepend_one(const ex& e, const ex& arg)
2787{
2788 ex h;
2789 std::string name;
2790 if (is_a<function>(e)) {
2791 name = ex_to<function>(e).get_name();
2792 }
2793 if (name == "H") {
2794 h = e;
2795 } else {
2796 for (std::size_t i = 0; i < e.nops(); i++) {
2797 if (is_a<function>(e.op(i))) {
2798 std::string name = ex_to<function>(e.op(i)).get_name();
2799 if (name == "H") {
2800 h = e.op(i);
2801 }
2802 }
2803 }
2804 }
2805 if (h != 0) {
2806 lst newparameter = ex_to<lst>(h.op(0));
2807 newparameter.prepend(1);
2808 return e.subs(h == H(newparameter, h.op(1)).hold()).expand();
2809 } else {
2810 return (e * H(lst{ex(1)},(1-arg)/(1+arg)).hold()).expand();
2811 }
2812}
2813
2814
2815// do x -> 1-x transformation
2816struct map_trafo_H_1mx : public map_function
2817{
2818 ex operator()(const ex& e) override
2819 {
2820 if (is_a<add>(e) || is_a<mul>(e)) {
2821 return e.map(*this);
2822 }
2823
2824 if (is_a<function>(e)) {
2825 std::string name = ex_to<function>(e).get_name();
2826 if (name == "H") {
2827
2828 lst parameter = ex_to<lst>(e.op(0));
2829 ex arg = e.op(1);
2830
2831 // special cases if all parameters are either 0, 1 or -1
2832 bool allthesame = true;
2833 if (parameter.op(0) == 0) {
2834 for (std::size_t i = 1; i < parameter.nops(); i++) {
2835 if (parameter.op(i) != 0) {
2836 allthesame = false;
2837 break;
2838 }
2839 }
2840 if (allthesame) {
2841 lst newparameter;
2842 for (int i=parameter.nops(); i>0; i--) {
2843 newparameter.append(1);
2844 }
2845 return pow(-1, parameter.nops()) * H(newparameter, 1-arg).hold();
2846 }
2847 } else if (parameter.op(0) == -1) {
2848 throw std::runtime_error("map_trafo_H_1mx: cannot handle weights equal -1!");
2849 } else {
2850 for (std::size_t i = 1; i < parameter.nops(); i++) {
2851 if (parameter.op(i) != 1) {
2852 allthesame = false;
2853 break;
2854 }
2855 }
2856 if (allthesame) {
2857 lst newparameter;
2858 for (int i=parameter.nops(); i>0; i--) {
2859 newparameter.append(0);
2860 }
2861 return pow(-1, parameter.nops()) * H(newparameter, 1-arg).hold();
2862 }
2863 }
2864
2865 lst newparameter = parameter;
2866 newparameter.remove_first();
2867
2868 if (parameter.op(0) == 0) {
2869
2870 // leading zero
2871 ex res = convert_H_to_zeta(parameter);
2872 //ex res = convert_from_RV(parameter, 1).subs(H(wild(1),wild(2))==zeta(wild(1)));
2873 map_trafo_H_1mx recursion;
2874 ex buffer = recursion(H(newparameter, arg).hold());
2875 if (is_a<add>(buffer)) {
2876 for (std::size_t i = 0; i < buffer.nops(); i++) {
2877 res -= trafo_H_prepend_one(buffer.op(i), arg);
2878 }
2879 } else {
2880 res -= trafo_H_prepend_one(buffer, arg);
2881 }
2882 return res;
2883
2884 } else {
2885
2886 // leading one
2887 map_trafo_H_1mx recursion;
2888 map_trafo_H_mult unify;
2889 ex res = H(lst{ex(1)}, arg).hold() * H(newparameter, arg).hold();
2890 std::size_t firstzero = 0;
2891 while (parameter.op(firstzero) == 1) {
2892 firstzero++;
2893 }
2894 for (std::size_t i = firstzero-1; i < parameter.nops()-1; i++) {
2895 lst newparameter;
2896 std::size_t j=0;
2897 for (; j<=i; j++) {
2898 newparameter.append(parameter[j+1]);
2899 }
2900 newparameter.append(1);
2901 for (; j<parameter.nops()-1; j++) {
2902 newparameter.append(parameter[j+1]);
2903 }
2904 res -= H(newparameter, arg).hold();
2905 }
2906 res = recursion(res).expand() / firstzero;
2907 return unify(res);
2908 }
2909 }
2910 }
2911 return e;
2912 }
2913};
2914
2915
2916// do x -> 1/x transformation
2917struct map_trafo_H_1overx : public map_function
2918{
2919 ex operator()(const ex& e) override
2920 {
2921 if (is_a<add>(e) || is_a<mul>(e)) {
2922 return e.map(*this);
2923 }
2924
2925 if (is_a<function>(e)) {
2926 std::string name = ex_to<function>(e).get_name();
2927 if (name == "H") {
2928
2929 lst parameter = ex_to<lst>(e.op(0));
2930 ex arg = e.op(1);
2931
2932 // special cases if all parameters are either 0, 1 or -1
2933 bool allthesame = true;
2934 if (parameter.op(0) == 0) {
2935 for (std::size_t i = 1; i < parameter.nops(); i++) {
2936 if (parameter.op(i) != 0) {
2937 allthesame = false;
2938 break;
2939 }
2940 }
2941 if (allthesame) {
2942 return pow(-1, parameter.nops()) * H(parameter, 1/arg).hold();
2943 }
2944 } else if (parameter.op(0) == -1) {
2945 for (std::size_t i = 1; i < parameter.nops(); i++) {
2946 if (parameter.op(i) != -1) {
2947 allthesame = false;
2948 break;
2949 }
2950 }
2951 if (allthesame) {
2952 map_trafo_H_mult unify;
2953 return unify((pow(H(lst{ex(-1)},1/arg).hold() - H(lst{ex(0)},1/arg).hold(), parameter.nops())
2954 / factorial(parameter.nops())).expand());
2955 }
2956 } else {
2957 for (std::size_t i = 1; i < parameter.nops(); i++) {
2958 if (parameter.op(i) != 1) {
2959 allthesame = false;
2960 break;
2961 }
2962 }
2963 if (allthesame) {
2964 map_trafo_H_mult unify;
2965 return unify((pow(H(lst{ex(1)},1/arg).hold() + H(lst{ex(0)},1/arg).hold() + H_polesign, parameter.nops())
2966 / factorial(parameter.nops())).expand());
2967 }
2968 }
2969
2970 lst newparameter = parameter;
2971 newparameter.remove_first();
2972
2973 if (parameter.op(0) == 0) {
2974
2975 // leading zero
2976 ex res = convert_H_to_zeta(parameter);
2977 map_trafo_H_1overx recursion;
2978 ex buffer = recursion(H(newparameter, arg).hold());
2979 if (is_a<add>(buffer)) {
2980 for (std::size_t i = 0; i < buffer.nops(); i++) {
2981 res += trafo_H_1tx_prepend_zero(buffer.op(i), arg);
2982 }
2983 } else {
2984 res += trafo_H_1tx_prepend_zero(buffer, arg);
2985 }
2986 return res;
2987
2988 } else if (parameter.op(0) == -1) {
2989
2990 // leading negative one
2991 ex res = convert_H_to_zeta(parameter);
2992 map_trafo_H_1overx recursion;
2993 ex buffer = recursion(H(newparameter, arg).hold());
2994 if (is_a<add>(buffer)) {
2995 for (std::size_t i = 0; i < buffer.nops(); i++) {
2996 res += trafo_H_1tx_prepend_zero(buffer.op(i), arg) - trafo_H_1tx_prepend_minusone(buffer.op(i), arg);
2997 }
2998 } else {
2999 res += trafo_H_1tx_prepend_zero(buffer, arg) - trafo_H_1tx_prepend_minusone(buffer, arg);
3000 }
3001 return res;
3002
3003 } else {
3004
3005 // leading one
3006 map_trafo_H_1overx recursion;
3007 map_trafo_H_mult unify;
3008 ex res = H(lst{ex(1)}, arg).hold() * H(newparameter, arg).hold();
3009 std::size_t firstzero = 0;
3010 while (parameter.op(firstzero) == 1) {
3011 firstzero++;
3012 }
3013 for (std::size_t i = firstzero-1; i < parameter.nops() - 1; i++) {
3014 lst newparameter;
3015 std::size_t j = 0;
3016 for (; j<=i; j++) {
3017 newparameter.append(parameter[j+1]);
3018 }
3019 newparameter.append(1);
3020 for (; j<parameter.nops()-1; j++) {
3021 newparameter.append(parameter[j+1]);
3022 }
3023 res -= H(newparameter, arg).hold();
3024 }
3025 res = recursion(res).expand() / firstzero;
3026 return unify(res);
3027
3028 }
3029
3030 }
3031 }
3032 return e;
3033 }
3034};
3035
3036
3037// do x -> (1-x)/(1+x) transformation
3038struct map_trafo_H_1mxt1px : public map_function
3039{
3040 ex operator()(const ex& e) override
3041 {
3042 if (is_a<add>(e) || is_a<mul>(e)) {
3043 return e.map(*this);
3044 }
3045
3046 if (is_a<function>(e)) {
3047 std::string name = ex_to<function>(e).get_name();
3048 if (name == "H") {
3049
3050 lst parameter = ex_to<lst>(e.op(0));
3051 ex arg = e.op(1);
3052
3053 // special cases if all parameters are either 0, 1 or -1
3054 bool allthesame = true;
3055 if (parameter.op(0) == 0) {
3056 for (std::size_t i = 1; i < parameter.nops(); i++) {
3057 if (parameter.op(i) != 0) {
3058 allthesame = false;
3059 break;
3060 }
3061 }
3062 if (allthesame) {
3063 map_trafo_H_mult unify;
3064 return unify((pow(-H(lst{ex(1)},(1-arg)/(1+arg)).hold() - H(lst{ex(-1)},(1-arg)/(1+arg)).hold(), parameter.nops())
3065 / factorial(parameter.nops())).expand());
3066 }
3067 } else if (parameter.op(0) == -1) {
3068 for (std::size_t i = 1; i < parameter.nops(); i++) {
3069 if (parameter.op(i) != -1) {
3070 allthesame = false;
3071 break;
3072 }
3073 }
3074 if (allthesame) {
3075 map_trafo_H_mult unify;
3076 return unify((pow(log(2) - H(lst{ex(-1)},(1-arg)/(1+arg)).hold(), parameter.nops())
3077 / factorial(parameter.nops())).expand());
3078 }
3079 } else {
3080 for (std::size_t i = 1; i < parameter.nops(); i++) {
3081 if (parameter.op(i) != 1) {
3082 allthesame = false;
3083 break;
3084 }
3085 }
3086 if (allthesame) {
3087 map_trafo_H_mult unify;
3088 return unify((pow(-log(2) - H(lst{ex(0)},(1-arg)/(1+arg)).hold() + H(lst{ex(-1)},(1-arg)/(1+arg)).hold(), parameter.nops())
3089 / factorial(parameter.nops())).expand());
3090 }
3091 }
3092
3093 lst newparameter = parameter;
3094 newparameter.remove_first();
3095
3096 if (parameter.op(0) == 0) {
3097
3098 // leading zero
3099 ex res = convert_H_to_zeta(parameter);
3100 map_trafo_H_1mxt1px recursion;
3101 ex buffer = recursion(H(newparameter, arg).hold());
3102 if (is_a<add>(buffer)) {
3103 for (std::size_t i = 0; i < buffer.nops(); i++) {
3104 res -= trafo_H_1mxt1px_prepend_one(buffer.op(i), arg) + trafo_H_1mxt1px_prepend_minusone(buffer.op(i), arg);
3105 }
3106 } else {
3107 res -= trafo_H_1mxt1px_prepend_one(buffer, arg) + trafo_H_1mxt1px_prepend_minusone(buffer, arg);
3108 }
3109 return res;
3110
3111 } else if (parameter.op(0) == -1) {
3112
3113 // leading negative one
3114 ex res = convert_H_to_zeta(parameter);
3115 map_trafo_H_1mxt1px recursion;
3116 ex buffer = recursion(H(newparameter, arg).hold());
3117 if (is_a<add>(buffer)) {
3118 for (std::size_t i = 0; i < buffer.nops(); i++) {
3119 res -= trafo_H_1mxt1px_prepend_minusone(buffer.op(i), arg);
3120 }
3121 } else {
3122 res -= trafo_H_1mxt1px_prepend_minusone(buffer, arg);
3123 }
3124 return res;
3125
3126 } else {
3127
3128 // leading one
3129 map_trafo_H_1mxt1px recursion;
3130 map_trafo_H_mult unify;
3131 ex res = H(lst{ex(1)}, arg).hold() * H(newparameter, arg).hold();
3132 std::size_t firstzero = 0;
3133 while (parameter.op(firstzero) == 1) {
3134 firstzero++;
3135 }
3136 for (std::size_t i = firstzero - 1; i < parameter.nops() - 1; i++) {
3137 lst newparameter;
3138 std::size_t j=0;
3139 for (; j<=i; j++) {
3140 newparameter.append(parameter[j+1]);
3141 }
3142 newparameter.append(1);
3143 for (; j<parameter.nops()-1; j++) {
3144 newparameter.append(parameter[j+1]);
3145 }
3146 res -= H(newparameter, arg).hold();
3147 }
3148 res = recursion(res).expand() / firstzero;
3149 return unify(res);
3150
3151 }
3152
3153 }
3154 }
3155 return e;
3156 }
3157};
3158
3159
3160// do the actual summation.
3161cln::cl_N H_do_sum(const std::vector<int>& m, const cln::cl_N& x)
3162{
3163 const int j = m.size();
3164
3165 std::vector<cln::cl_N> t(j);
3166
3167 cln::cl_F one = cln::cl_float(1, cln::float_format(Digits));
3168 cln::cl_N factor = cln::expt(x, j) * one;
3169 cln::cl_N t0buf;
3170 int q = 0;
3171 do {
3172 t0buf = t[0];
3173 q++;
3174 t[j-1] = t[j-1] + 1 / cln::expt(cln::cl_I(q),m[j-1]);
3175 for (int k=j-2; k>=1; k--) {
3176 t[k] = t[k] + t[k+1] / cln::expt(cln::cl_I(q+j-1-k), m[k]);
3177 }
3178 t[0] = t[0] + t[1] * factor / cln::expt(cln::cl_I(q+j-1), m[0]);
3179 factor = factor * x;
3180 } while (t[0] != t0buf);
3181
3182 return t[0];
3183}
3184
3185
3186} // end of anonymous namespace
3187
3188
3190//
3191// Harmonic polylogarithm H(m,x)
3192//
3193// GiNaC function
3194//
3196
3197
3198static ex H_evalf(const ex& x1, const ex& x2)
3199{
3200 if (is_a<lst>(x1)) {
3201
3202 cln::cl_N x;
3203 if (is_a<numeric>(x2)) {
3204 x = ex_to<numeric>(x2).to_cl_N();
3205 } else {
3206 ex x2_val = x2.evalf();
3207 if (is_a<numeric>(x2_val)) {
3208 x = ex_to<numeric>(x2_val).to_cl_N();
3209 }
3210 }
3211
3212 for (std::size_t i = 0; i < x1.nops(); i++) {
3213 if (!x1.op(i).info(info_flags::integer)) {
3214 return H(x1, x2).hold();
3215 }
3216 }
3217 if (x1.nops() < 1) {
3218 return H(x1, x2).hold();
3219 }
3220
3221 const lst& morg = ex_to<lst>(x1);
3222 // remove trailing zeros ...
3223 if (*(--morg.end()) == 0) {
3224 symbol xtemp("xtemp");
3225 map_trafo_H_reduce_trailing_zeros filter;
3226 return filter(H(x1, xtemp).hold()).subs(xtemp==x2).evalf();
3227 }
3228 // ... and expand parameter notation
3229 lst m;
3230 for (const auto & it : morg) {
3231 if (it > 1) {
3232 for (ex count=it-1; count > 0; count--) {
3233 m.append(0);
3234 }
3235 m.append(1);
3236 } else if (it <= -1) {
3237 for (ex count=it+1; count < 0; count++) {
3238 m.append(0);
3239 }
3240 m.append(-1);
3241 } else {
3242 m.append(it);
3243 }
3244 }
3245
3246 // do summation
3247 if (cln::abs(x) < 0.95) {
3248 lst m_lst;
3249 lst s_lst;
3250 ex pf;
3251 if (convert_parameter_H_to_Li(m, m_lst, s_lst, pf)) {
3252 // negative parameters -> s_lst is filled
3253 std::vector<int> m_int;
3254 std::vector<cln::cl_N> x_cln;
3255 for (auto it_int = m_lst.begin(), it_cln = s_lst.begin();
3256 it_int != m_lst.end(); it_int++, it_cln++) {
3257 m_int.push_back(ex_to<numeric>(*it_int).to_int());
3258 x_cln.push_back(ex_to<numeric>(*it_cln).to_cl_N());
3259 }
3260 x_cln.front() = x_cln.front() * x;
3261 return pf * numeric(multipleLi_do_sum(m_int, x_cln));
3262 } else {
3263 // only positive parameters
3264 //TODO
3265 if (m_lst.nops() == 1) {
3266 return Li(m_lst.op(0), x2).evalf();
3267 }
3268 std::vector<int> m_int;
3269 for (const auto & it : m_lst) {
3270 m_int.push_back(ex_to<numeric>(it).to_int());
3271 }
3272 return numeric(H_do_sum(m_int, x));
3273 }
3274 }
3275
3276 symbol xtemp("xtemp");
3277 ex res = 1;
3278
3279 // ensure that the realpart of the argument is positive
3280 if (cln::realpart(x) < 0) {
3281 x = -x;
3282 for (std::size_t i = 0; i < m.nops(); i++) {
3283 if (m.op(i) != 0) {
3284 m.let_op(i) = -m.op(i);
3285 res *= -1;
3286 }
3287 }
3288 }
3289
3290 // x -> 1/x
3291 if (cln::abs(x) >= 2.0) {
3292 map_trafo_H_1overx trafo;
3293 res *= trafo(H(m, xtemp).hold());
3294 if (cln::imagpart(x) <= 0) {
3295 res = res.subs(H_polesign == -I*Pi);
3296 } else {
3297 res = res.subs(H_polesign == I*Pi);
3298 }
3299 return res.subs(xtemp == numeric(x)).evalf();
3300 }
3301
3302 // check for letters (-1)
3303 bool has_minus_one = false;
3304 for (const auto & it : m) {
3305 if (it == -1)
3306 has_minus_one = true;
3307 }
3308
3309 // check transformations for 0.95 <= |x| < 2.0
3310
3311 // |(1-x)/(1+x)| < 0.9 -> circular area with center=9.53+0i and radius=9.47
3312 if (cln::abs(x-9.53) <= 9.47) {
3313 // x -> (1-x)/(1+x)
3314 map_trafo_H_1mxt1px trafo;
3315 res *= trafo(H(m, xtemp).hold());
3316 } else {
3317 // x -> 1-x
3318 if (has_minus_one) {
3319 map_trafo_H_convert_to_Li filter;
3320 // 09.06.2021: bug fix: don't forget a possible minus sign from the case realpart(x) < 0
3321 res *= filter(H(m, numeric(x)).hold()).evalf();
3322 return res;
3323 }
3324 map_trafo_H_1mx trafo;
3325 res *= trafo(H(m, xtemp).hold());
3326 }
3327
3328 return res.subs(xtemp == numeric(x)).evalf();
3329 }
3330
3331 return H(x1,x2).hold();
3332}
3333
3334
3335static ex H_eval(const ex& m_, const ex& x)
3336{
3337 lst m;
3338 if (is_a<lst>(m_)) {
3339 m = ex_to<lst>(m_);
3340 } else {
3341 m = lst{m_};
3342 }
3343 if (m.nops() == 0) {
3344 return _ex1;
3345 }
3346 ex pos1;
3347 ex pos2;
3348 ex n;
3349 ex p;
3350 int step = 0;
3351 if (*m.begin() > _ex1) {
3352 step++;
3353 pos1 = _ex0;
3354 pos2 = _ex1;
3355 n = *m.begin()-1;
3356 p = _ex1;
3357 } else if (*m.begin() < _ex_1) {
3358 step++;
3359 pos1 = _ex0;
3360 pos2 = _ex_1;
3361 n = -*m.begin()-1;
3362 p = _ex1;
3363 } else if (*m.begin() == _ex0) {
3364 pos1 = _ex0;
3365 n = _ex1;
3366 } else {
3367 pos1 = *m.begin();
3368 p = _ex1;
3369 }
3370 for (auto it = ++m.begin(); it != m.end(); it++) {
3371 if (it->info(info_flags::integer)) {
3372 if (step == 0) {
3373 if (*it > _ex1) {
3374 if (pos1 == _ex0) {
3375 step = 1;
3376 pos2 = _ex1;
3377 n += *it-1;
3378 p = _ex1;
3379 } else {
3380 step = 2;
3381 }
3382 } else if (*it < _ex_1) {
3383 if (pos1 == _ex0) {
3384 step = 1;
3385 pos2 = _ex_1;
3386 n += -*it-1;
3387 p = _ex1;
3388 } else {
3389 step = 2;
3390 }
3391 } else {
3392 if (*it != pos1) {
3393 step = 1;
3394 pos2 = *it;
3395 }
3396 if (*it == _ex0) {
3397 n++;
3398 } else {
3399 p++;
3400 }
3401 }
3402 } else if (step == 1) {
3403 if (*it != pos2) {
3404 step = 2;
3405 } else {
3406 if (*it == _ex0) {
3407 n++;
3408 } else {
3409 p++;
3410 }
3411 }
3412 }
3413 } else {
3414 // if some m_i is not an integer
3415 return H(m_, x).hold();
3416 }
3417 }
3418 if ((x == _ex1) && (*(--m.end()) != _ex0)) {
3419 return convert_H_to_zeta(m);
3420 }
3421 if (step == 0) {
3422 if (pos1 == _ex0) {
3423 // all zero
3424 if (x == _ex0) {
3425 return H(m_, x).hold();
3426 }
3427 return pow(log(x), m.nops()) / factorial(m.nops());
3428 } else {
3429 // all (minus) one
3430 return pow(-pos1*log(1-pos1*x), m.nops()) / factorial(m.nops());
3431 }
3432 } else if ((step == 1) && (pos1 == _ex0)){
3433 // convertible to S
3434 if (pos2 == _ex1) {
3435 return S(n, p, x);
3436 } else {
3437 return pow(-1, p) * S(n, p, -x);
3438 }
3439 }
3440 if (x == _ex0) {
3441 return _ex0;
3442 }
3444 return H(m_, x).evalf();
3445 }
3446 return H(m_, x).hold();
3447}
3448
3449
3450static ex H_series(const ex& m, const ex& x, const relational& rel, int order, unsigned options)
3451{
3452 epvector seq { expair(H(m, x), 0) };
3453 return pseries(rel, std::move(seq));
3454}
3455
3456
3457static ex H_deriv(const ex& m_, const ex& x, unsigned deriv_param)
3458{
3459 GINAC_ASSERT(deriv_param < 2);
3460 if (deriv_param == 0) {
3461 return _ex0;
3462 }
3463 lst m;
3464 if (is_a<lst>(m_)) {
3465 m = ex_to<lst>(m_);
3466 } else {
3467 m = lst{m_};
3468 }
3469 ex mb = *m.begin();
3470 if (mb > _ex1) {
3471 m[0]--;
3472 return H(m, x) / x;
3473 }
3474 if (mb < _ex_1) {
3475 m[0]++;
3476 return H(m, x) / x;
3477 }
3478 m.remove_first();
3479 if (mb == _ex1) {
3480 return 1/(1-x) * H(m, x);
3481 } else if (mb == _ex_1) {
3482 return 1/(1+x) * H(m, x);
3483 } else {
3484 return H(m, x) / x;
3485 }
3486}
3487
3488
3489static void H_print_latex(const ex& m_, const ex& x, const print_context& c)
3490{
3491 lst m;
3492 if (is_a<lst>(m_)) {
3493 m = ex_to<lst>(m_);
3494 } else {
3495 m = lst{m_};
3496 }
3497 c.s << "\\mathrm{H}_{";
3498 auto itm = m.begin();
3499 (*itm).print(c);
3500 itm++;
3501 for (; itm != m.end(); itm++) {
3502 c.s << ",";
3503 (*itm).print(c);
3504 }
3505 c.s << "}(";
3506 x.print(c);
3507 c.s << ")";
3508}
3509
3510
3512 evalf_func(H_evalf).
3513 eval_func(H_eval).
3514 series_func(H_series).
3515 derivative_func(H_deriv).
3516 print_func<print_latex>(H_print_latex).
3517 do_not_evalf_params());
3518
3519
3520// takes a parameter list for H and returns an expression with corresponding multiple polylogarithms
3521ex convert_H_to_Li(const ex& m, const ex& x)
3522{
3523 map_trafo_H_reduce_trailing_zeros filter;
3524 map_trafo_H_convert_to_Li filter2;
3525 if (is_a<lst>(m)) {
3526 return filter2(filter(H(m, x).hold()));
3527 } else {
3528 return filter2(filter(H(lst{m}, x).hold()));
3529 }
3530}
3531
3532
3534//
3535// Multiple zeta values zeta(x) and zeta(x,s)
3536//
3537// helper functions
3538//
3540
3541
3542// anonymous namespace for helper functions
3543namespace {
3544
3545
3546// parameters and data for [Cra] algorithm
3547const cln::cl_N lambda = cln::cl_N("319/320");
3548
3549void halfcyclic_convolute(const std::vector<cln::cl_N>& a, const std::vector<cln::cl_N>& b, std::vector<cln::cl_N>& c)
3550{
3551 const int size = a.size();
3552 for (int n=0; n<size; n++) {
3553 c[n] = 0;
3554 for (int m=0; m<=n; m++) {
3555 c[n] = c[n] + a[m]*b[n-m];
3556 }
3557 }
3558}
3559
3560
3561// [Cra] section 4
3562static void initcX(std::vector<cln::cl_N>& crX,
3563 const std::vector<int>& s,
3564 const int L2)
3565{
3566 std::vector<cln::cl_N> crB(L2 + 1);
3567 for (int i=0; i<=L2; i++)
3568 crB[i] = bernoulli(i).to_cl_N() / cln::factorial(i);
3569
3570 int Sm = 0;
3571 int Smp1 = 0;
3572 std::vector<std::vector<cln::cl_N>> crG(s.size() - 1, std::vector<cln::cl_N>(L2 + 1));
3573 for (int m=0; m < (int)s.size() - 1; m++) {
3574 Sm += s[m];
3575 Smp1 = Sm + s[m+1];
3576 for (int i = 0; i <= L2; i++)
3577 crG[m][i] = cln::factorial(i + Sm - m - 2) / cln::factorial(i + Smp1 - m - 2);
3578 }
3579
3580 crX = crB;
3581
3582 for (std::size_t m = 0; m < s.size() - 1; m++) {
3583 std::vector<cln::cl_N> Xbuf(L2 + 1);
3584 for (int i = 0; i <= L2; i++)
3585 Xbuf[i] = crX[i] * crG[m][i];
3586
3587 halfcyclic_convolute(Xbuf, crB, crX);
3588 }
3589}
3590
3591
3592// [Cra] section 4
3593static cln::cl_N crandall_Y_loop(const cln::cl_N& Sqk,
3594 const std::vector<cln::cl_N>& crX)
3595{
3596 cln::cl_F one = cln::cl_float(1, cln::float_format(Digits));
3597 cln::cl_N factor = cln::expt(lambda, Sqk);
3598 cln::cl_N res = factor / Sqk * crX[0] * one;
3599 cln::cl_N resbuf;
3600 int N = 0;
3601 do {
3602 resbuf = res;
3603 factor = factor * lambda;
3604 N++;
3605 res = res + crX[N] * factor / (N+Sqk);
3606 } while (((res != resbuf) || cln::zerop(crX[N])) && (N+1 < crX.size()));
3607 return res;
3608}
3609
3610
3611// [Cra] section 4
3612static void calc_f(std::vector<std::vector<cln::cl_N>>& f_kj,
3613 const int maxr, const int L1)
3614{
3615 cln::cl_N t0, t1, t2, t3, t4;
3616 int i, j, k;
3617 auto it = f_kj.begin();
3618 cln::cl_F one = cln::cl_float(1, cln::float_format(Digits));
3619
3620 t0 = cln::exp(-lambda);
3621 t2 = 1;
3622 for (k=1; k<=L1; k++) {
3623 t1 = k * lambda;
3624 t2 = t0 * t2;
3625 for (j=1; j<=maxr; j++) {
3626 t3 = 1;
3627 t4 = 1;
3628 for (i=2; i<=j; i++) {
3629 t4 = t4 * (j-i+1);
3630 t3 = t1 * t3 + t4;
3631 }
3632 (*it).push_back(t2 * t3 * cln::expt(cln::cl_I(k),-j) * one);
3633 }
3634 it++;
3635 }
3636}
3637
3638
3639// [Cra] (3.1)
3640static cln::cl_N crandall_Z(const std::vector<int>& s,
3641 const std::vector<std::vector<cln::cl_N>>& f_kj)
3642{
3643 const int j = s.size();
3644
3645 if (j == 1) {
3646 cln::cl_N t0;
3647 cln::cl_N t0buf;
3648 int q = 0;
3649 do {
3650 t0buf = t0;
3651 q++;
3652 t0 = t0 + f_kj[q+j-2][s[0]-1];
3653 } while ((t0 != t0buf) && (q+j-1 < f_kj.size()));
3654
3655 return t0 / cln::factorial(s[0]-1);
3656 }
3657
3658 std::vector<cln::cl_N> t(j);
3659
3660 cln::cl_N t0buf;
3661 int q = 0;
3662 do {
3663 t0buf = t[0];
3664 q++;
3665 t[j-1] = t[j-1] + 1 / cln::expt(cln::cl_I(q),s[j-1]);
3666 for (int k=j-2; k>=1; k--) {
3667 t[k] = t[k] + t[k+1] / cln::expt(cln::cl_I(q+j-1-k), s[k]);
3668 }
3669 t[0] = t[0] + t[1] * f_kj[q+j-2][s[0]-1];
3670 } while ((t[0] != t0buf) && (q+j-1 < f_kj.size()));
3671
3672 return t[0] / cln::factorial(s[0]-1);
3673}
3674
3675
3676// [Cra] (2.4)
3677cln::cl_N zeta_do_sum_Crandall(const std::vector<int>& s)
3678{
3679 std::vector<int> r = s;
3680 const int j = r.size();
3681
3682 std::size_t L1;
3683
3684 // decide on maximal size of f_kj for crandall_Z
3685 if (Digits < 50) {
3686 L1 = 150;
3687 } else {
3688 L1 = Digits * 3 + j*2;
3689 }
3690
3691 std::size_t L2;
3692 // decide on maximal size of crX for crandall_Y
3693 if (Digits < 38) {
3694 L2 = 63;
3695 } else if (Digits < 86) {
3696 L2 = 127;
3697 } else if (Digits < 192) {
3698 L2 = 255;
3699 } else if (Digits < 394) {
3700 L2 = 511;
3701 } else if (Digits < 808) {
3702 L2 = 1023;
3703 } else if (Digits < 1636) {
3704 L2 = 2047;
3705 } else {
3706 // [Cra] section 6, log10(lambda/2/Pi) approx -0.79 for lambda=319/320, add some extra digits
3707 L2 = std::pow(2, ceil( std::log2((long(Digits))/0.79 + 40 )) ) - 1;
3708 }
3709
3710 cln::cl_N res;
3711
3712 int maxr = 0;
3713 int S = 0;
3714 for (int i=0; i<j; i++) {
3715 S += r[i];
3716 if (r[i] > maxr) {
3717 maxr = r[i];
3718 }
3719 }
3720
3721 std::vector<std::vector<cln::cl_N>> f_kj(L1);
3722 calc_f(f_kj, maxr, L1);
3723
3724 const cln::cl_N r0factorial = cln::factorial(r[0]-1);
3725
3726 std::vector<int> rz;
3727 int skp1buf;
3728 int Srun = S;
3729 for (int k=r.size()-1; k>0; k--) {
3730
3731 rz.insert(rz.begin(), r.back());
3732 skp1buf = rz.front();
3733 Srun -= skp1buf;
3734 r.pop_back();
3735
3736 std::vector<cln::cl_N> crX;
3737 initcX(crX, r, L2);
3738
3739 for (int q=0; q<skp1buf; q++) {
3740
3741 cln::cl_N pp1 = crandall_Y_loop(Srun+q-k, crX);
3742 cln::cl_N pp2 = crandall_Z(rz, f_kj);
3743
3744 rz.front()--;
3745
3746 if (q & 1) {
3747 res = res - pp1 * pp2 / cln::factorial(q);
3748 } else {
3749 res = res + pp1 * pp2 / cln::factorial(q);
3750 }
3751 }
3752 rz.front() = skp1buf;
3753 }
3754 rz.insert(rz.begin(), r.back());
3755
3756 std::vector<cln::cl_N> crX;
3757 initcX(crX, rz, L2);
3758
3759 res = (res + crandall_Y_loop(S-j, crX)) / r0factorial
3760 + crandall_Z(rz, f_kj);
3761
3762 return res;
3763}
3764
3765
3766cln::cl_N zeta_do_sum_simple(const std::vector<int>& r)
3767{
3768 const int j = r.size();
3769
3770 // buffer for subsums
3771 std::vector<cln::cl_N> t(j);
3772 cln::cl_F one = cln::cl_float(1, cln::float_format(Digits));
3773
3774 cln::cl_N t0buf;
3775 int q = 0;
3776 do {
3777 t0buf = t[0];
3778 q++;
3779 t[j-1] = t[j-1] + one / cln::expt(cln::cl_I(q),r[j-1]);
3780 for (int k=j-2; k>=0; k--) {
3781 t[k] = t[k] + one * t[k+1] / cln::expt(cln::cl_I(q+j-1-k), r[k]);
3782 }
3783 } while (t[0] != t0buf);
3784
3785 return t[0];
3786}
3787
3788
3789// does Hoelder convolution. see [BBB] (7.0)
3790cln::cl_N zeta_do_Hoelder_convolution(const std::vector<int>& m_, const std::vector<int>& s_)
3791{
3792 // prepare parameters
3793 // holds Li arguments in [BBB] notation
3794 std::vector<int> s = s_;
3795 std::vector<int> m_p = m_;
3796 std::vector<int> m_q;
3797 // holds Li arguments in nested sums notation
3798 std::vector<cln::cl_N> s_p(s.size(), cln::cl_N(1));
3799 s_p[0] = s_p[0] * cln::cl_N("1/2");
3800 // convert notations
3801 int sig = 1;
3802 for (std::size_t i = 0; i < s_.size(); i++) {
3803 if (s_[i] < 0) {
3804 sig = -sig;
3805 s_p[i] = -s_p[i];
3806 }
3807 s[i] = sig * std::abs(s[i]);
3808 }
3809 std::vector<cln::cl_N> s_q;
3810 cln::cl_N signum = 1;
3811
3812 // first term
3813 cln::cl_N res = multipleLi_do_sum(m_p, s_p);
3814
3815 // middle terms
3816 do {
3817
3818 // change parameters
3819 if (s.front() > 0) {
3820 if (m_p.front() == 1) {
3821 m_p.erase(m_p.begin());
3822 s_p.erase(s_p.begin());
3823 if (s_p.size() > 0) {
3824 s_p.front() = s_p.front() * cln::cl_N("1/2");
3825 }
3826 s.erase(s.begin());
3827 m_q.front()++;
3828 } else {
3829 m_p.front()--;
3830 m_q.insert(m_q.begin(), 1);
3831 if (s_q.size() > 0) {
3832 s_q.front() = s_q.front() * 2;
3833 }
3834 s_q.insert(s_q.begin(), cln::cl_N("1/2"));
3835 }
3836 } else {
3837 if (m_p.front() == 1) {
3838 m_p.erase(m_p.begin());
3839 cln::cl_N spbuf = s_p.front();
3840 s_p.erase(s_p.begin());
3841 if (s_p.size() > 0) {
3842 s_p.front() = s_p.front() * spbuf;
3843 }
3844 s.erase(s.begin());
3845 m_q.insert(m_q.begin(), 1);
3846 if (s_q.size() > 0) {
3847 s_q.front() = s_q.front() * 4;
3848 }
3849 s_q.insert(s_q.begin(), cln::cl_N("1/4"));
3850 signum = -signum;
3851 } else {
3852 m_p.front()--;
3853 m_q.insert(m_q.begin(), 1);
3854 if (s_q.size() > 0) {
3855 s_q.front() = s_q.front() * 2;
3856 }
3857 s_q.insert(s_q.begin(), cln::cl_N("1/2"));
3858 }
3859 }
3860
3861 // exiting the loop
3862 if (m_p.size() == 0) break;
3863
3864 res = res + signum * multipleLi_do_sum(m_p, s_p) * multipleLi_do_sum(m_q, s_q);
3865
3866 } while (true);
3867
3868 // last term
3869 res = res + signum * multipleLi_do_sum(m_q, s_q);
3870
3871 return res;
3872}
3873
3874
3875} // end of anonymous namespace
3876
3877
3879//
3880// Multiple zeta values zeta(x)
3881//
3882// GiNaC function
3883//
3885
3886
3887static ex zeta1_evalf(const ex& x)
3888{
3889 if (is_exactly_a<lst>(x) && (x.nops()>1)) {
3890
3891 // multiple zeta value
3892 const int count = x.nops();
3893 const lst& xlst = ex_to<lst>(x);
3894 std::vector<int> r(count);
3895 std::vector<int> si(count);
3896
3897 // check parameters and convert them
3898 auto it1 = xlst.begin();
3899 auto it2 = r.begin();
3900 auto it_swrite = si.begin();
3901 do {
3902 if (!(*it1).info(info_flags::posint)) {
3903 return zeta(x).hold();
3904 }
3905 *it2 = ex_to<numeric>(*it1).to_int();
3906 *it_swrite = 1;
3907 it1++;
3908 it2++;
3909 it_swrite++;
3910 } while (it2 != r.end());
3911
3912 // check for divergence
3913 if (r[0] == 1) {
3914 return zeta(x).hold();
3915 }
3916
3917 // use Hoelder convolution if Digits is large
3918 if (Digits>50)
3919 return numeric(zeta_do_Hoelder_convolution(r, si));
3920
3921 // decide on summation algorithm
3922 // this is still a bit clumsy
3923 int limit = (Digits>17) ? 10 : 6;
3924 if ((r[0] < limit) || ((count > 3) && (r[1] < limit/2))) {
3925 return numeric(zeta_do_sum_Crandall(r));
3926 } else {
3927 return numeric(zeta_do_sum_simple(r));
3928 }
3929 }
3930
3931 // single zeta value
3932 if (is_exactly_a<numeric>(x) && (x != 1)) {
3933 try {
3934 return zeta(ex_to<numeric>(x));
3935 } catch (const dunno &e) { }
3936 }
3937
3938 return zeta(x).hold();
3939}
3940
3941
3942static ex zeta1_eval(const ex& m)
3943{
3944 if (is_exactly_a<lst>(m)) {
3945 if (m.nops() == 1) {
3946 return zeta(m.op(0));
3947 }
3948 return zeta(m).hold();
3949 }
3950
3951 if (m.info(info_flags::numeric)) {
3952 const numeric& y = ex_to<numeric>(m);
3953 // trap integer arguments:
3954 if (y.is_integer()) {
3955 if (y.is_zero()) {
3956 return _ex_1_2;
3957 }
3958 if (y.is_equal(*_num1_p)) {
3959 return zeta(m).hold();
3960 }
3961 if (y.info(info_flags::posint)) {
3962 if (y.info(info_flags::odd)) {
3963 return zeta(m).hold();
3964 } else {
3965 return abs(bernoulli(y)) * pow(Pi, y) * pow(*_num2_p, y-(*_num1_p)) / factorial(y);
3966 }
3967 } else {
3968 if (y.info(info_flags::odd)) {
3969 return -bernoulli((*_num1_p)-y) / ((*_num1_p)-y);
3970 } else {
3971 return _ex0;
3972 }
3973 }
3974 }
3975 // zeta(float)
3977 return zeta1_evalf(m);
3978 }
3979 }
3980 return zeta(m).hold();
3981}
3982
3983
3984static ex zeta1_deriv(const ex& m, unsigned deriv_param)
3985{
3986 GINAC_ASSERT(deriv_param==0);
3987
3988 if (is_exactly_a<lst>(m)) {
3989 return _ex0;
3990 } else {
3991 return zetaderiv(_ex1, m);
3992 }
3993}
3994
3995
3996static void zeta1_print_latex(const ex& m_, const print_context& c)
3997{
3998 c.s << "\\zeta(";
3999 if (is_a<lst>(m_)) {
4000 const lst& m = ex_to<lst>(m_);
4001 auto it = m.begin();
4002 (*it).print(c);
4003 it++;
4004 for (; it != m.end(); it++) {
4005 c.s << ",";
4006 (*it).print(c);
4007 }
4008 } else {
4009 m_.print(c);
4010 }
4011 c.s << ")";
4012}
4013
4014
4015unsigned zeta1_SERIAL::serial = function::register_new(function_options("zeta", 1).
4016 evalf_func(zeta1_evalf).
4017 eval_func(zeta1_eval).
4018 derivative_func(zeta1_deriv).
4019 print_func<print_latex>(zeta1_print_latex).
4020 do_not_evalf_params().
4021 overloaded(2));
4022
4023
4025//
4026// Alternating Euler sum zeta(x,s)
4027//
4028// GiNaC function
4029//
4031
4032
4033static ex zeta2_evalf(const ex& x, const ex& s)
4034{
4035 if (is_exactly_a<lst>(x)) {
4036
4037 // alternating Euler sum
4038 const int count = x.nops();
4039 const lst& xlst = ex_to<lst>(x);
4040 const lst& slst = ex_to<lst>(s);
4041 std::vector<int> xi(count);
4042 std::vector<int> si(count);
4043
4044 // check parameters and convert them
4045 auto it_xread = xlst.begin();
4046 auto it_sread = slst.begin();
4047 auto it_xwrite = xi.begin();
4048 auto it_swrite = si.begin();
4049 do {
4050 if (!(*it_xread).info(info_flags::posint)) {
4051 return zeta(x, s).hold();
4052 }
4053 *it_xwrite = ex_to<numeric>(*it_xread).to_int();
4054 if (*it_sread > 0) {
4055 *it_swrite = 1;
4056 } else {
4057 *it_swrite = -1;
4058 }
4059 it_xread++;
4060 it_sread++;
4061 it_xwrite++;
4062 it_swrite++;
4063 } while (it_xwrite != xi.end());
4064
4065 // check for divergence
4066 if ((xi[0] == 1) && (si[0] == 1)) {
4067 return zeta(x, s).hold();
4068 }
4069
4070 // use Hoelder convolution
4071 return numeric(zeta_do_Hoelder_convolution(xi, si));
4072 }
4073
4074 // x and s are not lists: convert to lists
4075 return zeta(lst{x}, lst{s}).evalf();
4076}
4077
4078
4079static ex zeta2_eval(const ex& m, const ex& s_)
4080{
4081 if (is_exactly_a<lst>(s_)) {
4082 const lst& s = ex_to<lst>(s_);
4083 for (const auto & it : s) {
4084 if (it.info(info_flags::positive)) {
4085 continue;
4086 }
4087 return zeta(m, s_).hold();
4088 }
4089 return zeta(m);
4090 } else if (s_.info(info_flags::positive)) {
4091 return zeta(m);
4092 }
4093
4094 return zeta(m, s_).hold();
4095}
4096
4097
4098static ex zeta2_deriv(const ex& m, const ex& s, unsigned deriv_param)
4099{
4100 GINAC_ASSERT(deriv_param==0);
4101
4102 if (is_exactly_a<lst>(m)) {
4103 return _ex0;
4104 } else {
4105 if ((is_exactly_a<lst>(s) && s.op(0).info(info_flags::positive)) || s.info(info_flags::positive)) {
4106 return zetaderiv(_ex1, m);
4107 }
4108 return _ex0;
4109 }
4110}
4111
4112
4113static void zeta2_print_latex(const ex& m_, const ex& s_, const print_context& c)
4114{
4115 lst m;
4116 if (is_a<lst>(m_)) {
4117 m = ex_to<lst>(m_);
4118 } else {
4119 m = lst{m_};
4120 }
4121 lst s;
4122 if (is_a<lst>(s_)) {
4123 s = ex_to<lst>(s_);
4124 } else {
4125 s = lst{s_};
4126 }
4127 c.s << "\\zeta(";
4128 auto itm = m.begin();
4129 auto its = s.begin();
4130 if (*its < 0) {
4131 c.s << "\\overline{";
4132 (*itm).print(c);
4133 c.s << "}";
4134 } else {
4135 (*itm).print(c);
4136 }
4137 its++;
4138 itm++;
4139 for (; itm != m.end(); itm++, its++) {
4140 c.s << ",";
4141 if (*its < 0) {
4142 c.s << "\\overline{";
4143 (*itm).print(c);
4144 c.s << "}";
4145 } else {
4146 (*itm).print(c);
4147 }
4148 }
4149 c.s << ")";
4150}
4151
4152
4153unsigned zeta2_SERIAL::serial = function::register_new(function_options("zeta", 2).
4154 evalf_func(zeta2_evalf).
4155 eval_func(zeta2_eval).
4156 derivative_func(zeta2_deriv).
4157 print_func<print_latex>(zeta2_print_latex).
4158 do_not_evalf_params().
4159 overloaded(2));
4160
4161
4162} // namespace GiNaC
4163
Interface to GiNaC's sums of expressions.
#define GINAC_ASSERT(X)
Assertion macro for checking invariances.
Definition assertion.h:32
static unsigned serial
Definition inifcns.h:127
static unsigned serial
Definition inifcns.h:133
virtual size_t nops() const
Number of operands/members.
Definition basic.cpp:228
const basic & hold() const
Stop further evaluation.
Definition basic.cpp:886
virtual ex map(map_function &f) const
Construct new expression by applying the specified function to all sub-expressions (one level only,...
Definition basic.cpp:293
Wrapper template for making GiNaC classes out of STL containers.
Definition container.h:72
const_iterator end() const
Definition container.h:239
const_iterator begin() const
Definition container.h:238
size_t nops() const override
Number of operands/members.
Definition container.h:117
ex op(size_t i) const override
Return operand/member at position i.
Definition container.h:294
container & append(const ex &b)
Add element at back.
Definition container.h:390
Exception class thrown by classes which provide their own series expansion to signal that ordinary Ta...
Definition function.h:667
Exception class thrown by functions to signal unimplemented functionality so the expression may just ...
Definition utils.h:36
Lightweight wrapper for GiNaC's symbolic objects.
Definition ex.h:72
ex map(map_function &f) const
Definition ex.h:162
const_iterator begin() const noexcept
Definition ex.h:662
bool is_equal(const ex &other) const
Definition ex.h:345
ex & let_op(size_t i)
Return modifiable operand/member at position i.
Definition ex.cpp:206
ex evalf() const
Definition ex.h:121
const_iterator end() const noexcept
Definition ex.h:667
size_t nops() const
Definition ex.h:135
ex series(const ex &r, int order, unsigned options=0) const
Compute the truncated series expansion of an expression.
Definition pseries.cpp:1271
ex subs(const exmap &m, unsigned options=0) const
Definition ex.h:841
bool info(unsigned inf) const
Definition ex.h:132
bool is_zero() const
Definition ex.h:213
void print(const print_context &c, unsigned level=0) const
Print expression to stream.
Definition ex.cpp:54
ex op(size_t i) const
Definition ex.h:136
A pair of expressions.
Definition expair.h:37
static unsigned register_new(function_options const &opt)
This class is a wrapper around CLN-numbers within the GiNaC class hierarchy.
Definition numeric.h:81
bool info(unsigned inf) const override
Information about the object.
Definition numeric.cpp:683
bool is_integer() const
True if object is a non-complex integer.
Definition numeric.cpp:1153
cln::cl_N to_cl_N() const
Returns a new CLN object of type cl_N, representing the value of *this.
Definition numeric.cpp:1331
bool is_equal(const numeric &other) const
Definition numeric.cpp:1121
int to_int() const
Converts numeric types to machine's int.
Definition numeric.cpp:1302
bool is_zero() const
True if object is zero.
Definition numeric.cpp:1128
This class holds a two-component object, a basis and and exponent representing exponentiation.
Definition power.h:38
Base class for print_contexts.
Definition print.h:101
This class holds a extended truncated power series (positive and negative integer powers).
Definition pseries.h:35
This class holds a relation consisting of two expressions and a logical relation between them.
Definition relational.h:34
@ no_pattern
disable pattern matching
Definition flags.h:50
Basic CAS symbol.
Definition symbol.h:38
static unsigned serial
Definition inifcns.h:108
static unsigned serial
Definition inifcns.h:114
Interface to GiNaC's constant types and some special constants.
static const bool value
Definition factor.cpp:198
unsigned options
Definition factor.cpp:2473
vector< int > k
Definition factor.cpp:1434
size_t n
Definition factor.cpp:1431
size_t c
Definition factor.cpp:756
ex x
Definition factor.cpp:1609
size_t r
Definition factor.cpp:756
umodpoly one
Definition factor.cpp:1430
mvec m
Definition factor.cpp:757
size_t last
Definition factor.cpp:1433
#define REGISTER_FUNCTION(NAME, OPT)
Definition function.h:119
Interface to GiNaC's initially known functions.
Definition of GiNaC's lst.
Interface to GiNaC's products of expressions.
Definition add.cpp:35
static ex G3_eval(const ex &x_, const ex &s_, const ex &y)
const numeric I
Imaginary unit.
Definition numeric.cpp:1432
static ex zeta2_evalf(const ex &x, const ex &s)
static ex Li_evalf(const ex &m_, const ex &x_)
const ex _ex2
Definition utils.cpp:388
const numeric pow(const numeric &x, const numeric &y)
Definition numeric.h:250
const ex _ex_1_2
Definition utils.cpp:355
static ex H_deriv(const ex &m_, const ex &x, unsigned deriv_param)
static ex S_eval(const ex &n, const ex &p, const ex &x)
container< std::list > lst
Definition lst.h:31
static ex Li_deriv(const ex &m_, const ex &x_, unsigned deriv_param)
std::map< ex, ex, ex_is_less > exmap
Definition basic.h:49
const numeric bernoulli(const numeric &nn)
Bernoulli number.
Definition numeric.cpp:2170
std::vector< expair > epvector
expair-vector
Definition expairseq.h:32
const numeric abs(const numeric &x)
Absolute value.
Definition numeric.cpp:2319
function zeta(const T1 &p1)
Definition inifcns.h:110
const ex _ex1
Definition utils.cpp:384
static void S_print_latex(const ex &n, const ex &p, const ex &x, const print_context &c)
static ex S_evalf(const ex &n, const ex &p, const ex &x)
ex conjugate(const ex &thisex)
Definition ex.h:733
static ex zeta2_eval(const ex &m, const ex &s_)
const numeric imag(const numeric &x)
Definition numeric.h:313
const numeric * _num2_p
Definition utils.cpp:387
static ex S_deriv(const ex &n, const ex &p, const ex &x, unsigned deriv_param)
static ex Li_series(const ex &m, const ex &x, const relational &rel, int order, unsigned options)
const numeric factorial(const numeric &n)
Factorial combinatorial function.
Definition numeric.cpp:2112
const ex _ex_1
Definition utils.cpp:351
static void H_print_latex(const ex &m_, const ex &x, const print_context &c)
const constant Pi("Pi", PiEvalf, "\\pi", domain::positive)
Pi.
Definition constant.h:84
static ex Li_eval(const ex &m_, const ex &x_)
static ex H_series(const ex &m, const ex &x, const relational &rel, int order, unsigned options)
static void Li_print_latex(const ex &m_, const ex &x_, const print_context &c)
static ex zeta1_eval(const ex &m)
static ex G2_eval(const ex &x_, const ex &y)
const numeric log(const numeric &x)
Natural logarithm.
Definition numeric.cpp:1449
ex evalf(const ex &thisex)
Definition ex.h:784
_numeric_digits Digits
Accuracy in decimal digits.
Definition numeric.cpp:2590
bool is_real(const numeric &x)
Definition numeric.h:292
ex op(const ex &thisex, size_t i)
Definition ex.h:826
const numeric * _num1_p
Definition utils.cpp:383
static void zeta1_print_latex(const ex &m_, const print_context &c)
const constant Catalan("Catalan", CatalanEvalf, "G", domain::positive)
Catalan's constant.
Definition constant.h:85
static ex zeta2_deriv(const ex &m, const ex &s, unsigned deriv_param)
static void zeta2_print_latex(const ex &m_, const ex &s_, const print_context &c)
static ex G2_evalf(const ex &x_, const ex &y)
static ex S_series(const ex &n, const ex &p, const ex &x, const relational &rel, int order, unsigned options)
ex factor(const ex &poly, unsigned options)
Interface function to the outside world.
Definition factor.cpp:2574
int to_int(const numeric &x)
Definition numeric.h:301
ex convert_H_to_Li(const ex &parameterlst, const ex &arg)
Converts a given list containing parameters for H in Remiddi/Vermaseren notation into the correspondi...
static ex H_evalf(const ex &x1, const ex &x2)
const ex _ex_48
Definition utils.cpp:279
static ex G3_evalf(const ex &x_, const ex &s_, const ex &y)
const ex _ex0
Definition utils.cpp:368
static ex zeta1_evalf(const ex &x)
std::vector< ex > exvector
Definition basic.h:47
static ex zeta1_deriv(const ex &m, unsigned deriv_param)
static ex H_eval(const ex &m_, const ex &x)
numeric step(const numeric &x)
Definition numeric.h:256
function G(const T1 &x, const T2 &y)
Definition inifcns.h:129
ex expand(const ex &thisex, unsigned options=0)
Definition ex.h:730
Makes the interface to the underlying bignum package available.
Interface to GiNaC's overloaded operators.
Interface to GiNaC's symbolic exponentiation (basis^exponent).
Interface to class for extended truncated power series.
Interface to relations between expressions.
Interface to GiNaC's symbolic objects.
Interface to several small and furry utilities needed within GiNaC but not of any interest to the use...
Interface to GiNaC's wildcard objects.

This page is part of the GiNaC developer's reference. It was generated automatically by doxygen. For an introduction, see the tutorial.