# Path: drudge/term.py
# class ATerms(abc.ABC):
#     """Abstract base class for terms.
# 
#     This abstract class is meant for things that can be interpreted as a local
#     collection of some tensor terms, mostly used for user input of tensor terms.
# 
#     """
# 
#     @property
#     @abc.abstractmethod
#     def terms(self) -> typing.List['Term']:
#         """Get an list for the terms.
#         """
#         pass
# 
#     #
#     # Mathematical operations.
#     #
# 
#     _op_priority = 19.0  # Just less than the full tensor.
# 
#     def __mul__(self, other):
#         """Multiply something on the right."""
# 
#         if is_higher(other, self._op_priority):
#             return NotImplemented
#         return self._mul(self.terms, parse_terms(other))
# 
#     def __rmul__(self, other):
#         """Multiply something on the left."""
# 
#         if is_higher(other, self._op_priority):
#             return NotImplemented
#         return self._mul(parse_terms(other), self.terms)
# 
#     @staticmethod
#     def _mul(left_terms, right_terms):
#         """Multiply the left terms with the right terms.
# 
#         Note that the terms should not have any conflict in dummies.  Actually,
#         by the common scheme in user input by drudge, the terms should normally
#         have no summations at all.  So this function has different semantics
#         than the term multiplication function from the Terms class.
#         """
# 
#         prod_terms = []
#         for i, j in itertools.product(left_terms, right_terms):
#             # A shallow checking on sums, normally we have no sums by design.
#             sums = _cat_sums(i.sums, j.sums)
#             amp = i.amp * j.amp
#             vecs = i.vecs + j.vecs
# 
#             prod_terms.append(Term(sums, amp, vecs))
#             continue
# 
#         return Terms(prod_terms)
# 
#     def __add__(self, other):
#         """Add something on the right."""
# 
#         if is_higher(other, self._op_priority):
#             return NotImplemented
#         return self._add(self.terms, parse_terms(other))
# 
#     def __radd__(self, other):
#         """Add something on the left."""
# 
#         if is_higher(other, self._op_priority):
#             return NotImplemented
#         return self._add(parse_terms(other), self.terms)
# 
#     def __sub__(self, other):
#         """Subtract something on the right."""
# 
#         if is_higher(other, self._op_priority):
#             return NotImplemented
#         other_terms = self._neg_terms(parse_terms(other))
#         return self._add(self.terms, other_terms)
# 
#     def __rsub__(self, other):
#         """Be subtracted from something on the left."""
# 
#         if is_higher(other, self._op_priority):
#             return NotImplemented
#         self_terms = self._neg_terms(parse_terms(self))
#         return self._add(parse_terms(other), self_terms)
# 
#     def __neg__(self):
#         """Negate the terms."""
#         return Terms(self._neg_terms(parse_terms(self)))
# 
#     @staticmethod
#     def _add(left_terms, right_terms):
#         """Add the terms together.
#         """
#         return Terms(itertools.chain(left_terms, right_terms))
# 
#     @staticmethod
#     def _neg_terms(terms: typing.Iterable['Term']):
#         """Negate the given terms.
# 
#         The resulted terms are lazily evaluated.
#         """
#         return (
#             Term(i.sums, i.amp * _NEG_UNITY, i.vecs)
#             for i in terms
#         )
# 
#     def __truediv__(self, other):
#         """Make division with another object."""
#         other = sympify(other)
#         return Terms([
#             i.scale(1 / other) for i in self.terms
#         ])
# 
#     def __rtruediv__(self, other):
#         """Being divided over by other object."""
#         raise NotImplementedError('General tensors cannot inversed')
# 
#     def __or__(self, other):
#         """Compute the commutator with another object.
#         """
#         if is_higher(other, self._op_priority):
#             return NotImplemented
#         return self * other - other * self
# 
#     def __ror__(self, other):
#         """Compute the commutator with another object on the right."""
#         if is_higher(other, self._op_priority):
#             return NotImplemented
#         return other * self - self * other


