+ if (x.info(info_flags::numeric)) {
+ if (x.is_zero()) // log(0) -> infinity
+ throw(pole_error("log_eval(): log(0)",0));
+ if (x.info(info_flags::rational) && 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*_ex1_2);
+ if (x.is_equal(-I)) // log(-I) -> -Pi*I/2
+ return (Pi*I*_ex_1_2);
+
+ // log(float) -> float
+ if (!x.info(info_flags::crational))
+ return log(ex_to<numeric>(x));
+ }
+
+ // log(exp(t)) -> t (if -Pi < t.imag() <= Pi):
+ if (is_ex_the_function(x, exp)) {
+ const ex &t = x.op(0);
+ if (t.info(info_flags::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_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 (arg.diff(ex_to<symbol>(rel.lhs())).is_zero()) {
+ throw do_taylor();
+ }
+
+ 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)
+ if (n == 0 && coeff == 1) {
+ epvector epv;
+ ex acc = (new pseries(rel, epv))->setflag(status_flags::dynallocated);
+ epv.reserve(2);
+ epv.push_back(expair(-1, _ex0));
+ epv.push_back(expair(Order(_ex1), order));
+ ex rest = pseries(rel, epv).add_series(argser);
+ for (int i = order-1; i>0; --i) {
+ epvector cterm;
+ cterm.reserve(1);
+ cterm.push_back(expair(i%2 ? _ex1/i : _ex_1/i, _ex0));
+ acc = pseries(rel, cterm).add_series(ex_to<pseries>(acc));
+ acc = (ex_to<pseries>(rest)).mul_series(ex_to<pseries>(acc));
+ }
+ return acc;
+ }
+ 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);
+ 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()
+}
+
+static ex log_real_part(const ex & x)
+{
+ if (x.info(info_flags::nonnegative))
+ return log(x).hold();
+ return log(abs(x));
+}
+
+static ex log_imag_part(const ex & x)
+{
+ if (x.info(info_flags::nonnegative))
+ return 0;
+ return atan2(GiNaC::imag_part(x), GiNaC::real_part(x));
+}
+
+static ex log_expand(const ex & arg, unsigned options)
+{
+ if ((options & expand_options::expand_transcendental)
+ && is_exactly_a<mul>(arg) && !arg.info(info_flags::indefinite)) {
+ exvector sumseq;
+ exvector prodseq;
+ sumseq.reserve(arg.nops());
+ prodseq.reserve(arg.nops());
+ bool possign=true;
+
+ // searching for positive/negative factors
+ for (const_iterator i = arg.begin(); i != arg.end(); ++i) {
+ ex e;
+ if (options & expand_options::expand_function_args)
+ e=i->expand(options);
+ else
+ e=*i;
+ if (e.info(info_flags::positive))
+ sumseq.push_back(log(e));
+ else if (e.info(info_flags::negative)) {
+ sumseq.push_back(log(-e));
+ possign = !possign;
+ } else
+ prodseq.push_back(e);
+ }
+
+ if (sumseq.size() > 0) {
+ ex newarg;
+ if (options & expand_options::expand_function_args)
+ newarg=((possign?_ex1:_ex_1)*mul(prodseq)).expand(options);
+ else {
+ newarg=(possign?_ex1:_ex_1)*mul(prodseq);
+ ex_to<basic>(newarg).setflag(status_flags::purely_indefinite);
+ }
+ return add(sumseq)+log(newarg);
+ } else {
+ if (!(options & expand_options::expand_function_args))
+ ex_to<basic>(arg).setflag(status_flags::purely_indefinite);
+ }
+ }
+
+ if (options & expand_options::expand_function_args)
+ return log(arg.expand(options)).hold();
+ else
+ return log(arg).hold();
+}
+
+static ex log_conjugate(const ex & x)
+{
+ // conjugate(log(x))==log(conjugate(x)) unless on the branch cut which
+ // runs along the negative real axis.
+ if (x.info(info_flags::positive)) {
+ return log(x);
+ }
+ if (is_exactly_a<numeric>(x) &&
+ !x.imag_part().is_zero()) {
+ return log(x.conjugate());
+ }
+ return conjugate_function(log(x)).hold();
+}
+
+REGISTER_FUNCTION(log, eval_func(log_eval).
+ evalf_func(log_evalf).
+ expand_func(log_expand).
+ derivative_func(log_deriv).
+ series_func(log_series).
+ real_part_func(log_real_part).
+ imag_part_func(log_imag_part).
+ conjugate_func(log_conjugate).
+ latex_name("\\ln"));