]> www.ginac.de Git - ginac.git/blobdiff - ginac/power.cpp
Infrastructure: Update baseline language check M4 macros.
[ginac.git] / ginac / power.cpp
index f951e4fdbafdea30eb10df0102c29b8fffdf4bfe..1a452e3fcc91f36a0e78ec767e6d3cf7efc410db 100644 (file)
@@ -42,6 +42,7 @@
 #include <limits>
 #include <stdexcept>
 #include <vector>
+#include <algorithm>
 
 namespace GiNaC {
 
@@ -571,7 +572,7 @@ ex power::eval(int level) const
                if (num_exponent->is_pos_integer() &&
                    ebasis.return_type() != return_types::commutative &&
                    !is_a<matrix>(ebasis)) {
-                       return ncmul(exvector(num_exponent->to_int(), ebasis), true);
+                       return ncmul(exvector(num_exponent->to_int(), ebasis));
                }
        }
        
@@ -695,51 +696,71 @@ ex power::conjugate() const
 
 ex power::real_part() const
 {
+       // basis == a+I*b, exponent == c+I*d
+       const ex a = basis.real_part();
+       const ex c = exponent.real_part();
+       if (basis.is_equal(a) && exponent.is_equal(c)) {
+               // Re(a^c)
+               return *this;
+       }
+
+       const ex b = basis.imag_part();
        if (exponent.info(info_flags::integer)) {
-               ex basis_real = basis.real_part();
-               if (basis_real == basis)
-                       return *this;
-               realsymbol a("a"),b("b");
-               ex result;
-               if (exponent.info(info_flags::posint))
-                       result = power(a+I*b,exponent);
-               else
-                       result = power(a/(a*a+b*b)-I*b/(a*a+b*b),-exponent);
-               result = result.expand();
-               result = result.real_part();
-               result = result.subs(lst( a==basis_real, b==basis.imag_part() ));
+               // Re((a+I*b)^c)  w/  c ∈ ℤ
+               long N = ex_to<numeric>(c).to_long();
+               // Use real terms in Binomial expansion to construct
+               // Re(expand(power(a+I*b, N))).
+               long NN = N > 0 ? N : -N;
+               ex numer = N > 0 ? _ex1 : power(power(a,2) + power(b,2), NN);
+               ex result = 0;
+               for (long n = 0; n <= NN; n += 2) {
+                       ex term = binomial(NN, n) * power(a, NN-n) * power(b, n) / numer;
+                       if (n % 4 == 0) {
+                               result += term;  // sign: I^n w/ n == 4*m
+                       } else {
+                               result -= term;  // sign: I^n w/ n == 4*m+2
+                       }
+               }
                return result;
        }
-       
-       ex a = basis.real_part();
-       ex b = basis.imag_part();
-       ex c = exponent.real_part();
-       ex d = exponent.imag_part();
+
+       // Re((a+I*b)^(c+I*d))
+       const ex d = exponent.imag_part();
        return power(abs(basis),c)*exp(-d*atan2(b,a))*cos(c*atan2(b,a)+d*log(abs(basis)));
 }
 
 ex power::imag_part() const
 {
+       const ex a = basis.real_part();
+       const ex c = exponent.real_part();
+       if (basis.is_equal(a) && exponent.is_equal(c)) {
+               // Im(a^c)
+               return 0;
+       }
+
+       const ex b = basis.imag_part();
        if (exponent.info(info_flags::integer)) {
-               ex basis_real = basis.real_part();
-               if (basis_real == basis)
-                       return 0;
-               realsymbol a("a"),b("b");
-               ex result;
-               if (exponent.info(info_flags::posint))
-                       result = power(a+I*b,exponent);
-               else
-                       result = power(a/(a*a+b*b)-I*b/(a*a+b*b),-exponent);
-               result = result.expand();
-               result = result.imag_part();
-               result = result.subs(lst( a==basis_real, b==basis.imag_part() ));
+               // Im((a+I*b)^c)  w/  c ∈ ℤ
+               long N = ex_to<numeric>(c).to_long();
+               // Use imaginary terms in Binomial expansion to construct
+               // Im(expand(power(a+I*b, N))).
+               long p = N > 0 ? 1 : 3;  // modulus for positive sign
+               long NN = N > 0 ? N : -N;
+               ex numer = N > 0 ? _ex1 : power(power(a,2) + power(b,2), NN);
+               ex result = 0;
+               for (long n = 1; n <= NN; n += 2) {
+                       ex term = binomial(NN, n) * power(a, NN-n) * power(b, n) / numer;
+                       if (n % 4 == p) {
+                               result += term;  // sign: I^n w/ n == 4*m+p
+                       } else {
+                               result -= term;  // sign: I^n w/ n == 4*m+2+p
+                       }
+               }
                return result;
        }
-       
-       ex a=basis.real_part();
-       ex b=basis.imag_part();
-       ex c=exponent.real_part();
-       ex d=exponent.imag_part();
+
+       // Im((a+I*b)^(c+I*d))
+       const ex d = exponent.imag_part();
        return power(abs(basis),c)*exp(-d*atan2(b,a))*sin(c*atan2(b,a)+d*log(abs(basis)));
 }
 
