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
77
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
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
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: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
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.
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: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
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: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
@ 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
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 ls GINAC_BIND_UNARCHIVER)(lst)
Specialization of container::info() for lst.
Definition lst.cpp:42
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
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
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).
#define GINAC_IMPLEMENT_REGISTERED_CLASS_OPT(classname, supername, options)
Macro for inclusion in the implementation of each registered class.
Definition registrar.h:185
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.