{-| Module : Math.ExpPairs.RatioInf Copyright : (c) Andrew Lelechenko, 2014-2020 License : GPL-3 Maintainer : andrew.lelechenko@gmail.com Rational numbers extended with infinities. -} {-# LANGUAGE Safe #-} module Math.ExpPairs.RatioInf ( RatioInf (..) , RationalInf ) where import Data.Ratio (Ratio, numerator, denominator) import Data.Text.Prettyprint.Doc -- | Extend 'Ratio' @t@ with \( \pm \infty \) positive and negative -- infinities. data RatioInf t = InfMinus -- ^ \( - \infty \) | Finite !(Ratio t) -- ^ Finite value | InfPlus -- ^ \( + \infty \) deriving (Eq, Ord, Show) -- |Arbitrary-precision rational numbers with positive and negative -- infinities. type RationalInf = RatioInf Integer instance (Integral t, Pretty t) => Pretty (RatioInf t) where pretty InfMinus = pretty "-Inf" pretty (Finite x) | denominator x == 1 = pretty (numerator x) | otherwise = pretty (numerator x) <+> pretty "/" <+> pretty (denominator x) pretty InfPlus = pretty "+Inf" instance Integral t => Num (RatioInf t) where InfMinus + InfPlus = error "Cannot add up negative and positive infinities" InfPlus + InfMinus = error "Cannot add up negative and positive infinities" InfMinus + _ = InfMinus InfPlus + _ = InfPlus _ + InfMinus = InfMinus _ + InfPlus = InfPlus (Finite a) + (Finite b) = Finite (a+b) {-# SPECIALIZE (+) :: RationalInf -> RationalInf -> RationalInf #-} fromInteger = Finite . fromInteger {-# SPECIALIZE fromInteger :: Integer -> RationalInf #-} signum InfMinus = Finite (-1) signum InfPlus = Finite 1 signum (Finite r) = Finite (signum r) {-# SPECIALIZE signum :: RationalInf -> RationalInf #-} abs InfMinus = InfPlus abs InfPlus = InfPlus abs (Finite r) = Finite (abs r) {-# SPECIALIZE abs :: RationalInf -> RationalInf #-} negate InfMinus = InfPlus negate InfPlus = InfMinus negate (Finite r) = Finite (negate r) {-# SPECIALIZE negate :: RationalInf -> RationalInf #-} InfMinus * InfMinus = InfMinus InfMinus * InfPlus = InfMinus InfMinus * Finite a = case signum a of 1 -> InfMinus -1 -> InfPlus _ -> error "Cannot multiply infinity by zero" InfPlus * InfMinus = InfMinus InfPlus * InfPlus = InfPlus InfPlus * Finite a = case signum a of 1 -> InfPlus -1 -> InfMinus _ -> error "Cannot multiply infinity by zero" Finite a * InfMinus = case signum a of 1 -> InfMinus -1 -> InfPlus _ -> error "Cannot multiply infinity by zero" Finite a * InfPlus = case signum a of 1 -> InfPlus -1 -> InfMinus _ -> error "Cannot multiply infinity by zero" Finite a * Finite b = Finite (a * b) {-# SPECIALIZE (*) :: RationalInf -> RationalInf -> RationalInf #-} instance Integral t => Fractional (RatioInf t) where fromRational = Finite . fromRational {-# SPECIALIZE fromRational :: Rational -> RationalInf #-} InfMinus / InfMinus = error "Cannot divide infinity by infinity" InfMinus / InfPlus = error "Cannot divide infinity by infinity" InfMinus / Finite a = case signum a of 1 -> InfMinus -1 -> InfPlus _ -> error "Cannot divide infinity by zero" InfPlus / InfMinus = error "Cannot divide infinity by infinity" InfPlus / InfPlus = error "Cannot divide infinity by infinity" InfPlus / Finite a = case signum a of 1 -> InfPlus -1 -> InfMinus _ -> error "Cannot divide infinity by zero" Finite _ / InfPlus = Finite 0 Finite _ / InfMinus = Finite 0 Finite _ / Finite 0 = error "Cannot divide finite value by zero" Finite a / Finite b = Finite (a/b) {-# SPECIALIZE (/) :: RationalInf -> RationalInf -> RationalInf #-} instance Integral t => Real (RatioInf t) where toRational (Finite r) = toRational r toRational InfPlus = error "Cannot convert positive infinity into Rational" toRational InfMinus = error "Cannot convert negative infinity into Rational" {-# SPECIALIZE toRational :: RationalInf -> Rational #-}