# Multiparadigm Programming with Python 3
# Rational Representation Module -- Defer GCD
# Copyright (C) 2018, H. Conrad Cunningham
#
# 34567890123456789012345678901234567890123456789012345678901234567890
#
# 2018-10-19: Original Python version (referencing Haskell versions)
# 2018-11-01: Cleanup based on flake8, pylint, & black runs

from __future__ import annotations  # 3.7 deferred annotations
from typing import ClassVar, Optional, Tuple
import math

from rational_rep import RatRep


class RatDefer(RatRep):
    """
    Represent rational number as a tuple of integers,
    with (0,1) for zero
    """

    _zero: ClassVar[Optional[RatDefer]] = None
    r: Tuple[int, int]  # instance variable

    def __init__(self, x: int, y: int) -> None:
        """Initialize rational number x/y"""
        if y == 0:
            raise ZeroDivisionError
        elif x == 0:
            self.r = (0, 1)  # zeroRat instance
        else:
            self.r = (x, y)

    @classmethod
    def zeroRat(cls) -> RatRep:
        """Get canonical representation for zero"""
        if cls._zero is None:
            cls._zero = RatDefer(0, 1)
        return cls._zero

    def numer(self) -> int:
        """Get numerator of rational number"""
        x, y = self.r[0], self.r[1]
        if y < 0:
            x, y = -x, -y
        d = math.gcd(x, y)  # greatest common divisor
        return x // d       # integer division

    def denom(self) -> int:
        """Get denominator of rational number"""
        x, y = self.r[0], self.r[1]
        if y < 0:
            x, y = -x, -y
        d = math.gcd(x, y)  # greatest common divisor
        return y // d       # integer division

    def __repr__(self):
        """Return string representation of rational number"""
        return str(self.r)
