the destructor, copy constructor, and assignment operator (which were the
[ginac.git] / ginac / ncmul.cpp
1 /** @file ncmul.cpp
2  *
3  *  Implementation of GiNaC's non-commutative products of expressions. */
4
5 /*
6  *  GiNaC Copyright (C) 1999-2001 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 <algorithm>
24 #include <iostream>
25 #include <stdexcept>
26
27 #include "ncmul.h"
28 #include "ex.h"
29 #include "add.h"
30 #include "mul.h"
31 #include "archive.h"
32 #include "debugmsg.h"
33 #include "utils.h"
34
35 #ifndef NO_NAMESPACE_GINAC
36 namespace GiNaC {
37 #endif // ndef NO_NAMESPACE_GINAC
38
39 GINAC_IMPLEMENT_REGISTERED_CLASS(ncmul, exprseq)
40
41 //////////
42 // default constructor, destructor, copy constructor assignment operator and helpers
43 //////////
44
45 // public
46
47 ncmul::ncmul()
48 {
49         debugmsg("ncmul default constructor",LOGLEVEL_CONSTRUCT);
50         tinfo_key = TINFO_ncmul;
51 }
52
53 // protected
54
55 void ncmul::copy(const ncmul & other)
56 {
57         inherited::copy(other);
58 }
59
60 void ncmul::destroy(bool call_parent)
61 {
62         if (call_parent) inherited::destroy(call_parent);
63 }
64
65 //////////
66 // other constructors
67 //////////
68
69 // public
70
71 ncmul::ncmul(const ex & lh, const ex & rh) : inherited(lh,rh)
72 {
73         debugmsg("ncmul constructor from ex,ex",LOGLEVEL_CONSTRUCT);
74         tinfo_key = TINFO_ncmul;
75 }
76
77 ncmul::ncmul(const ex & f1, const ex & f2, const ex & f3) : inherited(f1,f2,f3)
78 {
79         debugmsg("ncmul constructor from 3 ex",LOGLEVEL_CONSTRUCT);
80         tinfo_key = TINFO_ncmul;
81 }
82
83 ncmul::ncmul(const ex & f1, const ex & f2, const ex & f3,
84              const ex & f4) : inherited(f1,f2,f3,f4)
85 {
86         debugmsg("ncmul constructor from 4 ex",LOGLEVEL_CONSTRUCT);
87         tinfo_key = TINFO_ncmul;
88 }
89
90 ncmul::ncmul(const ex & f1, const ex & f2, const ex & f3,
91              const ex & f4, const ex & f5) : inherited(f1,f2,f3,f4,f5)
92 {
93         debugmsg("ncmul constructor from 5 ex",LOGLEVEL_CONSTRUCT);
94         tinfo_key = TINFO_ncmul;
95 }
96
97 ncmul::ncmul(const ex & f1, const ex & f2, const ex & f3,
98              const ex & f4, const ex & f5, const ex & f6) : inherited(f1,f2,f3,f4,f5,f6)
99 {
100         debugmsg("ncmul constructor from 6 ex",LOGLEVEL_CONSTRUCT);
101         tinfo_key = TINFO_ncmul;
102 }
103
104 ncmul::ncmul(const exvector & v, bool discardable) : inherited(v,discardable)
105 {
106         debugmsg("ncmul constructor from exvector,bool",LOGLEVEL_CONSTRUCT);
107         tinfo_key = TINFO_ncmul;
108 }
109
110 ncmul::ncmul(exvector * vp) : inherited(vp)
111 {
112         debugmsg("ncmul constructor from exvector *",LOGLEVEL_CONSTRUCT);
113         tinfo_key = TINFO_ncmul;
114 }
115
116 //////////
117 // archiving
118 //////////
119
120 /** Construct object from archive_node. */
121 ncmul::ncmul(const archive_node &n, const lst &sym_lst) : inherited(n, sym_lst)
122 {
123         debugmsg("ncmul constructor from archive_node", LOGLEVEL_CONSTRUCT);
124 }
125
126 /** Unarchive the object. */
127 ex ncmul::unarchive(const archive_node &n, const lst &sym_lst)
128 {
129         return (new ncmul(n, sym_lst))->setflag(status_flags::dynallocated);
130 }
131
132 /** Archive the object. */
133 void ncmul::archive(archive_node &n) const
134 {
135         inherited::archive(n);
136 }
137
138         
139 //////////
140 // functions overriding virtual functions from bases classes
141 //////////
142
143 // public
144
145 basic * ncmul::duplicate() const
146 {
147         debugmsg("ncmul duplicate",LOGLEVEL_ASSIGNMENT);
148         return new ncmul(*this);
149 }
150
151 void ncmul::print(std::ostream & os, unsigned upper_precedence) const
152 {
153         debugmsg("ncmul print",LOGLEVEL_PRINT);
154         printseq(os,'(','%',')',precedence,upper_precedence);
155 }
156
157 void ncmul::printraw(std::ostream & os) const
158 {
159         debugmsg("ncmul printraw",LOGLEVEL_PRINT);
160         os << "%(";
161         for (exvector::const_iterator it=seq.begin(); it!=seq.end(); ++it) {
162                 (*it).bp->printraw(os);
163                 os << ",";
164         }
165         os << ",hash=" << hashvalue << ",flags=" << flags;
166         os << ")";
167 }
168
169 void ncmul::printcsrc(std::ostream & os, unsigned type, unsigned upper_precedence) const
170 {
171         debugmsg("ncmul print csrc",LOGLEVEL_PRINT);
172         exvector::const_iterator it;
173         exvector::const_iterator itend = seq.end()-1;
174         os << "ncmul(";
175         for (it=seq.begin(); it!=itend; ++it) {
176                 (*it).bp->printcsrc(os,precedence);
177                 os << ",";
178         }
179         (*it).bp->printcsrc(os,precedence);
180         os << ")";
181 }
182
183 bool ncmul::info(unsigned inf) const
184 {
185         throw(std::logic_error("which flags have to be implemented in ncmul::info()?"));
186 }
187
188 typedef std::vector<int> intvector;
189
190 ex ncmul::expand(unsigned options) const
191 {
192         exvector sub_expanded_seq;
193         intvector positions_of_adds;
194         intvector number_of_add_operands;
195
196         exvector expanded_seq=expandchildren(options);
197
198         positions_of_adds.resize(expanded_seq.size());
199         number_of_add_operands.resize(expanded_seq.size());
200
201         int number_of_adds=0;
202         int number_of_expanded_terms=1;
203
204         unsigned current_position=0;
205         exvector::const_iterator last=expanded_seq.end();
206         for (exvector::const_iterator cit=expanded_seq.begin(); cit!=last; ++cit) {
207                 if (is_ex_exactly_of_type((*cit),add)) {
208                         positions_of_adds[number_of_adds]=current_position;
209                         const add & expanded_addref=ex_to_add(*cit);
210                         number_of_add_operands[number_of_adds]=expanded_addref.seq.size();
211                         number_of_expanded_terms *= expanded_addref.seq.size();
212                         number_of_adds++;
213                 }
214                 current_position++;
215         }
216
217         if (number_of_adds==0) {
218                 return (new ncmul(expanded_seq,1))->setflag(status_flags::dynallocated ||
219                                                                                                         status_flags::expanded);
220         }
221
222         exvector distrseq;
223         distrseq.reserve(number_of_expanded_terms);
224
225         intvector k;
226         k.resize(number_of_adds);
227         
228         int l;
229         for (l=0; l<number_of_adds; l++) {
230                 k[l]=0;
231         }
232
233         while (1) {
234                 exvector term;
235                 term=expanded_seq;
236                 for (l=0; l<number_of_adds; l++) {
237                         GINAC_ASSERT(is_ex_exactly_of_type(expanded_seq[positions_of_adds[l]],add));
238                         const add & addref=ex_to_add(expanded_seq[positions_of_adds[l]]);
239                         term[positions_of_adds[l]]=addref.recombine_pair_to_ex(addref.seq[k[l]]);
240                 }
241                 distrseq.push_back((new ncmul(term,1))->setflag(status_flags::dynallocated |
242                                                                                                                 status_flags::expanded));
243
244                 // increment k[]
245                 l=number_of_adds-1;
246                 while ((l>=0)&&((++k[l])>=number_of_add_operands[l])) {
247                         k[l]=0;    
248                         l--;
249                 }
250                 if (l<0) break;
251         }
252
253         return (new add(distrseq))->setflag(status_flags::dynallocated |
254                                                                                 status_flags::expanded);
255 }
256
257 int ncmul::degree(const symbol & s) const
258 {
259         int deg_sum=0;
260         for (exvector::const_iterator cit=seq.begin(); cit!=seq.end(); ++cit) {
261                 deg_sum+=(*cit).degree(s);
262         }
263         return deg_sum;
264 }
265
266 int ncmul::ldegree(const symbol & s) const
267 {
268         int deg_sum=0;
269         for (exvector::const_iterator cit=seq.begin(); cit!=seq.end(); ++cit) {
270                 deg_sum+=(*cit).ldegree(s);
271         }
272         return deg_sum;
273 }
274
275 ex ncmul::coeff(const symbol & s, int n) const
276 {
277         exvector coeffseq;
278         coeffseq.reserve(seq.size());
279
280         if (n==0) {
281                 // product of individual coeffs
282                 // if a non-zero power of s is found, the resulting product will be 0
283                 exvector::const_iterator it=seq.begin();
284                 while (it!=seq.end()) {
285                         coeffseq.push_back((*it).coeff(s,n));
286                         ++it;
287                 }
288                 return (new ncmul(coeffseq,1))->setflag(status_flags::dynallocated);
289         }
290                  
291         exvector::const_iterator it=seq.begin();
292         bool coeff_found=0;
293         while (it!=seq.end()) {
294                 ex c=(*it).coeff(s,n);
295                 if (!c.is_zero()) {
296                         coeffseq.push_back(c);
297                         coeff_found=1;
298                 } else {
299                         coeffseq.push_back(*it);
300                 }
301                 ++it;
302         }
303
304         if (coeff_found) return (new ncmul(coeffseq,1))->setflag(status_flags::dynallocated);
305         
306         return _ex0();
307 }
308
309 unsigned ncmul::count_factors(const ex & e) const
310 {
311         if ((is_ex_exactly_of_type(e,mul)&&(e.return_type()!=return_types::commutative))||
312                 (is_ex_exactly_of_type(e,ncmul))) {
313                 unsigned factors=0;
314                 for (unsigned i=0; i<e.nops(); i++)
315                         factors += count_factors(e.op(i));
316                 
317                 return factors;
318         }
319         return 1;
320 }
321                 
322 void ncmul::append_factors(exvector & v, const ex & e) const
323 {
324         if ((is_ex_exactly_of_type(e,mul)&&(e.return_type()!=return_types::commutative))||
325                 (is_ex_exactly_of_type(e,ncmul))) {
326                 for (unsigned i=0; i<e.nops(); i++)
327                         append_factors(v,e.op(i));
328                 
329                 return;
330         }
331         v.push_back(e);
332 }
333
334 typedef std::vector<unsigned> unsignedvector;
335 typedef std::vector<exvector> exvectorvector;
336
337 ex ncmul::eval(int level) const
338 {
339         // simplifications: ncmul(...,*(x1,x2),...,ncmul(x3,x4),...) ->
340         //                      ncmul(...,x1,x2,...,x3,x4,...) (associativity)
341         //                  ncmul(x) -> x
342         //                  ncmul() -> 1
343         //                  ncmul(...,c1,...,c2,...)
344         //                      *(c1,c2,ncmul(...)) (pull out commutative elements)
345         //                  ncmul(x1,y1,x2,y2) -> *(ncmul(x1,x2),ncmul(y1,y2))
346         //                      (collect elements of same type)
347         //                  ncmul(x1,x2,x3,...) -> x::eval_ncmul(x1,x2,x3,...)
348         // the following rule would be nice, but produces a recursion,
349         // which must be trapped by introducing a flag that the sub-ncmuls()
350         // are already evaluated (maybe later...)
351         //                  ncmul(x1,x2,...,X,y1,y2,...) ->
352         //                      ncmul(ncmul(x1,x2,...),X,ncmul(y1,y2,...)
353         //                      (X noncommutative_composite)
354
355         if ((level==1)&&(flags & status_flags::evaluated)) {
356                 return *this;
357         }
358
359         exvector evaledseq=evalchildren(level);
360
361         // ncmul(...,*(x1,x2),...,ncmul(x3,x4),...) ->
362         //     ncmul(...,x1,x2,...,x3,x4,...) (associativity)
363         unsigned factors=0;
364         for (exvector::const_iterator cit=evaledseq.begin(); cit!=evaledseq.end(); ++cit) {
365                 factors += count_factors(*cit);
366         }
367
368         exvector assocseq;
369         assocseq.reserve(factors);
370         for (exvector::const_iterator cit=evaledseq.begin(); cit!=evaledseq.end(); ++cit) {
371                 append_factors(assocseq,*cit);
372         }
373
374         // ncmul(x) -> x
375         if (assocseq.size()==1) return *(seq.begin());
376
377         // ncmul() -> 1
378         if (assocseq.size()==0) return _ex1();
379
380         // determine return types
381         unsignedvector rettypes;
382         rettypes.reserve(assocseq.size());
383         unsigned i=0;
384         unsigned count_commutative=0;
385         unsigned count_noncommutative=0;
386         unsigned count_noncommutative_composite=0;
387         for (exvector::const_iterator cit=assocseq.begin(); cit!=assocseq.end(); ++cit) {
388                 switch (rettypes[i]=(*cit).return_type()) {
389                 case return_types::commutative:
390                         count_commutative++;
391                         break;
392                 case return_types::noncommutative:
393                         count_noncommutative++;
394                         break;
395                 case return_types::noncommutative_composite:
396                         count_noncommutative_composite++;
397                         break;
398                 default:
399                         throw(std::logic_error("ncmul::eval(): invalid return type"));
400                 }
401                 ++i;
402         }
403         GINAC_ASSERT(count_commutative+count_noncommutative+count_noncommutative_composite==assocseq.size());
404
405         // ncmul(...,c1,...,c2,...) ->
406         //     *(c1,c2,ncmul(...)) (pull out commutative elements)
407         if (count_commutative!=0) {
408                 exvector commutativeseq;
409                 commutativeseq.reserve(count_commutative+1);
410                 exvector noncommutativeseq;
411                 noncommutativeseq.reserve(assocseq.size()-count_commutative);
412                 for (i=0; i<assocseq.size(); ++i) {
413                         if (rettypes[i]==return_types::commutative) {
414                                 commutativeseq.push_back(assocseq[i]);
415                         } else {
416                                 noncommutativeseq.push_back(assocseq[i]);
417                         }
418                 }
419                 commutativeseq.push_back((new ncmul(noncommutativeseq,1))->setflag(status_flags::dynallocated));
420                 return (new mul(commutativeseq))->setflag(status_flags::dynallocated);
421         }
422                 
423         // ncmul(x1,y1,x2,y2) -> *(ncmul(x1,x2),ncmul(y1,y2))
424         //     (collect elements of same type)
425
426         if (count_noncommutative_composite==0) {
427                 // there are neither commutative nor noncommutative_composite
428                 // elements in assocseq
429                 GINAC_ASSERT(count_commutative==0);
430
431                 exvectorvector evv;
432                 unsignedvector rttinfos;
433                 evv.reserve(assocseq.size());
434                 rttinfos.reserve(assocseq.size());
435
436                 for (exvector::const_iterator cit=assocseq.begin(); cit!=assocseq.end(); ++cit) {
437                         unsigned ti=(*cit).return_type_tinfo();
438                         // search type in vector of known types
439                         for (i=0; i<rttinfos.size(); ++i) {
440                                 if (ti==rttinfos[i]) {
441                                         evv[i].push_back(*cit);
442                                         break;
443                                 }
444                         }
445                         if (i>=rttinfos.size()) {
446                                 // new type
447                                 rttinfos.push_back(ti);
448                                 evv.push_back(exvector());
449                                 (*(evv.end()-1)).reserve(assocseq.size());
450                                 (*(evv.end()-1)).push_back(*cit);
451                         }
452                 }
453
454 #ifdef DO_GINAC_ASSERT
455                 GINAC_ASSERT(evv.size()==rttinfos.size());
456                 GINAC_ASSERT(evv.size()>0);
457                 unsigned s=0;
458                 for (i=0; i<evv.size(); ++i) {
459                         s += evv[i].size();
460                 }
461                 GINAC_ASSERT(s==assocseq.size());
462 #endif // def DO_GINAC_ASSERT
463                 
464                 // if all elements are of same type, simplify the string
465                 if (evv.size()==1) {
466                         return evv[0][0].simplify_ncmul(evv[0]);
467                 }
468                 
469                 exvector splitseq;
470                 splitseq.reserve(evv.size());
471                 for (i=0; i<evv.size(); ++i) {
472                         splitseq.push_back((new ncmul(evv[i]))->setflag(status_flags::dynallocated));
473                 }
474
475                 return (new mul(splitseq))->setflag(status_flags::dynallocated);
476         }
477         
478         return (new ncmul(assocseq))->setflag(status_flags::dynallocated |
479                                                                                   status_flags::evaluated);
480 }
481
482 exvector ncmul::get_indices(void) const
483 {
484         // return union of indices of factors
485         exvector iv;
486         for (exvector::const_iterator cit=seq.begin(); cit!=seq.end(); ++cit) {
487                 exvector subiv=(*cit).get_indices();
488                 iv.reserve(iv.size()+subiv.size());
489                 for (exvector::const_iterator cit2=subiv.begin(); cit2!=subiv.end(); ++cit2) {
490                         iv.push_back(*cit2);
491                 }
492         }
493         return iv;
494 }
495
496 ex ncmul::subs(const lst & ls, const lst & lr) const
497 {
498         return ncmul(subschildren(ls, lr));
499 }
500
501 ex ncmul::thisexprseq(const exvector & v) const
502 {
503         return (new ncmul(v))->setflag(status_flags::dynallocated);
504 }
505
506 ex ncmul::thisexprseq(exvector * vp) const
507 {
508         return (new ncmul(vp))->setflag(status_flags::dynallocated);
509 }
510
511 // protected
512
513 /** Implementation of ex::diff() for a non-commutative product. It always returns 0.
514  *  @see ex::diff */
515 ex ncmul::derivative(const symbol & s) const
516 {
517         return _ex0();
518 }
519
520 int ncmul::compare_same_type(const basic & other) const
521 {
522         return inherited::compare_same_type(other);
523 }
524
525 unsigned ncmul::return_type(void) const
526 {
527         if (seq.size()==0) {
528                 // ncmul without factors: should not happen, but commutes
529                 return return_types::commutative;
530         }
531
532         bool all_commutative=1;
533         unsigned rt;
534         exvector::const_iterator cit_noncommutative_element; // point to first found nc element
535
536         for (exvector::const_iterator cit=seq.begin(); cit!=seq.end(); ++cit) {
537                 rt=(*cit).return_type();
538                 if (rt==return_types::noncommutative_composite) return rt; // one ncc -> mul also ncc
539                 if ((rt==return_types::noncommutative)&&(all_commutative)) {
540                         // first nc element found, remember position
541                         cit_noncommutative_element=cit;
542                         all_commutative=0;
543                 }
544                 if ((rt==return_types::noncommutative)&&(!all_commutative)) {
545                         // another nc element found, compare type_infos
546                         if ((*cit_noncommutative_element).return_type_tinfo()!=(*cit).return_type_tinfo()) {
547                                 // diffent types -> mul is ncc
548                                 return return_types::noncommutative_composite;
549                         }
550                 }
551         }
552         // all factors checked
553         GINAC_ASSERT(!all_commutative); // not all factors should commute, because this is a ncmul();
554         return all_commutative ? return_types::commutative : return_types::noncommutative;
555 }
556    
557 unsigned ncmul::return_type_tinfo(void) const
558 {
559         if (seq.size()==0) {
560                 // mul without factors: should not happen
561                 return tinfo_key;
562         }
563         // return type_info of first noncommutative element
564         for (exvector::const_iterator cit=seq.begin(); cit!=seq.end(); ++cit) {
565                 if ((*cit).return_type()==return_types::noncommutative) {
566                         return (*cit).return_type_tinfo();
567                 }
568         }
569         // no noncommutative element found, should not happen
570         return tinfo_key;
571 }
572
573 //////////
574 // new virtual functions which can be overridden by derived classes
575 //////////
576
577 // none
578
579 //////////
580 // non-virtual functions in this class
581 //////////
582
583 exvector ncmul::expandchildren(unsigned options) const
584 {
585         exvector s;
586         s.reserve(seq.size());
587
588         for (exvector::const_iterator it=seq.begin(); it!=seq.end(); ++it) {
589                 s.push_back((*it).expand(options));
590         }
591         return s;
592 }
593
594 const exvector & ncmul::get_factors(void) const
595 {
596         return seq;
597 }
598
599 //////////
600 // static member variables
601 //////////
602
603 // protected
604
605 unsigned ncmul::precedence=50;
606
607 //////////
608 // friend functions
609 //////////
610
611 ex nonsimplified_ncmul(const exvector & v)
612 {
613         return (new ncmul(v))->setflag(status_flags::dynallocated);
614 }
615
616 ex simplified_ncmul(const exvector & v)
617 {
618         if (v.size()==0) {
619                 return _ex1();
620         } else if (v.size()==1) {
621                 return v[0];
622         }
623         return (new ncmul(v))->setflag(status_flags::dynallocated |
624                                        status_flags::evaluated);
625 }
626
627 #ifndef NO_NAMESPACE_GINAC
628 } // namespace GiNaC
629 #endif // ndef NO_NAMESPACE_GINAC