]> www.ginac.de Git - ginac.git/blobdiff - ginac/mul.cpp
Converting terms into primitive polynomials optimized [A.Sheplyakov]
[ginac.git] / ginac / mul.cpp
index 4cea8167b4ee419081c67f275678c1e78b3565df..9ed27c6c4bf3f4c7435674de9138bb4aa16d76fc 100644 (file)
@@ -3,7 +3,7 @@
  *  Implementation of GiNaC's products of expressions. */
 
 /*
- *  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
@@ -34,6 +34,7 @@
 #include "lst.h"
 #include "archive.h"
 #include "utils.h"
+#include "compiler.h"
 
 namespace GiNaC {
 
@@ -84,20 +85,20 @@ mul::mul(const epvector & v)
        GINAC_ASSERT(is_canonical());
 }
 
-mul::mul(const epvector & v, const ex & oc)
+mul::mul(const epvector & v, const ex & oc, bool do_index_renaming)
 {
        tinfo_key = &mul::tinfo_static;
        overall_coeff = oc;
-       construct_from_epvector(v);
+       construct_from_epvector(v, do_index_renaming);
        GINAC_ASSERT(is_canonical());
 }
 
-mul::mul(std::auto_ptr<epvector> vp, const ex & oc)
+mul::mul(std::auto_ptr<epvector> vp, const ex & oc, bool do_index_renaming)
 {
        tinfo_key = &mul::tinfo_static;
        GINAC_ASSERT(vp.get()!=0);
        overall_coeff = oc;
-       construct_from_epvector(*vp);
+       construct_from_epvector(*vp, do_index_renaming);
        GINAC_ASSERT(is_canonical());
 }
 
@@ -218,8 +219,12 @@ void mul::do_print_csrc(const print_csrc & c, unsigned level) const
                c.s << "(";
 
        if (!overall_coeff.is_equal(_ex1)) {
-               overall_coeff.print(c, precedence());
-               c.s << "*";
+               if (overall_coeff.is_equal(_ex_1))
+                       c.s << "-";
+               else {
+                       overall_coeff.print(c, precedence());
+                       c.s << "*";
+               }
        }
 
        // Print arguments, separated by "*" or "/"
@@ -311,7 +316,11 @@ int mul::degree(const ex & s) const
        epvector::const_iterator i = seq.begin(), end = seq.end();
        while (i != end) {
                if (ex_to<numeric>(i->coeff).is_integer())
-                       deg_sum += i->rest.degree(s) * ex_to<numeric>(i->coeff).to_int();
+                       deg_sum += recombine_pair_to_ex(*i).degree(s);
+               else {
+                       if (i->rest.has(s))
+                               throw std::runtime_error("mul::degree() undefined degree because of non-integer exponent");
+               }
                ++i;
        }
        return deg_sum;
@@ -324,7 +333,11 @@ int mul::ldegree(const ex & s) const
        epvector::const_iterator i = seq.begin(), end = seq.end();
        while (i != end) {
                if (ex_to<numeric>(i->coeff).is_integer())
-                       deg_sum += i->rest.ldegree(s) * ex_to<numeric>(i->coeff).to_int();
+                       deg_sum += recombine_pair_to_ex(*i).ldegree(s);
+               else {
+                       if (i->rest.has(s))
+                               throw std::runtime_error("mul::ldegree() undefined degree because of non-integer exponent");
+               }
                ++i;
        }
        return deg_sum;
@@ -410,7 +423,7 @@ ex mul::eval(int level) const
                return *this;
        }
        
-       int seq_size = seq.size();
+       size_t seq_size = seq.size();
        if (overall_coeff.is_zero()) {
                // *(...,x;0) -> 0
                return _ex0;
@@ -434,9 +447,81 @@ ex mul::eval(int level) const
                }
                return (new add(distrseq,
                                ex_to<numeric>(addref.overall_coeff).
-                               mul_dyn(ex_to<numeric>(overall_coeff))))
-                     ->setflag(status_flags::dynallocated | status_flags::evaluated);
+                               mul_dyn(ex_to<numeric>(overall_coeff)))
+                      )->setflag(status_flags::dynallocated | status_flags::evaluated);
+       } else if ((seq_size >= 2) && (! (flags & status_flags::expanded))) {
+               // Strip the content and the unit part from each term. Thus
+               // things like (-x+a)*(3*x-3*a) automagically turn into - 3*(x-a)2
+
+               epvector::const_iterator last = seq.end();
+               epvector::const_iterator i = seq.begin();
+               epvector::const_iterator j = seq.begin();
+               std::auto_ptr<epvector> s(new epvector);
+               numeric oc = *_num1_p;
+               bool something_changed = false;
+               while (i!=last) {
+                       if (likely(! (is_a<add>(i->rest) && i->coeff.is_equal(_ex1)))) {
+                               // power::eval has such a rule, no need to handle powers here
+                               ++i;
+                               continue;
+                       }
+
+                       // XXX: What is the best way to check if the polynomial is a primitive? 
+                       numeric c = i->rest.integer_content();
+                       const numeric& lead_coeff =
+                               ex_to<numeric>(ex_to<add>(i->rest).seq.begin()->coeff).div_dyn(c);
+                       const bool canonicalizable = lead_coeff.is_integer();
+
+                       // XXX: The main variable is chosen in a random way, so this code 
+                       // does NOT transform the term into the canonical form (thus, in some
+                       // very unlucky event it can even loop forever). Hopefully the main
+                       // variable will be the same for all terms in *this
+                       const bool unit_normal = lead_coeff.is_pos_integer();
+                       if (likely((c == *_num1_p) && ((! canonicalizable) || unit_normal))) {
+                               ++i;
+                               continue;
+                       }
+
+                       if (! something_changed) {
+                               s->reserve(seq_size);
+                               something_changed = true;
+                       }
+
+                       while ((j!=i) && (j!=last)) {
+                               s->push_back(*j);
+                               ++j;
+                       }
+
+                       if (! unit_normal)
+                               c = c.mul(*_num_1_p);
+
+                       oc = oc.mul(c);
+
+                       // divide add by the number in place to save at least 2 .eval() calls
+                       const add& addref = ex_to<add>(i->rest);
+                       add* primitive = new add(addref);
+                       primitive->setflag(status_flags::dynallocated);
+                       primitive->clearflag(status_flags::hash_calculated);
+                       primitive->overall_coeff = ex_to<numeric>(primitive->overall_coeff).div_dyn(c);
+                       for (epvector::iterator ai = primitive->seq.begin();
+                                       ai != primitive->seq.end(); ++ai)
+                               ai->coeff = ex_to<numeric>(ai->coeff).div_dyn(c);
+                       
+                       s->push_back(expair(*primitive, _ex1));
+
+                       ++i;
+                       ++j;
+               }
+               if (something_changed) {
+                       while (j!=last) {
+                               s->push_back(*j);
+                               ++j;
+                       }
+                       return (new mul(s, ex_to<numeric>(overall_coeff).mul_dyn(oc))
+                              )->setflag(status_flags::dynallocated);
+               }
        }
+
        return this->hold();
 }
 
@@ -594,7 +679,7 @@ bool tryfactsubs(const ex & origfactor, const ex & patternfactor, int & nummatch
        return true;
 }
 
-/** Checks wheter e matches to the pattern pat and the (possibly to be updated
+/** Checks wheter e matches to the pattern pat and the (possibly to be updated)
   * list of replacements repls. This matching is in the sense of algebraic
   * substitutions. Matching starts with pat.op(factor) of the pattern because
   * the factors before this one have already been matched. The (possibly
@@ -650,6 +735,8 @@ ex mul::algebraic_subs_mul(const exmap & m, unsigned options) const
 {      
        std::vector<bool> subsed(seq.size(), false);
        exvector subsresult(seq.size());
+       ex divide_by = 1;
+       ex multiply_by = 1;
 
        for (exmap::const_iterator it = m.begin(); it != m.end(); ++it) {
 
@@ -657,36 +744,35 @@ ex mul::algebraic_subs_mul(const exmap & m, unsigned options) const
 retry1:
                        int nummatches = std::numeric_limits<int>::max();
                        std::vector<bool> currsubsed(seq.size(), false);
-                       bool succeed = true;
                        lst repls;
                        
                        if(!algebraic_match_mul_with_mul(*this, it->first, repls, 0, nummatches, subsed, currsubsed))
                                continue;
 
-                       bool foundfirstsubsedfactor = false;
-                       for (size_t j=0; j<subsed.size(); j++) {
-                               if (currsubsed[j]) {
-                                       if (foundfirstsubsedfactor)
-                                               subsresult[j] = op(j);
-                                       else {
-                                               foundfirstsubsedfactor = true;
-                                               subsresult[j] = op(j) * power(it->second.subs(ex(repls), subs_options::no_pattern) / it->first.subs(ex(repls), subs_options::no_pattern), nummatches);
-                                       }
+                       for (size_t j=0; j<subsed.size(); j++)
+                               if (currsubsed[j])
                                        subsed[j] = true;
-                               }
-                       }
+                       ex subsed_pattern
+                               = it->first.subs(ex(repls), subs_options::no_pattern);
+                       divide_by *= power(subsed_pattern, nummatches);
+                       ex subsed_result
+                               = it->second.subs(ex(repls), subs_options::no_pattern);
+                       multiply_by *= power(subsed_result, nummatches);
                        goto retry1;
 
                } else {
-retry2:
-                       int nummatches = std::numeric_limits<int>::max();
-                       lst repls;
 
                        for (size_t j=0; j<this->nops(); j++) {
-                               if (!subsed[j] && tryfactsubs(op(j), it->first, nummatches, repls)) {
+                               int nummatches = std::numeric_limits<int>::max();
+                               lst repls;
+                               if (!subsed[j] && tryfactsubs(op(j), it->first, nummatches, repls)){
                                        subsed[j] = true;
-                                       subsresult[j] = op(j) * power(it->second.subs(ex(repls), subs_options::no_pattern) / it->first.subs(ex(repls), subs_options::no_pattern), nummatches);
-                                       goto retry2;
+                                       ex subsed_pattern
+                                               = it->first.subs(ex(repls), subs_options::no_pattern);
+                                       divide_by *= power(subsed_pattern, nummatches);
+                                       ex subsed_result
+                                               = it->second.subs(ex(repls), subs_options::no_pattern);
+                                       multiply_by *= power(subsed_result, nummatches);
                                }
                        }
                }
@@ -702,15 +788,7 @@ retry2:
        if (!subsfound)
                return subs_one_level(m, options | subs_options::algebraic);
 
-       exvector ev; ev.reserve(nops());
-       for (size_t i=0; i<nops(); i++) {
-               if (subsed[i])
-                       ev.push_back(subsresult[i]);
-               else
-                       ev.push_back(op(i));
-       }
-
-       return (new mul(ev))->setflag(status_flags::dynallocated);
+       return ((*this)/divide_by)*multiply_by;
 }
 
 // protected
@@ -792,14 +870,14 @@ tinfo_t mul::return_type_tinfo() const
        return this;
 }
 
-ex mul::thisexpairseq(const epvector & v, const ex & oc) const
+ex mul::thisexpairseq(const epvector & v, const ex & oc, bool do_index_renaming) const
 {
-       return (new mul(v, oc))->setflag(status_flags::dynallocated);
+       return (new mul(v, oc, do_index_renaming))->setflag(status_flags::dynallocated);
 }
 
-ex mul::thisexpairseq(std::auto_ptr<epvector> vp, const ex & oc) const
+ex mul::thisexpairseq(std::auto_ptr<epvector> vp, const ex & oc, bool do_index_renaming) const
 {
-       return (new mul(vp, oc))->setflag(status_flags::dynallocated);
+       return (new mul(vp, oc, do_index_renaming))->setflag(status_flags::dynallocated);
 }
 
 expair mul::split_ex_to_pair(const ex & e) const