    {-# LANGUAGE CPP #-}
    #ifdef __GLASGOW_HASKELL__
    {-# LANGUAGE Safe #-}
    #endif
    -----------------------------------------
    -- Andy Gill and Colin Runciman, June 2006
    ------------------------------------------

    -- | Minor utilities for the HPC tools.

    module Trace.Hpc.Util
           ( HpcPos
           , fromHpcPos
           , toHpcPos
           , insideHpcPos
           , HpcHash(..)
           , Hash
           , catchIO
           ) where

    import qualified Control.Exception as Exception
    import Data.List(foldl')
    import Data.Char (ord)
    import Data.Bits (xor)
    import Data.Word

    -- | 'HpcPos' is an Hpc local rendition of a Span.
    data HpcPos = P !Int !Int !Int !Int deriving (Eq, Ord)

    -- | 'fromHpcPos' explodes the HpcPos into /line:column/-/line:column/
    fromHpcPos :: HpcPos -> (Int,Int,Int,Int)
    fromHpcPos (P l1 c1 l2 c2) = (l1,c1,l2,c2)

    -- | 'toHpcPos' implodes to HpcPos, from /line:column/-/line:column/
    toHpcPos :: (Int,Int,Int,Int) -> HpcPos
    toHpcPos (l1,c1,l2,c2) = P l1 c1 l2 c2

    -- | Predicate determining whether the first argument is inside the second argument.
    insideHpcPos :: HpcPos -> HpcPos -> Bool
    insideHpcPos small big =
                 sl1 >= bl1 &&
                 (sl1 /= bl1 || sc1 >= bc1) &&
                 sl2 <= bl2 &&
                 (sl2 /= bl2 || sc2 <= bc2)
      where (sl1,sc1,sl2,sc2) = fromHpcPos small
            (bl1,bc1,bl2,bc2) = fromHpcPos big

    instance Show HpcPos where
       show (P l1 c1 l2 c2) = show l1 ++ ':' : show c1 ++ '-' : show l2 ++ ':' : show c2

    instance Read HpcPos where
      readsPrec _i pos = [(toHpcPos (read l1,read c1,read l2,read c2),after)]
          where
             (before,after)   = span (/= ',') pos
             (lhs0,rhs0)    = case span (/= '-') before of
                                   (lhs,'-':rhs) -> (lhs,rhs)
                                   (lhs,"")      -> (lhs,lhs)
                                   _ -> error "bad parse"
             (l1,':':c1)      = span (/= ':') lhs0
             (l2,':':c2)      = span (/= ':') rhs0

    ------------------------------------------------------------------------------

    -- Very simple Hash number generators

    class HpcHash a where
      toHash :: a -> Hash

    newtype Hash = Hash Word32 deriving (Eq)

    instance Read Hash where
      readsPrec p n = [ (Hash v,rest)
                      | (v,rest) <- readsPrec p n
                      ]

    instance Show Hash where
      showsPrec p (Hash n) = showsPrec p n

    instance Num Hash where
      (Hash a) + (Hash b) = Hash $ a + b
      (Hash a) * (Hash b) = Hash $ a * b
      (Hash a) - (Hash b) = Hash $ a - b
      negate (Hash a)     = Hash $ negate a
      abs (Hash a)        = Hash $ abs a
      signum (Hash a)     = Hash $ signum a
      fromInteger n       = Hash $ fromInteger n

    instance HpcHash Int where
      toHash n = Hash $ fromIntegral n

    instance HpcHash Integer where
      toHash n = fromInteger n

    instance HpcHash Char where
      toHash c = Hash $ fromIntegral $ ord c

    instance HpcHash Bool where
      toHash True  = 1
      toHash False = 0

    instance HpcHash a => HpcHash [a] where
      toHash xs = foldl' (\ h c -> toHash c `hxor` (h * 33)) 5381 xs

    instance (HpcHash a,HpcHash b) => HpcHash (a,b) where
      toHash (a,b) = (toHash a * 33) `hxor` toHash b

    instance HpcHash HpcPos where
      toHash (P a b c d) = Hash $ fromIntegral $ a * 0x1000000 + b * 0x10000 + c * 0x100 + d

    hxor :: Hash -> Hash -> Hash
    hxor (Hash x) (Hash y) = Hash $ x `xor` y

    catchIO :: IO a -> (Exception.IOException -> IO a) -> IO a
    catchIO = Exception.catch