GiNaC  1.8.3
ncmul.cpp
Go to the documentation of this file.
1 
5 /*
6  * GiNaC Copyright (C) 1999-2022 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 "ncmul.h"
24 #include "ex.h"
25 #include "add.h"
26 #include "mul.h"
27 #include "clifford.h"
28 #include "matrix.h"
29 #include "archive.h"
30 #include "indexed.h"
31 #include "utils.h"
32 
33 #include <algorithm>
34 #include <iostream>
35 #include <stdexcept>
36 
37 namespace GiNaC {
38 
41  print_func<print_tree>(&ncmul::do_print_tree).
42  print_func<print_csrc>(&ncmul::do_print_csrc).
43  print_func<print_python_repr>(&ncmul::do_print_csrc))
44 
45 
46 // default constructor
49 
51 {
52 }
53 
55 // other constructors
57 
58 // public
59 
60 ncmul::ncmul(const ex & lh, const ex & rh) : inherited{lh,rh}
61 {
62 }
63 
64 ncmul::ncmul(const ex & f1, const ex & f2, const ex & f3) : inherited{f1,f2,f3}
65 {
66 }
67 
68 ncmul::ncmul(const ex & f1, const ex & f2, const ex & f3,
69  const ex & f4) : inherited{f1,f2,f3,f4}
70 {
71 }
72 
73 ncmul::ncmul(const ex & f1, const ex & f2, const ex & f3,
74  const ex & f4, const ex & f5) : inherited{f1,f2,f3,f4,f5}
75 {
76 }
77 
78 ncmul::ncmul(const ex & f1, const ex & f2, const ex & f3,
79  const ex & f4, const ex & f5, const ex & f6) : inherited{f1,f2,f3,f4,f5,f6}
80 {
81 }
82 
83 ncmul::ncmul(const exvector & v) : inherited(v)
84 {
85 }
86 
87 ncmul::ncmul(exvector && v) : inherited(std::move(v))
88 {
89 }
90 
92 // archiving
94 
95 
97 // functions overriding virtual functions from base classes
99 
100 // public
101 
102 void ncmul::do_print(const print_context & c, unsigned level) const
103 {
104  printseq(c, '(', '*', ')', precedence(), level);
105 }
106 
107 void ncmul::do_print_csrc(const print_context & c, unsigned level) const
108 {
109  c.s << class_name();
110  printseq(c, '(', ',', ')', precedence(), precedence());
111 }
112 
113 bool ncmul::info(unsigned inf) const
114 {
115  return inherited::info(inf);
116 }
117 
118 typedef std::vector<std::size_t> uintvector;
119 
120 ex ncmul::expand(unsigned options) const
121 {
122  // First, expand the children
124  const exvector &expanded_seq = v.empty() ? this->seq : v;
125 
126  // Now, look for all the factors that are sums and remember their
127  // position and number of terms.
128  uintvector positions_of_adds(expanded_seq.size());
129  uintvector number_of_add_operands(expanded_seq.size());
130 
131  size_t number_of_adds = 0;
132  size_t number_of_expanded_terms = 1;
133 
134  size_t current_position = 0;
135  for (auto & it : expanded_seq) {
136  if (is_exactly_a<add>(it)) {
137  positions_of_adds[number_of_adds] = current_position;
138  size_t num_ops = it.nops();
139  number_of_add_operands[number_of_adds] = num_ops;
140  number_of_expanded_terms *= num_ops;
141  number_of_adds++;
142  }
143  ++current_position;
144  }
145 
146  // If there are no sums, we are done
147  if (number_of_adds == 0) {
148  if (!v.empty())
149  return dynallocate<ncmul>(std::move(v)).setflag(options == 0 ? status_flags::expanded : 0);
150  else
151  return *this;
152  }
153 
154  // Now, form all possible products of the terms of the sums with the
155  // remaining factors, and add them together
156  exvector distrseq;
157  distrseq.reserve(number_of_expanded_terms);
158 
159  uintvector k(number_of_adds);
160 
161  /* Rename indices in the static members of the product */
162  exvector expanded_seq_mod;
163  size_t j = 0;
164  exvector va;
165 
166  for (size_t i=0; i<expanded_seq.size(); i++) {
167  if (i == positions_of_adds[j]) {
168  expanded_seq_mod.push_back(_ex1);
169  j++;
170  } else {
171  expanded_seq_mod.push_back(rename_dummy_indices_uniquely(va, expanded_seq[i], true));
172  }
173  }
174 
175  while (true) {
176  exvector term = expanded_seq_mod;
177  for (size_t i=0; i<number_of_adds; i++) {
178  term[positions_of_adds[i]] = rename_dummy_indices_uniquely(va, expanded_seq[positions_of_adds[i]].op(k[i]), true);
179  }
180 
181  distrseq.push_back(dynallocate<ncmul>(std::move(term)).setflag(options == 0 ? status_flags::expanded : 0));
182 
183  // increment k[]
184  int l = number_of_adds-1;
185  while ((l>=0) && ((++k[l]) >= number_of_add_operands[l])) {
186  k[l] = 0;
187  l--;
188  }
189  if (l<0)
190  break;
191  }
192 
193  return dynallocate<add>(distrseq).setflag(options == 0 ? status_flags::expanded : 0);
194 }
195 
196 int ncmul::degree(const ex & s) const
197 {
198  if (is_equal(ex_to<basic>(s)))
199  return 1;
200 
201  // Sum up degrees of factors
202  int deg_sum = 0;
203  for (auto & i : seq)
204  deg_sum += i.degree(s);
205  return deg_sum;
206 }
207 
208 int ncmul::ldegree(const ex & s) const
209 {
210  if (is_equal(ex_to<basic>(s)))
211  return 1;
212 
213  // Sum up degrees of factors
214  int deg_sum = 0;
215  for (auto & i : seq)
216  deg_sum += i.degree(s);
217  return deg_sum;
218 }
219 
220 ex ncmul::coeff(const ex & s, int n) const
221 {
222  if (is_equal(ex_to<basic>(s)))
223  return n==1 ? _ex1 : _ex0;
224 
225  exvector coeffseq;
226  coeffseq.reserve(seq.size());
227 
228  if (n == 0) {
229  // product of individual coeffs
230  // if a non-zero power of s is found, the resulting product will be 0
231  for (auto & it : seq)
232  coeffseq.push_back(it.coeff(s,n));
233  return dynallocate<ncmul>(std::move(coeffseq));
234  }
235 
236  bool coeff_found = false;
237  for (auto & i : seq) {
238  ex c = i.coeff(s,n);
239  if (c.is_zero()) {
240  coeffseq.push_back(i);
241  } else {
242  coeffseq.push_back(c);
243  coeff_found = true;
244  }
245  }
246 
247  if (coeff_found)
248  return dynallocate<ncmul>(std::move(coeffseq));
249 
250  return _ex0;
251 }
252 
253 size_t ncmul::count_factors(const ex & e) const
254 {
255  if ((is_exactly_a<mul>(e)&&(e.return_type()!=return_types::commutative))||
256  (is_exactly_a<ncmul>(e))) {
257  size_t factors=0;
258  for (size_t i=0; i<e.nops(); i++)
259  factors += count_factors(e.op(i));
260 
261  return factors;
262  }
263  return 1;
264 }
265 
266 void ncmul::append_factors(exvector & v, const ex & e) const
267 {
268  if ((is_exactly_a<mul>(e)&&(e.return_type()!=return_types::commutative))||
269  (is_exactly_a<ncmul>(e))) {
270  for (size_t i=0; i<e.nops(); i++)
271  append_factors(v, e.op(i));
272  } else
273  v.push_back(e);
274 }
275 
276 typedef std::vector<unsigned> unsignedvector;
277 typedef std::vector<exvector> exvectorvector;
278 
290 {
291  // The following additional rule would be nice, but produces a recursion,
292  // which must be trapped by introducing a flag that the sub-ncmuls()
293  // are already evaluated (maybe later...)
294  // ncmul(x1,x2,...,X,y1,y2,...) ->
295  // ncmul(ncmul(x1,x2,...),X,ncmul(y1,y2,...)
296  // (X noncommutative_composite)
297 
299  return *this;
300  }
301 
302  // ncmul(...,*(x1,x2),...,ncmul(x3,x4),...) ->
303  // ncmul(...,x1,x2,...,x3,x4,...) (associativity)
304  size_t factors = 0;
305  for (auto & it : seq)
306  factors += count_factors(it);
307 
308  exvector assocseq;
309  assocseq.reserve(factors);
310  make_flat_inserter mf(seq, true);
311  for (auto & it : seq) {
312  ex factor = mf.handle_factor(it, 1);
313  append_factors(assocseq, factor);
314  }
315 
316  // ncmul(x) -> x
317  if (assocseq.size()==1) return *(seq.begin());
318 
319  // ncmul() -> 1
320  if (assocseq.empty()) return _ex1;
321 
322  // determine return types
323  unsignedvector rettypes(assocseq.size());
324  size_t i = 0;
325  size_t count_commutative=0;
326  size_t count_noncommutative=0;
327  size_t count_noncommutative_composite=0;
328  for (auto & it : assocseq) {
329  rettypes[i] = it.return_type();
330  switch (rettypes[i]) {
332  count_commutative++;
333  break;
335  count_noncommutative++;
336  break;
338  count_noncommutative_composite++;
339  break;
340  default:
341  throw(std::logic_error("ncmul::eval(): invalid return type"));
342  }
343  ++i;
344  }
345  GINAC_ASSERT(count_commutative+count_noncommutative+count_noncommutative_composite==assocseq.size());
346 
347  // ncmul(...,c1,...,c2,...) ->
348  // *(c1,c2,ncmul(...)) (pull out commutative elements)
349  if (count_commutative!=0) {
350  exvector commutativeseq;
351  commutativeseq.reserve(count_commutative+1);
352  exvector noncommutativeseq;
353  noncommutativeseq.reserve(assocseq.size()-count_commutative);
354  size_t num = assocseq.size();
355  for (size_t i=0; i<num; ++i) {
356  if (rettypes[i]==return_types::commutative)
357  commutativeseq.push_back(assocseq[i]);
358  else
359  noncommutativeseq.push_back(assocseq[i]);
360  }
361  commutativeseq.push_back(dynallocate<ncmul>(std::move(noncommutativeseq)));
362  return dynallocate<mul>(std::move(commutativeseq));
363  }
364 
365  // ncmul(x1,y1,x2,y2) -> *(ncmul(x1,x2),ncmul(y1,y2))
366  // (collect elements of same type)
367 
368  if (count_noncommutative_composite==0) {
369  // there are neither commutative nor noncommutative_composite
370  // elements in assocseq
371  GINAC_ASSERT(count_commutative==0);
372 
373  size_t assoc_num = assocseq.size();
374  exvectorvector evv;
375  std::vector<return_type_t> rttinfos;
376  evv.reserve(assoc_num);
377  rttinfos.reserve(assoc_num);
378 
379  for (auto & it : assocseq) {
380  return_type_t ti = it.return_type_tinfo();
381  size_t rtt_num = rttinfos.size();
382  // search type in vector of known types
383  for (i=0; i<rtt_num; ++i) {
384  if(ti == rttinfos[i]) {
385  evv[i].push_back(it);
386  break;
387  }
388  }
389  if (i >= rtt_num) {
390  // new type
391  rttinfos.push_back(ti);
392  evv.push_back(exvector());
393  (evv.end()-1)->reserve(assoc_num);
394  (evv.end()-1)->push_back(it);
395  }
396  }
397 
398  size_t evv_num = evv.size();
399 #ifdef DO_GINAC_ASSERT
400  GINAC_ASSERT(evv_num == rttinfos.size());
401  GINAC_ASSERT(evv_num > 0);
402  size_t s=0;
403  for (i=0; i<evv_num; ++i)
404  s += evv[i].size();
405  GINAC_ASSERT(s == assoc_num);
406 #endif // def DO_GINAC_ASSERT
407 
408  // if all elements are of same type, simplify the string
409  if (evv_num == 1) {
410  return evv[0][0].eval_ncmul(evv[0]);
411  }
412 
413  exvector splitseq;
414  splitseq.reserve(evv_num);
415  for (i=0; i<evv_num; ++i)
416  splitseq.push_back(dynallocate<ncmul>(evv[i]));
417 
418  return dynallocate<mul>(splitseq);
419  }
420 
421  return dynallocate<ncmul>(assocseq).setflag(status_flags::evaluated);
422 }
423 
425 {
426  // Evaluate children first
427  exvector s;
428  s.reserve(seq.size());
429  for (auto & it : seq)
430  s.push_back(it.evalm());
431 
432  // If there are only matrices, simply multiply them
433  auto it = s.begin(), itend = s.end();
434  if (is_a<matrix>(*it)) {
435  matrix prod(ex_to<matrix>(*it));
436  it++;
437  while (it != itend) {
438  if (!is_a<matrix>(*it))
439  goto no_matrix;
440  prod = prod.mul(ex_to<matrix>(*it));
441  it++;
442  }
443  return prod;
444  }
445 
446 no_matrix:
447  return dynallocate<ncmul>(std::move(s));
448 }
449 
451 {
452  return dynallocate<ncmul>(v);
453 }
454 
456 {
457  return dynallocate<ncmul>(std::move(v));
458 }
459 
461 {
463  return exprseq::conjugate();
464  }
465 
467  return exprseq::conjugate();
468  }
469 
470  exvector ev;
471  ev.reserve(nops());
472  for (auto i=end(); i!=begin();) {
473  --i;
474  ev.push_back(i->conjugate());
475  }
476  return dynallocate<ncmul>(std::move(ev));
477 }
478 
480 {
481  return basic::real_part();
482 }
483 
485 {
486  return basic::imag_part();
487 }
488 
489 // protected
490 
494 ex ncmul::derivative(const symbol & s) const
495 {
496  size_t num = seq.size();
497  exvector addseq;
498  addseq.reserve(num);
499 
500  // D(a*b*c) = D(a)*b*c + a*D(b)*c + a*b*D(c)
501  exvector ncmulseq = seq;
502  for (size_t i=0; i<num; ++i) {
503  ex e = seq[i].diff(s);
504  e.swap(ncmulseq[i]);
505  addseq.push_back(dynallocate<ncmul>(ncmulseq));
506  e.swap(ncmulseq[i]);
507  }
508  return dynallocate<add>(addseq);
509 }
510 
511 int ncmul::compare_same_type(const basic & other) const
512 {
513  return inherited::compare_same_type(other);
514 }
515 
516 unsigned ncmul::return_type() const
517 {
518  if (seq.empty())
520 
521  bool all_commutative = true;
522  exvector::const_iterator noncommutative_element; // point to first found nc element
523 
524  auto i = seq.begin(), end = seq.end();
525  while (i != end) {
526  unsigned rt = i->return_type();
528  return rt; // one ncc -> mul also ncc
529  if ((rt == return_types::noncommutative) && (all_commutative)) {
530  // first nc element found, remember position
531  noncommutative_element = i;
532  all_commutative = false;
533  }
534  if ((rt == return_types::noncommutative) && (!all_commutative)) {
535  // another nc element found, compare type_infos
536  if(noncommutative_element->return_type_tinfo() != i->return_type_tinfo())
538  }
539  ++i;
540  }
541  // all factors checked
542  GINAC_ASSERT(!all_commutative); // not all factors should commutate, because this is a ncmul();
543  return all_commutative ? return_types::commutative : return_types::noncommutative;
544 }
545 
547 {
548  if (seq.empty())
549  return make_return_type_t<ncmul>();
550 
551  // return type_info of first noncommutative element
552  for (auto & i : seq)
553  if (i.return_type() == return_types::noncommutative)
554  return i.return_type_tinfo();
555 
556  // no noncommutative element found, should not happen
557  return make_return_type_t<ncmul>();
558 }
559 
561 // new virtual functions which can be overridden by derived classes
563 
564 // none
565 
567 // non-virtual functions in this class
569 
571 {
572  auto cit = this->seq.begin(), end = this->seq.end();
573  while (cit != end) {
574  const ex & expanded_ex = cit->expand(options);
575  if (!are_ex_trivially_equal(*cit, expanded_ex)) {
576 
577  // copy first part of seq which hasn't changed
578  exvector s(this->seq.begin(), cit);
579  s.reserve(this->seq.size());
580 
581  // insert changed element
582  s.push_back(expanded_ex);
583  ++cit;
584 
585  // copy rest
586  while (cit != end) {
587  s.push_back(cit->expand(options));
588  ++cit;
589  }
590 
591  return s;
592  }
593 
594  ++cit;
595  }
596 
597  return exvector(); // nothing has changed
598 }
599 
601 {
602  return seq;
603 }
604 
606 // friend functions
608 
610 {
611  return dynallocate<ncmul>(v);
612 }
613 
615 {
616  if (v.empty())
617  return _ex1;
618  else if (v.size() == 1)
619  return v[0];
620  else
621  return dynallocate<ncmul>(v).setflag(status_flags::evaluated);
622 }
623 
625 
626 } // 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
This class is the ABC (abstract base class) of GiNaC's class hierarchy.
Definition: basic.h:105
virtual ex imag_part() const
Definition: basic.cpp:681
unsigned flags
of type status_flags
Definition: basic.h:302
bool is_equal(const basic &other) const
Test for syntactic equality.
Definition: basic.cpp:863
const basic & setflag(unsigned f) const
Set some status_flags.
Definition: basic.h:288
virtual ex real_part() const
Definition: basic.cpp:676
virtual int compare_same_type(const basic &other) const
Returns order relation between two objects of same type.
Definition: basic.cpp:719
void reserve(size_t)
Definition: container.h:54
Wrapper template for making GiNaC classes out of STL containers.
Definition: container.h:73
virtual void printseq(const print_context &c, char openbracket, char delim, char closebracket, unsigned this_precedence, unsigned upper_precedence=0) const
Print sequence of contained elements.
Definition: container.h:451
const_iterator end() const
Definition: container.h:240
ex conjugate() const override
Definition: container.h:147
const_iterator begin() const
Definition: container.h:239
size_t nops() const override
Number of operands/members.
Definition: container.h:118
ex op(size_t i) const override
Return operand/member at position i.
Definition: container.h:295
void do_print_tree(const print_tree &c, unsigned level) const
Definition: container.h:267
Lightweight wrapper for GiNaC's symbolic objects.
Definition: ex.h:72
const_iterator begin() const noexcept
Definition: ex.h:647
ex expand(unsigned options=0) const
Definition: ex.cpp:73
unsigned return_type() const
Definition: ex.h:230
void swap(ex &other) noexcept
Efficiently swap the contents of two expressions.
Definition: ex.h:104
size_t nops() const
Definition: ex.h:135
ex op(size_t i) const
Definition: ex.h:136
Class to handle the renaming of dummy indices.
Definition: expairseq.h:136
ex handle_factor(const ex &x, const ex &coeff)
Definition: expairseq.h:153
Symbolic matrices.
Definition: matrix.h:38
matrix mul(const matrix &other) const
Product of matrices.
Definition: matrix.cpp:589
Non-commutative product of expressions.
Definition: ncmul.h:33
unsigned precedence() const override
Return relative operator precedence (for parenthezing output).
Definition: ncmul.h:57
int ldegree(const ex &s) const override
Return degree of lowest power in object s.
Definition: ncmul.cpp:208
int degree(const ex &s) const override
Return degree of highest power in object s.
Definition: ncmul.cpp:196
void append_factors(exvector &v, const ex &e) const
Definition: ncmul.cpp:266
ex coeff(const ex &s, int n=1) const override
Return coefficient of degree n in object s.
Definition: ncmul.cpp:220
return_type_t return_type_tinfo() const override
Definition: ncmul.cpp:546
void do_print(const print_context &c, unsigned level) const
Definition: ncmul.cpp:102
ex expand(unsigned options=0) const override
Expand expression, i.e.
Definition: ncmul.cpp:120
exvector expandchildren(unsigned options) const
Definition: ncmul.cpp:570
ex imag_part() const override
Definition: ncmul.cpp:484
ncmul(const ex &lh, const ex &rh)
Definition: ncmul.cpp:60
ex real_part() const override
Definition: ncmul.cpp:479
void do_print_csrc(const print_context &c, unsigned level) const
Definition: ncmul.cpp:107
ex eval() const override
Perform automatic term rewriting rules in this class.
Definition: ncmul.cpp:289
ex evalm() const override
Evaluate sums, products and integer powers of matrices.
Definition: ncmul.cpp:424
ex derivative(const symbol &s) const override
Implementation of ex::diff() for a non-commutative product.
Definition: ncmul.cpp:494
unsigned return_type() const override
Definition: ncmul.cpp:516
bool info(unsigned inf) const override
Information about the object.
Definition: ncmul.cpp:113
const exvector & get_factors() const
Definition: ncmul.cpp:600
size_t count_factors(const ex &e) const
Definition: ncmul.cpp:253
ex thiscontainer(const exvector &v) const override
Definition: ncmul.cpp:450
ex conjugate() const override
Definition: ncmul.cpp:460
Base class for print_contexts.
Definition: print.h:103
@ 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
Basic CAS symbol.
Definition: symbol.h:39
Interface to GiNaC's clifford algebra (Dirac gamma) objects.
Interface to GiNaC's light-weight expression handles.
upvec factors
Definition: factor.cpp:1461
unsigned options
Definition: factor.cpp:2480
vector< int > k
Definition: factor.cpp:1466
size_t n
Definition: factor.cpp:1463
size_t c
Definition: factor.cpp:770
Interface to GiNaC's indexed expressions.
Interface to symbolic matrices.
Interface to GiNaC's products of expressions.
Definition: add.cpp:38
ex hold_ncmul(const exvector &v)
Definition: ncmul.cpp:614
bool is_clifford_tinfo(const return_type_t &ti)
Check whether a given return_type_t object (as returned by return_type_tinfo() is that of a clifford ...
Definition: clifford.h:194
ex reeval_ncmul(const exvector &v)
Definition: ncmul.cpp:609
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:684
std::vector< exvector > exvectorvector
Definition: ncmul.cpp:277
std::vector< std::size_t > uintvector
Definition: ncmul.cpp:118
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
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
ex factor(const ex &poly, unsigned options)
Interface function to the outside world.
Definition: factor.cpp:2581
std::vector< unsigned > unsignedvector
Definition: ncmul.cpp:276
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
std::vector< ex > exvector
Definition: basic.h:46
Definition: ex.h:972
Interface to GiNaC's non-commutative products of expressions.
To distinguish between different kinds of non-commutative objects.
Definition: registrar.h:44
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.