import re
import shutil
import subprocess
import warnings
from jinja2 import Environment, PackageLoader
from sympy.printing.latex import LatexPrinter
from .term import ATerms
            tags of LaTeX.

        kwargs

            All the rest of the keyword arguments are forwarded to the
            :py:meth:`Drudge.format_latex` method.

        Note
        ----

        **For long equations in LaTeX environments,** normally ``env='align'``
        and ``sep_lines=True`` can be set to allow each term to occupy separate
        lines, automatic page break will be inserted, or ``env='dmath'`` and
        ``sep_lines=False`` can be used to use ``breqn`` package to
        automatically flow the terms.

        """

        if env == '[':
            opening, closing = r'\[', r'\]'
        else:
            opening, closing = [
                r'\{}{{{}}}'.format(i, env)
                for i in ['begin', 'end']
            ]

        if content is None:
            expr = None
        elif hasattr(content, 'latex'):
            expr = content.latex(**kwargs)

# Path: drudge/term.py
# class ATerms(abc.ABC):
#     """Abstract base class for terms.
# 
#     This abstract class is meant for things that can be interpreted as a local
#     collection of some tensor terms, mostly used for user input of tensor terms.
# 
#     """
# 
#     @property
#     @abc.abstractmethod
#     def terms(self) -> typing.List['Term']:
#         """Get an list for the terms.
#         """
#         pass
# 
#     #
#     # Mathematical operations.
#     #
# 
#     _op_priority = 19.0  # Just less than the full tensor.
# 
#     def __mul__(self, other):
#         """Multiply something on the right."""
# 
#         if is_higher(other, self._op_priority):
#             return NotImplemented
#         return self._mul(self.terms, parse_terms(other))
# 
#     def __rmul__(self, other):
#         """Multiply something on the left."""
# 
#         if is_higher(other, self._op_priority):
#             return NotImplemented
#         return self._mul(parse_terms(other), self.terms)
# 
#     @staticmethod
#     def _mul(left_terms, right_terms):
#         """Multiply the left terms with the right terms.
# 
#         Note that the terms should not have any conflict in dummies.  Actually,
#         by the common scheme in user input by drudge, the terms should normally
#         have no summations at all.  So this function has different semantics
#         than the term multiplication function from the Terms class.
#         """
# 
#         prod_terms = []
#         for i, j in itertools.product(left_terms, right_terms):
#             # A shallow checking on sums, normally we have no sums by design.
#             sums = _cat_sums(i.sums, j.sums)
#             amp = i.amp * j.amp
#             vecs = i.vecs + j.vecs
# 
#             prod_terms.append(Term(sums, amp, vecs))
#             continue
# 
#         return Terms(prod_terms)
# 
#     def __add__(self, other):
#         """Add something on the right."""
# 
#         if is_higher(other, self._op_priority):
#             return NotImplemented
#         return self._add(self.terms, parse_terms(other))
# 
#     def __radd__(self, other):
#         """Add something on the left."""
# 
#         if is_higher(other, self._op_priority):
#             return NotImplemented
#         return self._add(parse_terms(other), self.terms)
# 
#     def __sub__(self, other):
#         """Subtract something on the right."""
# 
#         if is_higher(other, self._op_priority):
#             return NotImplemented
#         other_terms = self._neg_terms(parse_terms(other))
#         return self._add(self.terms, other_terms)
# 
#     def __rsub__(self, other):
#         """Be subtracted from something on the left."""
# 
#         if is_higher(other, self._op_priority):
#             return NotImplemented
#         self_terms = self._neg_terms(parse_terms(self))
#         return self._add(parse_terms(other), self_terms)
# 
#     def __neg__(self):
#         """Negate the terms."""
#         return Terms(self._neg_terms(parse_terms(self)))
# 
#     @staticmethod
#     def _add(left_terms, right_terms):
#         """Add the terms together.
#         """
#         return Terms(itertools.chain(left_terms, right_terms))
# 
#     @staticmethod
#     def _neg_terms(terms: typing.Iterable['Term']):
#         """Negate the given terms.
# 
#         The resulted terms are lazily evaluated.
#         """
#         return (
#             Term(i.sums, i.amp * _NEG_UNITY, i.vecs)
#             for i in terms
#         )
# 
#     def __truediv__(self, other):
#         """Make division with another object."""
#         other = sympify(other)
#         return Terms([
#             i.scale(1 / other) for i in self.terms
#         ])
# 
#     def __rtruediv__(self, other):
#         """Being divided over by other object."""
#         raise NotImplementedError('General tensors cannot inversed')
# 
#     def __or__(self, other):
#         """Compute the commutator with another object.
#         """
#         if is_higher(other, self._op_priority):
#             return NotImplemented
#         return self * other - other * self
# 
#     def __ror__(self, other):
#         """Compute the commutator with another object on the right."""
#         if is_higher(other, self._op_priority):
#             return NotImplemented
#         return other * self - self * other


# Path: drudge/report.py
import re
import shutil
import subprocess
import warnings
from jinja2 import Environment, PackageLoader
from sympy.printing.latex import LatexPrinter
from .term import ATerms

            tags of LaTeX.

        kwargs

            All the rest of the keyword arguments are forwarded to the
            :py:meth:`Drudge.format_latex` method.

        Note
        ----

        **For long equations in LaTeX environments,** normally ``env='align'``
        and ``sep_lines=True`` can be set to allow each term to occupy separate
        lines, automatic page break will be inserted, or ``env='dmath'`` and
        ``sep_lines=False`` can be used to use ``breqn`` package to
        automatically flow the terms.

        """

        if env == '[':
            opening, closing = r'\[', r'\]'
        else:
            opening, closing = [
                r'\{}{{{}}}'.format(i, env)
                for i in ['begin', 'end']
            ]

        if content is None:
            expr = None
        elif hasattr(content, 'latex'):
            expr = content.latex(**kwargs)

        elif isinstance(content, ATerms):