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

from typing import Type

from rational_rep import RatRep


class Rat:
    """Rational arithmetic object"""

    ratrepcl: Type[RatRep]
    _zeroRat: RatRep

    def __init__(self, rep: Type[RatRep]) -> None:
        """Initialize Rat with class object for a RatRep subclass"""
        self.ratrepcl = rep
        self._zeroRat = rep.zeroRat()

    def negRat(self, x: RatRep) -> RatRep:
        """Negate rational number"""
        return self.makeRat(-x.numer(), x.denom())

    def addRat(self, x: RatRep, y: RatRep) -> RatRep:
        """Add rational numbers"""
        return self.makeRat(
            x.numer() * y.denom() + y.numer() * x.denom(),
            x.denom() * y.denom()
        )

    def subRat(self, x: RatRep, y: RatRep) -> RatRep:
        """Subtract one rational number from another"""
        return self.makeRat(
            x.numer() * y.denom() - y.numer() * x.denom(),
            x.denom() * y.denom()
        )

    def mulRat(self, x: RatRep, y: RatRep) -> RatRep:
        """Multiply rational numbers"""
        return self.makeRat(
            x.numer() * y.numer(), x.denom() * y.denom()
        )

    def divRat(self, x: RatRep, y: RatRep) -> RatRep:
        """Divide one rational number by another"""
        if y == self.zeroRat:
            raise ZeroDivisionError
        else:
            return self.makeRat(
                x.numer() * y.denom(), x.denom() * y.numer()
            )

    def eqRat(self, x: RatRep, y: RatRep) -> bool:
        """Are rational numbers equal?"""
        return x.numer() * y.denom() == y.numer() * x.denom()

    # Methods implemented by RatRep abstraction
    def makeRat(self, x: int, y: int) -> RatRep:
        """
        Create a new rational number x/y using the representation
        """
        return self.ratrepcl(x, y)

    @property
    def zeroRat(self) -> RatRep:
        """Canonical representation for rational number zero"""
        return self._zeroRat

    def numer(self, x: RatRep) -> int:
        """Get numerator of rational number"""
        return x.numer()

    def denom(self, x: RatRep) -> int:
        """Get denominator of rational number"""
        return x.denom()
