GHC/Ix.hs

    {-# LANGUAGE NoImplicitPrelude, MagicHash, UnboxedTuples #-}
    {-# OPTIONS_HADDOCK not-home #-}

    -----------------------------------------------------------------------------
    -- |
    -- Module      :  GHC.Ix
    -- Copyright   :  (c) The University of Glasgow, 1994-2000
    -- License     :  see libraries/base/LICENSE
    --
    -- Maintainer  :  cvs-ghc@haskell.org
    -- Stability   :  internal
    -- Portability :  non-portable (GHC extensions)
    --
    -- GHC\'s Ix typeclass implementation.
    --
    -----------------------------------------------------------------------------

    module GHC.Ix (
            Ix(..)
        ) where

    import GHC.Enum
    import GHC.Num
    import GHC.Base
    import GHC.Real( fromIntegral )
    import GHC.Show

    -- | The 'Ix' class is used to map a contiguous subrange of values in
    -- a type onto integers.  It is used primarily for array indexing
    -- (see the array package).
    --
    -- The first argument @(l,u)@ of each of these operations is a pair
    -- specifying the lower and upper bounds of a contiguous subrange of values.
    --
    -- An implementation is entitled to assume the following laws about these
    -- operations:
    --
    -- * @'inRange' (l,u) i == 'elem' i ('range' (l,u))@ @ @
    --
    -- * @'range' (l,u) '!!' 'index' (l,u) i == i@, when @'inRange' (l,u) i@
    --
    -- * @'map' ('index' (l,u)) ('range' (l,u))) == [0..'rangeSize' (l,u)-1]@ @ @
    --
    -- * @'rangeSize' (l,u) == 'length' ('range' (l,u))@ @ @
    --
    class (Ord a) => Ix a where
        {-# MINIMAL range, (index | unsafeIndex), inRange #-}

        -- | The list of values in the subrange defined by a bounding pair.
        range               :: (a,a) -> [a]
        -- | The position of a subscript in the subrange.
        index               :: (a,a) -> a -> Int
        -- | Like 'index', but without checking that the value is in range.
        unsafeIndex         :: (a,a) -> a -> Int
        -- | Returns 'True' the given subscript lies in the range defined
        -- the bounding pair.
        inRange             :: (a,a) -> a -> Bool
        -- | The size of the subrange defined by a bounding pair.
        rangeSize           :: (a,a) -> Int
        -- | like 'rangeSize', but without checking that the upper bound is
        -- in range.
        unsafeRangeSize     :: (a,a) -> Int

            -- Must specify one of index, unsafeIndex

            -- 'index' is typically over-ridden in instances, with essentially
            -- the same code, but using indexError instead of hopelessIndexError
            -- Reason: we have 'Show' at the instances
        {-# INLINE index #-}  -- See Note [Inlining index]
        index b i | inRange b i = unsafeIndex b i
                  | otherwise   = hopelessIndexError

        unsafeIndex b i = index b i

        rangeSize b@(_l,h) | inRange b h = unsafeIndex b h + 1
                           | otherwise   = 0        -- This case is only here to
                                                    -- check for an empty range
            -- NB: replacing (inRange b h) by (l <= h) fails for
            --     tuples.  E.g.  (1,2) <= (2,1) but the range is empty

        unsafeRangeSize b@(_l,h) = unsafeIndex b h + 1

    {-
    Note that the following is NOT right
            rangeSize (l,h) | l <= h    = index b h + 1
                            | otherwise = 0

    Because it might be the case that l<h, but the range
    is nevertheless empty.  Consider
            ((1,2),(2,1))
    Here l<h, but the second index ranges from 2..1 and
    hence is empty

    Note [Inlining index]
    ~~~~~~~~~~~~~~~~~~~~~
    We inline the 'index' operation,

     * Partly because it generates much faster code
       (although bigger); see #1216

     * Partly because it exposes the bounds checks to the simplifier which
       might help a big.

    If you make a per-instance index method, you may consider inlining it.

    Note [Double bounds-checking of index values]
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    When you index an array, a!x, there are two possible bounds checks we might make:

      (A) Check that (inRange (bounds a) x) holds.

          (A) is checked in the method for 'index'

      (B) Check that (index (bounds a) x) lies in the range 0..n,
          where n is the size of the underlying array

          (B) is checked in the top-level function (!), in safeIndex.

    Of course it *should* be the case that (A) holds iff (B) holds, but that
    is a property of the particular instances of index, bounds, and inRange,
    so GHC cannot guarantee it.

     * If you do (A) and not (B), then you might get a seg-fault,
       by indexing at some bizarre location.  #1610

     * If you do (B) but not (A), you may get no complaint when you index
       an array out of its semantic bounds.  #2120

    At various times we have had (A) and not (B), or (B) and not (A); both
    led to complaints.  So now we implement *both* checks (#2669).

    For 1-d, 2-d, and 3-d arrays of Int we have specialised instances to avoid this.

    Note [Out-of-bounds error messages]
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    The default method for 'index' generates hoplelessIndexError, because
    Ix doesn't have Show as a superclass.  For particular base types we
    can do better, so we override the default method for index.
    -}

    -- Abstract these errors from the relevant index functions so that
    -- the guts of the function will be small enough to inline.

    {-# NOINLINE indexError #-}
    indexError :: Show a => (a,a) -> a -> String -> b
    indexError rng i tp
      = errorWithoutStackTrace (showString "Ix{" . showString tp . showString "}.index: Index " .
               showParen True (showsPrec 0 i) .
               showString " out of range " $
               showParen True (showsPrec 0 rng) "")

    hopelessIndexError :: Int -- Try to use 'indexError' instead!
    hopelessIndexError = errorWithoutStackTrace "Error in array index"

    ----------------------------------------------------------------------
    -- | @since 2.01
    instance  Ix Char  where
        {-# INLINE range #-}
        range (m,n) = [m..n]

        {-# INLINE unsafeIndex #-}
        unsafeIndex (m,_n) i = fromEnum i - fromEnum m

        {-# INLINE index #-}  -- See Note [Out-of-bounds error messages]
                              -- and Note [Inlining index]
        index b i | inRange b i =  unsafeIndex b i
                  | otherwise   =  indexError b i "Char"

        inRange (m,n) i     =  m <= i && i <= n

    ----------------------------------------------------------------------
    -- | @since 2.01
    instance  Ix Int  where
        {-# INLINE range #-}
            -- The INLINE stops the build in the RHS from getting inlined,
            -- so that callers can fuse with the result of range
        range (m,n) = [m..n]

        {-# INLINE unsafeIndex #-}
        unsafeIndex (m,_n) i = i - m

        {-# INLINE index #-}  -- See Note [Out-of-bounds error messages]
                              -- and Note [Inlining index]
        index b i | inRange b i =  unsafeIndex b i
                  | otherwise   =  indexError b i "Int"

        {-# INLINE inRange #-}
        inRange (I# m,I# n) (I# i) =  isTrue# (m <=# i) && isTrue# (i <=# n)

    -- | @since 4.6.0.0
    instance Ix Word where
        range (m,n)         = [m..n]
        unsafeIndex (m,_) i = fromIntegral (i - m)
        inRange (m,n) i     = m <= i && i <= n

    ----------------------------------------------------------------------
    -- | @since 2.01
    instance  Ix Integer  where
        {-# INLINE range #-}
        range (m,n) = [m..n]

        {-# INLINE unsafeIndex #-}
        unsafeIndex (m,_n) i   = fromInteger (i - m)

        {-# INLINE index #-}  -- See Note [Out-of-bounds error messages]
                              -- and Note [Inlining index]
        index b i | inRange b i =  unsafeIndex b i
                  | otherwise   =  indexError b i "Integer"

        inRange (m,n) i     =  m <= i && i <= n

    ----------------------------------------------------------------------
    -- | @since 4.8.0.0
    instance Ix Natural where
        range (m,n) = [m..n]
        inRange (m,n) i = m <= i && i <= n
        unsafeIndex (m,_) i = fromIntegral (i-m)
        index b i | inRange b i = unsafeIndex b i
                  | otherwise   = indexError b i "Natural"

    ----------------------------------------------------------------------
    -- | @since 2.01
    instance Ix Bool where -- as derived
        {-# INLINE range #-}
        range (m,n) = [m..n]

        {-# INLINE unsafeIndex #-}
        unsafeIndex (l,_) i = fromEnum i - fromEnum l

        {-# INLINE index #-}  -- See Note [Out-of-bounds error messages]
                              -- and Note [Inlining index]
        index b i | inRange b i =  unsafeIndex b i
                  | otherwise   =  indexError b i "Bool"

        inRange (l,u) i = fromEnum i >= fromEnum l && fromEnum i <= fromEnum u

    ----------------------------------------------------------------------
    -- | @since 2.01
    instance Ix Ordering where -- as derived
        {-# INLINE range #-}
        range (m,n) = [m..n]

        {-# INLINE unsafeIndex #-}
        unsafeIndex (l,_) i = fromEnum i - fromEnum l

        {-# INLINE index #-}  -- See Note [Out-of-bounds error messages]
                              -- and Note [Inlining index]
        index b i | inRange b i =  unsafeIndex b i
                  | otherwise   =  indexError b i "Ordering"

        inRange (l,u) i = fromEnum i >= fromEnum l && fromEnum i <= fromEnum u

    ----------------------------------------------------------------------
    -- | @since 2.01
    instance Ix () where
        {-# INLINE range #-}
        range   ((), ())    = [()]
        {-# INLINE unsafeIndex #-}
        unsafeIndex   ((), ()) () = 0
        {-# INLINE inRange #-}
        inRange ((), ()) () = True

        {-# INLINE index #-}  -- See Note [Inlining index]
        index b i = unsafeIndex b i

    ----------------------------------------------------------------------
    -- | @since 2.01
    instance (Ix a, Ix b) => Ix (a, b) where -- as derived
        {-# SPECIALISE instance Ix (Int,Int) #-}

        {-# INLINE range #-}
        range ((l1,l2),(u1,u2)) =
          [ (i1,i2) | i1 <- range (l1,u1), i2 <- range (l2,u2) ]

        {-# INLINE unsafeIndex #-}
        unsafeIndex ((l1,l2),(u1,u2)) (i1,i2) =
          unsafeIndex (l1,u1) i1 * unsafeRangeSize (l2,u2) + unsafeIndex (l2,u2) i2

        {-# INLINE inRange #-}
        inRange ((l1,l2),(u1,u2)) (i1,i2) =
          inRange (l1,u1) i1 && inRange (l2,u2) i2

        -- Default method for index

    ----------------------------------------------------------------------
    -- | @since 2.01
    instance  (Ix a1, Ix a2, Ix a3) => Ix (a1,a2,a3)  where
        {-# SPECIALISE instance Ix (Int,Int,Int) #-}

        range ((l1,l2,l3),(u1,u2,u3)) =
            [(i1,i2,i3) | i1 <- range (l1,u1),
                          i2 <- range (l2,u2),
                          i3 <- range (l3,u3)]

        unsafeIndex ((l1,l2,l3),(u1,u2,u3)) (i1,i2,i3) =
          unsafeIndex (l3,u3) i3 + unsafeRangeSize (l3,u3) * (
          unsafeIndex (l2,u2) i2 + unsafeRangeSize (l2,u2) * (
          unsafeIndex (l1,u1) i1))

        inRange ((l1,l2,l3),(u1,u2,u3)) (i1,i2,i3) =
          inRange (l1,u1) i1 && inRange (l2,u2) i2 &&
          inRange (l3,u3) i3

        -- Default method for index

    ----------------------------------------------------------------------
    -- | @since 2.01
    instance  (Ix a1, Ix a2, Ix a3, Ix a4) => Ix (a1,a2,a3,a4)  where
        range ((l1,l2,l3,l4),(u1,u2,u3,u4)) =
          [(i1,i2,i3,i4) | i1 <- range (l1,u1),
                           i2 <- range (l2,u2),
                           i3 <- range (l3,u3),
                           i4 <- range (l4,u4)]

        unsafeIndex ((l1,l2,l3,l4),(u1,u2,u3,u4)) (i1,i2,i3,i4) =
          unsafeIndex (l4,u4) i4 + unsafeRangeSize (l4,u4) * (
          unsafeIndex (l3,u3) i3 + unsafeRangeSize (l3,u3) * (
          unsafeIndex (l2,u2) i2 + unsafeRangeSize (l2,u2) * (
          unsafeIndex (l1,u1) i1)))

        inRange ((l1,l2,l3,l4),(u1,u2,u3,u4)) (i1,i2,i3,i4) =
          inRange (l1,u1) i1 && inRange (l2,u2) i2 &&
          inRange (l3,u3) i3 && inRange (l4,u4) i4

        -- Default method for index
    -- | @since 2.01
    instance  (Ix a1, Ix a2, Ix a3, Ix a4, Ix a5) => Ix (a1,a2,a3,a4,a5)  where
        range ((l1,l2,l3,l4,l5),(u1,u2,u3,u4,u5)) =
          [(i1,i2,i3,i4,i5) | i1 <- range (l1,u1),
                              i2 <- range (l2,u2),
                              i3 <- range (l3,u3),
                              i4 <- range (l4,u4),
                              i5 <- range (l5,u5)]

        unsafeIndex ((l1,l2,l3,l4,l5),(u1,u2,u3,u4,u5)) (i1,i2,i3,i4,i5) =
          unsafeIndex (l5,u5) i5 + unsafeRangeSize (l5,u5) * (
          unsafeIndex (l4,u4) i4 + unsafeRangeSize (l4,u4) * (
          unsafeIndex (l3,u3) i3 + unsafeRangeSize (l3,u3) * (
          unsafeIndex (l2,u2) i2 + unsafeRangeSize (l2,u2) * (
          unsafeIndex (l1,u1) i1))))

        inRange ((l1,l2,l3,l4,l5),(u1,u2,u3,u4,u5)) (i1,i2,i3,i4,i5) =
          inRange (l1,u1) i1 && inRange (l2,u2) i2 &&
          inRange (l3,u3) i3 && inRange (l4,u4) i4 &&
          inRange (l5,u5) i5

        -- Default method for index