]> www.ginac.de Git - ginac.git/blobdiff - ginac/pseries.cpp
fixed another power::series() bug [Alexei Sheplyakov]
[ginac.git] / ginac / pseries.cpp
index 8820e4ea750d30d1e5f735188e6e9b8493d404d3..4ec5bcefca4e1f559a7b326f9b9c400fc5f55f4f 100644 (file)
@@ -4,7 +4,7 @@
  *  methods for series expansion. */
 
 /*
- *  GiNaC Copyright (C) 1999-2003 Johannes Gutenberg University Mainz, Germany
+ *  GiNaC Copyright (C) 1999-2004 Johannes Gutenberg University Mainz, Germany
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -21,7 +21,7 @@
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
-#include <iostream>
+#include <numeric>
 #include <stdexcept>
 
 #include "pseries.h"
@@ -188,7 +188,7 @@ void pseries::do_print_python(const print_python & c, unsigned level) const
 
 void pseries::do_print_tree(const print_tree & c, unsigned level) const
 {
-       c.s << std::string(level, ' ') << class_name()
+       c.s << std::string(level, ' ') << class_name() << " @" << this
            << std::hex << ", hash=0x" << hashvalue << ", flags=0x" << flags << std::dec
            << std::endl;
        size_t num = seq.size();
@@ -407,6 +407,23 @@ ex pseries::evalf(int level) const
        return (new pseries(relational(var,point), new_seq))->setflag(status_flags::dynallocated | status_flags::evaluated);
 }
 
+ex pseries::conjugate() const
+{
+       epvector * newseq = conjugateepvector(seq);
+       ex newvar = var.conjugate();
+       ex newpoint = point.conjugate();
+
+       if (!newseq     && are_ex_trivially_equal(newvar, var) && are_ex_trivially_equal(point, newpoint)) {
+               return *this;
+       }
+
+       ex result = (new pseries(newvar==newpoint, newseq ? *newseq : seq))->setflag(status_flags::dynallocated);
+       if (newseq) {
+               delete newseq;
+       }
+       return result;
+}
+
 ex pseries::subs(const exmap & m, unsigned options) const
 {
        // If expansion variable is being substituted, convert the series to a
@@ -512,14 +529,23 @@ bool pseries::is_terminating() const
 ex basic::series(const relational & r, int order, unsigned options) const
 {
        epvector seq;
+       const symbol &s = ex_to<symbol>(r.lhs());
+
+       // default for order-values that make no sense for Taylor expansion
+       if ((order <= 0) && this->has(s)) {
+               seq.push_back(expair(Order(_ex1), order));
+               return pseries(r, seq);
+       }
+
+       // do Taylor expansion
        numeric fac = 1;
        ex deriv = *this;
        ex coeff = deriv.subs(r, subs_options::no_pattern);
-       const symbol &s = ex_to<symbol>(r.lhs());
-       
-       if (!coeff.is_zero())
+
+       if (!coeff.is_zero()) {
                seq.push_back(expair(coeff, _ex0));
-       
+       }
+
        int n;
        for (n=1; n<order; ++n) {
                fac = fac.mul(n);
@@ -748,18 +774,65 @@ ex mul::series(const relational & r, int order, unsigned options) const
 {
        pseries acc; // Series accumulator
 
-       // Multiply with remaining terms
+       GINAC_ASSERT(is_a<symbol>(r.lhs()));
+       const ex& sym = r.lhs();
+               
+       // holds ldegrees of the series of individual factors
+       std::vector<int> ldegrees;
+
+       // find minimal degrees
        const epvector::const_iterator itbeg = seq.begin();
        const epvector::const_iterator itend = seq.end();
        for (epvector::const_iterator it=itbeg; it!=itend; ++it) {
-               ex op = recombine_pair_to_ex(*it).series(r, order, options);
+
+               ex expon = it->coeff;
+               int factor = 1;
+               ex buf;
+               if (expon.info(info_flags::integer)) {
+                       buf = it->rest;
+                       factor = ex_to<numeric>(expon).to_int();
+               } else {
+                       buf = recombine_pair_to_ex(*it);
+               }
+
+               int real_ldegree = 0;
+               try {
+                       real_ldegree = buf.expand().ldegree(sym-r.rhs());
+               } catch (std::runtime_error) {}
+
+               if (real_ldegree == 0) {
+                       int orderloop = 0;
+                       do {
+                               orderloop++;
+                               real_ldegree = buf.series(r, orderloop, options).ldegree(sym);
+                       } while (real_ldegree == orderloop);
+               }
+
+               ldegrees.push_back(factor * real_ldegree);
+       }
+
+       int degsum = std::accumulate(ldegrees.begin(), ldegrees.end(), 0);
+
+       if (degsum >= order) {
+               epvector epv;
+               epv.push_back(expair(Order(_ex1), order));
+               return (new pseries(r, epv))->setflag(status_flags::dynallocated);
+       }
+
+       // Multiply with remaining terms
+       std::vector<int>::const_iterator itd = ldegrees.begin();
+       for (epvector::const_iterator it=itbeg; it!=itend; ++it, ++itd) {
+
+               // do series expansion with adjusted order
+               ex op = recombine_pair_to_ex(*it).series(r, order-degsum+(*itd), options);
 
                // Series multiplication
-               if (it==itbeg)
+               if (it == itbeg)
                        acc = ex_to<pseries>(op);
                else
                        acc = ex_to<pseries>(acc.mul_series(ex_to<pseries>(op)));
        }
+
        return acc.mul_const(ex_to<numeric>(overall_coeff));
 }
 
@@ -806,6 +879,9 @@ ex pseries::power_const(const numeric &p, int deg) const
        if (!(p*ldeg).is_integer())
                throw std::runtime_error("pseries::power_const(): trying to assemble a Puiseux series");
 
+       // adjust number of coefficients
+       deg = deg - (p*ldeg).to_int();
+       
        // O(x^n)^(-m) is undefined
        if (seq.size() == 1 && is_order_function(seq[0].rest) && p.real().is_negative())
                throw pole_error("pseries::power_const(): division by zero",1);
@@ -814,7 +890,6 @@ ex pseries::power_const(const numeric &p, int deg) const
        exvector co;
        co.reserve(deg);
        co.push_back(power(coeff(var, ldeg), p));
-       bool all_sums_zero = true;
        for (int i=1; i<deg; ++i) {
                ex sum = _ex0;
                for (int j=1; j<=i; ++j) {
@@ -825,8 +900,6 @@ ex pseries::power_const(const numeric &p, int deg) const
                        } else
                                sum += (p * j - (i - j)) * co[i - j] * c;
                }
-               if (!sum.is_zero())
-                       all_sums_zero = false;
                co.push_back(sum / coeff(var, ldeg) / i);
        }
        
@@ -841,8 +914,9 @@ ex pseries::power_const(const numeric &p, int deg) const
                        break;
                }
        }
-       if (!higher_order && !all_sums_zero)
+       if (!higher_order)
                new_seq.push_back(expair(Order(_ex1), p * ldeg + deg));
+
        return pseries(relational(var,point), new_seq);
 }
 
@@ -876,13 +950,15 @@ ex power::series(const relational & r, int order, unsigned options) const
        } catch (pole_error) {
                must_expand_basis = true;
        }
-               
+
        // Is the expression of type something^(-int)?
-       if (!must_expand_basis && !exponent.info(info_flags::negint))
+       if (!must_expand_basis && !exponent.info(info_flags::negint)
+        && (!is_a<add>(basis) || !is_a<numeric>(exponent)))
                return basic::series(r, order, options);
-               
+
        // Is the expression of type 0^something?
-       if (!must_expand_basis && !basis.subs(r, subs_options::no_pattern).is_zero())
+       if (!must_expand_basis && !basis.subs(r, subs_options::no_pattern).is_zero()
+        && (!is_a<add>(basis) || !is_a<numeric>(exponent)))
                return basic::series(r, order, options);
 
        // Singularity encountered, is the basis equal to (var - point)?
@@ -896,8 +972,38 @@ ex power::series(const relational & r, int order, unsigned options) const
        }
 
        // No, expand basis into series
-       ex e = basis.series(r, order, options);
-       return ex_to<pseries>(e).power_const(ex_to<numeric>(exponent), order);
+
+       numeric numexp;
+       if (is_a<numeric>(exponent)) {
+               numexp = ex_to<numeric>(exponent);
+       } else {
+               numexp = 0;
+       }
+       const ex& sym = r.lhs();
+       // find existing minimal degree
+       int real_ldegree = basis.expand().ldegree(sym-r.rhs());
+       if (real_ldegree == 0) {
+               int orderloop = 0;
+               do {
+                       orderloop++;
+                       real_ldegree = basis.series(r, orderloop, options).ldegree(sym);
+               } while (real_ldegree == orderloop);
+       }
+
+       if (!(real_ldegree*numexp).is_integer())
+               throw std::runtime_error("pseries::power_const(): trying to assemble a Puiseux series");
+       ex e = basis.series(r, (order + real_ldegree*(1-numexp)).to_int(), options);
+       
+       ex result;
+       try {
+               result = ex_to<pseries>(e).power_const(numexp, order);
+       } catch (pole_error) {
+               epvector ser;
+               ser.push_back(expair(Order(_ex1), order));
+               result = pseries(r, ser);
+       }
+
+       return result;
 }