-- | Integers augmented with Infinity.
module Util.IntPlus(
   IntPlus,
   infinity
   ) where

-- --------------------------------------------------------------------
-- The datatype
-- --------------------------------------------------------------------

-- | The Bool is a sign, with True meaning positive infinity.
data IntPlus = Infinite Bool | Finite Integer deriving Eq

-- --------------------------------------------------------------------
-- The interface
-- --------------------------------------------------------------------

infinity :: IntPlus
infinity = Infinite True

instance Ord IntPlus where
   compare i1 i2 = case (i1,i2) of
      (Infinite b1,Infinite b2) -> compare b1 b2
      (Finite _,Infinite b) -> if b then LT else GT
      (Infinite b,Finite _) -> if b then GT else LT
      (Finite i1,Finite i2) -> compare i1 i2

instance Show IntPlus where
   showsPrec _ (Infinite b) s = if b then "infinity"++s else "-infinity"++s
   showsPrec p (Finite i) s = showsPrec p i s

instance Num IntPlus where
   (+) i1 i2 = case (i1,i2) of
      (Finite i1,Finite i2) -> Finite (i1 + i2)
      (Infinite b,Finite _) -> Infinite b
      (Finite _,Infinite b) -> Infinite b
      (Infinite b1,Infinite b2) ->
         if b1 == b2 then Infinite b1 else
            error "IntPlus: attempt to subtract infinities of like sign"
   (*) i1 i2 = case (i1,i2) of
      (Finite i1,Finite i2) -> Finite (i1*i2)
      (Finite i,Infinite b) -> mul i b
      (Infinite b,Finite i) -> mul i b
      (Infinite b1,Infinite b2) -> Infinite (b1 == b2)
      where
         mul i b = case compare i 0 of
            LT -> Infinite (not b)
            EQ -> Finite 0
            GT -> Infinite b

   negate (Finite i) = Finite (negate i)
   negate (Infinite b) = Infinite (not b)

   abs (Finite i) = Finite (abs i)
   abs (Infinite _) = infinity

   signum i = case compare i 0 of
      LT -> -1
      EQ -> 0
      GT -> 1

   fromInteger i = Finite i