<gh_stars>1-10
from typing import Sequence, Any, SupportsInt
from fields import OperatorField
from utils import InfiniteSetError, IntegerDivisionError

import math
import utils


class Fraction:
    """ Integer fraction (n / d) in reduced normalized form: gcd(n, d) == 1, d > 0, n = 0 -> d = 1 """
    n: int
    d: int

    def __init__(self, n: int, d: int):
        if d < 0:
            n, d = -n, -d
        if d == 0:
            raise ZeroDivisionError
        if n == 0:
            d = 1
        g = math.gcd(n, d)
        self.n = n // g
        self.d = d // g

    def __add__(self, other) -> 'Fraction':
        a, b = self, fraction(other)
        n = a.n * b.d + b.n * a.d
        d = a.d * b.d
        return Fraction(n, d)

    def __mul__(self, other) -> 'Fraction':
        a, b = self, fraction(other)
        n = a.n * b.n
        d = a.d * b.d
        return Fraction(n, d)

    def __eq__(self, other) -> bool:
        return utils.same(self, other) and self.n == other.n and self.d == other.d

    def __neg__(self) -> 'Fraction': return Fraction(-self.n, self.d)
    def __abs__(self) -> 'Fraction': return Fraction(abs(self.n), self.d)
    def __invert__(self) -> 'Fraction': return self.reciprocal()
    def __pow__(self, power, modulo=None) -> 'Fraction': return Fraction(pow(self.n, power), pow(self.d, power))

    def __sub__(self, other) -> 'Fraction': return self.__add__(-fraction(other))
    def __truediv__(self, other) -> 'Fraction': return self.__floordiv__(other)
    def __floordiv__(self, other) -> 'Fraction': return self.__mul__(fraction(other).reciprocal())

    def __radd__(self, other) -> 'Fraction': return fraction(other).__add__(self)
    def __rmul__(self, other) -> 'Fraction': return fraction(other).__mul__(self)
    def __rsub__(self, other) -> 'Fraction': return fraction(other).__sub__(self)
    def __rtruediv__(self, other) -> 'Fraction': return fraction(other).__truediv__(self)
    def __rfloordiv__(self, other) -> 'Fraction': return fraction(other).__floordiv__(self)

    def __ne__(self, other) -> bool: return not self.__eq__(other)
    def __lt__(self, other) -> bool: return self.ratio() < fraction(other).ratio()
    def __gt__(self, other) -> bool: return self.ratio() > fraction(other).ratio()
    def __le__(self, other) -> bool: return self.ratio() <= fraction(other).ratio()
    def __ge__(self, other) -> bool: return self.ratio() >= fraction(other).ratio()

    def __int__(self) -> int:
        if self.d == 1:
            return self.n
        raise IntegerDivisionError

    def __str__(self) -> str: return '%d / %d' % (self.n, self.d)
    def __repr__(self) -> str: return self.__str__()

    def reciprocal(self) -> 'Fraction': return Fraction(self.d, self.n)
    def ratio(self) -> float: return self.n / self.d


class FractionsInstance(OperatorField[Fraction]):
    """ The field of fractions (rationals) """

    def of(self, *args: Any) -> Fraction: return fraction(*args)
    def elements(self) -> Sequence[Fraction]: raise InfiniteSetError

    def identity(self) -> Fraction: return fraction(0)
    def midentity(self) -> Fraction: return fraction(1)

    def radd(self, value: Fraction, n: int) -> Fraction: return Fraction(value.n * n, value.d)
    def rmul(self, value: Fraction, n: int) -> Fraction: return Fraction(value.n ** n, value.d ** n)

    def __str__(self) -> str: return '{Rationals}'


Fractions = FractionsInstance()


def fraction(*n: Any) -> Fraction:
    """ Converts n to a representable fraction """
    if len(n) == 1:
        n, *_ = n
    if isinstance(n, Fraction):
        return n
    if isinstance(n, SupportsInt):
        n = int(n)
    if isinstance(n, int):
        return Fraction(n, 1)
    if isinstance(n, Sequence) and len(n) == 2:
        a, b = n
        if isinstance(a, SupportsInt) and isinstance(b, SupportsInt):
            return Fraction(int(a), int(b))
    raise TypeError
