    {-# LANGUAGE BangPatterns #-}

    -- | Functions specialised for arrays of dimension 2.
    module Data.Array.Repa.Specialised.Dim2
    	( isInside2
    	, isOutside2
    	, clampToBorder2
    	, makeBordered2)
    where
    import Data.Array.Repa.Index
    import Data.Array.Repa.Base
    import Data.Array.Repa.Repr.Partitioned
    import Data.Array.Repa.Repr.Undefined

    -- | Check if an index lies inside the given extent.
    --   As opposed to `inRange` from "Data.Array.Repa.Index",
    --   this is a short-circuited test that checks that lowest dimension first.
    isInside2
    	:: DIM2 	-- ^ Extent of array.
    	-> DIM2 	-- ^ Index to check.
    	-> Bool

    {-# INLINE isInside2 #-}
    isInside2 ex 	= not . isOutside2 ex

    -- | Check if an index lies outside the given extent.
    --   As opposed to `inRange` from "Data.Array.Repa.Index",
    --   this is a short-circuited test that checks the lowest dimension first.
    isOutside2
    	:: DIM2		-- ^ Extent of array.
    	-> DIM2		-- ^ Index to check.
    	-> Bool

    {-# INLINE isOutside2 #-}
    isOutside2 (_ :. yLen :. xLen) (_ :. yy :. xx)
    	| xx < 0	= True
    	| xx >= xLen	= True
    	| yy < 0	= True
    	| yy >= yLen	= True
    	| otherwise	= False

    -- | Given the extent of an array, clamp the components of an index so they
    --   lie within the given array. Outlying indices are clamped to the index
    --   of the nearest border element.
    clampToBorder2
    	:: DIM2 	-- ^ Extent of array.
    	-> DIM2		-- ^ Index to clamp.
    	-> DIM2

    {-# INLINE clampToBorder2 #-}
    clampToBorder2 (_ :. yLen :. xLen) (sh :. j :. i)
     = clampX j i
     where 	{-# INLINE clampX #-}
    	clampX !y !x
    	  | x < 0	= clampY y 0
    	  | x >= xLen	= clampY y (xLen - 1)
    	  | otherwise	= clampY y x

    	{-# INLINE clampY #-}
    	clampY !y !x
    	  | y < 0	= sh :. 0	   :. x
    	  | y >= yLen	= sh :. (yLen - 1) :. x
    	  | otherwise	= sh :. y	   :. x

    -- | Make a 2D partitioned array from two others, one to produce the elements
    --   in the internal region, and one to produce elements in the border region.
    --   The two arrays must have the same extent.
    --   The border must be the same width on all sides.
    --
    makeBordered2
    	:: (Source r1 a, Source r2 a)
            => DIM2			-- ^ Extent of array.
    	-> Int			-- ^ Width of border.
    	-> Array r1 DIM2 a	-- ^ Array for internal elements.
    	-> Array r2 DIM2 a	-- ^ Array for border elements.
    	-> Array (P r1 (P r2 (P r2 (P r2 (P r2 X))))) DIM2 a

    {-# INLINE makeBordered2 #-}
    makeBordered2 sh@(_ :. aHeight :. aWidth) bWidth arrInternal arrBorder
     = checkDims `seq`
       let
    	-- minimum and maximum indicies of values in the inner part of the image.
    	!inX		= bWidth
    	!inY		= bWidth
            !inW            = aWidth  - 2 * bWidth
            !inH            = aHeight - 2 * bWidth

    	inInternal (Z :. y :. x)
    		=  x >= inX && x < (inX + inW)
    		&& y >= inY && y < (inY + inH)
            {-# INLINE inInternal #-}

    	inBorder 	= not . inInternal
            {-# INLINE inBorder #-}

       in
        --  internal region
            APart sh (Range (Z :. inY     :. inX)       (Z :. inH :. inW )    inInternal) arrInternal

        --  border regions
        $   APart sh (Range (Z :. 0         :. 0)         (Z :. bWidth :. aWidth) inBorder) arrBorder
        $   APart sh (Range (Z :. inY + inH :. 0)         (Z :. bWidth :. aWidth) inBorder) arrBorder
        $   APart sh (Range (Z :. inY       :. 0)         (Z :. inH    :. bWidth) inBorder) arrBorder
        $   APart sh (Range (Z :. inY       :. inX + inW) (Z :. inH    :. bWidth) inBorder) arrBorder
        $   AUndefined sh

     where
            checkDims
             = if (extent arrInternal) == (extent arrBorder)
                    then ()
                    else error "makeBordered2: internal and border arrays have different extents"
            {-# NOINLINE checkDims #-}
            --  NOINLINE because we don't want the branch in the core code.