+ // dirac_slash() object is printed differently
+ if (is_dirac_slash(seq[0])) {
+ c.s << "{";
+ seq[0].print(c, precedence());
+ c.s << "\\hspace{-1.0ex}/}";
+ } else {
+ c.s << "\\clifford[" << int(representation_label) << "]";
+ this->print_dispatch<inherited>(c, level);
+ }
+}
+
+DEFAULT_COMPARE(diracone)
+DEFAULT_COMPARE(cliffordunit)
+DEFAULT_COMPARE(diracgamma)
+DEFAULT_COMPARE(diracgamma5)
+DEFAULT_COMPARE(diracgammaL)
+DEFAULT_COMPARE(diracgammaR)
+
+DEFAULT_PRINT_LATEX(diracone, "ONE", "\\mathbf{1}")
+DEFAULT_PRINT_LATEX(cliffordunit, "e", "e")
+DEFAULT_PRINT_LATEX(diracgamma, "gamma", "\\gamma")
+DEFAULT_PRINT_LATEX(diracgamma5, "gamma5", "{\\gamma^5}")
+DEFAULT_PRINT_LATEX(diracgammaL, "gammaL", "{\\gamma_L}")
+DEFAULT_PRINT_LATEX(diracgammaR, "gammaR", "{\\gamma_R}")
+
+/** This function decomposes gamma~mu -> (1, mu) and a\ -> (a.ix, ix) */
+static void base_and_index(const ex & c, ex & b, ex & i)
+{
+ GINAC_ASSERT(is_a<clifford>(c));
+ GINAC_ASSERT(c.nops() == 2+1);
+
+ if (is_a<cliffordunit>(c.op(0))) { // proper dirac gamma object or clifford unit
+ i = c.op(1);
+ b = _ex1;
+ } else if (is_a<diracgamma5>(c.op(0)) || is_a<diracgammaL>(c.op(0)) || is_a<diracgammaR>(c.op(0))) { // gamma5/L/R
+ i = _ex0;
+ b = _ex1;
+ } else { // slash object, generate new dummy index
+ varidx ix((new symbol)->setflag(status_flags::dynallocated), ex_to<idx>(c.op(1)).get_dim());
+ b = indexed(c.op(0), ix.toggle_variance());
+ i = ix;
+ }
+}
+
+/** Predicate for finding non-clifford objects. */
+struct is_not_a_clifford : public std::unary_function<ex, bool> {
+ bool operator()(const ex & e)
+ {
+ return !is_a<clifford>(e);
+ }
+};
+
+/** Contraction of a gamma matrix with something else. */
+bool diracgamma::contract_with(exvector::iterator self, exvector::iterator other, exvector & v) const
+{
+ GINAC_ASSERT(is_a<clifford>(*self));
+ GINAC_ASSERT(is_a<indexed>(*other));
+ GINAC_ASSERT(is_a<diracgamma>(self->op(0)));
+ unsigned char rl = ex_to<clifford>(*self).get_representation_label();
+
+ ex dim = ex_to<idx>(self->op(1)).get_dim();
+ if (other->nops() > 1)
+ dim = minimal_dim(dim, ex_to<idx>(other->op(1)).get_dim());
+
+ if (is_a<clifford>(*other)) {
+
+ // Contraction only makes sense if the represenation labels are equal
+ if (ex_to<clifford>(*other).get_representation_label() != rl)
+ return false;
+
+ size_t num = other - self;
+
+ // gamma~mu gamma.mu = dim ONE
+ if (num == 1) {
+ *self = dim;
+ *other = dirac_ONE(rl);
+ return true;
+
+ // gamma~mu gamma~alpha gamma.mu = (2-dim) gamma~alpha
+ } else if (num == 2
+ && is_a<clifford>(self[1])) {
+ *self = 2 - dim;
+ *other = _ex1;
+ return true;
+
+ // gamma~mu gamma~alpha gamma~beta gamma.mu = 4 g~alpha~beta + (dim-4) gamam~alpha gamma~beta
+ } else if (num == 3
+ && is_a<clifford>(self[1])
+ && is_a<clifford>(self[2])) {
+ ex b1, i1, b2, i2;
+ base_and_index(self[1], b1, i1);
+ base_and_index(self[2], b2, i2);
+ *self = 4 * lorentz_g(i1, i2) * b1 * b2 * dirac_ONE(rl) + (dim - 4) * self[1] * self[2];
+ self[1] = _ex1;
+ self[2] = _ex1;
+ *other = _ex1;
+ return true;
+
+ // gamma~mu gamma~alpha gamma~beta gamma~delta gamma.mu = -2 gamma~delta gamma~beta gamma~alpha - (dim-4) gamam~alpha gamma~beta gamma~delta
+ } else if (num == 4
+ && is_a<clifford>(self[1])
+ && is_a<clifford>(self[2])
+ && is_a<clifford>(self[3])) {
+ *self = -2 * self[3] * self[2] * self[1] - (dim - 4) * self[1] * self[2] * self[3];
+ self[1] = _ex1;
+ self[2] = _ex1;
+ self[3] = _ex1;
+ *other = _ex1;
+ return true;
+
+ // gamma~mu Sodd gamma.mu = -2 Sodd_R
+ // (Chisholm identity in 4 dimensions)
+ } else if (!((other - self) & 1) && dim.is_equal(4)) {
+ if (std::find_if(self + 1, other, is_not_a_clifford()) != other)
+ return false;
+
+ *self = ncmul(exvector(std::reverse_iterator<exvector::const_iterator>(other), std::reverse_iterator<exvector::const_iterator>(self + 1)), true);
+ std::fill(self + 1, other, _ex1);
+ *other = _ex_2;
+ return true;
+
+ // gamma~mu Sodd gamma~alpha gamma.mu = 2 gamma~alpha Sodd + 2 Sodd_R gamma~alpha
+ // (commutate contracted indices towards each other, then use
+ // Chisholm identity in 4 dimensions)
+ } else if (((other - self) & 1) && dim.is_equal(4)) {
+ if (std::find_if(self + 1, other, is_not_a_clifford()) != other)
+ return false;
+
+ exvector::iterator next_to_last = other - 1;
+ ex S = ncmul(exvector(self + 1, next_to_last), true);
+ ex SR = ncmul(exvector(std::reverse_iterator<exvector::const_iterator>(next_to_last), std::reverse_iterator<exvector::const_iterator>(self + 1)), true);
+
+ *self = (*next_to_last) * S + SR * (*next_to_last);
+ std::fill(self + 1, other, _ex1);
+ *other = _ex2;
+ return true;
+
+ // gamma~mu S gamma~alpha gamma.mu = 2 gamma~alpha S - gamma~mu S gamma.mu gamma~alpha
+ // (commutate contracted indices towards each other, simplify_indexed()
+ // will re-expand and re-run the simplification)
+ } else {
+ if (std::find_if(self + 1, other, is_not_a_clifford()) != other)
+ return false;
+
+ exvector::iterator next_to_last = other - 1;
+ ex S = ncmul(exvector(self + 1, next_to_last), true);
+
+ *self = 2 * (*next_to_last) * S - (*self) * S * (*other) * (*next_to_last);
+ std::fill(self + 1, other + 1, _ex1);
+ return true;
+ }
+
+ } else if (is_a<symbol>(other->op(0)) && other->nops() == 2) {
+
+ // x.mu gamma~mu -> x-slash
+ *self = dirac_slash(other->op(0), dim, rl);
+ *other = _ex1;
+ return true;
+ }
+
+ return false;
+}
+
+/** An utility function looking for a given metric within an exvector,
+ * used in cliffordunit::contract_with(). */
+static int find_same_metric(exvector & v, ex & c)
+{
+ for (size_t i=0; i<v.size(); i++) {
+ if (is_a<indexed>(v[i]) && !is_a<clifford>(v[i])
+ && ((ex_to<varidx>(c.op(1)) == ex_to<indexed>(v[i]).get_indices()[0]
+ && ex_to<varidx>(c.op(1)) == ex_to<indexed>(v[i]).get_indices()[1])
+ || (ex_to<varidx>(c.op(1)).toggle_variance() == ex_to<indexed>(v[i]).get_indices()[0]
+ && ex_to<varidx>(c.op(1)).toggle_variance() == ex_to<indexed>(v[i]).get_indices()[1]))) {
+ return i; // the index of the found term
+ }
+ }
+ return -1; //nothing found
+}
+
+/** Contraction of a Clifford unit with something else. */
+bool cliffordunit::contract_with(exvector::iterator self, exvector::iterator other, exvector & v) const
+{
+ GINAC_ASSERT(is_a<clifford>(*self));
+ GINAC_ASSERT(is_a<indexed>(*other));
+ GINAC_ASSERT(is_a<cliffordunit>(self->op(0)));
+ clifford unit = ex_to<clifford>(*self);
+ unsigned char rl = unit.get_representation_label();
+
+ if (is_a<clifford>(*other)) {
+ // Contraction only makes sense if the represenation labels are equal
+ // and the metrics are the same
+ if ((ex_to<clifford>(*other).get_representation_label() != rl)
+ && unit.same_metric(*other))
+ return false;
+
+ // Find if a previous contraction produces the square of self
+ int prev_square = find_same_metric(v, *self);
+ const varidx d((new symbol)->setflag(status_flags::dynallocated), ex_to<idx>(self->op(1)).get_dim()),
+ in1((new symbol)->setflag(status_flags::dynallocated), ex_to<idx>(self->op(1)).get_dim()),
+ in2((new symbol)->setflag(status_flags::dynallocated), ex_to<idx>(self->op(1)).get_dim());
+ ex squared_metric;
+ if (prev_square > -1)
+ squared_metric = simplify_indexed(indexed(v[prev_square].op(0), in1, d)
+ * unit.get_metric(d.toggle_variance(), in2, true)).op(0);
+
+ exvector::iterator before_other = other - 1;
+ const varidx & mu = ex_to<varidx>(self->op(1));
+ const varidx & mu_toggle = ex_to<varidx>(other->op(1));
+ const varidx & alpha = ex_to<varidx>(before_other->op(1));
+
+ // e~mu e.mu = Tr ONE
+ if (other - self == 1) {
+ if (prev_square > -1) {
+ *self = indexed(squared_metric, mu, mu_toggle);
+ v[prev_square] = _ex1;
+ } else {
+ *self = unit.get_metric(mu, mu_toggle, true);
+ }
+ *other = dirac_ONE(rl);
+ return true;
+
+ } else if (other - self == 2) {
+ if (is_a<clifford>(*before_other) && ex_to<clifford>(*before_other).get_representation_label() == rl) {
+ if (ex_to<clifford>(*self).is_anticommuting()) {
+ // e~mu e~alpha e.mu = (2*pow(e~alpha, 2) -Tr(B)) e~alpha
+ if (prev_square > -1) {
+ *self = 2 * indexed(squared_metric, alpha, alpha)
+ - indexed(squared_metric, mu, mu_toggle);
+ v[prev_square] = _ex1;
+ } else {
+ *self = 2 * unit.get_metric(alpha, alpha, true) - unit.get_metric(mu, mu_toggle, true);
+ }
+ *other = _ex1;
+ return true;
+
+ } else {
+ // e~mu e~alpha e.mu = 2*e~mu B(alpha, mu.toggle_variance())-Tr(B) e~alpha
+ *self = 2 * (*self) * unit.get_metric(alpha, mu_toggle, true) - unit.get_metric(mu, mu_toggle, true) * (*before_other);
+ *before_other = _ex1;
+ *other = _ex1;
+ return true;
+ }
+ } else {
+ // e~mu S e.mu = Tr S ONE
+ *self = unit.get_metric(mu, mu_toggle, true);
+ *other = dirac_ONE(rl);
+ return true;
+ }
+ } else {
+ // e~mu S e~alpha e.mu = 2 e~mu S B(alpha, mu.toggle_variance()) - e~mu S e.mu e~alpha
+ // (commutate contracted indices towards each other, simplify_indexed()
+ // will re-expand and re-run the simplification)
+ if (std::find_if(self + 1, other, is_not_a_clifford()) != other) {
+ return false;
+ }
+
+ ex S = ncmul(exvector(self + 1, before_other), true);
+
+ if (is_a<clifford>(*before_other) && ex_to<clifford>(*before_other).get_representation_label() == rl) {
+ if (ex_to<clifford>(*self).is_anticommuting()) {
+ if (prev_square > -1) {
+ *self = 2 * (*before_other) * S * indexed(squared_metric, alpha, alpha)
+ - (*self) * S * (*other) * (*before_other);
+ } else {
+ *self = 2 * (*before_other) * S * unit.get_metric(alpha, alpha, true) - (*self) * S * (*other) * (*before_other);
+ }
+ } else {
+ *self = 2 * (*self) * S * unit.get_metric(alpha, mu_toggle, true) - (*self) * S * (*other) * (*before_other);
+ }
+ } else {
+ // simply commutes
+ *self = (*self) * S * (*other) * (*before_other);
+ }
+
+ std::fill(self + 1, other + 1, _ex1);
+ return true;
+ }
+ }
+ return false;
+}
+
+/** Perform automatic simplification on noncommutative product of clifford
+ * objects. This removes superfluous ONEs, permutes gamma5/L/R's to the front
+ * and removes squares of gamma objects. */
+ex clifford::eval_ncmul(const exvector & v) const
+{
+ exvector s;
+ s.reserve(v.size());
+
+ // Remove superfluous ONEs
+ exvector::const_iterator cit = v.begin(), citend = v.end();
+ while (cit != citend) {
+ if (!is_a<clifford>(*cit) || !is_a<diracone>(cit->op(0)))
+ s.push_back(*cit);
+ cit++;
+ }
+
+ bool something_changed = false;
+ int sign = 1;
+
+ // Anticommutate gamma5/L/R's to the front
+ if (s.size() >= 2) {
+ exvector::iterator first = s.begin(), next_to_last = s.end() - 2;
+ while (true) {
+ exvector::iterator it = next_to_last;
+ while (true) {
+ exvector::iterator it2 = it + 1;
+ if (is_a<clifford>(*it) && is_a<clifford>(*it2)) {
+ ex e1 = it->op(0), e2 = it2->op(0);
+
+ if (is_a<diracgamma5>(e2)) {
+
+ if (is_a<diracgammaL>(e1) || is_a<diracgammaR>(e1)) {
+
+ // gammaL/R gamma5 -> gamma5 gammaL/R
+ it->swap(*it2);
+ something_changed = true;
+
+ } else if (!is_a<diracgamma5>(e1)) {
+
+ // gamma5 gamma5 -> gamma5 gamma5 (do nothing)
+ // x gamma5 -> -gamma5 x
+ it->swap(*it2);
+ sign = -sign;
+ something_changed = true;
+ }
+
+ } else if (is_a<diracgammaL>(e2)) {
+
+ if (is_a<diracgammaR>(e1)) {
+
+ // gammaR gammaL -> 0
+ return _ex0;
+
+ } else if (!is_a<diracgammaL>(e1) && !is_a<diracgamma5>(e1)) {
+
+ // gammaL gammaL -> gammaL gammaL (do nothing)
+ // gamma5 gammaL -> gamma5 gammaL (do nothing)
+ // x gammaL -> gammaR x
+ it->swap(*it2);
+ *it = clifford(diracgammaR(), ex_to<clifford>(*it).get_representation_label());
+ something_changed = true;
+ }
+
+ } else if (is_a<diracgammaR>(e2)) {
+
+ if (is_a<diracgammaL>(e1)) {
+
+ // gammaL gammaR -> 0
+ return _ex0;
+
+ } else if (!is_a<diracgammaR>(e1) && !is_a<diracgamma5>(e1)) {
+
+ // gammaR gammaR -> gammaR gammaR (do nothing)
+ // gamma5 gammaR -> gamma5 gammaR (do nothing)
+ // x gammaR -> gammaL x
+ it->swap(*it2);
+ *it = clifford(diracgammaL(), ex_to<clifford>(*it).get_representation_label());
+ something_changed = true;
+ }
+ }
+ }
+ if (it == first)
+ break;
+ --it;
+ }
+ if (next_to_last == first)
+ break;
+ --next_to_last;
+ }
+ }
+
+ // Remove equal adjacent gammas
+ if (s.size() >= 2) {
+ exvector::iterator it, itend = s.end() - 1;
+ for (it = s.begin(); it != itend; ++it) {
+ ex & a = it[0];
+ ex & b = it[1];
+ if (!is_a<clifford>(a) || !is_a<clifford>(b))
+ continue;
+
+ const ex & ag = a.op(0);
+ const ex & bg = b.op(0);
+ bool a_is_cliffordunit = is_a<cliffordunit>(ag);
+ bool b_is_cliffordunit = is_a<cliffordunit>(bg);
+
+ if (a_is_cliffordunit && b_is_cliffordunit && ex_to<clifford>(a).same_metric(b)
+ && (ex_to<clifford>(a).get_commutator_sign() == -1)) {
+ // This is done only for Clifford algebras
+
+ const ex & ia = a.op(1);
+ const ex & ib = b.op(1);
+ if (ia.is_equal(ib)) { // gamma~alpha gamma~alpha -> g~alpha~alpha
+ a = ex_to<clifford>(a).get_metric(ia, ib, true);
+ b = dirac_ONE(representation_label);
+ something_changed = true;
+ }
+
+ } else if ((is_a<diracgamma5>(ag) && is_a<diracgamma5>(bg))) {
+
+ // Remove squares of gamma5
+ a = dirac_ONE(representation_label);
+ b = dirac_ONE(representation_label);
+ something_changed = true;
+
+ } else if ((is_a<diracgammaL>(ag) && is_a<diracgammaL>(bg))
+ || (is_a<diracgammaR>(ag) && is_a<diracgammaR>(bg))) {
+
+ // Remove squares of gammaL/R
+ b = dirac_ONE(representation_label);
+ something_changed = true;
+
+ } else if (is_a<diracgammaL>(ag) && is_a<diracgammaR>(bg)) {
+
+ // gammaL and gammaR are orthogonal
+ return _ex0;
+
+ } else if (is_a<diracgamma5>(ag) && is_a<diracgammaL>(bg)) {
+
+ // gamma5 gammaL -> -gammaL
+ a = dirac_ONE(representation_label);
+ sign = -sign;
+ something_changed = true;
+
+ } else if (is_a<diracgamma5>(ag) && is_a<diracgammaR>(bg)) {
+
+ // gamma5 gammaR -> gammaR
+ a = dirac_ONE(representation_label);
+ something_changed = true;
+
+ } else if (!a_is_cliffordunit && !b_is_cliffordunit && ag.is_equal(bg)) {
+
+ // a\ a\ -> a^2
+ varidx ix((new symbol)->setflag(status_flags::dynallocated), ex_to<idx>(a.op(1)).minimal_dim(ex_to<idx>(b.op(1))));
+
+ a = indexed(ag, ix) * indexed(ag, ix.toggle_variance());
+ b = dirac_ONE(representation_label);
+ something_changed = true;
+ }
+ }
+ }
+
+ if (s.empty())
+ return dirac_ONE(representation_label) * sign;
+ if (something_changed)
+ return reeval_ncmul(s) * sign;
+ else
+ return hold_ncmul(s) * sign;
+}
+
+ex clifford::thiscontainer(const exvector & v) const
+{
+ return clifford(representation_label, metric, anticommuting, commutator_sign, v);
+}
+
+ex clifford::thiscontainer(std::auto_ptr<exvector> vp) const
+{
+ return clifford(representation_label, metric, anticommuting, commutator_sign, vp);
+}
+
+ex diracgamma5::conjugate() const
+{
+ return _ex_1 * (*this);
+}
+
+ex diracgammaL::conjugate() const
+{
+ return (new diracgammaR)->setflag(status_flags::dynallocated);
+}
+
+ex diracgammaR::conjugate() const
+{
+ return (new diracgammaL)->setflag(status_flags::dynallocated);