181 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			181 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| """UnitDbl module."""
 | |
| 
 | |
| import functools
 | |
| import operator
 | |
| 
 | |
| from matplotlib import _api
 | |
| 
 | |
| 
 | |
| class UnitDbl:
 | |
|     """Class UnitDbl in development."""
 | |
| 
 | |
|     # Unit conversion table.  Small subset of the full one but enough
 | |
|     # to test the required functions.  First field is a scale factor to
 | |
|     # convert the input units to the units of the second field.  Only
 | |
|     # units in this table are allowed.
 | |
|     allowed = {
 | |
|         "m": (0.001, "km"),
 | |
|         "km": (1, "km"),
 | |
|         "mile": (1.609344, "km"),
 | |
| 
 | |
|         "rad": (1, "rad"),
 | |
|         "deg": (1.745329251994330e-02, "rad"),
 | |
| 
 | |
|         "sec": (1, "sec"),
 | |
|         "min": (60.0, "sec"),
 | |
|         "hour": (3600, "sec"),
 | |
|         }
 | |
| 
 | |
|     _types = {
 | |
|         "km": "distance",
 | |
|         "rad": "angle",
 | |
|         "sec": "time",
 | |
|         }
 | |
| 
 | |
|     def __init__(self, value, units):
 | |
|         """
 | |
|         Create a new UnitDbl object.
 | |
| 
 | |
|         Units are internally converted to km, rad, and sec.  The only
 | |
|         valid inputs for units are [m, km, mile, rad, deg, sec, min, hour].
 | |
| 
 | |
|         The field UnitDbl.value will contain the converted value.  Use
 | |
|         the convert() method to get a specific type of units back.
 | |
| 
 | |
|         = ERROR CONDITIONS
 | |
|         - If the input units are not in the allowed list, an error is thrown.
 | |
| 
 | |
|         = INPUT VARIABLES
 | |
|         - value     The numeric value of the UnitDbl.
 | |
|         - units     The string name of the units the value is in.
 | |
|         """
 | |
|         data = _api.check_getitem(self.allowed, units=units)
 | |
|         self._value = float(value * data[0])
 | |
|         self._units = data[1]
 | |
| 
 | |
|     def convert(self, units):
 | |
|         """
 | |
|         Convert the UnitDbl to a specific set of units.
 | |
| 
 | |
|         = ERROR CONDITIONS
 | |
|         - If the input units are not in the allowed list, an error is thrown.
 | |
| 
 | |
|         = INPUT VARIABLES
 | |
|         - units     The string name of the units to convert to.
 | |
| 
 | |
|         = RETURN VALUE
 | |
|         - Returns the value of the UnitDbl in the requested units as a floating
 | |
|           point number.
 | |
|         """
 | |
|         if self._units == units:
 | |
|             return self._value
 | |
|         data = _api.check_getitem(self.allowed, units=units)
 | |
|         if self._units != data[1]:
 | |
|             raise ValueError(f"Error trying to convert to different units.\n"
 | |
|                              f"    Invalid conversion requested.\n"
 | |
|                              f"    UnitDbl: {self}\n"
 | |
|                              f"    Units:   {units}\n")
 | |
|         return self._value / data[0]
 | |
| 
 | |
|     def __abs__(self):
 | |
|         """Return the absolute value of this UnitDbl."""
 | |
|         return UnitDbl(abs(self._value), self._units)
 | |
| 
 | |
|     def __neg__(self):
 | |
|         """Return the negative value of this UnitDbl."""
 | |
|         return UnitDbl(-self._value, self._units)
 | |
| 
 | |
|     def __bool__(self):
 | |
|         """Return the truth value of a UnitDbl."""
 | |
|         return bool(self._value)
 | |
| 
 | |
|     def _cmp(self, op, rhs):
 | |
|         """Check that *self* and *rhs* share units; compare them using *op*."""
 | |
|         self.checkSameUnits(rhs, "compare")
 | |
