GiNaC 1.8.7
mul.cpp
Go to the documentation of this file.
1
5/*
6 * GiNaC Copyright (C) 1999-2023 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23#include "mul.h"
24#include "add.h"
25#include "power.h"
26#include "operators.h"
27#include "matrix.h"
28#include "indexed.h"
29#include "lst.h"
30#include "archive.h"
31#include "utils.h"
32#include "symbol.h"
33#include "compiler.h"
34
35#include <iostream>
36#include <limits>
37#include <stdexcept>
38#include <vector>
39
40namespace GiNaC {
41
44 print_func<print_latex>(&mul::do_print_latex).
45 print_func<print_csrc>(&mul::do_print_csrc).
46 print_func<print_tree>(&mul::do_print_tree).
47 print_func<print_python_repr>(&mul::do_print_python_repr))
48
49
50
51// default constructor
53
55{
56}
57
59// other constructors
61
62// public
63
64mul::mul(const ex & lh, const ex & rh)
65{
69}
70
72{
76}
77
79{
83}
84
85mul::mul(const epvector & v, const ex & oc, bool do_index_renaming)
86{
87 overall_coeff = oc;
88 construct_from_epvector(v, do_index_renaming);
90}
91
93{
95 construct_from_epvector(std::move(vp));
97}
98
99mul::mul(epvector && vp, const ex & oc, bool do_index_renaming)
100{
101 overall_coeff = oc;
102 construct_from_epvector(std::move(vp), do_index_renaming);
104}
105
106mul::mul(const ex & lh, const ex & mh, const ex & rh)
107{
109 factors.reserve(3);
110 factors.push_back(lh);
111 factors.push_back(mh);
112 factors.push_back(rh);
116}
117
119// archiving
121
123// functions overriding virtual functions from base classes
125
126void mul::print_overall_coeff(const print_context & c, const char *mul_sym) const
127{
128 const numeric &coeff = ex_to<numeric>(overall_coeff);
129 if (coeff.csgn() == -1)
130 c.s << '-';
131 if (!coeff.is_equal(*_num1_p) &&
133 if (coeff.is_rational()) {
134 if (coeff.is_negative())
135 (-coeff).print(c);
136 else
137 coeff.print(c);
138 } else {
139 if (coeff.csgn() == -1)
140 (-coeff).print(c, precedence());
141 else
143 }
144 c.s << mul_sym;
145 }
146}
147
148void mul::do_print(const print_context & c, unsigned level) const
149{
150 if (precedence() <= level)
151 c.s << '(';
152
154
155 bool first = true;
156 for (auto & it : seq) {
157 if (!first)
158 c.s << '*';
159 else
160 first = false;
162 }
163
164 if (precedence() <= level)
165 c.s << ')';
166}
167
168void mul::do_print_latex(const print_latex & c, unsigned level) const
169{
170 if (precedence() <= level)
171 c.s << "{(";
172
174
175 // Separate factors into those with negative numeric exponent
176 // and all others
177 exvector neg_powers, others;
178 for (auto & it : seq) {
179 GINAC_ASSERT(is_exactly_a<numeric>(it.coeff));
180 if (ex_to<numeric>(it.coeff).is_negative())
181 neg_powers.push_back(recombine_pair_to_ex(expair(it.rest, -it.coeff)));
182 else
183 others.push_back(recombine_pair_to_ex(it));
184 }
185
186 if (!neg_powers.empty()) {
187
188 // Factors with negative exponent are printed as a fraction
189 c.s << "\\frac{";
190 mul(others).eval().print(c);
191 c.s << "}{";
192 mul(neg_powers).eval().print(c);
193 c.s << "}";
194
195 } else {
196
197 // All other factors are printed in the ordinary way
198 for (auto & vit : others) {
199 c.s << ' ';
200 vit.print(c, precedence());
201 }
202 }
203
204 if (precedence() <= level)
205 c.s << ")}";
206}
207
208void mul::do_print_csrc(const print_csrc & c, unsigned level) const
209{
210 if (precedence() <= level)
211 c.s << "(";
212
215 c.s << "-";
216 else {
218 c.s << "*";
219 }
220 }
221
222 // Print arguments, separated by "*" or "/"
223 auto it = seq.begin(), itend = seq.end();
224 while (it != itend) {
225
226 // If the first argument is a negative integer power, it gets printed as "1.0/<expr>"
227 bool needclosingparenthesis = false;
228 if (it == seq.begin() && it->coeff.info(info_flags::negint)) {
229 if (is_a<print_csrc_cl_N>(c)) {
230 c.s << "recip(";
231 needclosingparenthesis = true;
232 } else
233 c.s << "1.0/";
234 }
235
236 // If the exponent is 1 or -1, it is left out
237 if (it->coeff.is_equal(_ex1) || it->coeff.is_equal(_ex_1))
238 it->rest.print(c, precedence());
239 else if (it->coeff.info(info_flags::negint))
240 ex(power(it->rest, -ex_to<numeric>(it->coeff))).print(c, level);
241 else
242 ex(power(it->rest, ex_to<numeric>(it->coeff))).print(c, level);
243
244 if (needclosingparenthesis)
245 c.s << ")";
246
247 // Separator is "/" for negative integer powers, "*" otherwise
248 ++it;
249 if (it != itend) {
250 if (it->coeff.info(info_flags::negint))
251 c.s << "/";
252 else
253 c.s << "*";
254 }
255 }
256
257 if (precedence() <= level)
258 c.s << ")";
259}
260
261void mul::do_print_python_repr(const print_python_repr & c, unsigned level) const
262{
263 c.s << class_name() << '(';
264 op(0).print(c);
265 for (size_t i=1; i<nops(); ++i) {
266 c.s << ',';
267 op(i).print(c);
268 }
269 c.s << ')';
270}
271
272bool mul::info(unsigned inf) const
273{
274 switch (inf) {
279 case info_flags::real:
284 case info_flags::even:
287 for (auto & it : seq) {
288 if (!recombine_pair_to_ex(it).info(inf))
289 return false;
290 }
292 return true;
293 return overall_coeff.info(inf);
294 }
298 return true;
300 return true;
302 return false;
303
304 bool pos = true;
305 for (auto & it : seq) {
306 const ex& factor = recombine_pair_to_ex(it);
308 continue;
310 pos = !pos;
311 else
312 return false;
313 }
315 pos = !pos;
317 return (inf == info_flags::positive? pos : !pos);
318 }
321 return true;
322 bool pos = true;
323 for (auto & it : seq) {
324 const ex& factor = recombine_pair_to_ex(it);
326 continue;
328 pos = !pos;
329 else
330 return false;
331 }
332 return (overall_coeff.info(info_flags::negative)? !pos : pos);
333 }
335 case info_flags::negint: {
336 bool pos = true;
337 for (auto & it : seq) {
338 const ex& factor = recombine_pair_to_ex(it);
340 continue;
342 pos = !pos;
343 else
344 return false;
345 }
347 pos = !pos;
349 return false;
350 return (inf ==info_flags::posint? pos : !pos);
351 }
353 bool pos = true;
354 for (auto & it : seq) {
355 const ex& factor = recombine_pair_to_ex(it);
357 continue;
359 pos = !pos;
360 else
361 return false;
362 }
364 pos = !pos;
366 return false;
367 return pos;
368 }
371 return true;
373 return false;
374 for (auto & it : seq) {
375 const ex& term = recombine_pair_to_ex(it);
377 return false;
378 }
380 return true;
381 }
382 }
383 return inherited::info(inf);
384}
385
386bool mul::is_polynomial(const ex & var) const
387{
388 for (auto & it : seq) {
389 if (!it.rest.is_polynomial(var) ||
390 (it.rest.has(var) && !it.coeff.info(info_flags::nonnegint))) {
391 return false;
392 }
393 }
394 return true;
395}
396
397int mul::degree(const ex & s) const
398{
399 // Sum up degrees of factors
400 int deg_sum = 0;
401 for (auto & it : seq) {
402 if (ex_to<numeric>(it.coeff).is_integer())
403 deg_sum += recombine_pair_to_ex(it).degree(s);
404 else {
405 if (it.rest.has(s))
406 throw std::runtime_error("mul::degree() undefined degree because of non-integer exponent");
407 }
408 }
409 return deg_sum;
410}
411
412int mul::ldegree(const ex & s) const
413{
414 // Sum up degrees of factors
415 int deg_sum = 0;
416 for (auto & it : seq) {
417 if (ex_to<numeric>(it.coeff).is_integer())
418 deg_sum += recombine_pair_to_ex(it).ldegree(s);
419 else {
420 if (it.rest.has(s))
421 throw std::runtime_error("mul::ldegree() undefined degree because of non-integer exponent");
422 }
423 }
424 return deg_sum;
425}
426
427ex mul::coeff(const ex & s, int n) const
428{
429 exvector coeffseq;
430 coeffseq.reserve(seq.size()+1);
431
432 if (n==0) {
433 // product of individual coeffs
434 // if a non-zero power of s is found, the resulting product will be 0
435 for (auto & it : seq)
436 coeffseq.push_back(recombine_pair_to_ex(it).coeff(s,n));
437 coeffseq.push_back(overall_coeff);
438 return dynallocate<mul>(coeffseq);
439 }
440
441 bool coeff_found = false;
442 for (auto & it : seq) {
443 ex t = recombine_pair_to_ex(it);
444 ex c = t.coeff(s, n);
445 if (!c.is_zero()) {
446 coeffseq.push_back(c);
447 coeff_found = 1;
448 } else {
449 coeffseq.push_back(t);
450 }
451 }
452 if (coeff_found) {
453 coeffseq.push_back(overall_coeff);
454 return dynallocate<mul>(coeffseq);
455 }
456
457 return _ex0;
458}
459
469{
471 GINAC_ASSERT(seq.size()>0);
473 return *this;
474 }
475
476 const epvector evaled = evalchildren();
477 if (unlikely(!evaled.empty())) {
478 // start over evaluating a new object
479 return dynallocate<mul>(std::move(evaled), overall_coeff);
480 }
481
482 size_t seq_size = seq.size();
483 if (overall_coeff.is_zero()) {
484 // *(...,x;0) -> 0
485 return _ex0;
486 } else if (seq_size==0) {
487 // *(;c) -> c
488 return overall_coeff;
489 } else if (seq_size==1 && overall_coeff.is_equal(_ex1)) {
490 // *(x;1) -> x
491 return recombine_pair_to_ex(*(seq.begin()));
492 } else if ((seq_size==1) &&
493 is_exactly_a<add>((*seq.begin()).rest) &&
494 ex_to<numeric>((*seq.begin()).coeff).is_equal(*_num1_p)) {
495 // *(+(x,y,...);c) -> +(*(x,c),*(y,c),...) (c numeric(), no powers of +())
496 const add & addref = ex_to<add>((*seq.begin()).rest);
497 epvector distrseq;
498 distrseq.reserve(addref.seq.size());
499 for (auto & it : addref.seq) {
500 distrseq.push_back(addref.combine_pair_with_coeff_to_pair(it, overall_coeff));
501 }
502 return dynallocate<add>(std::move(distrseq),
503 ex_to<numeric>(addref.overall_coeff).mul_dyn(ex_to<numeric>(overall_coeff)))
504 .setflag(status_flags::evaluated);
505 } else if ((seq_size >= 2) && (! (flags & status_flags::expanded))) {
506 // Strip the content and the unit part from each term. Thus
507 // things like (-x+a)*(3*x-3*a) automagically turn into - 3*(x-a)^2
508
509 auto i = seq.begin(), last = seq.end();
510 auto j = seq.begin();
511 epvector s;
512 numeric oc = *_num1_p;
513 bool something_changed = false;
514 while (i!=last) {
515 if (likely(! (is_a<add>(i->rest) && i->coeff.is_equal(_ex1)))) {
516 // power::eval has such a rule, no need to handle powers here
517 ++i;
518 continue;
519 }
520
521 // XXX: What is the best way to check if the polynomial is a primitive?
522 numeric c = i->rest.integer_content();
523 const numeric lead_coeff =
524 ex_to<numeric>(ex_to<add>(i->rest).seq.begin()->coeff).div(c);
525 const bool canonicalizable = lead_coeff.is_integer();
526
527 // XXX: The main variable is chosen in a random way, so this code
528 // does NOT transform the term into the canonical form (thus, in some
529 // very unlucky event it can even loop forever). Hopefully the main
530 // variable will be the same for all terms in *this
531 const bool unit_normal = lead_coeff.is_pos_integer();
532 if (likely((c == *_num1_p) && ((! canonicalizable) || unit_normal))) {
533 ++i;
534 continue;
535 }
536
537 if (! something_changed) {
538 s.reserve(seq_size);
539 something_changed = true;
540 }
541
542 while ((j!=i) && (j!=last)) {
543 s.push_back(*j);
544 ++j;
545 }
546
547 if (! unit_normal)
548 c = c.mul(*_num_1_p);
549
550 oc = oc.mul(c);
551
552 // divide add by the number in place to save at least 2 .eval() calls
553 const add& addref = ex_to<add>(i->rest);
554 add & primitive = dynallocate<add>(addref);
556 primitive.overall_coeff = ex_to<numeric>(primitive.overall_coeff).div_dyn(c);
557 for (auto & ai : primitive.seq)
558 ai.coeff = ex_to<numeric>(ai.coeff).div_dyn(c);
559
560 s.push_back(expair(primitive, _ex1));
561
562 ++i;
563 ++j;
564 }
565 if (something_changed) {
566 while (j!=last) {
567 s.push_back(*j);
568 ++j;
569 }
570 return dynallocate<mul>(std::move(s), ex_to<numeric>(overall_coeff).mul_dyn(oc));
571 }
572 }
573
574 return this->hold();
575}
576
578{
579 epvector s;
580 s.reserve(seq.size());
581
582 for (auto & it : seq)
583 s.push_back(expair(it.rest.evalf(), it.coeff));
584 return dynallocate<mul>(std::move(s), overall_coeff.evalf());
585}
586
587void mul::find_real_imag(ex & rp, ex & ip) const
588{
591 for (auto & it : seq) {
593 ex new_rp = factor.real_part();
594 ex new_ip = factor.imag_part();
595 if (new_ip.is_zero()) {
596 rp *= new_rp;
597 ip *= new_rp;
598 } else {
599 ex temp = rp*new_rp - ip*new_ip;
600 ip = ip*new_rp + rp*new_ip;
601 rp = temp;
602 }
603 }
604 rp = rp.expand();
605 ip = ip.expand();
606}
607
609{
610 ex rp, ip;
611 find_real_imag(rp, ip);
612 return rp;
613}
614
616{
617 ex rp, ip;
618 find_real_imag(rp, ip);
619 return ip;
620}
621
623{
624 // numeric*matrix
625 if (seq.size() == 1 && seq[0].coeff.is_equal(_ex1)
626 && is_a<matrix>(seq[0].rest))
627 return ex_to<matrix>(seq[0].rest).mul(ex_to<numeric>(overall_coeff));
628
629 // Evaluate children first, look whether there are any matrices at all
630 // (there can be either no matrices or one matrix; if there were more
631 // than one matrix, it would be a non-commutative product)
632 epvector s;
633 s.reserve(seq.size());
634
635 bool have_matrix = false;
636 epvector::iterator the_matrix;
637
638 for (auto & it : seq) {
639 const ex &m = recombine_pair_to_ex(it).evalm();
640 s.push_back(split_ex_to_pair(m));
641 if (is_a<matrix>(m)) {
642 have_matrix = true;
643 the_matrix = s.end() - 1;
644 }
645 }
646
647 if (have_matrix) {
648
649 // The product contained a matrix. We will multiply all other factors
650 // into that matrix.
651 matrix m = ex_to<matrix>(the_matrix->rest);
652 s.erase(the_matrix);
653 ex scalar = dynallocate<mul>(std::move(s), overall_coeff);
654 return m.mul_scalar(scalar);
655
656 } else
657 return dynallocate<mul>(std::move(s), overall_coeff);
658}
659
661{
662 if (seq.empty())
663 return inherited::eval_ncmul(v);
664
665 // Find first noncommutative element and call its eval_ncmul()
666 for (auto & it : seq)
667 if (it.rest.return_type() == return_types::noncommutative)
668 return it.rest.eval_ncmul(v);
669 return inherited::eval_ncmul(v);
670}
671
672bool tryfactsubs(const ex & origfactor, const ex & patternfactor, int & nummatches, exmap& repls)
673{
674 ex origbase;
675 int origexponent;
676 int origexpsign;
677
678 if (is_exactly_a<power>(origfactor) && origfactor.op(1).info(info_flags::integer)) {
679 origbase = origfactor.op(0);
680 int expon = ex_to<numeric>(origfactor.op(1)).to_int();
681 origexponent = expon > 0 ? expon : -expon;
682 origexpsign = expon > 0 ? 1 : -1;
683 } else {
684 origbase = origfactor;
685 origexponent = 1;
686 origexpsign = 1;
687 }
688
689 ex patternbase;
690 int patternexponent;
691 int patternexpsign;
692
693 if (is_exactly_a<power>(patternfactor) && patternfactor.op(1).info(info_flags::integer)) {
694 patternbase = patternfactor.op(0);
695 int expon = ex_to<numeric>(patternfactor.op(1)).to_int();
696 patternexponent = expon > 0 ? expon : -expon;
697 patternexpsign = expon > 0 ? 1 : -1;
698 } else {
699 patternbase = patternfactor;
700 patternexponent = 1;
701 patternexpsign = 1;
702 }
703
704 exmap saverepls = repls;
705 if (origexponent < patternexponent || origexpsign != patternexpsign || !origbase.match(patternbase,saverepls))
706 return false;
707 repls = saverepls;
708
709 int newnummatches = origexponent / patternexponent;
710 if (newnummatches < nummatches)
711 nummatches = newnummatches;
712 return true;
713}
714
723bool algebraic_match_mul_with_mul(const mul &e, const ex &pat, exmap& repls,
724 int factor, int &nummatches, const std::vector<bool> &subsed,
725 std::vector<bool> &matched)
726{
727 GINAC_ASSERT(subsed.size() == e.nops());
728 GINAC_ASSERT(matched.size() == e.nops());
729
730 if (factor == (int)pat.nops())
731 return true;
732
733 for (size_t i=0; i<e.nops(); ++i) {
734 if(subsed[i] || matched[i])
735 continue;
736 exmap newrepls = repls;
737 int newnummatches = nummatches;
738 if (tryfactsubs(e.op(i), pat.op(factor), newnummatches, newrepls)) {
739 matched[i] = true;
740 if (algebraic_match_mul_with_mul(e, pat, newrepls, factor+1,
741 newnummatches, subsed, matched)) {
742 repls = newrepls;
743 nummatches = newnummatches;
744 return true;
745 }
746 else
747 matched[i] = false;
748 }
749 }
750
751 return false;
752}
753
754bool mul::has(const ex & pattern, unsigned options) const
755{
757 return basic::has(pattern,options);
758 if(is_a<mul>(pattern)) {
759 exmap repls;
760 int nummatches = std::numeric_limits<int>::max();
761 std::vector<bool> subsed(nops(), false);
762 std::vector<bool> matched(nops(), false);
763 if(algebraic_match_mul_with_mul(*this, pattern, repls, 0, nummatches,
764 subsed, matched))
765 return true;
766 }
767 return basic::has(pattern, options);
768}
769
770ex mul::algebraic_subs_mul(const exmap & m, unsigned options) const
771{
772 std::vector<bool> subsed(nops(), false);
773 ex divide_by = 1;
774 ex multiply_by = 1;
775
776 for (auto & it : m) {
777
778 if (is_exactly_a<mul>(it.first)) {
779retry1:
780 int nummatches = std::numeric_limits<int>::max();
781 std::vector<bool> currsubsed(nops(), false);
782 exmap repls;
783
784 if (!algebraic_match_mul_with_mul(*this, it.first, repls, 0, nummatches, subsed, currsubsed))
785 continue;
786
787 for (size_t j=0; j<subsed.size(); j++)
788 if (currsubsed[j])
789 subsed[j] = true;
790 ex subsed_pattern
791 = it.first.subs(repls, subs_options::no_pattern);
792 divide_by *= pow(subsed_pattern, nummatches);
793 ex subsed_result
794 = it.second.subs(repls, subs_options::no_pattern);
795 multiply_by *= pow(subsed_result, nummatches);
796 goto retry1;
797
798 } else {
799
800 for (size_t j=0; j<this->nops(); j++) {
801 int nummatches = std::numeric_limits<int>::max();
802 exmap repls;
803 if (!subsed[j] && tryfactsubs(op(j), it.first, nummatches, repls)){
804 subsed[j] = true;
805 ex subsed_pattern
806 = it.first.subs(repls, subs_options::no_pattern);
807 divide_by *= pow(subsed_pattern, nummatches);
808 ex subsed_result
809 = it.second.subs(repls, subs_options::no_pattern);
810 multiply_by *= pow(subsed_result, nummatches);
811 }
812 }
813 }
814 }
815
816 bool subsfound = false;
817 for (size_t i=0; i<subsed.size(); i++) {
818 if (subsed[i]) {
819 subsfound = true;
820 break;
821 }
822 }
823 if (!subsfound)
825
826 return ((*this)/divide_by)*multiply_by;
827}
828
830{
831 // The base class' method is wrong here because we have to be careful at
832 // branch cuts. power::conjugate takes care of that already, so use it.
833 std::unique_ptr<epvector> newepv(nullptr);
834 for (auto i=seq.begin(); i!=seq.end(); ++i) {
835 if (newepv) {
836 newepv->push_back(split_ex_to_pair(recombine_pair_to_ex(*i).conjugate()));
837 continue;
838 }
840 ex c = x.conjugate();
841 if (c.is_equal(x)) {
842 continue;
843 }
844 newepv.reset(new epvector);
845 newepv->reserve(seq.size());
846 for (auto j=seq.begin(); j!=i; ++j) {
847 newepv->push_back(*j);
848 }
849 newepv->push_back(split_ex_to_pair(c));
850 }
852 if (!newepv && are_ex_trivially_equal(x, overall_coeff)) {
853 return *this;
854 }
855 return thisexpairseq(newepv ? std::move(*newepv) : seq, x);
856}
857
858
859// protected
860
863ex mul::derivative(const symbol & s) const
864{
865 size_t num = seq.size();
866 exvector addseq;
867 addseq.reserve(num);
868
869 // D(a*b*c) = D(a)*b*c + a*D(b)*c + a*b*D(c)
870 epvector mulseq = seq;
871 auto i = seq.begin(), end = seq.end();
872 auto i2 = mulseq.begin();
873 while (i != end) {
874 expair ep = split_ex_to_pair(pow(i->rest, i->coeff - _ex1) *
875 i->rest.diff(s));
876 ep.swap(*i2);
877 addseq.push_back(dynallocate<mul>(mulseq, overall_coeff * i->coeff));
878 ep.swap(*i2);
879 ++i; ++i2;
880 }
881 return dynallocate<add>(addseq);
882}
883
884int mul::compare_same_type(const basic & other) const
885{
886 return inherited::compare_same_type(other);
887}
888
889unsigned mul::return_type() const
890{
891 if (seq.empty()) {
892 // mul without factors: should not happen, but commutates
894 }
895
896 bool all_commutative = true;
897 epvector::const_iterator noncommutative_element; // point to first found nc element
898
899 epvector::const_iterator i = seq.begin(), end = seq.end();
900 while (i != end) {
901 unsigned rt = i->rest.return_type();
903 return rt; // one ncc -> mul also ncc
904 if ((rt == return_types::noncommutative) && (all_commutative)) {
905 // first nc element found, remember position
906 noncommutative_element = i;
907 all_commutative = false;
908 }
909 if ((rt == return_types::noncommutative) && (!all_commutative)) {
910 // another nc element found, compare type_infos
911 if (noncommutative_element->rest.return_type_tinfo() != i->rest.return_type_tinfo()) {
912 // different types -> mul is ncc
914 }
915 }
916 ++i;
917 }
918 // all factors checked
920}
921
923{
924 if (seq.empty())
925 return make_return_type_t<mul>(); // mul without factors: should not happen
926
927 // return type_info of first noncommutative element
928 for (auto & it : seq)
929 if (it.rest.return_type() == return_types::noncommutative)
930 return it.rest.return_type_tinfo();
931
932 // no noncommutative element found, should not happen
933 return make_return_type_t<mul>();
934}
935
936ex mul::thisexpairseq(const epvector & v, const ex & oc, bool do_index_renaming) const
937{
938 return dynallocate<mul>(v, oc, do_index_renaming);
939}
940
941ex mul::thisexpairseq(epvector && vp, const ex & oc, bool do_index_renaming) const
942{
943 return dynallocate<mul>(std::move(vp), oc, do_index_renaming);
944}
945
947{
948 if (is_exactly_a<power>(e)) {
949 const power & powerref = ex_to<power>(e);
950 if (is_exactly_a<numeric>(powerref.exponent))
951 return expair(powerref.basis,powerref.exponent);
952 }
953 return expair(e,_ex1);
954}
955
957 const ex & c) const
958{
959 GINAC_ASSERT(is_exactly_a<numeric>(c));
960
961 // First, try a common shortcut:
962 if (is_exactly_a<symbol>(e))
963 return expair(e, c);
964
965 // trivial case: exponent 1
966 if (c.is_equal(_ex1))
967 return split_ex_to_pair(e);
968
969 // to avoid duplication of power simplification rules,
970 // we create a temporary power object
971 // otherwise it would be hard to correctly evaluate
972 // expression like (4^(1/3))^(3/2)
973 return split_ex_to_pair(pow(e,c));
974}
975
977 const ex & c) const
978{
979 GINAC_ASSERT(is_exactly_a<numeric>(p.coeff));
980 GINAC_ASSERT(is_exactly_a<numeric>(c));
981
982 // First, try a common shortcut:
983 if (is_exactly_a<symbol>(p.rest))
984 return expair(p.rest, p.coeff * c);
985
986 // trivial case: exponent 1
987 if (c.is_equal(_ex1))
988 return p;
989 if (p.coeff.is_equal(_ex1))
990 return expair(p.rest, c);
991
992 // to avoid duplication of power simplification rules,
993 // we create a temporary power object
994 // otherwise it would be hard to correctly evaluate
995 // expression like (4^(1/3))^(3/2)
997}
998
1000{
1001 if (p.coeff.is_equal(_ex1))
1002 return p.rest;
1003 else
1004 return dynallocate<power>(p.rest, p.coeff);
1005}
1006
1008{
1009 if (is_exactly_a<mul>(it->rest) &&
1010 ex_to<numeric>(it->coeff).is_integer()) {
1011 // combined pair is product with integer power -> expand it
1013 return true;
1014 }
1015 if (is_exactly_a<numeric>(it->rest)) {
1016 if (it->coeff.is_equal(_ex1)) {
1017 // pair has coeff 1 and must be moved to the end
1018 return true;
1019 }
1021 if (!ep.is_equal(*it)) {
1022 // combined pair is a numeric power which can be simplified
1023 *it = ep;
1024 return true;
1025 }
1026 }
1027 return false;
1028}
1029
1031{
1032 return _ex1;
1033}
1034
1036{
1037 GINAC_ASSERT(is_exactly_a<numeric>(overall_coeff));
1038 GINAC_ASSERT(is_exactly_a<numeric>(c));
1039 overall_coeff = ex_to<numeric>(overall_coeff).mul_dyn(ex_to<numeric>(c));
1040}
1041
1042void mul::combine_overall_coeff(const ex & c1, const ex & c2)
1043{
1044 GINAC_ASSERT(is_exactly_a<numeric>(overall_coeff));
1045 GINAC_ASSERT(is_exactly_a<numeric>(c1));
1046 GINAC_ASSERT(is_exactly_a<numeric>(c2));
1047 overall_coeff = ex_to<numeric>(overall_coeff).mul_dyn(ex_to<numeric>(c1).power(ex_to<numeric>(c2)));
1048}
1049
1050bool mul::can_make_flat(const expair & p) const
1051{
1052 GINAC_ASSERT(is_exactly_a<numeric>(p.coeff));
1053
1054 // (x*y)^c == x^c*y^c if c ∈ ℤ
1055 return p.coeff.info(info_flags::integer);
1056}
1057
1059{
1060 if (is_exactly_a<mul>(e)) {
1061 for (auto & it : ex_to<mul>(e).seq) {
1062 if (is_exactly_a<add>(it.rest) && it.coeff.info(info_flags::posint))
1063 return true;
1064 }
1065 } else if (is_exactly_a<power>(e)) {
1066 if (is_exactly_a<add>(e.op(0)) && e.op(1).info(info_flags::posint))
1067 return true;
1068 }
1069 return false;
1070}
1071
1072ex mul::expand(unsigned options) const
1073{
1074 // Check for trivial case: expanding the monomial (~ 30% of all calls)
1075 bool monomial_case = true;
1076 for (const auto & i : seq) {
1077 if (!is_a<symbol>(i.rest) || !i.coeff.info(info_flags::integer)) {
1078 monomial_case = false;
1079 break;
1080 }
1081 }
1082 if (monomial_case) {
1084 return *this;
1085 }
1086
1087 // do not rename indices if the object has no indices at all
1091
1092 const bool skip_idx_rename = !(options & expand_options::expand_rename_idx);
1093
1094 // First, expand the children
1095 epvector expanded = expandchildren(options);
1096 const epvector & expanded_seq = (expanded.empty() ? seq : expanded);
1097
1098 // Now, look for all the factors that are sums and multiply each one out
1099 // with the next one that is found while collecting the factors which are
1100 // not sums
1101 ex last_expanded = _ex1;
1102
1103 epvector non_adds;
1104 non_adds.reserve(expanded_seq.size());
1105
1106 for (const auto & cit : expanded_seq) {
1107 if (is_exactly_a<add>(cit.rest) &&
1108 (cit.coeff.is_equal(_ex1))) {
1109 if (is_exactly_a<add>(last_expanded)) {
1110
1111 // Expand a product of two sums, aggressive version.
1112 // Caring for the overall coefficients in separate loops can
1113 // sometimes give a performance gain of up to 15%!
1114
1115 const int sizedifference = ex_to<add>(last_expanded).seq.size()-ex_to<add>(cit.rest).seq.size();
1116 // add2 is for the inner loop and should be the bigger of the two sums
1117 // in the presence of asymptotically good sorting:
1118 const add& add1 = (sizedifference<0 ? ex_to<add>(last_expanded) : ex_to<add>(cit.rest));
1119 const add& add2 = (sizedifference<0 ? ex_to<add>(cit.rest) : ex_to<add>(last_expanded));
1120 epvector distrseq;
1121 distrseq.reserve(add1.seq.size()+add2.seq.size());
1122
1123 // Multiply add2 with the overall coefficient of add1 and append it to distrseq:
1124 if (!add1.overall_coeff.is_zero()) {
1125 if (add1.overall_coeff.is_equal(_ex1))
1126 distrseq.insert(distrseq.end(), add2.seq.begin(), add2.seq.end());
1127 else
1128 for (const auto & i : add2.seq)
1129 distrseq.push_back(expair(i.rest, ex_to<numeric>(i.coeff).mul_dyn(ex_to<numeric>(add1.overall_coeff))));
1130 }
1131
1132 // Multiply add1 with the overall coefficient of add2 and append it to distrseq:
1133 if (!add2.overall_coeff.is_zero()) {
1134 if (add2.overall_coeff.is_equal(_ex1))
1135 distrseq.insert(distrseq.end(), add1.seq.begin(), add1.seq.end());
1136 else
1137 for (const auto & i : add1.seq)
1138 distrseq.push_back(expair(i.rest, ex_to<numeric>(i.coeff).mul_dyn(ex_to<numeric>(add2.overall_coeff))));
1139 }
1140
1141 // Compute the new overall coefficient and put it together:
1142 ex tmp_accu = dynallocate<add>(distrseq, add1.overall_coeff*add2.overall_coeff);
1143
1144 exvector add1_dummy_indices, add2_dummy_indices, add_indices;
1145 lst dummy_subs;
1146
1147 if (!skip_idx_rename) {
1148 for (const auto & i : add1.seq) {
1149 add_indices = get_all_dummy_indices_safely(i.rest);
1150 add1_dummy_indices.insert(add1_dummy_indices.end(), add_indices.begin(), add_indices.end());
1151 }
1152 for (const auto & i : add2.seq) {
1153 add_indices = get_all_dummy_indices_safely(i.rest);
1154 add2_dummy_indices.insert(add2_dummy_indices.end(), add_indices.begin(), add_indices.end());
1155 }
1156
1157 sort(add1_dummy_indices.begin(), add1_dummy_indices.end(), ex_is_less());
1158 sort(add2_dummy_indices.begin(), add2_dummy_indices.end(), ex_is_less());
1159 dummy_subs = rename_dummy_indices_uniquely(add1_dummy_indices, add2_dummy_indices);
1160 }
1161
1162 // Multiply explicitly all non-numeric terms of add1 and add2:
1163 for (const auto & i2 : add2.seq) {
1164 // We really have to combine terms here in order to compactify
1165 // the result. Otherwise it would become waayy tooo bigg.
1166 numeric oc(*_num0_p);
1167 epvector distrseq2;
1168 distrseq2.reserve(add1.seq.size());
1169 const ex i2_new = (skip_idx_rename || (dummy_subs.op(0).nops() == 0) ?
1170 i2.rest :
1171 i2.rest.subs(ex_to<lst>(dummy_subs.op(0)),
1172 ex_to<lst>(dummy_subs.op(1)), subs_options::no_pattern));
1173 for (const auto & i1 : add1.seq) {
1174 // Don't push_back expairs which might have a rest that evaluates to a numeric,
1175 // since that would violate an invariant of expairseq:
1176 const ex rest = dynallocate<mul>(i1.rest, i2_new);
1177 if (is_exactly_a<numeric>(rest)) {
1178 oc += ex_to<numeric>(rest).mul(ex_to<numeric>(i1.coeff).mul(ex_to<numeric>(i2.coeff)));
1179 } else {
1180 distrseq2.push_back(expair(rest, ex_to<numeric>(i1.coeff).mul_dyn(ex_to<numeric>(i2.coeff))));
1181 }
1182 }
1183 tmp_accu += dynallocate<add>(std::move(distrseq2), oc);
1184 }
1185 last_expanded = tmp_accu;
1186 } else {
1187 if (!last_expanded.is_equal(_ex1))
1188 non_adds.push_back(split_ex_to_pair(last_expanded));
1189 last_expanded = cit.rest;
1190 }
1191
1192 } else {
1193 non_adds.push_back(cit);
1194 }
1195 }
1196
1197 // Now the only remaining thing to do is to multiply the factors which
1198 // were not sums into the "last_expanded" sum
1199 if (is_exactly_a<add>(last_expanded)) {
1200 size_t n = last_expanded.nops();
1201 exvector distrseq;
1202 distrseq.reserve(n);
1203 exvector va;
1204 if (! skip_idx_rename) {
1205 va = get_all_dummy_indices_safely(mul(non_adds));
1206 sort(va.begin(), va.end(), ex_is_less());
1207 }
1208
1209 for (size_t i=0; i<n; ++i) {
1210 epvector factors = non_adds;
1211 if (skip_idx_rename)
1212 factors.push_back(split_ex_to_pair(last_expanded.op(i)));
1213 else
1214 factors.push_back(split_ex_to_pair(rename_dummy_indices_uniquely(va, last_expanded.op(i))));
1215 ex term = dynallocate<mul>(factors, overall_coeff);
1216 if (can_be_further_expanded(term)) {
1217 distrseq.push_back(term.expand());
1218 } else {
1219 if (options == 0)
1220 ex_to<basic>(term).setflag(status_flags::expanded);
1221 distrseq.push_back(term);
1222 }
1223 }
1224
1225 return dynallocate<add>(distrseq).setflag(options == 0 ? status_flags::expanded : 0);
1226 }
1227
1228 non_adds.push_back(split_ex_to_pair(last_expanded));
1229 ex result = dynallocate<mul>(non_adds, overall_coeff);
1230 if (can_be_further_expanded(result)) {
1231 return result.expand();
1232 } else {
1233 if (options == 0)
1234 ex_to<basic>(result).setflag(status_flags::expanded);
1235 return result;
1236 }
1237}
1238
1239
1241// new virtual functions which can be overridden by derived classes
1243
1244// none
1245
1247// non-virtual functions in this class
1249
1250
1259{
1260 auto cit = seq.begin(), last = seq.end();
1261 while (cit!=last) {
1262 const ex & factor = recombine_pair_to_ex(*cit);
1263 const ex & expanded_factor = factor.expand(options);
1264 if (!are_ex_trivially_equal(factor,expanded_factor)) {
1265
1266 // something changed, copy seq, eval and return it
1267 epvector s;
1268 s.reserve(seq.size());
1269
1270 // copy parts of seq which are known not to have changed
1271 auto cit2 = seq.begin();
1272 while (cit2!=cit) {
1273 s.push_back(*cit2);
1274 ++cit2;
1275 }
1276
1277 // copy first changed element
1278 s.push_back(split_ex_to_pair(expanded_factor));
1279 ++cit2;
1280
1281 // copy rest
1282 while (cit2!=last) {
1284 ++cit2;
1285 }
1286 return s;
1287 }
1288 ++cit;
1289 }
1290
1291 return epvector(); // nothing has changed
1292}
1293
1295
1296} // namespace GiNaC
Interface to GiNaC's sums of expressions.
Archiving of GiNaC expressions.
#define GINAC_ASSERT(X)
Assertion macro for checking invariances.
Definition: assertion.h:33
Sum of expressions.
Definition: add.h:32
expair combine_pair_with_coeff_to_pair(const expair &p, const ex &c) const override
Definition: add.cpp:550
This class is the ABC (abstract base class) of GiNaC's class hierarchy.
Definition: basic.h:105
const basic & clearflag(unsigned f) const
Clear some status_flags.
Definition: basic.h:291
const basic & setflag(unsigned f) const
Set some status_flags.
Definition: basic.h:288
ex diff(const symbol &s, unsigned nth=1) const
Default interface of nth derivative ex::diff(s, n).
Definition: basic.cpp:646
virtual bool has(const ex &other, unsigned options=0) const
Test for occurrence of a pattern.
Definition: basic.cpp:280
friend class ex
Definition: basic.h:108
unsigned flags
of type status_flags
Definition: basic.h:302
virtual void print(const print_context &c, unsigned level=0) const
Output to stream.
Definition: basic.cpp:116
ex subs_one_level(const exmap &m, unsigned options) const
Helper function for subs().
Definition: basic.cpp:585
const basic & hold() const
Stop further evaluation.
Definition: basic.cpp:887
virtual int compare_same_type(const basic &other) const
Returns order relation between two objects of same type.
Definition: basic.cpp:719
Wrapper template for making GiNaC classes out of STL containers.
Definition: container.h:73
ex op(size_t i) const override
Return operand/member at position i.
Definition: container.h:295
Lightweight wrapper for GiNaC's symbolic objects.
Definition: ex.h:72
bool match(const ex &pattern) const
Check whether expression matches a specified pattern.
Definition: ex.cpp:97
ex expand(unsigned options=0) const
Expand an expression.
Definition: ex.cpp:75
bool is_equal(const ex &other) const
Definition: ex.h:345
int degree(const ex &s) const
Definition: ex.h:173
ex evalf() const
Definition: ex.h:121
ex conjugate() const
Definition: ex.h:146
const_iterator end() const noexcept
Definition: ex.h:667
size_t nops() const
Definition: ex.h:135
ex imag_part() const
Definition: ex.h:148
ex subs(const exmap &m, unsigned options=0) const
Definition: ex.h:841
bool info(unsigned inf) const
Definition: ex.h:132
bool is_zero() const
Definition: ex.h:213
void print(const print_context &c, unsigned level=0) const
Print expression to stream.
Definition: ex.cpp:56
ex op(size_t i) const
Definition: ex.h:136
int ldegree(const ex &s) const
Definition: ex.h:174
ex real_part() const
Definition: ex.h:147
ex evalm() const
Definition: ex.h:122
ex coeff(const ex &s, int n=1) const
Definition: ex.h:175
A pair of expressions.
Definition: expair.h:38
void swap(expair &other)
Swap contents with other expair.
Definition: expair.h:82
ex rest
first member of pair, an arbitrary expression
Definition: expair.h:90
ex coeff
second member of pair, must be numeric
Definition: expair.h:91
bool is_equal(const expair &other) const
Member-wise check for canonical ordering equality.
Definition: expair.h:49
A sequence of class expair.
Definition: expairseq.h:50
size_t nops() const override
Number of operands/members.
Definition: expairseq.cpp:202
epvector seq
Definition: expairseq.h:127
void construct_from_epvector(const epvector &v, bool do_index_renaming=false)
Definition: expairseq.cpp:793
void construct_from_2_ex(const ex &lh, const ex &rh)
Definition: expairseq.cpp:605
bool is_canonical() const
Check if this expairseq is in sorted (canonical) form.
Definition: expairseq.cpp:972
void construct_from_exvector(const exvector &v)
Definition: expairseq.cpp:781
epvector evalchildren() const
Member-wise evaluate the expairs in this sequence.
Definition: expairseq.cpp:1047
ex op(size_t i) const override
Return operand/member at position i.
Definition: expairseq.cpp:210
void do_print_tree(const print_tree &c, unsigned level) const
Definition: expairseq.cpp:156
@ expand_rename_idx
used internally by mul::expand()
Definition: flags.h:34
@ algebraic
enable algebraic matching
Definition: flags.h:43
@ integer_polynomial
Definition: flags.h:256
@ cinteger_polynomial
Definition: flags.h:257
@ crational_polynomial
Definition: flags.h:259
@ rational_polynomial
Definition: flags.h:258
Symbolic matrices.
Definition: matrix.h:38
Product of expressions.
Definition: mul.h:32
ex thisexpairseq(const epvector &v, const ex &oc, bool do_index_renaming=false) const override
Create an object of this type.
Definition: mul.cpp:936
bool info(unsigned inf) const override
Information about the object.
Definition: mul.cpp:272
ex real_part() const override
Definition: mul.cpp:608
bool expair_needs_further_processing(epp it) override
Definition: mul.cpp:1007
ex evalf() const override
Evaluate object numerically.
Definition: mul.cpp:577
epvector expandchildren(unsigned options) const
Member-wise expand the expairs representing this sequence.
Definition: mul.cpp:1258
unsigned precedence() const override
Return relative operator precedence (for parenthezing output).
Definition: mul.h:51
bool is_polynomial(const ex &var) const override
Check whether this is a polynomial in the given variables.
Definition: mul.cpp:386
return_type_t return_type_tinfo() const override
Definition: mul.cpp:922
ex default_overall_coeff() const override
Definition: mul.cpp:1030
void do_print_csrc(const print_csrc &c, unsigned level) const
Definition: mul.cpp:208
unsigned return_type() const override
Definition: mul.cpp:889
void do_print(const print_context &c, unsigned level) const
Definition: mul.cpp:148
void do_print_python_repr(const print_python_repr &c, unsigned level) const
Definition: mul.cpp:261
ex conjugate() const override
Definition: mul.cpp:829
bool can_make_flat(const expair &p) const override
Definition: mul.cpp:1050
ex imag_part() const override
Definition: mul.cpp:615
expair combine_ex_with_coeff_to_pair(const ex &e, const ex &c) const override
Definition: mul.cpp:956
void combine_overall_coeff(const ex &c) override
Definition: mul.cpp:1035
int ldegree(const ex &s) const override
Return degree of lowest power in object s.
Definition: mul.cpp:412
ex eval() const override
Perform automatic term rewriting rules in this class.
Definition: mul.cpp:468
expair split_ex_to_pair(const ex &e) const override
Form an expair from an ex, using the corresponding semantics.
Definition: mul.cpp:946
ex expand(unsigned options=0) const override
Expand expression, i.e.
Definition: mul.cpp:1072
int degree(const ex &s) const override
Return degree of highest power in object s.
Definition: mul.cpp:397
void do_print_latex(const print_latex &c, unsigned level) const
Definition: mul.cpp:168
static bool can_be_further_expanded(const ex &e)
Definition: mul.cpp:1058
ex derivative(const symbol &s) const override
Implementation of ex::diff() for a product.
Definition: mul.cpp:863
void find_real_imag(ex &, ex &) const
Definition: mul.cpp:587
ex coeff(const ex &s, int n=1) const override
Return coefficient of degree n in object s.
Definition: mul.cpp:427
friend class power
Definition: mul.h:37
ex recombine_pair_to_ex(const expair &p) const override
Form an ex out of an expair, using the corresponding semantics.
Definition: mul.cpp:999
ex evalm() const override
Evaluate sums, products and integer powers of matrices.
Definition: mul.cpp:622
void print_overall_coeff(const print_context &c, const char *mul_sym) const
Definition: mul.cpp:126
expair combine_pair_with_coeff_to_pair(const expair &p, const ex &c) const override
Definition: mul.cpp:976
ex algebraic_subs_mul(const exmap &m, unsigned options) const
Definition: mul.cpp:770
ex eval_ncmul(const exvector &v) const override
Definition: mul.cpp:660
bool has(const ex &other, unsigned options=0) const override
Test for occurrence of a pattern.
Definition: mul.cpp:754
mul(const ex &lh, const ex &rh)
Definition: mul.cpp:64
This class is a wrapper around CLN-numbers within the GiNaC class hierarchy.
Definition: numeric.h:82
bool is_pos_integer() const
True if object is an exact integer greater than zero.
Definition: numeric.cpp:1161
bool is_integer() const
True if object is a non-complex integer.
Definition: numeric.cpp:1154
const numeric mul(const numeric &other) const
Numerical multiplication method.
Definition: numeric.cpp:880
const numeric div(const numeric &other) const
Numerical division method.
Definition: numeric.cpp:890
This class holds a two-component object, a basis and and exponent representing exponentiation.
Definition: power.h:39
ex basis
Definition: power.h:105
ex exponent
Definition: power.h:106
Base class for print_contexts.
Definition: print.h:103
Base context for C source output.
Definition: print.h:158
Context for latex-parsable output.
Definition: print.h:123
Context for python-parsable output.
Definition: print.h:139
@ noncommutative_composite
Definition: flags.h:282
@ expanded
.expand(0) has already done its job (other expand() options ignore this flag)
Definition: flags.h:204
@ evaluated
.eval() has already done its job
Definition: flags.h:203
@ hash_calculated
.calchash() has already done its job
Definition: flags.h:205
@ no_pattern
disable pattern matching
Definition: flags.h:51
@ algebraic
enable algebraic substitutions
Definition: flags.h:53
Basic CAS symbol.
Definition: symbol.h:39
Definition of optimizing macros.
#define likely(cond)
Definition: compiler.h:32
#define unlikely(cond)
Definition: compiler.h:31
upvec factors
Definition: factor.cpp:1430
unsigned options
Definition: factor.cpp:2475
size_t n
Definition: factor.cpp:1432
size_t c
Definition: factor.cpp:757
ex x
Definition: factor.cpp:1610
mvec m
Definition: factor.cpp:758
size_t last
Definition: factor.cpp:1434
Interface to GiNaC's indexed expressions.
Definition of GiNaC's lst.
Interface to symbolic matrices.
Interface to GiNaC's products of expressions.
Definition: add.cpp:38
const numeric * _num_1_p
Definition: utils.cpp:351
const numeric pow(const numeric &x, const numeric &y)
Definition: numeric.h:251
std::map< ex, ex, ex_is_less > exmap
Definition: basic.h:50
std::vector< expair > epvector
expair-vector
Definition: expairseq.h:33
const ex _ex1
Definition: utils.cpp:385
bool are_ex_trivially_equal(const ex &e1, const ex &e2)
Compare two objects of class quickly without doing a deep tree traversal.
Definition: ex.h:699
const ex _ex_1
Definition: utils.cpp:352
bool algebraic_match_mul_with_mul(const mul &e, const ex &pat, exmap &repls, int factor, int &nummatches, const std::vector< bool > &subsed, std::vector< bool > &matched)
Checks whether e matches to the pattern pat and the (possibly to be updated) list of replacements rep...
Definition: mul.cpp:723
print_func< print_context >(&varidx::do_print). print_func< print_latex >(&varidx
Definition: idx.cpp:45
GINAC_IMPLEMENT_REGISTERED_CLASS_OPT(add, expairseq, print_func< print_context >(&add::do_print). print_func< print_latex >(&add::do_print_latex). print_func< print_csrc >(&add::do_print_csrc). print_func< print_tree >(&add::do_print_tree). print_func< print_python_repr >(&add::do_print_python_repr)) add
Definition: add.cpp:40
const numeric * _num1_p
Definition: utils.cpp:384
epvector::iterator epp
expair-vector pointer
Definition: expairseq.h:34
ex factor(const ex &poly, unsigned options)
Interface function to the outside world.
Definition: factor.cpp:2576
lst rename_dummy_indices_uniquely(const exvector &va, const exvector &vb)
Similar to above, where va and vb are the same and the return value is a list of two lists for substi...
Definition: indexed.cpp:1460
GINAC_IMPLEMENT_REGISTERED_CLASS_OPT_T(lst, basic, print_func< print_context >(&lst::do_print). print_func< print_tree >(&lst::do_print_tree)) template<> bool lst GINAC_BIND_UNARCHIVER(lst)
Specialization of container::info() for lst.
Definition: lst.cpp:42
const ex _ex0
Definition: utils.cpp:369
bool tryfactsubs(const ex &origfactor, const ex &patternfactor, int &nummatches, exmap &repls)
Definition: mul.cpp:672
std::vector< ex > exvector
Definition: basic.h:48
const numeric * _num0_p
Definition: utils.cpp:367
exvector get_all_dummy_indices_safely(const ex &e)
More reliable version of the form.
Definition: indexed.cpp:1394
Interface to GiNaC's overloaded operators.
Interface to GiNaC's symbolic exponentiation (basis^exponent).
To distinguish between different kinds of non-commutative objects.
Definition: registrar.h:44
Interface to GiNaC's symbolic objects.
Interface to several small and furry utilities needed within GiNaC but not of any interest to the use...

This page is part of the GiNaC developer's reference. It was generated automatically by doxygen. For an introduction, see the tutorial.