"""Numeros racionales"""

def mcd(a, b):
    if a%b == 0:
        return b
    else:
        return mcd(b, a%b)

def get_attr(x, attr):
    if type(x) in (int, long):
        if attr == 'num':
            return x
        else:
            return 1
    if type(x) is Racional:
        return getattr(x, attr)
    raise ValueError, 'el argumento no es ni entero ni racional'
    
def num(x):
    return get_attr(x, 'num')

def den(x):
    return get_attr(x, 'den')
    
def normalize(n, d):
    return num(n)*den(d), den(n)*num(d)
        

class Racional(object):

    def __init__(self, num, den=1):
        if not den:
            raise ValueError, 'no existe racional con denominador 0'

        valid = (int, long, Racional)
        if not (type(num) in valid and type(den) in valid):
            raise ValueError, 'numerador y denominador deben ser enteros o racionales'

        num, den = normalize(num, den)
        d = abs(mcd(num, den))
        sg = abs(den)/den
        self.num = sg*num/d
        self.den = abs(den)/d

    def __repr__(self):
        if self.den == 1:
            return repr(self.num)
        
        return '%d/%d' %(self.num, self.den)

    __str__ = __repr__
    
    def __add__(self, other):
        if type(other) not in (int, long, Racional):
            return NotImplemented
        other = Racional(other)
        return Racional(self.num*other.den+other.num*self.den, self.den*other.den)

    __radd__ = __add__

    def __mul__(self, other):
        if type(other) not in (int, long, Racional):
            return NotImplemented
        other = Racional(other)
        return Racional(self.num*other.num, self.den*other.den)

    __rmul__ = __mul__

    def __sub__(self, other):
        return self + (-other)

    def __rsub__(self, other):
        return other + (-self)

    def __pow__(self, other):
        if type(other) is int:
            if other >= 0:
                return Racional(self.num**other, self.den**other)
            else:
                return Racional(self.den**(-other), self.num**(-other))
        else:
            return float(self)**float(other)

    def __div__(self, other):
        if type(other) not in (int, long, Racional):
            return NotImplemented
        other = Racional(other)
        return self * (other**(-1))

    def __rdiv__(self, other):
        return (self**(-1)) * other
    
    def __neg__(self):
        return (-1)*self

    def __pos__(self):
        return self

    def __abs__(self):
        return Racional(abs(self.num), self.den)

    def __hash__(self):
        return self.num ^ self.den
    
    def __float__(self):
        return float(self.num)/self.den

    def __int__(self):
        return int(float(self))

    def __eq__(self, other):
        if type(other) not in (int, long, Racional):
            return NotImplemented
        other = Racional(other)
        return self.num == other.num and self.den == other.den

    def __ne__(self, other):
        return not self == other

    def __le__(self, other):
        if type(other) not in (int, long, Racional):
            return NotImplemented
        other = Racional(other)
        return self.num*other.den <= self.den*other.num

    def __lt__(self, other):
        if type(other) not in (int, long, Racional):
            return NotImplemented
        other = Racional(other)
        return self.num*other.den < self.den*other.num

    def __ge__(self, other):
        return not self < other

    def __gt__(self, other):
        return not self <= other
        
    def __nonzero__(self):
        return self != 0
