-- |Provides a type for natural numbers.
module Data.Natural (

    Natural

) where

    import Data.Monoid

    -- |The type of natural numbers.
    newtype Natural = Natural Integer deriving (Eq, Ord)

    instance Enum Natural where

        -- We define succ explicitely to avoid the unnecessary negativity check.
        succ (Natural integer) = Natural (succ integer)

        toEnum = fromInteger . toEnum

        fromEnum = fromEnum . toInteger

        {-
            The following methods have to be implemented explicitely, because the default
            implementation uses Int values as intermediate values, which would preclude the use of
            very large numbers. Furthermore, some methods can avoid the negativity check.
        -}
        enumFrom (Natural start) = map Natural (enumFrom start)

        enumFromThen (Natural start) (Natural next) = map fromInteger (enumFromThen start next)

        enumFromTo (Natural start) (Natural end) = map Natural (enumFromTo start end)

        enumFromThenTo (Natural start) (Natural next) (Natural end) = map fromInteger $
                                                                      enumFromThenTo start next end

    instance Show Natural where

        showsPrec prec (Natural integer) = showsPrec prec integer

    instance Read Natural where

        readsPrec prec str = map (first fromInteger) (readsPrec prec str) where

            -- This is Control.Arrow.first, specialized to (->).
            first :: (val -> val') -> (val,other) -> (val',other)
            first fun (val,other) = (fun val,other)

    instance Num Natural where

        Natural integer1 + Natural integer2 = Natural (integer1 + integer2)

        Natural integer1 * Natural integer2 = Natural (integer1 * integer2)

        negate = fromInteger . negate . toInteger

        abs = id

        signum (Natural integer) = Natural (signum integer)

        fromInteger integer | integer >= 0 = Natural integer
                            | otherwise    = error "Data.Natural: natural cannot be negative"

    instance Real Natural where

        toRational = toRational . toInteger

    instance Integral Natural where

        quotRem (Natural integer1) (Natural integer2) = let

                                                            (quot,rem) = quotRem integer1 integer2

                                                        in (Natural quot,Natural rem)

        toInteger (Natural integer) = integer

    instance Monoid Natural where

        mempty = 0

        mappend = (+)

        mconcat = sum