|         return op(self._value, rhs._value)
 | |
| 
 | |
|     __eq__ = functools.partialmethod(_cmp, operator.eq)
 | |
|     __ne__ = functools.partialmethod(_cmp, operator.ne)
 | |
|     __lt__ = functools.partialmethod(_cmp, operator.lt)
 | |
|     __le__ = functools.partialmethod(_cmp, operator.le)
 | |
|     __gt__ = functools.partialmethod(_cmp, operator.gt)
 | |
|     __ge__ = functools.partialmethod(_cmp, operator.ge)
 | |
| 
 | |
|     def _binop_unit_unit(self, op, rhs):
 | |
|         """Check that *self* and *rhs* share units; combine them using *op*."""
 | |
|         self.checkSameUnits(rhs, op.__name__)
 | |
|         return UnitDbl(op(self._value, rhs._value), self._units)
 | |
| 
 | |
|     __add__ = functools.partialmethod(_binop_unit_unit, operator.add)
 | |
|     __sub__ = functools.partialmethod(_binop_unit_unit, operator.sub)
 | |
| 
 | |
|     def _binop_unit_scalar(self, op, scalar):
 | |
|         """Combine *self* and *scalar* using *op*."""
 | |
|         return UnitDbl(op(self._value, scalar), self._units)
 | |
| 
 | |
|     __mul__ = functools.partialmethod(_binop_unit_scalar, operator.mul)
 | |
|     __rmul__ = functools.partialmethod(_binop_unit_scalar, operator.mul)
 | |
| 
 | |
|     def __str__(self):
 | |
|         """Print the UnitDbl."""
 | |
|         return f"{self._value:g} *{self._units}"
 | |
| 
 | |
|     def __repr__(self):
 | |
|         """Print the UnitDbl."""
 | |
|         return f"UnitDbl({self._value:g}, '{self._units}')"
 | |
| 
 | |
|     def type(self):
 | |
|         """Return the type of UnitDbl data."""
 | |
|         return self._types[self._units]
 | |
| 
 | |
|     @staticmethod
 | |
|     def range(start, stop, step=None):
 | |
|         """
 | |
|         Generate a range of UnitDbl objects.
 | |
| 
 | |
|         Similar to the Python range() method.  Returns the range [
 | |
|         start, stop) at the requested step.  Each element will be a
 | |
|         UnitDbl object.
 | |
| 
 | |
|         = INPUT VARIABLES
 | |
|         - start     The starting value of the range.
 | |
|         - stop      The stop value of the range.
 | |
|         - step      Optional step to use.  If set to None, then a UnitDbl of
 | |
|                       value 1 w/ the units of the start is used.
 | |
| 
 | |
|         = RETURN VALUE
 | |
|         - Returns a list containing the requested UnitDbl values.
 | |
|         """
 | |
|         if step is None:
 | |
|             step = UnitDbl(1, start._units)
 | |
| 
 | |
|         elems = []
 | |
| 
 | |
|         i = 0
 | |
|         while True:
 | |
|             d = start + i * step
 | |
|             if d >= stop:
 | |
|                 break
 | |
| 
 | |
|             elems.append(d)
 | |
|             i += 1
 | |
| 
 | |
|         return elems
 | |
| 
 | |
|     def checkSameUnits(self, rhs, func):
 | |
|         """
 | |
|         Check to see if units are the same.
 | |
| 
 | |
|         = ERROR CONDITIONS
 | |
|         - If the units of the rhs UnitDbl are not the same as our units,
 | |
|           an error is thrown.
 | |
| 
 | |
|         = INPUT VARIABLES
 | |
|         - rhs     The UnitDbl to check for the same units
 | |
|         - func    The name of the function doing the check.
 | |
|         """
 | |
|         if self._units != rhs._units:
 | |
|             raise ValueError(f"Cannot {func} units of different types.\n"
 | |
|                              f"LHS: {self._units}\n"
 | |
|                              f"RHS: {rhs._units}")
 |