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