<commit_before>"""Classes for use in unit conversion."""
import pytac
import numpy
from scipy.interpolate import PchipInterpolator


class UnitsException(Exception):
    pass


def unit_function(value):
    """Default value for the pre and post functions used in unit conversion.

    Args:
        value (float): The value to be converted.

    Returns:
        value (float): The result of the conversion.
    """
    return value


class UnitConv(object):
    """Class to convert between physics and engineering units.

    This class does not do conversion but does return values if the target
    units are the same as the provided units. Subclasses should implement
    _raw_eng_to_phys() and _raw_phys_to_eng() in order to provide complete
    unit conversion.

    The two arguments to this function represent functions that are
    applied to the result of the initial conversion. One happens after
    the conversion, the other happens before the conversion back.
    """

    def __init__(self, post_eng_to_phys=unit_function, pre_phys_to_eng=unit_function):
        """
        Args:
            post_eng_to_phys (function): Function to be applied after the initial
                conversion.
            pre_phys_to_eng (function): Function to be applied before the initial
                conversion.
        """
        self._post_eng_to_phys = post_eng_to_phys
        self._pre_phys_to_eng = pre_phys_to_eng

    def _raw_eng_to_phys(self, value):
        """Function to be implemented by child classes.

        Args:
            value (float): Value to be converted from engineering to physics
                units.
        """
        raise NotImplementedError('No eng-to-phys conversion provided')

    def eng_to_phys(self, value):
        """Function that does the unit conversion.

        Conversion from engineering to physics units. An additional function may
        be casted on the initial conversion.

        Args:
            value (float): Value to be converted from engineering to physics units.

        Returns:
            result (float): The result value.
        """
        x = self._raw_eng_to_phys(value)
        result = self._post_eng_to_phys(x)
        return result

    def _raw_phys_to_eng(self, value):
        """Function to be implemented by child classes.

        Args:
            value (float): Value to be converted from physics to engineering units.
        """
        raise NotImplementedError('No phys-to-eng conversion provided')

    def phys_to_eng(self, value):
        """Function that does the unit conversion.

        Conversion from physics to engineering units. An additional function may
        be casted on the initial conversion.

        Args:
            value (float): Value to be converted from physics to engineering units.

        Returns:
            result (float): The result value.
        """
        x = self._pre_phys_to_eng(value)
        result = self._raw_phys_to_eng(x)
        return result

    def convert(self, value, origin, target):
        if origin == target:
            return value
        if origin == pytac.PHYS and target == pytac.ENG:
            return self.phys_to_eng(value)
        if origin == pytac.ENG and target == pytac.PHYS:
            return self.eng_to_phys(value)
        raise UnitsException(
            'Conversion {} to {} not understood'.format(origin, target)
        )


class PolyUnitConv(UnitConv):
    def __init__(self, coef, post_eng_to_phys=unit_function, pre_phys_to_eng=unit_function):
        """Linear interpolation for converting between physics and engineering units.

        Args:
            coef (array_like): The polynomial's coefficients, in decreasing powers.
        """
        super(self.__class__, self).__init__(post_eng_to_phys, pre_phys_to_eng)
        self.p = numpy.poly1d(coef)

    def _raw_eng_to_phys(self, eng_value):
        """Convert between engineering and physics units.

        Args:
            eng_value (float): The engineering value to be converted to the engineering unit.

        Returns:
            float: The physics value determined using the engineering value.
        """
        return self.p(eng_value)

    def _raw_phys_to_eng(self, physics_value):
        """Convert between physics and engineering units.

        Args:
            physics_value(float): The physics value to be converted to the
                engineering value.

        Returns:
            float: The converted engineering value from the given physics value.

        Raises:
            ValueError: An error occured when there exist no or more than one roots.
        """
        roots = (self.p - physics_value).roots
        if len(roots) == 1:
            x = roots[0]
            return x
        else:
            raise ValueError("There doesn't exist a corresponding engineering value or "
                             "they are not unique:", roots)