@@ -755,7 +776,7 @@ ex power::derivative(const symbol & s) const
                newseq.reserve(2);
                newseq.push_back(expair(basis, exponent - _ex1));
                newseq.push_back(expair(basis.diff(s), _ex1));
-               return mul(newseq, exponent);
+               return mul(std::move(newseq), exponent);
        } else {
                // D(b^e) = b^e * (D(e)*ln(b) + e*D(b)/b)
                return mul(*this,
@@ -829,9 +850,9 @@ ex power::expand(unsigned options) const
                // In either case we set a flag to avoid the second run on a part
                // which does not have positive/negative terms.
                if (prodseq.size() > 0) {
-                       ex newbasis = coeff*mul(powseq);
+                       ex newbasis = coeff*mul(std::move(powseq));
                        ex_to<basic>(newbasis).setflag(status_flags::purely_indefinite);
-                       return ((new mul(prodseq))->setflag(status_flags::dynallocated)*(new power(newbasis, exponent))->setflag(status_flags::dynallocated).expand(options)).expand(options);
+                       return ((new mul(std::move(prodseq)))->setflag(status_flags::dynallocated)*(new power(newbasis, exponent))->setflag(status_flags::dynallocated).expand(options)).expand(options);
                } else
                        ex_to<basic>(basis).setflag(status_flags::purely_indefinite);
        }
@@ -851,7 +872,7 @@ ex power::expand(unsigned options) const
                // Make sure that e.g. (x+y)^(2+a) expands the (x+y)^2 factor
                if (ex_to<numeric>(a.overall_coeff).is_integer()) {
                        const numeric &num_exponent = ex_to<numeric>(a.overall_coeff);
-                       int int_exponent = num_exponent.to_int();
+                       long int_exponent = num_exponent.to_int();
                        if (int_exponent > 0 && is_exactly_a<add>(expanded_basis))
                                distrseq.push_back(expand_add(ex_to<add>(expanded_basis), int_exponent, options));
                        else
@@ -875,7 +896,7 @@ ex power::expand(unsigned options) const
        
        // integer numeric exponent
        const numeric & num_exponent = ex_to<numeric>(expanded_exponent);
-       int int_exponent = num_exponent.to_int();
+       long int_exponent = num_exponent.to_long();
        
        // (x+y)^n, n>0
        if (int_exponent > 0 && is_exactly_a<add>(expanded_basis))
@@ -1071,7 +1092,7 @@ public:
  *  where n = p1+p2+...+pk, i.e. p is a partition of n.
  */
 const numeric
-multinomial_coefficient(const std::vector<int> p)
+multinomial_coefficient(const std::vector<int> p)
 {
        numeric n = 0, d = 1;
        for (auto & it : p) {
@@ -1083,9 +1104,10 @@ multinomial_coefficient(const std::vector<int> p)
 
 }  // anonymous namespace
 
+
 /** expand a^n where a is an add and n is a positive integer.
  *  @see power::expand */
-ex power::expand_add(const add & a, int n, unsigned options) const
+ex power::expand_add(const add & a, long n, unsigned options)
 {
        // The special case power(+(x,...y;x),2) can be optimized better.
        if (n==2)
@@ -1147,7 +1169,7 @@ ex power::expand_add(const add & a, int n, unsigned options) const
        // i.e. the number of unordered arrangements of m nonnegative integers
        // which sum up to n.  It is frequently written as C_n(m) and directly
        // related with binomial coefficients: binomial(n+m-1,m-1).
-       size_t result_size = binomial(numeric(n+a.nops()-1), numeric(a.nops()-1)).to_int();
+       size_t result_size = binomial(numeric(n+a.nops()-1), numeric(a.nops()-1)).to_long();
        if (!a.overall_coeff.is_zero()) {
                // the result's overall_coeff is one of the terms
                --result_size;
@@ -1176,18 +1198,19 @@ ex power::expand_add(const add & a, int n, unsigned options) const
                partition_generator partitions(k, a.seq.size());
                do {
                        const std::vector<int>& partition = partitions.current();
+                       // All monomials of this partition have the same number of terms and the same coefficient.
+                       const unsigned msize = std::count_if(partition.begin(), partition.end(), [](int i) { return i > 0; });
                        const numeric coeff = multinomial_coefficient(partition) * binomial_coefficient;
 
                        // Iterate over all compositions of the current partition.
                        composition_generator compositions(partition);
                        do {
                                const std::vector<int>& exponent = compositions.current();
-                               exvector term;
-                               term.reserve(n);
+                               exvector monomial;
+                               monomial.reserve(msize);
                                numeric factor = coeff;
                                for (unsigned i = 0; i < exponent.size(); ++i) {
                                        const ex & r = a.seq[i].rest;
-                                       const ex & c = a.seq[i].coeff;
                                        GINAC_ASSERT(!is_exactly_a<add>(r));
                                        GINAC_ASSERT(!is_exactly_a<power>(r) ||
                                                     !is_exactly_a<numeric>(ex_to<power>(r).exponent) ||
@@ -1195,18 +1218,22 @@ ex power::expand_add(const add & a, int n, unsigned options) const
                                                     !is_exactly_a<add>(ex_to<power>(r).basis) ||
                                                     !is_exactly_a<mul>(ex_to<power>(r).basis) ||
                                                     !is_exactly_a<power>(ex_to<power>(r).basis));
+                                       GINAC_ASSERT(is_exactly_a<numeric>(a.seq[i].coeff));
+                                       const numeric & c = ex_to<numeric>(a.seq[i].coeff);
                                        if (exponent[i] == 0) {
                                                // optimize away
                                        } else if (exponent[i] == 1) {
                                                // optimized
-                                               term.push_back(r);
-                                               factor = factor.mul(ex_to<numeric>(c));
+                                               monomial.push_back(r);
+                                               if (c != *_num1_p)
+                                                       factor = factor.mul(c);
                                        } else { // general case exponent[i] > 1
-                                               term.push_back((new power(r, exponent[i]))->setflag(status_flags::dynallocated));
-                                               factor = factor.mul(ex_to<numeric>(c).power(exponent[i]));
+                                               monomial.push_back((new power(r, exponent[i]))->setflag(status_flags::dynallocated));
+                                               if (c != *_num1_p)
+                                                       factor = factor.mul(c.power(exponent[i]));
                                        }
                                }
-                               result.push_back(a.combine_ex_with_coeff_to_pair(mul(term).expand(options), factor));
+                               result.push_back(a.combine_ex_with_coeff_to_pair(mul(monomial).expand(options), factor));
                        } while (compositions.next());
                } while (partitions.next());
        }
@@ -1214,22 +1241,27 @@ ex power::expand_add(const add & a, int n, unsigned options) const
        GINAC_ASSERT(result.size() == result_size);
 
        if (a.overall_coeff.is_zero()) {
-               return (new add(result))->setflag(status_flags::dynallocated |
-                                                 status_flags::expanded);
+               return (new add(std::move(result)))->setflag(status_flags::dynallocated |
+                                                            status_flags::expanded);
        } else {
-               return (new add(result, ex_to<numeric>(a.overall_coeff).power(n)))->setflag(status_flags::dynallocated |
-                                                                                           status_flags::expanded);
+               return (new add(std::move(result), ex_to<numeric>(a.overall_coeff).power(n)))->setflag(status_flags::dynallocated |
+                                                                                                      status_flags::expanded);
        }
 }
 
 
 /** Special case of power::expand_add. Expands a^2 where a is an add.
  *  @see power::expand_add */
-ex power::expand_add_2(const add & a, unsigned options) const
+ex power::expand_add_2(const add & a, unsigned options)
 {
-       epvector sum;
-       size_t a_nops = a.nops();
-       sum.reserve((a_nops*(a_nops+1))/2);
+       epvector result;
+       size_t result_size = (a.nops() * (a.nops()+1)) / 2;
+       if (!a.overall_coeff.is_zero()) {
+               // the result's overall_coeff is one of the terms
+               --result_size;
+       }
+       result.reserve(result_size);
+
        epvector::const_iterator last = a.seq.end();
 
        // power(+(x,...,z;c),2)=power(+(x,...,z;0),2)+2*c*+(x,...,z;0)+c*c
@@ -1248,50 +1280,50 @@ ex power::expand_add_2(const add & a, unsigned options) const
                
                if (c.is_equal(_ex1)) {
                        if (is_exactly_a<mul>(r)) {
-                               sum.push_back(a.combine_ex_with_coeff_to_pair(expand_mul(ex_to<mul>(r), *_num2_p, options, true),
-                                                                             _ex1));
+                               result.push_back(a.combine_ex_with_coeff_to_pair(expand_mul(ex_to<mul>(r), *_num2_p, options, true),
+                                                                                _ex1));
                        } else {
-                               sum.push_back(a.combine_ex_with_coeff_to_pair((new power(r,_ex2))->setflag(status_flags::dynallocated),
-                                                                             _ex1));
+                               result.push_back(a.combine_ex_with_coeff_to_pair((new power(r,_ex2))->setflag(status_flags::dynallocated),
+                                                                                _ex1));
                        }
                } else {
                        if (is_exactly_a<mul>(r)) {
-                               sum.push_back(a.combine_ex_with_coeff_to_pair(expand_mul(ex_to<mul>(r), *_num2_p, options, true),
-                                                    ex_to<numeric>(c).power_dyn(*_num2_p)));
+                               result.push_back(a.combine_ex_with_coeff_to_pair(expand_mul(ex_to<mul>(r), *_num2_p, options, true),
+                                                                                ex_to<numeric>(c).power_dyn(*_num2_p)));
                        } else {
-                               sum.push_back(a.combine_ex_with_coeff_to_pair((new power(r,_ex2))->setflag(status_flags::dynallocated),
-                                                    ex_to<numeric>(c).power_dyn(*_num2_p)));
+                               result.push_back(a.combine_ex_with_coeff_to_pair((new power(r,_ex2))->setflag(status_flags::dynallocated),
+                                                                                ex_to<numeric>(c).power_dyn(*_num2_p)));
                        }
                }
 
                for (epvector::const_iterator cit1=cit0+1; cit1!=last; ++cit1) {
                        const ex & r1 = cit1->rest;
                        const ex & c1 = cit1->coeff;
-                       sum.push_back(a.combine_ex_with_coeff_to_pair(mul(r,r1).expand(options),
-                                                                     _num2_p->mul(ex_to<numeric>(c)).mul_dyn(ex_to<numeric>(c1))));
+                       result.push_back(a.combine_ex_with_coeff_to_pair(mul(r,r1).expand(options),
+                                                                        _num2_p->mul(ex_to<numeric>(c)).mul_dyn(ex_to<numeric>(c1))));
                }
        }
        
-       GINAC_ASSERT(sum.size()==(a.seq.size()*(a.seq.size()+1))/2);
-       
        // second part: add terms coming from overall_coeff (if != 0)
        if (!a.overall_coeff.is_zero()) {
-               epvector::const_iterator i = a.seq.begin(), end = a.seq.end();
-               while (i != end) {
-                       sum.push_back(a.combine_pair_with_coeff_to_pair(*i, ex_to<numeric>(a.overall_coeff).mul_dyn(*_num2_p)));
-                       ++i;
-               }
-               sum.push_back(expair(ex_to<numeric>(a.overall_coeff).power_dyn(*_num2_p),_ex1));
+               for (auto & i : a.seq)
+                       result.push_back(a.combine_pair_with_coeff_to_pair(i, ex_to<numeric>(a.overall_coeff).mul_dyn(*_num2_p)));
+       }
+
+       GINAC_ASSERT(result.size() == result_size);
+
+       if (a.overall_coeff.is_zero()) {
+               return (new add(std::move(result)))->setflag(status_flags::dynallocated |
+                                                            status_flags::expanded);
+       } else {
+               return (new add(std::move(result), ex_to<numeric>(a.overall_coeff).power(2)))->setflag(status_flags::dynallocated |
+                                                                                                      status_flags::expanded);
        }
-       
-       GINAC_ASSERT(sum.size()==(a_nops*(a_nops+1))/2);
-       
-       return (new add(sum))->setflag(status_flags::dynallocated | status_flags::expanded);
 }
 
 /** Expand factors of m in m^n where m is a mul and n is an integer.
  *  @see power::expand */
-ex power::expand_mul(const mul & m, const numeric & n, unsigned options, bool from_expand) const
+ex power::expand_mul(const mul & m, const numeric & n, unsigned options, bool from_expand)
 {
        GINAC_ASSERT(n.is_integer());