- introduced info_flags::cinteger, info_flags::crational,
[ginac.git] / ginac / mul.cpp
1 /** @file mul.cpp
2  *
3  *  Implementation of GiNaC's products of expressions. */
4
5 /*
6  *  GiNaC Copyright (C) 1999 Johannes Gutenberg University Mainz, Germany
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program; if not, write to the Free Software
20  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 #include <vector>
24 #include <stdexcept>
25
26 #include "mul.h"
27 #include "add.h"
28 #include "power.h"
29 #include "debugmsg.h"
30
31 #ifndef NO_GINAC_NAMESPACE
32 namespace GiNaC {
33 #endif // ndef NO_GINAC_NAMESPACE
34
35 //////////
36 // default constructor, destructor, copy constructor assignment operator and helpers
37 //////////
38
39 // public
40
41 mul::mul()
42 {
43     debugmsg("mul default constructor",LOGLEVEL_CONSTRUCT);
44     tinfo_key = TINFO_mul;
45 }
46
47 mul::~mul()
48 {
49     debugmsg("mul destructor",LOGLEVEL_DESTRUCT);
50     destroy(0);
51 }
52
53 mul::mul(mul const & other)
54 {
55     debugmsg("mul copy constructor",LOGLEVEL_CONSTRUCT);
56     copy(other);
57 }
58
59 mul const & mul::operator=(mul const & other)
60 {
61     debugmsg("mul operator=",LOGLEVEL_ASSIGNMENT);
62     if (this != &other) {
63         destroy(1);
64         copy(other);
65     }
66     return *this;
67 }
68
69 // protected
70
71 void mul::copy(mul const & other)
72 {
73     expairseq::copy(other);
74 }
75
76 void mul::destroy(bool call_parent)
77 {
78     if (call_parent) expairseq::destroy(call_parent);
79 }
80
81 //////////
82 // other constructors
83 //////////
84
85 // public
86
87 mul::mul(ex const & lh, ex const & rh)
88 {
89     debugmsg("mul constructor from ex,ex",LOGLEVEL_CONSTRUCT);
90     tinfo_key = TINFO_mul;
91     overall_coeff=exONE();
92     construct_from_2_ex(lh,rh);
93     GINAC_ASSERT(is_canonical());
94 }
95
96 mul::mul(exvector const & v)
97 {
98     debugmsg("mul constructor from exvector",LOGLEVEL_CONSTRUCT);
99     tinfo_key = TINFO_mul;
100     overall_coeff=exONE();
101     construct_from_exvector(v);
102     GINAC_ASSERT(is_canonical());
103 }
104
105 /*
106 mul::mul(epvector const & v, bool do_not_canonicalize)
107 {
108     debugmsg("mul constructor from epvector,bool",LOGLEVEL_CONSTRUCT);
109     tinfo_key = TINFO_mul;
110     if (do_not_canonicalize) {
111         seq=v;
112 #ifdef EXPAIRSEQ_USE_HASHTAB
113         combine_same_terms(); // to build hashtab
114 #endif // def EXPAIRSEQ_USE_HASHTAB
115     } else {
116         construct_from_epvector(v);
117     }
118     GINAC_ASSERT(is_canonical());
119 }
120 */
121
122 mul::mul(epvector const & v)
123 {
124     debugmsg("mul constructor from epvector",LOGLEVEL_CONSTRUCT);
125     tinfo_key = TINFO_mul;
126     overall_coeff=exONE();
127     construct_from_epvector(v);
128     GINAC_ASSERT(is_canonical());
129 }
130
131 mul::mul(epvector const & v, ex const & oc)
132 {
133     debugmsg("mul constructor from epvector,ex",LOGLEVEL_CONSTRUCT);
134     tinfo_key = TINFO_mul;
135     overall_coeff=oc;
136     construct_from_epvector(v);
137     GINAC_ASSERT(is_canonical());
138 }
139
140 mul::mul(epvector * vp, ex const & oc)
141 {
142     debugmsg("mul constructor from epvector *,ex",LOGLEVEL_CONSTRUCT);
143     tinfo_key = TINFO_mul;
144     GINAC_ASSERT(vp!=0);
145     overall_coeff=oc;
146     construct_from_epvector(*vp);
147     delete vp;
148     GINAC_ASSERT(is_canonical());
149 }
150
151 mul::mul(ex const & lh, ex const & mh, ex const & rh)
152 {
153     debugmsg("mul constructor from ex,ex,ex",LOGLEVEL_CONSTRUCT);
154     tinfo_key = TINFO_mul;
155     exvector factors;
156     factors.reserve(3);
157     factors.push_back(lh);
158     factors.push_back(mh);
159     factors.push_back(rh);
160     overall_coeff=exONE();
161     construct_from_exvector(factors);
162     GINAC_ASSERT(is_canonical());
163 }
164
165 //////////
166 // functions overriding virtual functions from bases classes
167 //////////
168
169 // public
170
171 basic * mul::duplicate() const
172 {
173     debugmsg("mul duplicate",LOGLEVEL_ASSIGNMENT);
174     return new mul(*this);
175 }
176
177 void mul::print(ostream & os, unsigned upper_precedence) const
178 {
179     debugmsg("mul print",LOGLEVEL_PRINT);
180     if (precedence<=upper_precedence) os << "(";
181     bool first=true;
182     // first print the overall numeric coefficient:
183     if (ex_to_numeric(overall_coeff).csgn()==-1) os << '-';
184     if (!overall_coeff.is_equal(exONE()) &&
185         !overall_coeff.is_equal(exMINUSONE())) {
186         if (ex_to_numeric(overall_coeff).csgn()==-1)
187             (numMINUSONE()*overall_coeff).print(os, precedence);
188         else
189             overall_coeff.print(os, precedence);
190         os << '*';
191     }
192     // then proceed with the remaining factors:
193     for (epvector::const_iterator cit=seq.begin(); cit!=seq.end(); ++cit) {
194         if (!first) {
195             os << '*';
196         } else {
197             first=false;
198         }
199         recombine_pair_to_ex(*cit).print(os,precedence);
200     }
201     if (precedence<=upper_precedence) os << ")";
202 }
203
204 void mul::printraw(ostream & os) const
205 {
206     debugmsg("mul printraw",LOGLEVEL_PRINT);
207
208     os << "*(";
209     for (epvector::const_iterator it=seq.begin(); it!=seq.end(); ++it) {
210         os << "(";
211         (*it).rest.bp->printraw(os);
212         os << ",";
213         (*it).coeff.bp->printraw(os);
214         os << "),";
215     }
216     os << ",hash=" << hashvalue << ",flags=" << flags;
217     os << ")";
218 }
219
220 void mul::printcsrc(ostream & os, unsigned type, unsigned upper_precedence) const
221 {
222     debugmsg("mul print csrc", LOGLEVEL_PRINT);
223     if (precedence <= upper_precedence)
224         os << "(";
225
226     if (!overall_coeff.is_equal(exONE())) {
227         overall_coeff.bp->printcsrc(os,type,precedence);
228         os << "*";
229     }
230     
231     // Print arguments, separated by "*" or "/"
232     epvector::const_iterator it = seq.begin();
233     epvector::const_iterator itend = seq.end();
234     while (it != itend) {
235
236         // If the first argument is a negative integer power, it gets printed as "1.0/<expr>"
237         if (it == seq.begin() && ex_to_numeric(it->coeff).is_integer() && it->coeff.compare(numZERO()) < 0) {
238             if (type == csrc_types::ctype_cl_N)
239                 os << "recip(";
240             else
241                 os << "1.0/";
242         }
243
244         // If the exponent is 1 or -1, it is left out
245         if (it->coeff.compare(exONE()) == 0 || it->coeff.compare(numMINUSONE()) == 0)
246             it->rest.bp->printcsrc(os, type, precedence);
247         else
248             // outer parens around ex needed for broken gcc-2.95 parser:
249             (ex(power(it->rest, abs(ex_to_numeric(it->coeff))))).bp->printcsrc(os, type, upper_precedence);
250
251         // Separator is "/" for negative integer powers, "*" otherwise
252         it++;
253         if (it != itend) {
254             if (ex_to_numeric(it->coeff).is_integer() && it->coeff.compare(numZERO()) < 0)
255                 os << "/";
256             else
257                 os << "*";
258         }
259     }
260     if (precedence <= upper_precedence)
261         os << ")";
262 }
263
264 bool mul::info(unsigned inf) const
265 {
266     // TODO: optimize
267     if (inf==info_flags::polynomial ||
268         inf==info_flags::integer_polynomial ||
269         inf==info_flags::cinteger_polynomial ||
270         inf==info_flags::rational_polynomial ||
271         inf==info_flags::crational_polynomial ||
272         inf==info_flags::rational_function) {
273         for (epvector::const_iterator it=seq.begin(); it!=seq.end(); ++it) {
274             if (!(recombine_pair_to_ex(*it).info(inf)))
275                 return false;
276         }
277         return overall_coeff.info(inf);
278     } else {
279         return expairseq::info(inf);
280     }
281 }
282
283 typedef vector<int> intvector;
284
285 int mul::degree(symbol const & s) const
286 {
287     int deg_sum=0;
288     for (epvector::const_iterator cit=seq.begin(); cit!=seq.end(); ++cit) {
289         deg_sum+=(*cit).rest.degree(s) * ex_to_numeric((*cit).coeff).to_int();
290     }
291     return deg_sum;
292 }
293
294 int mul::ldegree(symbol const & s) const
295 {
296     int deg_sum=0;
297     for (epvector::const_iterator cit=seq.begin(); cit!=seq.end(); ++cit) {
298         deg_sum+=(*cit).rest.ldegree(s) * ex_to_numeric((*cit).coeff).to_int();
299     }
300     return deg_sum;
301 }
302
303 ex mul::coeff(symbol const & s, int const n) const
304 {
305     exvector coeffseq;
306     coeffseq.reserve(seq.size()+1);
307     
308     if (n==0) {
309         // product of individual coeffs
310         // if a non-zero power of s is found, the resulting product will be 0
311         epvector::const_iterator it=seq.begin();
312         while (it!=seq.end()) {
313             coeffseq.push_back(recombine_pair_to_ex(*it).coeff(s,n));
314             ++it;
315         }
316         coeffseq.push_back(overall_coeff);
317         return (new mul(coeffseq))->setflag(status_flags::dynallocated);
318     }
319          
320     epvector::const_iterator it=seq.begin();
321     bool coeff_found=0;
322     while (it!=seq.end()) {
323         ex t=recombine_pair_to_ex(*it);
324         ex c=t.coeff(s,n);
325         if (!c.is_zero()) {
326             coeffseq.push_back(c);
327             coeff_found=1;
328         } else {
329             coeffseq.push_back(t);
330         }
331         ++it;
332     }
333     if (coeff_found) {
334         coeffseq.push_back(overall_coeff);
335         return (new mul(coeffseq))->setflag(status_flags::dynallocated);
336     }
337     
338     return exZERO();
339 }
340
341 ex mul::eval(int level) const
342 {
343     // simplifications  *(...,x;0) -> 0
344     //                  *(+(x,y,...);c) -> *(+(*(x,c),*(y,c),...)) (c numeric())
345     //                  *(x;1) -> x
346     //                  *(;c) -> c
347
348     debugmsg("mul eval",LOGLEVEL_MEMBER_FUNCTION);
349
350     epvector * evaled_seqp=evalchildren(level);
351     if (evaled_seqp!=0) {
352         // do more evaluation later
353         return (new mul(evaled_seqp,overall_coeff))->
354                    setflag(status_flags::dynallocated);
355     }
356
357 #ifdef DO_GINAC_ASSERT
358     for (epvector::const_iterator cit=seq.begin(); cit!=seq.end(); ++cit) {
359         GINAC_ASSERT((!is_ex_exactly_of_type((*cit).rest,mul))||
360                (!(ex_to_numeric((*cit).coeff).is_integer())));
361         GINAC_ASSERT(!((*cit).is_numeric_with_coeff_1()));
362         if (is_ex_exactly_of_type(recombine_pair_to_ex(*cit),numeric)) {
363             printtree(cerr,0);
364         }
365         GINAC_ASSERT(!is_ex_exactly_of_type(recombine_pair_to_ex(*cit),numeric));
366         /* for paranoia */
367         expair p=split_ex_to_pair(recombine_pair_to_ex(*cit));
368         GINAC_ASSERT(p.rest.is_equal((*cit).rest));
369         GINAC_ASSERT(p.coeff.is_equal((*cit).coeff));
370         /* end paranoia */
371     }
372 #endif // def DO_GINAC_ASSERT
373
374     if (flags & status_flags::evaluated) {
375         GINAC_ASSERT(seq.size()>0);
376         GINAC_ASSERT((seq.size()>1)||!overall_coeff.is_equal(exONE()));
377         return *this;
378     }
379
380     int seq_size=seq.size();
381     if (overall_coeff.is_equal(exZERO())) {
382         // *(...,x;0) -> 0
383         return exZERO();
384     } else if (seq_size==0) {
385         // *(;c) -> c
386         return overall_coeff;
387     } else if ((seq_size==1)&&overall_coeff.is_equal(exONE())) {
388         // *(x;1) -> x
389         return recombine_pair_to_ex(*(seq.begin()));
390     } else if ((seq_size==1) &&
391                is_ex_exactly_of_type((*seq.begin()).rest,add) &&
392                ex_to_numeric((*seq.begin()).coeff).is_equal(numONE())) {
393         // *(+(x,y,...);c) -> +(*(x,c),*(y,c),...) (c numeric(), no powers of +())
394         add const & addref=ex_to_add((*seq.begin()).rest);
395         epvector distrseq;
396         distrseq.reserve(addref.seq.size());
397         for (epvector::const_iterator cit=addref.seq.begin(); cit!=addref.seq.end(); ++cit) {
398             distrseq.push_back(addref.combine_pair_with_coeff_to_pair(*cit,
399                                    overall_coeff));
400         }
401         return (new add(distrseq,
402                         ex_to_numeric(addref.overall_coeff).
403                         mul_dyn(ex_to_numeric(overall_coeff))))
404             ->setflag(status_flags::dynallocated  |
405                       status_flags::evaluated );
406     }
407     return this->hold();
408 }
409
410 exvector mul::get_indices(void) const
411 {
412     // return union of indices of factors
413     exvector iv;
414     for (epvector::const_iterator cit=seq.begin(); cit!=seq.end(); ++cit) {
415         exvector subiv=(*cit).rest.get_indices();
416         iv.reserve(iv.size()+subiv.size());
417         for (exvector::const_iterator cit2=subiv.begin(); cit2!=subiv.end(); ++cit2) {
418             iv.push_back(*cit2);
419         }
420     }
421     return iv;
422 }
423
424 ex mul::simplify_ncmul(exvector const & v) const
425 {
426     throw(std::logic_error("mul::simplify_ncmul() should never have been called!"));
427 }
428
429 // protected
430
431 int mul::compare_same_type(basic const & other) const
432 {
433     return expairseq::compare_same_type(other);
434 }
435
436 bool mul::is_equal_same_type(basic const & other) const
437 {
438     return expairseq::is_equal_same_type(other);
439 }
440
441 unsigned mul::return_type(void) const
442 {
443     if (seq.size()==0) {
444         // mul without factors: should not happen, but commutes
445         return return_types::commutative;
446     }
447
448     bool all_commutative=1;
449     unsigned rt;
450     epvector::const_iterator cit_noncommutative_element; // point to first found nc element
451
452     for (epvector::const_iterator cit=seq.begin(); cit!=seq.end(); ++cit) {
453         rt=(*cit).rest.return_type();
454         if (rt==return_types::noncommutative_composite) return rt; // one ncc -> mul also ncc
455         if ((rt==return_types::noncommutative)&&(all_commutative)) {
456             // first nc element found, remember position
457             cit_noncommutative_element=cit;
458             all_commutative=0;
459         }
460         if ((rt==return_types::noncommutative)&&(!all_commutative)) {
461             // another nc element found, compare type_infos
462             if ((*cit_noncommutative_element).rest.return_type_tinfo()!=(*cit).rest.return_type_tinfo()) {
463                 // diffent types -> mul is ncc
464                 return return_types::noncommutative_composite;
465             }
466         }
467     }
468     // all factors checked
469     return all_commutative ? return_types::commutative : return_types::noncommutative;
470 }
471    
472 unsigned mul::return_type_tinfo(void) const
473 {
474     if (seq.size()==0) {
475         // mul without factors: should not happen
476         return tinfo_key;
477     }
478     // return type_info of first noncommutative element
479     for (epvector::const_iterator cit=seq.begin(); cit!=seq.end(); ++cit) {
480         if ((*cit).rest.return_type()==return_types::noncommutative) {
481             return (*cit).rest.return_type_tinfo();
482         }
483     }
484     // no noncommutative element found, should not happen
485     return tinfo_key;
486 }
487
488 ex mul::thisexpairseq(epvector const & v, ex const & oc) const
489 {
490     return (new mul(v,oc))->setflag(status_flags::dynallocated);
491 }
492
493 ex mul::thisexpairseq(epvector * vp, ex const & oc) const
494 {
495     return (new mul(vp,oc))->setflag(status_flags::dynallocated);
496 }
497
498 expair mul::split_ex_to_pair(ex const & e) const
499 {
500     if (is_ex_exactly_of_type(e,power)) {
501         power const & powerref=ex_to_power(e);
502         if (is_ex_exactly_of_type(powerref.exponent,numeric)) {
503             return expair(powerref.basis,powerref.exponent);
504         }
505     }
506     return expair(e,exONE());
507 }
508     
509 expair mul::combine_ex_with_coeff_to_pair(ex const & e,
510                                           ex const & c) const
511 {
512     // to avoid duplication of power simplification rules,
513     // we create a temporary power object
514     // otherwise it would be hard to correctly simplify
515     // expression like (4^(1/3))^(3/2)
516     if (are_ex_trivially_equal(c,exONE())) {
517         return split_ex_to_pair(e);
518     }
519     return split_ex_to_pair(power(e,c));
520 }
521     
522 expair mul::combine_pair_with_coeff_to_pair(expair const & p,
523                                             ex const & c) const
524 {
525     // to avoid duplication of power simplification rules,
526     // we create a temporary power object
527     // otherwise it would be hard to correctly simplify
528     // expression like (4^(1/3))^(3/2)
529     if (are_ex_trivially_equal(c,exONE())) {
530         return p;
531     }
532     return split_ex_to_pair(power(recombine_pair_to_ex(p),c));
533 }
534     
535 ex mul::recombine_pair_to_ex(expair const & p) const
536 {
537     // if (p.coeff.compare(exONE())==0) {
538     // if (are_ex_trivially_equal(p.coeff,exONE())) {
539     if (ex_to_numeric(p.coeff).is_equal(numONE())) {
540         return p.rest;
541     } else {
542         return power(p.rest,p.coeff);
543     }
544 }
545
546 bool mul::expair_needs_further_processing(epp it)
547 {
548     if (is_ex_exactly_of_type((*it).rest,mul) &&
549         ex_to_numeric((*it).coeff).is_integer()) {
550         // combined pair is product with integer power -> expand it
551         *it=split_ex_to_pair(recombine_pair_to_ex(*it));
552         return true;
553     }
554     if (is_ex_exactly_of_type((*it).rest,numeric)) {
555         expair ep=split_ex_to_pair(recombine_pair_to_ex(*it));
556         if (!ep.is_equal(*it)) {
557             // combined pair is a numeric power which can be simplified
558             *it=ep;
559             return true;
560         }
561         if (ex_to_numeric((*it).coeff).is_equal(numONE())) {
562             // combined pair has coeff 1 and must be moved to the end
563             return true;
564         }
565     }
566     return false;
567 }       
568
569 ex mul::default_overall_coeff(void) const
570 {
571     return exONE();
572 }
573
574 void mul::combine_overall_coeff(ex const & c)
575 {
576     GINAC_ASSERT(is_ex_exactly_of_type(overall_coeff,numeric));
577     GINAC_ASSERT(is_ex_exactly_of_type(c,numeric));
578     overall_coeff = ex_to_numeric(overall_coeff).mul_dyn(ex_to_numeric(c));
579 }
580
581 void mul::combine_overall_coeff(ex const & c1, ex const & c2)
582 {
583     GINAC_ASSERT(is_ex_exactly_of_type(overall_coeff,numeric));
584     GINAC_ASSERT(is_ex_exactly_of_type(c1,numeric));
585     GINAC_ASSERT(is_ex_exactly_of_type(c2,numeric));
586     overall_coeff = ex_to_numeric(overall_coeff).
587                         mul_dyn(ex_to_numeric(c1).power(ex_to_numeric(c2)));
588 }
589
590 bool mul::can_make_flat(expair const & p) const
591 {
592     GINAC_ASSERT(is_ex_exactly_of_type(p.coeff,numeric));
593     // this assertion will probably fail somewhere
594     // it would require a more careful make_flat, obeying the power laws
595     // probably should return true only if p.coeff is integer
596     return ex_to_numeric(p.coeff).is_equal(numONE());
597 }
598
599 ex mul::expand(unsigned options) const
600 {
601     exvector sub_expanded_seq;
602     intvector positions_of_adds;
603     intvector number_of_add_operands;
604
605     epvector * expanded_seqp=expandchildren(options);
606
607     epvector const & expanded_seq = expanded_seqp==0 ? seq : *expanded_seqp;
608
609     positions_of_adds.resize(expanded_seq.size());
610     number_of_add_operands.resize(expanded_seq.size());
611
612     int number_of_adds=0;
613     int number_of_expanded_terms=1;
614
615     unsigned current_position=0;
616     epvector::const_iterator last=expanded_seq.end();
617     for (epvector::const_iterator cit=expanded_seq.begin(); cit!=last; ++cit) {
618         if (is_ex_exactly_of_type((*cit).rest,add)&&
619             (ex_to_numeric((*cit).coeff).is_equal(numONE()))) {
620             positions_of_adds[number_of_adds]=current_position;
621             add const & expanded_addref=ex_to_add((*cit).rest);
622             int addref_nops=expanded_addref.nops();
623             number_of_add_operands[number_of_adds]=addref_nops;
624             number_of_expanded_terms *= addref_nops;
625             number_of_adds++;
626         }
627         current_position++;
628     }
629
630     if (number_of_adds==0) {
631         if (expanded_seqp==0) {
632             return this->setflag(status_flags::expanded);
633         }
634         return (new mul(expanded_seqp,overall_coeff))->
635                      setflag(status_flags::dynallocated ||
636                              status_flags::expanded);
637     }
638
639     exvector distrseq;
640     distrseq.reserve(number_of_expanded_terms);
641
642     intvector k;
643     k.resize(number_of_adds);
644     
645     int l;
646     for (l=0; l<number_of_adds; l++) {
647         k[l]=0;
648     }
649
650     while (1) {
651         epvector term;
652         term=expanded_seq;
653         for (l=0; l<number_of_adds; l++) {
654             add const & addref=ex_to_add(expanded_seq[positions_of_adds[l]].rest);
655             GINAC_ASSERT(term[positions_of_adds[l]].coeff.compare(exONE())==0);
656             term[positions_of_adds[l]]=split_ex_to_pair(addref.op(k[l]));
657         }
658         /*
659         cout << "mul::expand() term begin" << endl;
660         for (epvector::const_iterator cit=term.begin(); cit!=term.end(); ++cit) {
661             cout << "rest" << endl;
662             (*cit).rest.printtree(cout);
663             cout << "coeff" << endl;
664             (*cit).coeff.printtree(cout);
665         }
666         cout << "mul::expand() term end" << endl;
667         */
668         distrseq.push_back((new mul(term,overall_coeff))->
669                                 setflag(status_flags::dynallocated |
670                                         status_flags::expanded));
671
672         // increment k[]
673         l=number_of_adds-1;
674         while ((l>=0)&&((++k[l])>=number_of_add_operands[l])) {
675             k[l]=0;    
676             l--;
677         }
678         if (l<0) break;
679     }
680
681     if (expanded_seqp!=0) {
682         delete expanded_seqp;
683     }
684     /*
685     cout << "mul::expand() distrseq begin" << endl;
686     for (exvector::const_iterator cit=distrseq.begin(); cit!=distrseq.end(); ++cit) {
687         (*cit).printtree(cout);
688     }
689     cout << "mul::expand() distrseq end" << endl;
690     */
691
692     return (new add(distrseq))->setflag(status_flags::dynallocated |
693                                         status_flags::expanded);
694 }
695
696 //////////
697 // new virtual functions which can be overridden by derived classes
698 //////////
699
700 // none
701
702 //////////
703 // non-virtual functions in this class
704 //////////
705
706 epvector * mul::expandchildren(unsigned options) const
707 {
708     epvector::const_iterator last=seq.end();
709     epvector::const_iterator cit=seq.begin();
710     while (cit!=last) {
711         ex const & factor=recombine_pair_to_ex(*cit);
712         ex const & expanded_factor=factor.expand(options);
713         if (!are_ex_trivially_equal(factor,expanded_factor)) {
714
715             // something changed, copy seq, eval and return it
716             epvector *s=new epvector;
717             s->reserve(seq.size());
718
719             // copy parts of seq which are known not to have changed
720             epvector::const_iterator cit2=seq.begin();
721             while (cit2!=cit) {
722                 s->push_back(*cit2);
723                 ++cit2;
724             }
725             // copy first changed element
726             s->push_back(split_ex_to_pair(expanded_factor));
727             ++cit2;
728             // copy rest
729             while (cit2!=last) {
730                 s->push_back(split_ex_to_pair(recombine_pair_to_ex(*cit2).expand(options)));
731                 ++cit2;
732             }
733             return s;
734         }
735         ++cit;
736     }
737     
738     return 0; // nothing has changed
739 }
740    
741 //////////
742 // static member variables
743 //////////
744
745 // protected
746
747 unsigned mul::precedence=50;
748
749
750 //////////
751 // global constants
752 //////////
753
754 const mul some_mul;
755 type_info const & typeid_mul=typeid(some_mul);
756
757 #ifndef NO_GINAC_NAMESPACE
758 } // namespace GiNaC
759 #endif // ndef NO_GINAC_NAMESPACE