# -*- coding: utf-8 -*-
"""
    pint.quantity
    ~~~~~~~~~~~~~

    :copyright: 2013 by Pint Authors, see AUTHORS for more details.
    :license: BSD, see LICENSE for more details.
"""

from __future__ import division, unicode_literals, print_function, absolute_import

import copy
import math
import operator
import functools
import bisect

from .formatting import remove_custom_flags
from .errors import (DimensionalityError, OffsetUnitCalculusError,
                     UndefinedUnitError)
from .definitions import UnitDefinition
from .compat import string_types, ndarray, np, _to_magnitude, long_type
from .util import (logger, UnitsContainer, SharedRegistryObject,
                   to_units_container, infer_base_unit)


def _eq(first, second, check_all):
    """Comparison of scalars and arrays
    """
    out = first == second
    if check_all and isinstance(out, ndarray):
        return np.all(out)
    return out


class _Exception(Exception):            # pragma: no cover

    def __init__(self, internal):
        self.internal = internal


class _Quantity(SharedRegistryObject):
    """Implements a class to describe a physical quantity:
    the product of a numerical value and a unit of measurement.

    :param value: value of the physical quantity to be created.
    :type value: str, Quantity or any numeric type.
    :param units: units of the physical quantity to be created.
    :type units: UnitsContainer, str or Quantity.
    """

    #: Default formatting string.
    default_format = ''

    def __reduce__(self):
        from . import _build_quantity
        return _build_quantity, (self.magnitude, self._units)

    def __new__(cls, value, units=None):
        if units is None:
            if isinstance(value, string_types):
                if value == '':
                    raise ValueError('Expression to parse as Quantity cannot '
                                     'be an empty string.')
                inst = cls._REGISTRY.parse_expression(value)
                return cls.__new__(cls, inst)
            elif isinstance(value, cls):
                inst = copy.copy(value)
            else:
                inst = object.__new__(cls)
                inst._magnitude = _to_magnitude(value, inst.force_ndarray)
                inst._units = UnitsContainer()
        elif isinstance(units, (UnitsContainer, UnitDefinition)):
            inst = object.__new__(cls)
            inst._magnitude = _to_magnitude(value, inst.force_ndarray)
            inst._units = units
        elif isinstance(units, string_types):