- GINAC_ASSERT(deriv_param==0);
-
- // d/dx log(x) -> 1/x
- return power(x, _ex_1());
-}
-
-/*static ex log_series(const ex &x, const relational &rel, int order)
-{
- const ex x_pt = x.subs(rel);
- if (!x_pt.info(info_flags::negative) && !x_pt.is_zero())
- throw do_taylor(); // caught by function::series()
- // now we either have to care for the branch cut or the branch point:
- if (x_pt.is_zero()) { // branch point: return a plain log(x).
- epvector seq;
- seq.push_back(expair(log(x), _ex0()));
- return pseries(rel, seq);
- } // the branch cut:
- const ex point = rel.rhs();
- const symbol *s = static_cast<symbol *>(rel.lhs().bp);
- const symbol foo;
- // compute the formal series:
- ex replx = series(log(x),*s==foo,order).subs(foo==point);
- epvector seq;
- // FIXME: this is probably off by 2 or so:
- seq.push_back(expair(-I*csgn(x*I)*Pi,_ex0()));
- seq.push_back(expair(Order(_ex1()),order));
- return series(replx + pseries(rel, seq),rel,order);
-}*/
-
-static ex log_series(const ex &x, const relational &r, int order)
-{
- if (x.subs(r).is_zero()) {
+ GINAC_ASSERT(deriv_param==0);
+
+ // d/dx log(x) -> 1/x
+ return power(x, _ex_1);
+}
+
+static ex log_series(const ex &arg,
+ const relational &rel,
+ int order,
+ unsigned options)
+{
+ GINAC_ASSERT(is_a<symbol>(rel.lhs()));
+ ex arg_pt;
+ bool must_expand_arg = false;
+ // maybe substitution of rel into arg fails because of a pole
+ try {
+ arg_pt = arg.subs(rel, subs_options::no_pattern);
+ } catch (pole_error) {
+ must_expand_arg = true;
+ }
+ // or we are at the branch point anyways
+ if (arg_pt.is_zero())
+ must_expand_arg = true;
+
+ if (must_expand_arg) {
+ // method:
+ // This is the branch point: Series expand the argument first, then
+ // trivially factorize it to isolate that part which has constant
+ // leading coefficient in this fashion:
+ // x^n + x^(n+1) +...+ Order(x^(n+m)) -> x^n * (1 + x +...+ Order(x^m)).
+ // Return a plain n*log(x) for the x^n part and series expand the
+ // other part. Add them together and reexpand again in order to have
+ // one unnested pseries object. All this also works for negative n.
+ pseries argser; // series expansion of log's argument
+ unsigned extra_ord = 0; // extra expansion order
+ do {
+ // oops, the argument expanded to a pure Order(x^something)...
+ argser = ex_to<pseries>(arg.series(rel, order+extra_ord, options));
+ ++extra_ord;
+ } while (!argser.is_terminating() && argser.nops()==1);
+
+ const symbol &s = ex_to<symbol>(rel.lhs());
+ const ex &point = rel.rhs();
+ const int n = argser.ldegree(s);
+ epvector seq;
+ // construct what we carelessly called the n*log(x) term above
+ const ex coeff = argser.coeff(s, n);
+ // expand the log, but only if coeff is real and > 0, since otherwise
+ // it would make the branch cut run into the wrong direction
+ if (coeff.info(info_flags::positive))
+ seq.push_back(expair(n*log(s-point)+log(coeff), _ex0));
+ else
+ seq.push_back(expair(log(coeff*pow(s-point, n)), _ex0));
+
+ if (!argser.is_terminating() || argser.nops()!=1) {
+ // in this case n more (or less) terms are needed
+ // (sadly, to generate them, we have to start from the beginning)
+ const ex newarg = ex_to<pseries>((arg/coeff).series(rel, order+n, options)).shift_exponents(-n).convert_to_poly(true);
+ return pseries(rel, seq).add_series(ex_to<pseries>(log(newarg).series(rel, order, options)));
+ } else // it was a monomial
+ return pseries(rel, seq);
+ }
+ if (!(options & series_options::suppress_branchcut) &&
+ arg_pt.info(info_flags::negative)) {
+ // 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;
+ const ex replarg = series(log(arg), s==foo, order).subs(foo==point, subs_options::no_pattern);