]> www.ginac.de Git - ginac.git/blobdiff - ginac/power.cpp
Improved CLN output [Sheplyakov].
[ginac.git] / ginac / power.cpp
index 6f6f178de07729254e28e6530455446ed3103abf..68bec71c949c4866fd2c949c2c9df0db78ec2112 100644 (file)
@@ -3,7 +3,7 @@
  *  Implementation of GiNaC's symbolic exponentiation (basis^exponent). */
 
 /*
- *  GiNaC Copyright (C) 1999-2006 Johannes Gutenberg University Mainz, Germany
+ *  GiNaC Copyright (C) 1999-2007 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
@@ -40,6 +40,8 @@
 #include "lst.h"
 #include "archive.h"
 #include "utils.h"
+#include "relational.h"
+#include "compiler.h"
 
 namespace GiNaC {
 
@@ -48,7 +50,8 @@ GINAC_IMPLEMENT_REGISTERED_CLASS_OPT(power, basic,
   print_func<print_latex>(&power::do_print_latex).
   print_func<print_csrc>(&power::do_print_csrc).
   print_func<print_python>(&power::do_print_python).
-  print_func<print_python_repr>(&power::do_print_python_repr))
+  print_func<print_python_repr>(&power::do_print_python_repr).
+  print_func<print_csrc_cl_N>(&power::do_print_csrc_cl_N))
 
 typedef std::vector<int> intvector;
 
@@ -160,6 +163,21 @@ static void print_sym_pow(const print_context & c, const symbol &x, int exp)
        }
 }
 
+void power::do_print_csrc_cl_N(const print_csrc_cl_N& c, unsigned level) const
+{
+       if (exponent.is_equal(_ex_1)) {
+               c.s << "recip(";
+               basis.print(c);
+               c.s << ')';
+               return;
+       }
+       c.s << "expt(";
+       basis.print(c);
+       c.s << ", ";
+       exponent.print(c);
+       c.s << ')';
+}
+
 void power::do_print_csrc(const print_csrc & c, unsigned level) const
 {
        // Integer powers of symbols are printed in a special, optimized way
@@ -170,29 +188,20 @@ void power::do_print_csrc(const print_csrc & c, unsigned level) const
                        c.s << '(';
                else {
                        exp = -exp;
-                       if (is_a<print_csrc_cl_N>(c))
-                               c.s << "recip(";
-                       else
-                               c.s << "1.0/(";
+                       c.s << "1.0/(";
                }
                print_sym_pow(c, ex_to<symbol>(basis), exp);
                c.s << ')';
 
        // <expr>^-1 is printed as "1.0/<expr>" or with the recip() function of CLN
        } else if (exponent.is_equal(_ex_1)) {
-               if (is_a<print_csrc_cl_N>(c))
-                       c.s << "recip(";
-               else
-                       c.s << "1.0/(";
+               c.s << "1.0/(";
                basis.print(c);
                c.s << ')';
 
-       // Otherwise, use the pow() or expt() (CLN) functions
+       // Otherwise, use the pow() function
        } else {
-               if (is_a<print_csrc_cl_N>(c))
-                       c.s << "expt(";
-               else
-                       c.s << "pow(";
+               c.s << "pow(";
                basis.print(c);
                c.s << ',';
                exponent.print(c);
@@ -230,6 +239,8 @@ bool power::info(unsigned inf) const
                case info_flags::algebraic:
                        return !exponent.info(info_flags::integer) ||
                               basis.info(inf);
+               case info_flags::expanded:
+                       return (flags & status_flags::expanded);
        }
        return inherited::info(inf);
 }
@@ -258,6 +269,15 @@ ex power::map(map_function & f) const
                return *this;
 }
 
+bool power::is_polynomial(const ex & var) const
+{
+       if (exponent.has(var))
+               return false;
+       if (!exponent.info(info_flags::nonnegint))
+               return false;
+       return basis.is_polynomial(var);
+}
+
 int power::degree(const ex & s) const
 {
        if (is_equal(ex_to<basic>(s)))
@@ -325,6 +345,7 @@ ex power::coeff(const ex & s, int n) const
  *  - ^(0,c) -> 0 or exception  (depending on the real part of c)
  *  - ^(1,x) -> 1
  *  - ^(c1,c2) -> *(c1^n,c1^(c2-n))  (so that 0<(c2-n)<1, try to evaluate roots, possibly in numerator and denominator of c1)
+ *  - ^(^(x,c1),c2) -> ^(x,c1*c2)  if x is positive and c1 is real.
  *  - ^(^(x,c1),c2) -> ^(x,c1*c2)  (c2 integer or -1 < c1 <= 1, case c1=1 should not happen, see below!)
  *  - ^(*(x,y,z),c) -> *(x^c,y^c,z^c)  (if c integer)
  *  - ^(*(x,c1),c2) -> ^(x,c2)*c1^c2  (c1>0)
@@ -385,6 +406,10 @@ ex power::eval(int level) const
        if (is_exactly_a<function>(ebasis))
                return ex_to<function>(ebasis).power(eexponent);
 
+       // Turn (x^c)^d into x^(c*d) in the case that x is positive and c is real.
+       if (is_exactly_a<power>(ebasis) && ebasis.op(0).info(info_flags::positive) && ebasis.op(1).info(info_flags::real))
+               return power(ebasis.op(0), ebasis.op(1) * eexponent);
+
        if (exponent_is_numerical) {
 
                // ^(c1,c2) -> c1^c2  (c1, c2 numeric(),
@@ -452,8 +477,9 @@ ex power::eval(int level) const
                        if (is_exactly_a<numeric>(sub_exponent)) {
                                const numeric & num_sub_exponent = ex_to<numeric>(sub_exponent);
                                GINAC_ASSERT(num_sub_exponent!=numeric(1));
-                               if (num_exponent->is_integer() || (abs(num_sub_exponent) - (*_num1_p)).is_negative())
+                               if (num_exponent->is_integer() || (abs(num_sub_exponent) - (*_num1_p)).is_negative()) {
                                        return power(sub_basis,num_sub_exponent.mul(*num_exponent));
+                               }
                        }
                }
        
@@ -461,7 +487,35 @@ ex power::eval(int level) const
                if (num_exponent->is_integer() && is_exactly_a<mul>(ebasis)) {
                        return expand_mul(ex_to<mul>(ebasis), *num_exponent, 0);
                }
-       
+
+               // (2*x + 6*y)^(-4) -> 1/16*(x + 3*y)^(-4)
+               if (num_exponent->is_integer() && is_exactly_a<add>(ebasis)) {
+                       numeric icont = ebasis.integer_content();
+                       const numeric& lead_coeff = 
+                               ex_to<numeric>(ex_to<add>(ebasis).seq.begin()->coeff).div_dyn(icont);
+
+                       const bool canonicalizable = lead_coeff.is_integer();
+                       const bool unit_normal = lead_coeff.is_pos_integer();
+                       if (canonicalizable && (! unit_normal))
+                               icont = icont.mul(*_num_1_p);
+                       
+                       if (canonicalizable && (icont != *_num1_p)) {
+                               const add& addref = ex_to<add>(ebasis);
+                               add* addp = new add(addref);
+                               addp->setflag(status_flags::dynallocated);
+                               addp->clearflag(status_flags::hash_calculated);
+                               addp->overall_coeff = ex_to<numeric>(addp->overall_coeff).div_dyn(icont);
+                               for (epvector::iterator i = addp->seq.begin(); i != addp->seq.end(); ++i)
+                                       i->coeff = ex_to<numeric>(i->coeff).div_dyn(icont);
+
+                               const numeric c = icont.power(*num_exponent);
+                               if (likely(c != *_num1_p))
+                                       return (new mul(power(*addp, *num_exponent), c))->setflag(status_flags::dynallocated);
+                               else
+                                       return power(*addp, *num_exponent);
+                       }
+               }
+
                // ^(*(...,x;c1),c2) -> *(^(*(...,x;1),c2),c1^c2)  (c1, c2 numeric(), c1>0)
                // ^(*(...,x;c1),c2) -> *(^(*(...,x;-1),c2),(-c1)^c2)  (c1, c2 numeric(), c1<0)
                if (is_exactly_a<mul>(ebasis)) {
@@ -605,13 +659,66 @@ ex power::conjugate() const
        return (new power(newbasis, newexponent))->setflag(status_flags::dynallocated);
 }
 
+ex power::real_part() const
+{
+       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() ));
+               return result;
+       }
+       
+       ex a = basis.real_part();
+       ex b = basis.imag_part();
+       ex c = exponent.real_part();
+       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
+{
+       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() ));
+               return result;
+       }
+       
+       ex a=basis.real_part();
+       ex b=basis.imag_part();
+       ex c=exponent.real_part();
+       ex d=exponent.imag_part();
+       return
+               power(abs(basis),c)*exp(-d*atan2(b,a))*sin(c*atan2(b,a)+d*log(abs(basis)));
+}
+
+// protected
+
 // protected
 
 /** Implementation of ex::diff() for a power.
  *  @see ex::diff */
 ex power::derivative(const symbol & s) const
 {
-       if (exponent.info(info_flags::real)) {
+       if (is_a<numeric>(exponent)) {
                // D(b^r) = r * b^(r-1) * D(b) (faster than the formula below)
                epvector newseq;
                newseq.reserve(2);
@@ -875,7 +982,7 @@ ex power::expand_add_2(const add & a, unsigned options) const
        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 and integer.
+/** 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
 {
@@ -903,19 +1010,13 @@ ex power::expand_mul(const mul & m, const numeric & n, unsigned options, bool fr
        epvector::const_iterator last = m.seq.end();
        epvector::const_iterator cit = m.seq.begin();
        while (cit!=last) {
-               if (is_exactly_a<numeric>(cit->rest)) {
-                       distrseq.push_back(m.combine_pair_with_coeff_to_pair(*cit, n));
-               } else {
-                       // it is safe not to call mul::combine_pair_with_coeff_to_pair()
-                       // since n is an integer
-                       numeric new_coeff = ex_to<numeric>(cit->coeff).mul(n);
-                       if (from_expand && is_exactly_a<add>(cit->rest) && new_coeff.is_pos_integer()) {
-                               // this happens when e.g. (a+b)^(1/2) gets squared and
-                               // the resulting product needs to be reexpanded
-                               need_reexpand = true;
-                       }
-                       distrseq.push_back(expair(cit->rest, new_coeff));
+               expair p = m.combine_pair_with_coeff_to_pair(*cit, n);
+               if (from_expand && is_exactly_a<add>(cit->rest) && ex_to<numeric>(p.coeff).is_pos_integer()) {
+                       // this happens when e.g. (a+b)^(1/2) gets squared and
+                       // the resulting product needs to be reexpanded
+                       need_reexpand = true;
                }
+               distrseq.push_back(p);
                ++cit;
        }