# Source code for sympy.physics.unitsystems.quantities

# -*- coding: utf-8 -*-

"""
Physical quantities.
"""

from __future__ import division
import numbers

from sympy import sympify, Expr, Number, Mul, Pow
from .units import Unit

#TODO: in operations, interpret a Unit as a quantity with factor 1

[docs]class Quantity(Expr):
"""
Physical quantity.

A quantity is defined from a factor and a unit.
"""

is_commutative = True

def __new__(cls, factor=1, unit=None, **assumptions):

if not isinstance(factor, str):
factor = sympify(factor)

# if the given unit is a number (because of some operations) and
# the factor is represented as a number, then return a number
if ((unit is None or isinstance(unit, (Number, numbers.Real)))
and isinstance(factor, (Number, numbers.Real))):
return factor * (unit or 1)

#TODO: if factor is of the form "1 m", parse the factor and the unit
if isinstance(factor, (Number, numbers.Real)):
if not isinstance(unit, Unit):
raise TypeError("'unit' should be a Unit instance; %s found"
% type(unit))
else:
raise NotImplementedError

obj = Expr.__new__(cls, factor, unit, **assumptions)
obj.factor, obj.unit = factor, unit

return obj

def __str__(self):
return "%g %s" % (self.factor, self.unit)

def __repr__(self):
return "%g %s" % (self.factor, repr(self.unit))

def __neg__(self):
return Quantity(-self.factor, self.unit)

"""

If the other object is not a quantity, raise an error.
Two quantities can be added only if they have the same unit: so we
convert first the other quantity to the same unit and, if it succedded,
"""

if isinstance(other, Quantity):
return Quantity(self.factor + other.convert_to(self.unit).factor,
self.unit)
else:
raise TypeError("Only quantities can be added")

def sub(self, other):

if isinstance(other, Quantity):
return Quantity(self.factor - other.convert_to(self.unit).factor,
self.unit)
else:
raise TypeError("Only quantities can be subtracted")

def mul(self, other):

other = sympify(other)

if isinstance(other, Quantity):
return Quantity(self.factor * other.factor,
self.unit.mul(other.unit))
elif isinstance(other, (Number, numbers.Real)):
return Quantity(self.factor * other, self.unit)
else:
return Mul(self, other)

def div(self, other):

other = sympify(other)
if isinstance(other, Quantity):
return Quantity(self.factor / other.factor,
self.unit.div(other.unit))
elif isinstance(other, (Number, numbers.Real)):
return Quantity(self.factor / other, self.unit)
else:
return Mul(self, Pow(other, -1))

def rdiv(self, other):

other = sympify(other)
if isinstance(other, Quantity):
return Quantity(other.factor / self.factor,
other.unit.div(self.unit))
elif isinstance(other, (Number, numbers.Real)):
return Quantity(other / self.factor, self.unit.pow(-1))
else:
return Mul(self**-1, other)

def pow(self, other):

other = sympify(other)
if isinstance(other, (Number, numbers.Real)):
f = self.factor**other
# without evalf a Pow instance is returned, and it can not be
# handled by Quantity.__new__
return Quantity(f.evalf(), self.unit.pow(other))
else:
return Pow(self, other)

@property
[docs]    def as_unit(self):
"""
Convert the quantity to a unit.
"""

from .units import Unit
return Unit(self.unit, factor=self.factor)

[docs]    def convert_to(self, unit):
"""
Convert the quantity to another (compatible) unit.
"""

if self.unit.is_compatible(unit) is False:
raise ValueError("Only compatible units can be converted; "
"'%s' found" % unit.dim)

return Quantity(self.factor * self.unit.factor / unit.factor, unit)