- if (x.info(info_flags::numeric)) {
- if (x.is_equal(_ex1())) // log(1) -> 0
- return _ex0();
- if (x.is_equal(_ex_1())) // log(-1) -> I*Pi
- return (I*Pi);
- if (x.is_equal(I)) // log(I) -> Pi*I/2
- return (Pi*I*_num1_2());
- if (x.is_equal(-I)) // log(-I) -> -Pi*I/2
- return (Pi*I*_num_1_2());
- if (x.is_equal(_ex0())) // log(0) -> infinity
- throw(std::domain_error("log_eval(): log(0)"));
- // log(float)
- if (!x.info(info_flags::crational))
- return log_evalf(x);
- }
- // log(exp(t)) -> t (if -Pi < t.imag() <= Pi):
- if (is_ex_the_function(x, exp)) {
- ex t = x.op(0);
- if (t.info(info_flags::numeric)) {
- numeric nt = ex_to_numeric(t);
- if (nt.is_real())
- return t;
- }
- }
-
- return log(x).hold();
-}
-
-static ex log_diff(const ex & x, unsigned diff_param)
-{
- GINAC_ASSERT(diff_param==0);
-
- // d/dx log(x) -> 1/x
- return power(x, _ex_1());
-}
-
-REGISTER_FUNCTION(log, log_eval, log_evalf, log_diff, NULL);
+ if (x.info(info_flags::numeric)) {
+ if (x.is_equal(_ex0())) // log(0) -> infinity
+ throw(pole_error("log_eval(): log(0)",0));
+ if (x.info(info_flags::real) && x.info(info_flags::negative))
+ return (log(-x)+I*Pi);
+ if (x.is_equal(_ex1())) // log(1) -> 0
+ return _ex0();
+ if (x.is_equal(I)) // log(I) -> Pi*I/2
+ return (Pi*I*_num1_2());
+ if (x.is_equal(-I)) // log(-I) -> -Pi*I/2
+ return (Pi*I*_num_1_2());
+ // log(float)
+ if (!x.info(info_flags::crational))
+ return log_evalf(x);
+ }
+ // log(exp(t)) -> t (if -Pi < t.imag() <= Pi):
+ if (is_ex_the_function(x, exp)) {
+ ex t = x.op(0);
+ if (t.info(info_flags::numeric)) {
+ numeric nt = ex_to_numeric(t);
+ if (nt.is_real())
+ return t;
+ }
+ }
+
+ return log(x).hold();
+}
+
+static ex log_deriv(const ex & x, unsigned deriv_param)
+{
+ 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_ex_exactly_of_type(rel.lhs(),symbol));
+ 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);
+ } 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 + Order(x^(n+m)) -> x^n * (1 + 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.
+ const pseries argser = ex_to_pseries(arg.series(rel, order, options));
+ const symbol *s = static_cast<symbol *>(rel.lhs().bp);
+ const ex point = rel.rhs();
+ const int n = argser.ldegree(*s);
+ epvector seq;
+ // construct what we carelessly called the n*log(x) term above
+ 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 terms are needed
+ // (sadly, to generate them, we have to start from the beginning)
+ 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 = static_cast<symbol *>(rel.lhs().bp);
+ const ex point = rel.rhs();
+ const symbol foo;
+ ex replarg = series(log(arg), *s==foo, order).subs(foo==point);
+ epvector seq;
+ seq.push_back(expair(-I*csgn(arg*I)*Pi, _ex0()));
+ seq.push_back(expair(Order(_ex1()), order));
+ return series(replarg - I*Pi + pseries(rel, seq), rel, order);
+ }
+ throw do_taylor(); // caught by function::series()
+}
+
+REGISTER_FUNCTION(log, eval_func(log_eval).
+ evalf_func(log_evalf).
+ derivative_func(log_deriv).
+ series_func(log_series));