repa 3.1.1.1 → 3.1.2.1
raw patch · 14 files changed
+211/−98 lines, 14 files
Files
- Data/Array/Repa/Eval/Cursored.hs +38/−36
- Data/Array/Repa/Eval/Fill.hs +4/−0
- Data/Array/Repa/Operators/Mapping.hs +3/−3
- Data/Array/Repa/Repr/Cursored.hs +10/−6
- Data/Array/Repa/Repr/Delayed.hs +9/−4
- Data/Array/Repa/Repr/ForeignPtr.hs +8/−2
- Data/Array/Repa/Repr/Partitioned.hs +6/−7
- Data/Array/Repa/Repr/Unboxed.hs +4/−0
- Data/Array/Repa/Repr/Undefined.hs +4/−2
- Data/Array/Repa/Repr/Vector.hs +5/−0
- Data/Array/Repa/Specialised/Dim2.hs +14/−12
- Data/Array/Repa/Stencil/Dim2.hs +30/−25
- Data/Array/Repa/Stencil/Partition.hs +74/−0
- repa.cabal +2/−1
Data/Array/Repa/Eval/Cursored.hs view
@@ -33,15 +33,15 @@ -> Int# -- ^ Width of the whole array. -> Int# -- ^ x0 lower left corner of block to fill -> Int# -- ^ y0 - -> Int# -- ^ x1 upper right corner of block to fill- -> Int# -- ^ y1+ -> Int# -- ^ w0 width of block to fill.+ -> Int# -- ^ h0 height of block to fill. -> IO () {-# INLINE [0] fillBlock2P #-}-fillBlock2P !write !getElem !imageWidth !x0 !y0 !x1 !y1+fillBlock2P !write !getElem !imageWidth !x0 !y0 !w0 h0 = fillCursoredBlock2P write id addDim getElem - imageWidth x0 y0 x1 y1+ imageWidth x0 y0 w0 h0 -- | Fill a block in a rank-2 array sequentially.@@ -60,15 +60,15 @@ -> Int# -- ^ Width of the whole array. -> Int# -- ^ x0 lower left corner of block to fill -> Int# -- ^ y0- -> Int# -- ^ x1 upper right corner of block to fill- -> Int# -- ^ y1+ -> Int# -- ^ w0 width of block to fill+ -> Int# -- ^ h0 height of block to filll -> IO () {-# INLINE [0] fillBlock2S #-}-fillBlock2S !write !getElem imageWidth x0 y0 x1 y1+fillBlock2S !write !getElem imageWidth x0 y0 w0 h0 = fillCursoredBlock2S write id addDim getElem - imageWidth x0 y0 x1 y1+ imageWidth x0 y0 w0 h0 -- Block filling ----------------------------------------------------------------------------------@@ -91,46 +91,46 @@ -> (DIM2 -> cursor) -- ^ Make a cursor to a particular element. -> (DIM2 -> cursor -> cursor) -- ^ Shift the cursor by an offset. -> (cursor -> a) -- ^ Function to evaluate the element at an index.- -> Int# -- ^ Width of the whole array.- -> Int# -- ^ x0 lower left corner of block to fill- -> Int# -- ^ y0- -> Int# -- ^ x1 upper right corner of block to fill- -> Int# -- ^ y1+ -> Int# -- ^ Width of the whole array.+ -> Int# -- ^ x0 lower left corner of block to fill+ -> Int# -- ^ y0+ -> Int# -- ^ w0 width of block to fill+ -> Int# -- ^ h0 height of block to fill -> IO () {-# INLINE [0] fillCursoredBlock2P #-} fillCursoredBlock2P !write !makeCursorFCB !shiftCursorFCB !getElemFCB- !imageWidth !x0 !y0 !x1 !y1+ !imageWidth !x0 !y0 !w0 !h0 = gangIO theGang fillBlock- where !(I# threads) = gangSize theGang- !blockWidth = x1 -# x0 +# 1#+ where + !(I# threads) = gangSize theGang -- All columns have at least this many pixels.- !colChunkLen = I# (blockWidth `quotInt#` threads)+ !colChunkLen = w0 `quotInt#` threads -- Extra pixels that we have to divide between some of the threads.- !colChunkSlack = I# (blockWidth `remInt#` threads)+ !colChunkSlack = w0 `remInt#` threads -- Get the starting pixel of a column in the image. {-# INLINE colIx #-} colIx !ix- | ix < colChunkSlack = (I# x0) + ix * (colChunkLen + 1)- | otherwise = (I# x0) + (ix * colChunkLen) + colChunkSlack+ | ix <# colChunkSlack = x0 +# (ix *# (colChunkLen +# 1#))+ | otherwise = x0 +# (ix *# colChunkLen) +# colChunkSlack -- Give one column to each thread {-# INLINE fillBlock #-} fillBlock :: Int -> IO ()- fillBlock !ix- = let !(I# x0') = colIx ix- !(I# x1') = colIx (ix + 1) - 1+ fillBlock !(I# ix)+ = let !x0' = colIx ix+ !w0' = colIx (ix +# 1#) -# x0' !y0' = y0- !y1' = y1+ !h0' = h0 in fillCursoredBlock2S write makeCursorFCB shiftCursorFCB getElemFCB- imageWidth x0' y0' x1' y1'+ imageWidth x0' y0' w0' h0' -- | Fill a block in a rank-2 array, sequentially.@@ -153,30 +153,32 @@ -> Int# -- ^ Width of the whole array. -> Int# -- ^ x0 lower left corner of block to fill. -> Int# -- ^ y0- -> Int# -- ^ x1 upper right corner of block to fill.- -> Int# -- ^ y1+ -> Int# -- ^ w0 width of block to fill+ -> Int# -- ^ h0 height of block to fill -> IO () {-# INLINE [0] fillCursoredBlock2S #-} fillCursoredBlock2S !write !makeCursor !shiftCursor !getElem- !imageWidth !x0 !y0 !x1 !y1+ !imageWidth !x0 !y0 !w0 h0 - = fillBlock y0+ = do fillBlock y0+ where !x1 = x0 +# w0+ !y1 = y0 +# h0 - where {-# INLINE fillBlock #-}+ {-# INLINE fillBlock #-} fillBlock !y- | y ># y1 = return ()+ | y >=# y1 = return () | otherwise = do fillLine4 x0 fillBlock (y +# 1#) where {-# INLINE fillLine4 #-} fillLine4 !x- | x +# 4# ># x1 = fillLine1 x+ | x +# 4# >=# x1 = fillLine1 x | otherwise- = do -- Compute each source cursor based on the previous one so that+ = do -- Compute each source cursor based on the previous one so that -- the variable live ranges in the generated code are shorter. let srcCur0 = makeCursor (Z :. (I# y) :. (I# x)) let srcCur1 = shiftCursor (Z :. 0 :. 1) srcCur0@@ -208,9 +210,9 @@ {-# INLINE fillLine1 #-} fillLine1 !x- | x ># x1 = return ()+ | x >=# x1 = return () | otherwise- = do write (I# (x +# (y *# imageWidth)))- (getElem $ makeCursor (Z :. (I# y) :. (I# x)))+ = do let val0 = (getElem $ makeCursor (Z :. (I# y) :. (I# x)))+ write (I# (x +# (y *# imageWidth))) val0 fillLine1 (x +# 1#)
Data/Array/Repa/Eval/Fill.hs view
@@ -29,6 +29,10 @@ -- | Ensure the strucure of a mutable array is fully evaluated. deepSeqMArr :: MArr r e -> a -> a + -- | Ensure the array is still live at this point.+ -- Needed when the mutable array is a ForeignPtr with a finalizer.+ touchMArr :: MArr r e -> IO ()+ -- | O(n). Construct a manifest array from a list. fromList
Data/Array/Repa/Operators/Mapping.hs view
@@ -179,9 +179,9 @@ -- Undefined ---------------------------instance Combine X a D b where- cmap = map- czipWith = zipWith+instance Combine X a X b where+ cmap _ (AUndefined sh) = AUndefined sh+ czipWith _ _ (AUndefined sh) = AUndefined sh
Data/Array/Repa/Repr/Cursored.hs view
@@ -68,7 +68,8 @@ fillCursoredBlock2P (unsafeWriteMArr marr) makec shiftc loadc- w 0# 0# (w -# 1#) (h -# 1#) + w 0# 0# w h+ touchMArr marr traceEventIO "Repa.fillP[Cursored]: end" {-# INLINE fillP #-} @@ -77,7 +78,8 @@ fillCursoredBlock2S (unsafeWriteMArr marr) makec shiftc loadc- w 0# 0# (w -# 1#) (h -# 1#) + w 0# 0# w h+ touchMArr marr traceEventIO "Repa.fillS[Cursored]: end" {-# INLINE fillS #-} @@ -85,23 +87,25 @@ -- | Compute a range of elements in a rank-2 array. instance (Fillable r2 e, Elt e) => FillRange C r2 DIM2 e where fillRangeP (ACursored (Z :. _h :. (I# w)) makec shiftc loadc) marr- (Z :. (I# y0) :. (I# x0)) (Z :. (I# y1) :. (I# x1))+ (Z :. (I# y0) :. (I# x0)) (Z :. (I# h0) :. (I# w0)) = do traceEventIO "Repa.fillRangeP[Cursored]: start" fillCursoredBlock2P (unsafeWriteMArr marr) makec shiftc loadc- w x0 y0 x1 y1+ w x0 y0 w0 h0+ touchMArr marr traceEventIO "Repa.fillRangeP[Cursored]: end" {-# INLINE fillRangeP #-} fillRangeS (ACursored (Z :. _h :. (I# w)) makec shiftc loadc) marr (Z :. (I# y0) :. (I# x0)) - (Z :. (I# y1) :. (I# x1))+ (Z :. (I# h0) :. (I# w0)) = do traceEventIO "Repa.fillRangeS[Cursored]: start" fillCursoredBlock2S (unsafeWriteMArr marr) makec shiftc loadc- w x0 y0 x1 y1+ w x0 y0 w0 h0+ touchMArr marr traceEventIO "Repa.fillRangeS[Cursored]: end" {-# INLINE fillRangeS #-}
Data/Array/Repa/Repr/Delayed.hs view
@@ -50,6 +50,7 @@ = marr `deepSeqMArr` do traceEventIO "Repa.fillP[Delayed]: start" fillChunkedP (size sh) (unsafeWriteMArr marr) (getElem . fromIndex sh) + touchMArr marr traceEventIO "Repa.fillP[Delayed]: end" {-# INLINE [4] fillP #-} @@ -57,29 +58,33 @@ = marr `deepSeqMArr` do traceEventIO "Repa.fillS[Delayed]: start" fillChunkedS (size sh) (unsafeWriteMArr marr) (getElem . fromIndex sh)+ touchMArr marr traceEventIO "Repa.fillS[Delayed]: end"+ {-# INLINE [4] fillS #-} -- | Compute a range of elements in a rank-2 array. instance (Fillable r2 e, Elt e) => FillRange D r2 DIM2 e where fillRangeP (ADelayed (Z :. _h :. (I# w)) getElem) marr- (Z :. (I# y0) :. (I# x0)) (Z :. (I# y1) :. (I# x1))+ (Z :. (I# y0) :. (I# x0)) (Z :. (I# h0) :. (I# w0)) = marr `deepSeqMArr` do traceEventIO "Repa.fillRangeP[Delayed]: start" fillBlock2P (unsafeWriteMArr marr) getElem- w x0 y0 x1 y1+ w x0 y0 w0 h0+ touchMArr marr traceEventIO "Repa.fillRangeP[Delayed]: end" {-# INLINE [1] fillRangeP #-} fillRangeS (ADelayed (Z :. _h :. (I# w)) getElem) marr- (Z :. (I# y0) :. (I# x0)) (Z :. (I# y1) :. (I# x1))+ (Z :. (I# y0) :. (I# x0)) (Z :. (I# h0) :. (I# w0)) = marr `deepSeqMArr` do traceEventIO "Repa.fillRangeS[Delayed]: start" fillBlock2S (unsafeWriteMArr marr) getElem- w x0 y0 x1 y1+ w x0 y0 w0 h0+ touchMArr marr traceEventIO "Repa.fillRangeS[Delayed]: end" {-# INLINE [1] fillRangeS #-}
Data/Array/Repa/Repr/ForeignPtr.hs view
@@ -62,6 +62,8 @@ return $ FPArr n fptr {-# INLINE newMArr #-} + -- CAREFUL: Unwrapping the foreignPtr like this means we need to be careful+ -- to touch it after the last use, otherwise the finaliser might run too early. unsafeWriteMArr (FPArr _ fptr) !ix !x = pokeElemOff (Unsafe.unsafeForeignPtrToPtr fptr) ix x {-# INLINE unsafeWriteMArr #-}@@ -70,9 +72,13 @@ = return $ AForeignPtr sh len fptr {-# INLINE unsafeFreezeMArr #-} - deepSeqMArr !(FPArr _ ptr) x- = Unsafe.unsafeForeignPtrToPtr ptr `seq` x+ deepSeqMArr !(FPArr _ fptr) x+ = Unsafe.unsafeForeignPtrToPtr fptr `seq` x {-# INLINE deepSeqMArr #-}++ touchMArr (FPArr _ fptr)+ = touchForeignPtr fptr+ {-# INLINE touchMArr #-} -- Conversions ----------------------------------------------------------------
Data/Array/Repa/Repr/Partitioned.hs view
@@ -9,7 +9,6 @@ import Data.Array.Repa.Shape import Data.Array.Repa.Eval import Data.Array.Repa.Repr.Delayed-import Data.Array.Repa.Repr.Undefined -- | Partitioned arrays.@@ -62,8 +61,8 @@ deepSeqRange :: Shape sh => Range sh -> b -> b-deepSeqRange (Range low high f) y- = low `deepSeq` high `deepSeq` f `seq` y+deepSeqRange (Range ix sz f) y+ = ix `deepSeq` sz `deepSeq` f `seq` y {-# INLINE deepSeqRange #-} @@ -71,12 +70,12 @@ instance ( FillRange r1 r3 sh e, Fill r2 r3 sh e , Fillable r3 e) => Fill (P r1 r2) r3 sh e where- fillP (APart _ (Range ix10 ix11 _) arr1 arr2) marr- = do fillRangeP arr1 marr ix10 ix11+ fillP (APart _ (Range ix sz _) arr1 arr2) marr+ = do fillRangeP arr1 marr ix sz fillP arr2 marr {-# INLINE fillP #-} - fillS (APart _ (Range ix10 ix11 _) arr1 arr2) marr- = do fillRangeS arr1 marr ix10 ix11+ fillS (APart _ (Range ix sz _) arr1 arr2) marr+ = do fillRangeS arr1 marr ix sz fillS arr2 marr {-# INLINE fillS #-}
Data/Array/Repa/Repr/Unboxed.hs view
@@ -75,6 +75,10 @@ = vec `seq` x {-# INLINE deepSeqMArr #-} + touchMArr _ + = return ()+ {-# INLINE touchMArr #-}+ -- Conversions ---------------------------------------------------------------- -- | Sequential computation of array elements..
Data/Array/Repa/Repr/Undefined.hs view
@@ -27,10 +27,12 @@ = sh {-# INLINE extent #-} - index (AUndefined _) _ = error "Repa: array element is undefined."+ index (AUndefined _) _ + = error $ "Repa: array element is undefined." {-# INLINE index #-} - linearIndex (AUndefined _) _ = error "Repa: array element is undefined."+ linearIndex (AUndefined _) ix+ = error $ "Repa: array element at " ++ show ix ++ " is undefined." {-# INLINE linearIndex #-}
Data/Array/Repa/Repr/Vector.hs view
@@ -68,6 +68,11 @@ = x {-# INLINE deepSeqMArr #-} + touchMArr _ + = return ()+ {-# INLINE touchMArr #-}++ -- Conversions ---------------------------------------------------------------- -- | Sequential computation of array elements. --
Data/Array/Repa/Specialised/Dim2.hs view
@@ -66,6 +66,7 @@ | 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.@@ -80,18 +81,18 @@ -> Array (P r1 (P r2 (P r2 (P r2 (P r2 X))))) DIM2 a {-# INLINE makeBordered2 #-}-makeBordered2 sh@(_ :. aHeight :. aWidth) borderWidth arrInternal arrBorder+makeBordered2 sh@(_ :. aHeight :. aWidth) bWidth arrInternal arrBorder = checkDims `seq` let -- minimum and maximum indicies of values in the inner part of the image.- !xMin = borderWidth- !yMin = borderWidth- !xMax = aWidth - borderWidth - 1- !yMax = aHeight - borderWidth - 1+ !inX = bWidth+ !inY = bWidth+ !inW = aWidth - 2 * bWidth + !inH = aHeight - 2 * bWidth inInternal (Z :. y :. x)- = x >= xMin && x <= xMax- && y >= yMin && y <= yMax+ = x >= inX && x < (inX + inW)+ && y >= inY && y < (inY + inH) {-# INLINE inInternal #-} inBorder = not . inInternal@@ -99,13 +100,13 @@ in -- internal region- APart sh (Range (Z :. yMin :. xMin) (Z :. yMax :. xMax ) inInternal) arrInternal+ APart sh (Range (Z :. inY :. inX) (Z :. inH :. inW ) inInternal) arrInternal -- border regions- $ APart sh (Range (Z :. 0 :. 0) (Z :. yMin -1 :. aWidth - 1) inBorder) arrBorder- $ APart sh (Range (Z :. yMax + 1 :. 0) (Z :. aHeight - 1 :. aWidth - 1) inBorder) arrBorder- $ APart sh (Range (Z :. yMin :. 0) (Z :. yMax :. xMin - 1) inBorder) arrBorder- $ APart sh (Range (Z :. yMin :. xMax + 1) (Z :. yMax :. aWidth - 1) inBorder) arrBorder+ $ 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@@ -115,3 +116,4 @@ else error "makeBordered2: internal and border arrays have different extents" {-# NOINLINE checkDims #-} -- NOINLINE because we don't want the branch in the core code.+
Data/Array/Repa/Stencil/Dim2.hs view
@@ -25,6 +25,7 @@ import Data.Array.Repa.Repr.Undefined import Data.Array.Repa.Stencil.Base import Data.Array.Repa.Stencil.Template+import Data.Array.Repa.Stencil.Partition import GHC.Exts -- | A index into the flat array.@@ -67,19 +68,24 @@ sHeight2 = sHeight `div` 2 sWidth2 = sWidth `div` 2 - -- minimum and maximum indicies of values in the inner part of the image.- !xMin = sWidth2- !yMin = sHeight2- !xMax = aWidth - sWidth2 - 1- !yMax = aHeight - sHeight2 - 1+ -- Partition the array into the internal and border regions.+ ![ Region inX inY inW inH+ , Region westX westY westW westH+ , Region eastX eastY eastW eastH+ , Region northX northY northW northH + , Region southX southY southW southH ] + = partitionForStencil + (Size aWidth aHeight) + (Size sWidth sHeight)+ (Offset sWidth2 sHeight2) {-# INLINE inInternal #-} inInternal (Z :. y :. x)- = x >= xMin && x <= xMax- && y >= yMin && y <= yMax+ = x >= inX && x < (inX + inW)+ && y >= inY && y < (inY + inH) {-# INLINE inBorder #-}- inBorder = not . inInternal+ inBorder = not . inInternal -- Cursor functions ---------------- {-# INLINE makec #-}@@ -92,32 +98,31 @@ $ case ix of Z :. y :. x -> off + y * aWidth + x - {-# INLINE getInner' #-}- getInner' cur- = unsafeAppStencilCursor2 shiftc stencil arr cur-- {-# INLINE getBorder' #-}- getBorder' ix- = case boundary of- BoundConst c -> c- BoundClamp -> unsafeAppStencilCursor2_clamp addDim stencil- arr ix- {-# INLINE arrInternal #-} arrInternal = makeCursored (extent arr) makec shiftc getInner' - ++ {-# INLINE getInner' #-}+ getInner' cur = unsafeAppStencilCursor2 shiftc stencil arr cur+ {-# INLINE arrBorder #-} arrBorder = ASmall (fromFunction (extent arr) getBorder') + {-# INLINE getBorder' #-}+ getBorder' ix+ = case boundary of+ BoundConst c -> c+ BoundClamp -> unsafeAppStencilCursor2_clamp addDim stencil+ arr ix+ in -- internal region- APart sh (Range (Z :. yMin :. xMin) (Z :. yMax :. xMax ) inInternal) arrInternal+ APart sh (Range (Z :. inY :. inX) (Z :. inH :. inW) inInternal) arrInternal -- border regions- $ APart sh (Range (Z :. 0 :. 0) (Z :. yMin -1 :. aWidth - 1) inBorder) arrBorder- $ APart sh (Range (Z :. yMax + 1 :. 0) (Z :. aHeight - 1 :. aWidth - 1) inBorder) arrBorder- $ APart sh (Range (Z :. yMin :. 0) (Z :. yMax :. xMin - 1) inBorder) arrBorder- $ APart sh (Range (Z :. yMin :. xMax + 1) (Z :. yMax :. aWidth - 1) inBorder) arrBorder+ $ APart sh (Range (Z :. westY :. westX) (Z :. westH :. westW) inBorder) arrBorder+ $ APart sh (Range (Z :. eastY :. eastX) (Z :. eastH :. eastW) inBorder) arrBorder+ $ APart sh (Range (Z :. northY :. northX) (Z :. northH :. northW) inBorder) arrBorder+ $ APart sh (Range (Z :. southY :. southX) (Z :. southH :. southW) inBorder) arrBorder $ AUndefined sh
+ Data/Array/Repa/Stencil/Partition.hs view
@@ -0,0 +1,74 @@++module Data.Array.Repa.Stencil.Partition+ ( Offset (..)+ , Size (..)+ , Region (..)+ , partitionForStencil)+where++-- | An offset in the 2d plane.+data Offset+ = Offset !Int !Int++-- | Size of a region in the 2d plane.+data Size+ = Size !Int !Int++-- | A region in the 2d plane.+data Region + = Region+ { regionX :: !Int+ , regionY :: !Int+ , regionWidth :: !Int+ , regionHeight :: !Int }+ deriving Show+++-- | Create a new region of the given size.+regionOfSize :: Size -> Region+regionOfSize (Size w h)+ = Region 0 0 w h+{-# INLINE regionOfSize #-}++-- | Offset a region.+offsetRegion :: Offset -> Region -> Region+offsetRegion (Offset x y) (Region x0 y0 w h)+ = Region (x0 + x) (y0 + y) w h+{-# INLINE offsetRegion #-}++-- | Partition a region into inner and border regions for the given stencil.+partitionForStencil+ :: Size -- ^ Size of array+ -> Size -- ^ Size of stencil+ -> Offset -- ^ Focus of stencil+ -> [Region]++partitionForStencil+ (Size arrW arrH)+ (Size krnW krnH)+ (Offset focX focY)+ = let + gapNorth = focY+ gapSouth = krnH - focY - 1+ gapWest = focX+ gapEast = krnW - focX - 1++ innerW = arrW - gapWest - gapEast+ innerH = arrH - gapNorth - gapSouth++ regionInner = offsetRegion (Offset gapWest gapNorth)+ $ regionOfSize (Size innerW innerH)++ regionNorth = regionOfSize (Size arrW gapNorth)++ regionSouth = offsetRegion (Offset 0 (gapNorth + innerH))+ $ regionOfSize (Size arrW gapSouth)++ regionWest = offsetRegion (Offset 0 gapNorth)+ $ regionOfSize (Size gapWest innerH)++ regionEast = offsetRegion (Offset (gapWest + innerW) gapNorth)+ $ regionOfSize (Size gapEast innerH)++ in [regionInner, regionNorth, regionSouth, regionWest, regionEast]+{-# INLINE partitionForStencil #-}
repa.cabal view
@@ -1,5 +1,5 @@ Name: repa-Version: 3.1.1.1+Version: 3.1.2.1 License: BSD3 License-file: LICENSE Author: The DPH Team@@ -68,6 +68,7 @@ Data.Array.Repa.Repr.Vector Data.Array.Repa.Specialised.Dim2 Data.Array.Repa.Stencil.Dim2+ Data.Array.Repa.Stencil.Partition Data.Array.Repa.Eval Data.Array.Repa.Index Data.Array.Repa.Shape