class PchipUnitConv(UnitConv):
    def __init__(self, x, y, post_eng_to_phys=unit_function, pre_phys_to_eng=unit_function):
        """ PChip interpolation for converting between physics and engineering units.

        Args:
            x(list): A list of points on the x axis. These must be in increasing order
                for the interpolation to work. Otherwise, a ValueError is raised.
            y(list): A list of points on the y axis. These must be in increasing or
                decreasing order. Otherwise, a ValueError is raised.

        Raises:
            ValueError: An error occured when the given y coefficients are neither in
            increasing or decreasing order.
        """
        super(self.__class__, self).__init__(post_eng_to_phys, pre_phys_to_eng)
        self.x = x
        self.y = y
        self.pp = PchipInterpolator(x, y)

        diff = numpy.diff(y)
        if not ((numpy.all(diff > 0)) or (numpy.all((diff < 0)))):
            raise ValueError("Given coefficients must be monotonically"
                             "decreasing.")

    def _raw_eng_to_phys(self, eng_value):
        """Convert between engineering and physics units.

        Args:
            eng_value(float): The engineering value to be converted to the engineering unit.
        Returns:
            float: The converted engineering value from the given engineering value.
        """
        return self.pp(eng_value)

    def _raw_phys_to_eng(self, physics_value):
        """Convert between physics and engineering units.

        Args:
            physics_value(float): The engineering value to be converted to the
                engineering value.

        Returns:
            float: The converted engineering value from the given physics value.

        Raises:
            ValueError: An error occured when there exist no or more than one roots.<commit_msg>Edit for description, grammar and syntax consistency.<commit_after>"""Classes for use in unit conversion."""
import pytac
import numpy
from scipy.interpolate import PchipInterpolator


class UnitsException(Exception):
    pass


def unit_function(value):
    """Default value for the pre and post functions used in unit conversion.

    Args:
        value (float): The value to be converted.

    Returns:
        value (float): The result of the conversion.
    """
    return value


class UnitConv(object):
    """Class to convert between physics and engineering units.

    This class does not do conversion but does return values if the target
    units are the same as the provided units. Subclasses should implement
    _raw_eng_to_phys() and _raw_phys_to_eng() in order to provide complete
    unit conversion.

    The two arguments to this function represent functions that are
    applied to the result of the initial conversion. One happens after
    the conversion, the other happens before the conversion back.

    .. Private Attributes:
           post_eng_to_phys (function): Function to be applied after the initial
                                         conversion.
           pre_phys_to_eng (function): Function to be applied before the initial
                                        conversion.
    """

    def __init__(self, post_eng_to_phys=unit_function, pre_phys_to_eng=unit_function):
        """.. The constructor method for the class, called whenever a 'UnitConv'
               object is constructed.

        Args:
            post_eng_to_phys (function): Function to be applied after the initial
                                          conversion.
            pre_phys_to_eng (function): Function to be applied before the initial
                                         conversion.
        """
        self._post_eng_to_phys = post_eng_to_phys
        self._pre_phys_to_eng = pre_phys_to_eng

    def _raw_eng_to_phys(self, value):
        """Function to be implemented by child classes.

        Args:
            value (float): Value to be converted from engineering to physics
                            units.
        """
        raise NotImplementedError('No eng-to-phys conversion provided')

    def eng_to_phys(self, value):
        """Function that does the unit conversion.

        Conversion from engineering to physics units. An additional function may
        be casted on the initial conversion.

        Args:
            value (float): Value to be converted from engineering to physics units.

        Returns:
            float: The result value.
        """
        x = self._raw_eng_to_phys(value)
        result = self._post_eng_to_phys(x)
        return result

    def _raw_phys_to_eng(self, value):
        """Function to be implemented by child classes.

        Args:
            value (float): Value to be converted from physics to engineering units.
        """
        raise NotImplementedError('No phys-to-eng conversion provided')

    def phys_to_eng(self, value):
        """Function that does the unit conversion.

        Conversion from physics to engineering units. An additional function may
        be casted on the initial conversion.

        Args:
            value (float): Value to be converted from physics to engineering units.

        Returns:
            float: The result value.
        """
        x = self._pre_phys_to_eng(value)
        result = self._raw_phys_to_eng(x)
        return result

    def convert(self, value, origin, target):
        """
        Args:
            value (float):
            origin (str): pytac.ENG or pytac.PHYS
            target (str): pytac.ENG or pytac.PHYS

        Returns:
            float: The result value.
        """
        if origin == target:
            return value
        if origin == pytac.PHYS and target == pytac.ENG:
            return self.phys_to_eng(value)
        if origin == pytac.ENG and target == pytac.PHYS:
            return self.eng_to_phys(value)
        raise UnitsException(
            'Conversion {} to {} not understood'.format(origin, target)
        )


class PolyUnitConv(UnitConv):
    """Linear interpolation for converting between physics and engineering units.

    **Attributes:**

    Attributes:
        p (poly1d): A one-dimensional polynomial of coefficients.
    """
    def __init__(self, coef, post_eng_to_phys=unit_function, pre_phys_to_eng=unit_function):
        """.. The constructor method for the class, called whenever a 'PolyUnitConv'
               object is constructed.

        Args:
            coef (array_like): The polynomial's coefficients, in decreasing powers.
            post_eng_to_phys (float): The value after conversion between ENG and PHYS.
            pre_eng_to_phys (float): The value before conversion.
        """
        super(self.__class__, self).__init__(post_eng_to_phys, pre_phys_to_eng)
        self.p = numpy.poly1d(coef)

    def _raw_eng_to_phys(self, eng_value):
        """Convert between engineering and physics units.

        Args:
            eng_value (float): The engineering value to be converted to the engineering unit.

        Returns:
            float: The physics value determined using the engineering value.
        """
        return self.p(eng_value)

    def _raw_phys_to_eng(self, physics_value):
        """Convert between physics and engineering units.

        Args:
            physics_value (float): The physics value to be converted to the
                engineering value.

        Returns:
            float: The converted engineering value from the given physics value.

        Raises:
            ValueError: An error occured when there exist no or more than one roots.
        """
        roots = (self.p - physics_value).roots
        if len(roots) == 1:
            x = roots[0]
            return x
        else:
            raise ValueError("There doesn't exist a corresponding engineering value or "
                             "they are not unique:", roots)


class PchipUnitConv(UnitConv):
    def __init__(self, x, y, post_eng_to_phys=unit_function, pre_phys_to_eng=unit_function):
        """ PChip interpolation for converting between physics and engineering units.

        Args:
            x (list): A list of points on the x axis. These must be in increasing order
                for the interpolation to work. Otherwise, a ValueError is raised.
            y (list): A list of points on the y axis. These must be in increasing or
                decreasing order. Otherwise, a ValueError is raised.

        Raises:
            ValueError: An error occured when the given y coefficients are neither in
             increasing or decreasing order.
        """
        super(self.__class__, self).__init__(post_eng_to_phys, pre_phys_to_eng)
        self.x = x
        self.y = y
        self.pp = PchipInterpolator(x, y)

        diff = numpy.diff(y)
        if not ((numpy.all(diff > 0)) or (numpy.all((diff < 0)))):
            raise ValueError("Given coefficients must be monotonically"
                             "decreasing.")

    def _raw_eng_to_phys(self, eng_value):
        """Convert between engineering and physics units.

        Args:
            eng_value (float): The engineering value to be converted to the engineering unit.
        Returns:
            float: The converted engineering value from the given engineering value.
        """
        return self.pp(eng_value)

    def _raw_phys_to_eng(self, physics_value):
        """Convert between physics and engineering units.

        Args:
            physics_value (float): The engineering value to be converted to the
                engineering value.

        Returns:
            float: The converted engineering value from the given physics value.

        Raises:
            ValueError: An error occured when there exist no or more than one roots.
{'fraction_non_alphanumeric': 0.06658052230685528, 'fraction_numerical': 0.0007480957562568008, 'mean_word_length': 2.1842789086184498, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 7, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1493732', 'n_tokens_mistral': 3744, 'n_tokens_neox': 3615, 'n_words': 1419}