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

AltStyle によって変換されたページ (->オリジナル) /