# the implementation of this file compared to week1 added:
# - assert and _invariant
# - more examples are added for __eq__ and __lt___
# - more methods are implemented

from typing import Any


class Rational:
    """
    A rational number
    num - numerator
    denum - denominator denum must not be 0
    """
    num: int
    denum: int

    def __init__(self, num: int, denum: int) -> None:
        """
        Create new Rational self with numerator num and
        denominator denom --- denom must not be 0.
        """
        self.num = num
        self.denum = denum
        self._invariant()

    def _invariant(self) -> None:
        """
        check if denum is zero
        """
        assert self.denum != 0, "denum must not be 0"

    def __eq__(self, other: Any) -> bool:
        """
        Return whether Rational self is equivalent to other.
        >>> r1 = Rational(3, 5)
        >>> r2 = Rational(1, 3)
        >>> r1 == r2
        False
        >>> r3 = Rational(4, 7)
        >>> r1.__eq__(r3)
        False
        >>> r4 = Rational(1, 3)
        >>> r5 = Rational(100000000000000001, 300000000000000000)
        >>> r4 == r5
        False
        >>> l=[r1,r2,r3]
        >>> r4 in l
        True
        """
        self._invariant()
        other._invariant()
        return (type(self) == type(other) and (
                self.num * other.denum == self.denum * other.num))
        # return (type(self) == type(other) and
        #         self.num / self.denum == other.num / other.denum)
        #  This method does not work
        #  see the example r4==r5 above
        #  becuase int/int is float and float numbers are approximated

    def __repr__(self) -> str:
        """
        Return a string representation of Rational self.
        >>> r1=Rational(1,2)
        >>> r2=Rational(2,6)
        >>> r1*r2
        2 / 12
        """
        return self.__str__()

    def __str__(self) -> str:
        """
        Return a user-friendly string representation of
        Rational self.
        >>> print(Rational(3, 5))
        3 / 5
        """
        return '{} / {}'.format(self.num, self.denum)

    def __lt__(self, other: Any) -> bool:
        """
        Return whether Rational self is less than other.
        >>> Rational(3, 5).__lt__(Rational(4, 7))
        False
        >>> Rational(3, 5).__lt__(Rational(5, 7))
        True
        >>> Rational(1, 2).__lt__(Rational(3, 6))
        False
        >>> Rational(1, 2).__lt__(Rational(1, -4))
        False
        >>> Rational(1, -4).__lt__(Rational(1, 2))
        True
        """
        # wrong do not use it
        # return self.num * other.denum < other.num * self.denum
        # wrong do not use it
        # return (self.num / self.denum < other.num / other.denum)
        self._invariant()
        other._invariant()
        return (self.num * other.denum < other.num * self.denum
        if self.denum * other.denum > 0
        else self.num * other.denum > other.num * self.denum)

    def __mul__(self, other: 'Rational') -> 'Rational':
        """
        Return the product of Rational self and Rational other.
        >>> print(Rational(3, 5).__mul__(Rational(4, 7)))
        12 / 35
        >>> print(Rational(3, 5) * Rational(4, 7))
        12 / 35
        """
        self._invariant()
        other._invariant()
        return Rational(self.num * other.num, self.denum * other.denum)

    def __add__(self, other: 'Rational') -> 'Rational':
        """
        Return the sum of Rational self and Rational other.
        >>> print(Rational(3, 5).__add__(Rational(4, 7)))
        41 / 35
        """
        self._invariant()
        return Rational(self.num * other.denum + self.denum * other.num,
                        self.denum * other.denum)


if __name__ == "__main__":
    import doctest

    doctest.testmod()
    r1 = Rational(3, 7)
    r2 = Rational(4, 7)
    # print(r1 < r2)
    # print(r1.__add__(r2))
    print(r1 + r2)
    # print(r1.__mul__(r2))
    # print(r1 * r2)
    # print(r1 == r2)
    # print(r1 == "three-fifths")
