<commit_before>import numbers
import math
from typing import List, Set, Tuple

import numpy as np

import clifford as cf
from . import general_exp
from . import _settings


class MultiVector(object):
    """An element of the algebra

    Parameters
    -------------
    layout: instance of :class:`clifford.Layout`
        the layout of the algebra

    value : sequence of length ``layout.gaDims``
        the coefficients of the base blades

    Notes
    ------
    The following operators are overloaded:

    * ``A * B`` : geometric product
    * ``A ^ B`` : outer product
    * ``A | B`` : inner product
    * ``A << B`` : left contraction
    * ``~M`` : reversion
    * ``M(N)`` : grade or subspace projection
    * ``M[N]`` : blade projection
    """

    def __init__(self, layout, value=None, string=None, *, dtype: np.dtype = np.float64) -> None:
        """Constructor."""

        self.layout = layout
        self.__array_priority__ = 100

        if value is None:
            if string is None:
                self.value = np.zeros((self.layout.gaDims,), dtype=dtype)
            else:
                self.value = layout.parse_multivector(string).value
        else:
            self.value = np.array(value)
            if self.value.shape != (self.layout.gaDims,):
                raise ValueError(
                    "value must be a sequence of length %s" %
                    self.layout.gaDims)

    def __array__(self) -> 'cf.MVArray':
        # we are a scalar, and the only appropriate dtype is an object array
        return cf.MVArray([self])

    def _checkOther(self, other, coerce=True) -> Tuple['MultiVector', bool]:
        """Ensure that the other argument has the same Layout or coerce value if
        necessary/requested.

        _checkOther(other, coerce=True) --> newOther, isMultiVector
        """
        if isinstance(other, numbers.Number):
            if coerce:
                # numeric scalar
                newOther = self._newMV(dtype=np.result_type(other))
                newOther[()] = other
                return newOther, True
            else:
                return other, False

        elif isinstance(other, MultiVector):
            if other.layout != self.layout:
                raise ValueError(
                    "cannot operate on MultiVectors with different Layouts")
            else:
                return other, True
        else:
            return other, False

    def _newMV(self, newValue=None, *, dtype: np.dtype = None) -> 'MultiVector':
        """Returns a new MultiVector (or derived class instance).
        """
        if newValue is None and dtype is None:
            raise TypeError("Must specify either a type or value")

        return self.__class__(self.layout, newValue, dtype=dtype)

    # numeric special methods
    # binary

    def exp(self) -> 'MultiVector':
        return general_exp(self)

    def vee(self, other) -> 'MultiVector':
        r"""
        Vee product :math:`A \vee B`.
<commit_msg>Improve performance of arithmetic on MultiVector pairs (#283)<commit_after>import numbers
import math
from typing import List, Set, Tuple

import numpy as np

import clifford as cf
from . import general_exp
from . import _settings


class MultiVector(object):
    """An element of the algebra

    Parameters
    -------------
    layout: instance of :class:`clifford.Layout`
        the layout of the algebra

    value : sequence of length ``layout.gaDims``
        the coefficients of the base blades

    Notes
    ------
    The following operators are overloaded:

    * ``A * B`` : geometric product
    * ``A ^ B`` : outer product
    * ``A | B`` : inner product
    * ``A << B`` : left contraction
    * ``~M`` : reversion
    * ``M(N)`` : grade or subspace projection
    * ``M[N]`` : blade projection
    """

    def __init__(self, layout, value=None, string=None, *, dtype: np.dtype = np.float64) -> None:
        """Constructor."""

        self.layout = layout
        self.__array_priority__ = 100

        if value is None:
            if string is None:
                self.value = np.zeros((self.layout.gaDims,), dtype=dtype)
            else:
                self.value = layout.parse_multivector(string).value
        else:
            self.value = np.array(value)
            if self.value.shape != (self.layout.gaDims,):
                raise ValueError(
                    "value must be a sequence of length %s" %
                    self.layout.gaDims)

    def __array__(self) -> 'cf.MVArray':
        # we are a scalar, and the only appropriate dtype is an object array
        return cf.MVArray([self])

    def _checkOther(self, other, coerce=True) -> Tuple['MultiVector', bool]:
        """Ensure that the other argument has the same Layout or coerce value if
        necessary/requested.

        _checkOther(other, coerce=True) --> newOther, isMultiVector
        """
        if isinstance(other, MultiVector):
            if other.layout != self.layout:
                raise ValueError(
                    "cannot operate on MultiVectors with different Layouts")
            else:
                return other, True
        elif isinstance(other, numbers.Number):
            if coerce:
                # numeric scalar
                newOther = self._newMV(dtype=np.result_type(other))
                newOther[()] = other
                return newOther, True
            else:
                return other, False

        else:
            return other, False

    def _newMV(self, newValue=None, *, dtype: np.dtype = None) -> 'MultiVector':
        """Returns a new MultiVector (or derived class instance).
        """
        if newValue is None and dtype is None:
            raise TypeError("Must specify either a type or value")

        return self.__class__(self.layout, newValue, dtype=dtype)

    # numeric special methods
    # binary

    def exp(self) -> 'MultiVector':
        return general_exp(self)

    def vee(self, other) -> 'MultiVector':
        r"""
        Vee product :math:`A \vee B`.

{'fraction_non_alphanumeric': 0.10650594451783356, 'fraction_numerical': 0.0021466314398943197, 'mean_word_length': 1.9488802336903603, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 17, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1171090', 'n_tokens_mistral': 1690, 'n_tokens_neox': 1602, 'n_words': 541}