"""
This is the missing docstring.
"""

from math import frexp

class RationalExponentError(Exception):
    """
    still missing
    """
    pass

def euclid(a, b):
    """
    still missing

    >>> euclid(9, 21)
    3
    """
    res = a % b
    if res == 0:
        return b
    else:
        return euclid(b, res)

def chrexpo(xArg):
    """
    Converts a possibly floating point value to an integer
    characteristic and a(n integer) power-of-two exponent and returns
    them as a tuple.
    """
    (chrst,expo) = frexp(xArg)
    while int(chrst) != chrst:
        chrst *= 65536
        expo -= 16
    return (int(chrst), expo)
            
class Rational(object):
    """
    Allows the performance of rational arithmetic, which is the most
    precise form of calculation possible on a digital computer.  It
    makes heavy use of Python's arbitrary precision 'long's.

    from math import pi
    print("    Rational(pi):", Rational(pi))
    print("Rational(2**0.5):", Rational(2**0.5))
    """
    def __init__(self, n, d=1):
        """
        >>> Rational(3, 2)
        Rational(3, 2)
        >>> Rational(5, 17)
        Rational(5, 17)
        """
        if d == 0:
            raise ZeroDivisionError

        # If n is, itself rational, do something, well, rational.
        # (This also takes care of a Rational d.)
        if isinstance(n, Rational):
            ratio = n / d
            n = ratio.n
            d = ratio.d

        negn = n < 0
        if negn:
            n = -n
        negd = d < 0
        if negd:
            d = -d
        if negn ^ negd:
            nsign = -1
        else:
            nsign = 1

        if int(n) == n:
            n = int(n) # force n to be an integer (it might have float type)
        else: # i.e. n is not an integer
            (chrst, expo) = chrexpo(n)
            if expo > 0:
                n = chrst * 2**(expo)
            else:
                n = chrst
                d *= 2**(-expo)

        if int(d) == d:
            d = int(d) # force n to be an integer (it might have float type)
        else: # i.e. d is not an integer
            (chrst, expo) = chrexpo(d)
            if expo > 0:
                d = chrst * 2**(expo)
            else:
                d = chrst
                n *= 2**(-expo)

        gcd = euclid(n, d)
        self.n = nsign * n // gcd
        self.d = d // gcd

    def __repr__(self):
        return 'Rational(' + repr(self.n) + ', ' + repr(self.d) + ')'

    def __str__(self):
        """
        >>> print(Rational(5, 17))
        (5/17)
        """
        return '(' + str(self.n) + '/' + str(self.d) + ')'

    def __lt__(self, other):
        """
        >>> r0 = Rational(3, 2)
        >>> r1 = Rational(5, 17)
        >>> r0 < r1
        False
        """
        if type(other) in (int, int):
            other = Rational(other, 1)
        return self.n * other.d < other.n * self.d

    def __eq__(self, other):
        if type(other) in (int, int):
            other = Rational(other, 1)
        return self.n * other.d == other.n * self.d

    def __neg__(self):
        return Rational(-self.n, self.d)

    def __add__(self, other):
        """
        >>> r0 = Rational(3, 2)
        >>> r1 = Rational(5, 17)
        >>> r0+r1
        Rational(61, 34)
        >>> r0+1
        Rational(5, 2)
        """
        if type(other) in (int, int):
            other = Rational(other, 1)
        return Rational(self.n * other.d + other.n * self.d, self.d * other.d)

    def __radd__(self, other):
        return self.__add__(other)

    def __sub__(self, other):
        """
        >>> r0 = Rational(3, 2)
        >>> r1 = Rational(5, 17)
        >>> r0-r1
        Rational(41, 34)
        """
        if type(other) in (int, int):
            other = Rational(other, 1)
        return Rational(self.n * other.d - other.n * self.d, self.d * other.d)

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

    def __mul__(self, other):
        """
        >>> r0 = Rational(3, 2)
        >>> r1 = Rational(5, 17)
        >>> r0*r1
        Rational(15, 34)
        >>> r1*3
        Rational(15, 17)
        """
        if type(other) in (int, int):
            other = Rational(other, 1)
        return Rational(self.n * other.n, self.d * other.d)

    def __rmul__(self, other):
        return Rational(other * self.n, self.d)

    def __truediv__(self, other):
        """
        >>> r0 = Rational(3, 2)
        >>> r1 = Rational(5, 17)
        >>> r0/r1
        Rational(51, 10)
        """
        if type(other) in (int, int):
            other = Rational(other, 1)
        return Rational(self.n * other.d, self.d * other.n)

    def __floordiv__(self, other):
        raise ValueError("Floor division not defined for Rationals.")

    def __rdiv__(self, other):
        return Rational(other * self.d, self.n)

    def __pow__(self, expo, base=None):
        """
        >>> r0 = Rational(3, 2)
        >>> r0**20
        Rational(3486784401, 1048576)
        """
        if base is not None:
            raise ValueError("Can't take the modulus of a Rational")
        if type(expo) in (int, int):
            return Rational(self.n ** expo, self.d ** expo)
        elif expo.d == 1:
            return Rational(self.n ** expo.n, self.d ** expo.n)
        else:
            raise RationalExponentError

def positiveRationals():
    sum_ = 2
    while True:
        for i in range(1, sum_):
            j = sum_ - i
            if euclid(i, j) == 1:
                yield Rational(i, j)
        sum_ += 1

if __name__ == '__main__':
    import doctest

    doctest.testmod()

