static ex Order_series(const ex & x, const relational & r, int order, unsigned options) { // Just wrap the function into a pseries object GINAC_ASSERT(is_a<symbol>(r.lhs())); const symbol &s = ex_to<symbol>(r.lhs()); epvector new_seq { expair(Order(_ex1), numeric(std::min(x.ldegree(s), order))) }; return pseries(r, std::move(new_seq)); }
static ex eta_series(const ex & x, const ex & y, const relational & rel, int order, unsigned options) { const ex x_pt = x.subs(rel, subs_options::no_pattern); const ex y_pt = y.subs(rel, subs_options::no_pattern); if ((x_pt.info(info_flags::numeric) && x_pt.info(info_flags::negative)) || (y_pt.info(info_flags::numeric) && y_pt.info(info_flags::negative)) || ((x_pt*y_pt).info(info_flags::numeric) && (x_pt*y_pt).info(info_flags::negative))) throw (std::domain_error("eta_series(): on discontinuity")); epvector seq { expair(eta(x_pt,y_pt), _ex0) }; return pseries(rel, std::move(seq)); }
static ex csgn_series(const ex & arg, const relational & rel, int order, unsigned options) { const ex arg_pt = arg.subs(rel, subs_options::no_pattern); if (arg_pt.info(info_flags::numeric) && ex_to<numeric>(arg_pt).real().is_zero() && !(options & series_options::suppress_branchcut)) throw (std::domain_error("csgn_series(): on imaginary axis")); epvector seq { expair(csgn(arg_pt), _ex0) }; return pseries(rel, std::move(seq)); }
static ex atan_series(const ex &arg, const relational &rel, int order, unsigned options) { GINAC_ASSERT(is_a<symbol>(rel.lhs())); // method: // Taylor series where there is no pole or cut falls back to atan_deriv. // There are two branch cuts, one runnig from I up the imaginary axis and // one running from -I down the imaginary axis. The points I and -I are // poles. // On the branch cuts and the poles series expand // (log(1+I*x)-log(1-I*x))/(2*I) // instead. const ex arg_pt = arg.subs(rel, subs_options::no_pattern); if (!(I*arg_pt).info(info_flags::real)) throw do_taylor(); // Re(x) != 0 if ((I*arg_pt).info(info_flags::real) && abs(I*arg_pt)<_ex1) throw do_taylor(); // Re(x) == 0, but abs(x)<1 // care for the poles, using the defining formula for atan()... if (arg_pt.is_equal(I) || arg_pt.is_equal(-I)) return ((log(1+I*arg)-log(1-I*arg))/(2*I)).series(rel, order, options); if (!(options & series_options::suppress_branchcut)) { // 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(atan(arg), s==foo, order).subs(foo==point, subs_options::no_pattern); ex Order0correction = replarg.op(0)+csgn(arg)*Pi*_ex_1_2; if ((I*arg_pt)<_ex0) Order0correction += log((I*arg_pt+_ex_1)/(I*arg_pt+_ex1))*I*_ex_1_2; else Order0correction += log((I*arg_pt+_ex1)/(I*arg_pt+_ex_1))*I*_ex1_2; epvector seq; if (order > 0) { seq.reserve(2); seq.push_back(expair(Order0correction, _ex0)); } seq.push_back(expair(Order(_ex1), order)); return series(replarg - pseries(rel, std::move(seq)), rel, order); } throw do_taylor(); }
double incbet( double aa, double bb, double xx ) { double a, b, t, x, xc, w, y; int flag; if( aa <= 0.0 || bb <= 0.0 ) goto domerr; if( (xx <= 0.0) || ( xx >= 1.0) ) { if( xx == 0.0 ) return(0.0); if( xx == 1.0 ) return( 1.0 ); domerr: mtherr( "incbet", DOMAIN ); return( 0.0 ); } flag = 0; if( (bb * xx) <= 1.0 && xx <= 0.95) { t = pseries(aa, bb, xx); goto done; } w = 1.0 - xx; /* Reverse a and b if x is greater than the mean. */ if( xx > (aa/(aa+bb)) ) { flag = 1; a = bb; b = aa; xc = xx; x = w; } else { a = aa; b = bb; xc = w; x = xx; } if( flag == 1 && (b * x) <= 1.0 && x <= 0.95) { t = pseries(a, b, x); goto done; } /* Choose expansion for better convergence. */ y = x * (a+b-2.0) - (a-1.0); if( y < 0.0 ) w = incbcf( a, b, x ); else w = incbd( a, b, x ) / xc; /* Multiply w by the factor a b _ _ _ x (1-x) | (a+b) / ( a | (a) | (b) ) . */ y = a * log(x); t = b * log(xc); if( (a+b) < MAXGAM && fabs(y) < MAXLOG && fabs(t) < MAXLOG ) { t = pow(xc,b); t *= pow(x,a); t /= a; t *= w; t *= gammafn(a+b) / (gammafn(a) * gammafn(b)); goto done; } /* Resort to logarithms. */ y += t + lgam(a+b) - lgam(a) - lgam(b); y += log(w/a); if( y < MINLOG ) t = 0.0; else t = exp(y); done: if( flag == 1 ) { if( t <= MACHEP ) t = 1.0 - MACHEP; else t = 1.0 - t; } return( t ); }
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) { ex rest = pseries(rel, epvector{expair(-1, _ex0), expair(Order(_ex1), order)}).add_series(argser); ex acc = dynallocate<pseries>(rel, epvector()); for (int i = order-1; i>0; --i) { epvector cterm { expair(i%2 ? _ex1/i : _ex_1/i, _ex0) }; acc = pseries(rel, std::move(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, std::move(seq)).add_series(ex_to<pseries>(log(newarg).series(rel, order, options))); } else // it was a monomial return pseries(rel, std::move(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; if (order > 0) { seq.reserve(2); seq.push_back(expair(-I*csgn(arg*I)*Pi, _ex0)); } seq.push_back(expair(Order(_ex1), order)); return series(replarg - I*Pi + pseries(rel, std::move(seq)), rel, order); } throw do_taylor(); // caught by function::series() }
static ex Li2_series(const ex &x, const relational &rel, int order, unsigned options) { const ex x_pt = x.subs(rel, subs_options::no_pattern); 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_p); // substitute the argument's series expansion ser = ser.subs(s==x.series(rel, order), subs_options::no_pattern); // maybe that was terminating, so add a proper order term epvector nseq { expair(Order(_ex1), order) }; ser += pseries(rel, std::move(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), subs_options::no_pattern); // maybe that was terminating, so add a proper order term epvector nseq { expair(Order(_ex1), order) }; ser += pseries(rel, std::move(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 (size_t 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, subs_options::no_pattern),i)); // append an order term: seq.push_back(expair(Order(_ex1), replarg.nops()-1)); return pseries(rel, std::move(seq)); } } // all other cases should be safe, by now: throw do_taylor(); // caught by function::series() }