{-|
    Module      :  Data.Number.ER.ExtendedInteger
    Description :  integer with infinities 
    Copyright   :  (c) Michal Konecny
    License     :  BSD3

    Maintainer  :  mik@konecny.aow.cz
    Stability   :  experimental
    Portability :  portable
    
    An arbitrary sized integer type with additional +infinity and -infinity.
    
    To be imported qualified, usually with prefix EI. 
-}
module Data.Number.ER.ExtendedInteger 
(
    ExtendedInteger(..),
    isInfinite, binaryLog, take
)
where

import Prelude hiding (isInfinite, take)
import qualified Prelude

data ExtendedInteger
    = MinusInfinity | Finite Integer | PlusInfinity
    deriving (Eq)

isInfinite :: ExtendedInteger -> Bool
isInfinite MinusInfinity = True
isInfinite PlusInfinity = True
isInfinite _ = False

{-|
    the smallest integer i for which 2^i <=  abs n
-}
binaryLog :: ExtendedInteger -> ExtendedInteger
binaryLog PlusInfinity = PlusInfinity
binaryLog MinusInfinity = PlusInfinity
binaryLog (Finite n) 
    | n < 0 = binaryLog (Finite (- n))
    | n == 0 = MinusInfinity
    | otherwise = -- (n > 0)
        -- how to do this fast?
        intBinaryLog n

intBinaryLog n 
    | n > 1 = 1 + (intBinaryLog (n `div` 2))
    | n == 1 = 0

instance Show ExtendedInteger where
    show MinusInfinity = "-InfInt"
    show PlusInfinity = "+InfInt"
    show (Finite i) = show i

take :: ExtendedInteger -> [a] -> [a]
take MinusInfinity _ = error "takeEI called with MinusInfinity"
take PlusInfinity list = list
take (Finite n) list = Prelude.take (fromInteger n) list

instance Ord ExtendedInteger where
    compare MinusInfinity MinusInfinity = EQ
    compare MinusInfinity _ = LT
    compare _ MinusInfinity = GT
    compare PlusInfinity PlusInfinity = EQ
    compare PlusInfinity _ = GT
    compare _ PlusInfinity = LT
    compare (Finite i1) (Finite i2) =
        compare i1 i2

instance Num ExtendedInteger where
    fromInteger i = Finite i
    {- abs -}
    abs MinusInfinity = PlusInfinity
    abs PlusInfinity = PlusInfinity
    abs (Finite i) = Finite $ abs i
    {- signum -}
    signum ei
        | ei < 0 = -1
        | ei > 0 = 1
        | otherwise = 0
    {- negate -}
    negate (Finite i) = Finite (-i)
    negate MinusInfinity = PlusInfinity
    negate PlusInfinity = MinusInfinity
    {- addition -}
    PlusInfinity + MinusInfinity = 
        error "cannot add PlusInfinity and MinusInfinity"
    MinusInfinity + PlusInfinity = 
        error "cannot add PlusInfinity and MinusInfinity"
    PlusInfinity + ei = PlusInfinity
    ei + PlusInfinity = PlusInfinity
    MinusInfinity + ei = MinusInfinity
    ei + MinusInfinity = MinusInfinity
    (Finite i1) + (Finite i2) = Finite $ i1 + i2
    {- multiplication -}
    ei1 * ei2 | ei1 > ei2 = ei2 * ei1
    MinusInfinity * ei 
        | ei < 0 = PlusInfinity
        | ei > 0 = MinusInfinity
        | otherwise = error "cannot multiply MinusInfinity and 0"
    ei * PlusInfinity
        | ei < 0 = MinusInfinity
        | ei > 0 = PlusInfinity
        | otherwise = error "cannot multiply PlusInfinity and 0"
    (Finite i1) * (Finite i2) = Finite $ i1 * i2

instance Enum ExtendedInteger where
    toEnum i = Finite $ toInteger i
    fromEnum (Finite i) = fromInteger i
    fromEnum _ = error "infinite integers cannot be enumerated"

instance Real ExtendedInteger where
    toRational (Finite i) = toRational i
    toRational _ = error "infinite integers cannot be converted to rational"
    
instance Integral ExtendedInteger where
    quotRem (Finite i) (Finite m) = 
        (Finite a, Finite b)
        where
        (a,b) = quotRem i m
    quotRem _ _ = error "cannot make a quotient involving an infinite integer"
    toInteger (Finite i) = i
    toInteger _ = error "infinite integers cannot be converted to Integer"