+static ex Li2_eval(const ex & x)
+{
+ if (x.info(info_flags::numeric)) {
+ // Li2(0) -> 0
+ if (x.is_zero())
+ return _ex0;
+ // Li2(1) -> Pi^2/6
+ if (x.is_equal(_ex1))
+ return power(Pi,_ex2)/_ex6;
+ // Li2(1/2) -> Pi^2/12 - log(2)^2/2
+ if (x.is_equal(_ex1_2))
+ return power(Pi,_ex2)/_ex12 + power(log(_ex2),_ex2)*_ex_1_2;
+ // Li2(-1) -> -Pi^2/12
+ if (x.is_equal(_ex_1))
+ return -power(Pi,_ex2)/_ex12;
+ // Li2(I) -> -Pi^2/48+Catalan*I
+ if (x.is_equal(I))
+ return power(Pi,_ex2)/_ex_48 + Catalan*I;
+ // Li2(-I) -> -Pi^2/48-Catalan*I
+ if (x.is_equal(-I))
+ return power(Pi,_ex2)/_ex_48 - Catalan*I;
+ // Li2(float)
+ if (!x.info(info_flags::crational))
+ return Li2(ex_to<numeric>(x));
+ }
+
+ return Li2(x).hold();
+}
+
+static ex Li2_deriv(const ex & x, unsigned deriv_param)
+{
+ GINAC_ASSERT(deriv_param==0);
+
+ // d/dx Li2(x) -> -log(1-x)/x
+ return -log(_ex1-x)/x;
+}
+
+static ex Li2_series(const ex &x, const relational &rel, int order, unsigned options)
+{
+ const ex x_pt = x.subs(rel);
+ if (x_pt.info(info_flags::numeric)) {
+ // First special case: x==0 (derivatives have poles)
+ if (x_pt.is_zero()) {
+ // method:
+ // The problem is that in d/dx Li2(x==0) == -log(1-x)/x we cannot
+ // simply substitute x==0. The limit, however, exists: it is 1.
+ // We also know all higher derivatives' limits:
+ // (d/dx)^n Li2(x) == n!/n^2.
+ // So the primitive series expansion is
+ // Li2(x==0) == x + x^2/4 + x^3/9 + ...
+ // and so on.
+ // We first construct such a primitive series expansion manually in
+ // a dummy symbol s and then insert the argument's series expansion
+ // for s. Reexpanding the resulting series returns the desired
+ // result.
+ const symbol s;
+ ex ser;
+ // manually construct the primitive expansion
+ for (int i=1; i<order; ++i)
+ ser += pow(s,i) / pow(numeric(i), _num2);
+ // substitute the argument's series expansion
+ ser = ser.subs(s==x.series(rel, order));
+ // maybe that was terminating, so add a proper order term
+ epvector nseq;
+ nseq.push_back(expair(Order(_ex1), order));
+ ser += pseries(rel, nseq);
+ // reexpanding it will collapse the series again
+ return ser.series(rel, order);
+ // NB: Of course, this still does not allow us to compute anything
+ // like sin(Li2(x)).series(x==0,2), since then this code here is
+ // not reached and the derivative of sin(Li2(x)) doesn't allow the
+ // substitution x==0. Probably limits *are* needed for the general
+ // cases. In case L'Hospital's rule is implemented for limits and
+ // basic::series() takes care of this, this whole block is probably
+ // obsolete!
+ }
+ // second special case: x==1 (branch point)
+ if (x_pt.is_equal(_ex1)) {
+ // method:
+ // construct series manually in a dummy symbol s
+ const symbol s;
+ ex ser = zeta(_ex2);
+ // manually construct the primitive expansion
+ for (int i=1; i<order; ++i)
+ ser += pow(1-s,i) * (numeric(1,i)*(I*Pi+log(s-1)) - numeric(1,i*i));
+ // substitute the argument's series expansion
+ ser = ser.subs(s==x.series(rel, order));
+ // maybe that was terminating, so add a proper order term
+ epvector nseq;
+ nseq.push_back(expair(Order(_ex1), order));
+ ser += pseries(rel, nseq);
+ // reexpanding it will collapse the series again
+ return ser.series(rel, order);
+ }
+ // third special case: x real, >=1 (branch cut)
+ if (!(options & series_options::suppress_branchcut) &&
+ ex_to<numeric>(x_pt).is_real() && ex_to<numeric>(x_pt)>1) {
+ // method:
+ // This is the branch cut: assemble the primitive series manually
+ // and then add the corresponding complex step function.
+ const symbol &s = ex_to<symbol>(rel.lhs());
+ const ex point = rel.rhs();
+ const symbol foo;
+ epvector seq;
+ // zeroth order term:
+ seq.push_back(expair(Li2(x_pt), _ex0));
+ // compute the intermediate terms:
+ ex replarg = series(Li2(x), s==foo, order);
+ for (unsigned i=1; i<replarg.nops()-1; ++i)
+ seq.push_back(expair((replarg.op(i)/power(s-foo,i)).series(foo==point,1,options).op(0).subs(foo==s),i));
+ // append an order term:
+ seq.push_back(expair(Order(_ex1), replarg.nops()-1));
+ return pseries(rel, seq);
+ }
+ }
+ // all other cases should be safe, by now:
+ throw do_taylor(); // caught by function::series()
+}
+
+REGISTER_FUNCTION(Li2, eval_func(Li2_eval).
+ evalf_func(Li2_evalf).
+ derivative_func(Li2_deriv).
+ series_func(Li2_series).
+ latex_name("\\mbox{Li}_2"));