{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeFamilies #-}
module Data.Dense.Generic
(
Array
, Shape (..)
, BArray
, UArray
, SArray
, PArray
, HasLayout (..)
, Layout
, extent
, size
, indexes
, indexesFrom
, indexesBetween
, vector
, values
, values'
, valuesBetween
, flat
, fromList
, fromListInto
, fromListInto_
, fromVectorInto
, fromVectorInto_
, replicate
, generate
, linearGenerate
, create
, replicateM
, generateM
, linearGenerateM
, empty
, null
, (!)
, (!?)
, unsafeIndex
, linearIndex
, unsafeLinearIndex
, indexM
, unsafeIndexM
, linearIndexM
, unsafeLinearIndexM
, (//)
, accum
, map
, imap
, Data.Dense.Generic.zip
, Data.Dense.Generic.zip3
, zipWith
, zipWith3
, izipWith
, izipWith3
, ixRow
, rows
, ixColumn
, columns
, ixPlane
, planes
, flattenPlane
, unsafeOrdinals
, MArray
, M.BMArray
, M.UMArray
, M.SMArray
, M.PMArray
, thaw
, freeze
, unsafeThaw
, unsafeFreeze
, Delayed
, delayed
, seqDelayed
, delay
, manifest
, seqManifest
, genDelayed
, indexDelayed
, affirm
, seqAffirm
, Focused
, focusOn
, unfocus
, unfocused
, extendFocus
, locale
, shiftFocus
, Boundary (..)
, peekB
, peeksB
, peekRelativeB
, streamGenerate
, streamGenerateM
, streamIndexes
, bundleGenerate
, bundleGenerateM
, bundleIndexes
) where
#if __GLASGOW_HASKELL__ <= 708
import Control.Applicative (Applicative, pure, (<*>))
import Data.Foldable (Foldable)
#endif
import Control.Comonad
import Control.Comonad.Store
import Control.Lens hiding (imap)
import Control.Monad (liftM)
import Control.Monad.Primitive
import Control.Monad.ST
import qualified Data.Foldable as F
import Data.Functor.Classes
import qualified Data.List as L
import Data.Maybe (fromMaybe)
import Data.Typeable
import qualified Data.Vector as B
import Data.Vector.Fusion.Bundle (MBundle)
import qualified Data.Vector.Fusion.Bundle as Bundle
import qualified Data.Vector.Fusion.Bundle.Monadic as MBundle
import Data.Vector.Fusion.Bundle.Size
import Data.Vector.Fusion.Stream.Monadic (Step (..), Stream (..))
import qualified Data.Vector.Fusion.Stream.Monadic as Stream
import Data.Vector.Generic (Vector)
import qualified Data.Vector.Generic as G
import qualified Data.Vector.Generic.Mutable as GM
import qualified Data.Vector.Primitive as P
import qualified Data.Vector.Storable as S
import qualified Data.Vector.Unboxed as U
import Linear hiding (vector)
import Data.Dense.Base
import Data.Dense.Index
import Data.Dense.Mutable (MArray (..))
import qualified Data.Dense.Mutable as M
import Prelude hiding (map, null, replicate,
zipWith, zipWith3)
type BArray = Array B.Vector
type UArray = Array U.Vector
type SArray = Array S.Vector
type PArray = Array P.Vector
values' :: (Shape f, Vector v a, Vector v b)
=> IndexedTraversal (f Int) (Array v f a) (Array v f b) a b
values' = values
{-# INLINE values' #-}
valuesBetween :: (Shape f, Vector v a) => f Int -> f Int -> IndexedTraversal' (f Int) (Array v f a) a
valuesBetween a b = unsafeOrdinals (toListOf (shapeIndexesFrom a) b)
{-# INLINE valuesBetween #-}
flat :: Vector w b => Iso (Array v V1 a) (Array w V1 b) (v a) (w b)
flat = iso (\(Array _ v) -> v) (\v -> Array (V1 $ G.length v) v)
{-# INLINE flat #-}
fromList :: Vector v a => [a] -> Array v V1 a
fromList = G.fromList
{-# INLINE fromList #-}
fromListInto :: (Shape f, Vector v a) => Layout f -> [a] -> Maybe (Array v f a)
fromListInto l as
| G.length v == n = Just $ Array l v
| otherwise = Nothing
where v = G.fromListN n as
n = shapeSize l
{-# INLINE fromListInto #-}
fromListInto_ :: (Shape f, Vector v a) => Layout f -> [a] -> Array v f a
fromListInto_ l as = fromMaybe err $ fromListInto l as
where
err = error $ "fromListInto_: shape " ++ showShape l ++ " is too large for list"
{-# INLINE fromListInto_ #-}
fromVectorInto :: (Shape f, Vector v a) => Layout f -> v a -> Maybe (Array v f a)
fromVectorInto l v
| shapeSize l == G.length v = Just $! Array l v
| otherwise = Nothing
{-# INLINE fromVectorInto #-}
fromVectorInto_ :: (Shape f, Vector v a) => Layout f -> v a -> Array v f a
fromVectorInto_ l as = fromMaybe err $ fromVectorInto l as
where
err = error $ "fromVectorInto_: shape " ++ showShape l ++ " is too large for the vector"
{-# INLINE fromVectorInto_ #-}
empty :: (Vector v a, Additive f) => Array v f a
empty = Array zero G.empty
{-# INLINE empty #-}
null :: Foldable f => Array v f a -> Bool
null (Array l _) = F.all (==0) l
{-# INLINE null #-}
(!) :: (Shape f, Vector v a) => Array v f a -> f Int -> a
(!) (Array l v) i = boundsCheck l i $ G.unsafeIndex v (shapeToIndex l i)
{-# INLINE (!) #-}
(!?) :: (Shape f, Vector v a) => Array v f a -> f Int -> Maybe a
Array l v !? i
| shapeInRange l i = Just $! G.unsafeIndex v (shapeToIndex l i)
| otherwise = Nothing
{-# INLINE (!?) #-}
unsafeIndex :: (Shape f, Vector v a) => Array v f a -> f Int -> a
unsafeIndex (Array l v) i = G.unsafeIndex v (shapeToIndex l i)
{-# INLINE unsafeIndex #-}
linearIndex :: Vector v a => Array v f a -> Int -> a
linearIndex (Array _ v) i = v G.! i
{-# INLINE linearIndex #-}
unsafeLinearIndex :: Vector v a => Array v f a -> Int -> a
unsafeLinearIndex (Array _ v) i = G.unsafeIndex v i
{-# INLINE unsafeLinearIndex #-}
indexM :: (Shape f, Vector v a, Monad m) => Array v f a -> f Int -> m a
indexM (Array l v) i = boundsCheck l i $ G.unsafeIndexM v (shapeToIndex l i)
{-# INLINE indexM #-}
unsafeIndexM :: (Shape f, Vector v a, Monad m) => Array v f a -> f Int -> m a
unsafeIndexM (Array l v) i = G.unsafeIndexM v (shapeToIndex l i)
{-# INLINE unsafeIndexM #-}
linearIndexM :: (Shape f, Vector v a, Monad m) => Array v f a -> Int -> m a
linearIndexM (Array l v) i = boundsCheck l (shapeFromIndex l i) $ G.unsafeIndexM v i
{-# INLINE linearIndexM #-}
unsafeLinearIndexM :: (Vector v a, Monad m) => Array v f a -> Int -> m a
unsafeLinearIndexM (Array _ v) = G.unsafeIndexM v
{-# INLINE unsafeLinearIndexM #-}
create :: Vector v a => (forall s. ST s (MArray (G.Mutable v) f s a)) -> Array v f a
create m = m `seq` runST (m >>= unsafeFreeze)
{-# INLINE create #-}
replicate :: (Shape f, Vector v a) => f Int -> a -> Array v f a
replicate l a
| n > 0 = Array l $ G.replicate n a
| otherwise = empty
where n = shapeSize l
{-# INLINE replicate #-}
linearGenerate :: (Shape f, Vector v a) => Layout f -> (Int -> a) -> Array v f a
linearGenerate l f
| n > 0 = Array l $ G.generate n f
| otherwise = empty
where n = shapeSize l
{-# INLINE linearGenerate #-}
generate :: (Shape f, Vector v a) => Layout f -> (f Int -> a) -> Array v f a
generate l f = Array l $ G.unstream (bundleGenerate l f)
{-# INLINE generate #-}
replicateM :: (Monad m, Shape f, Vector v a) => Layout f -> m a -> m (Array v f a)
replicateM l a
| n > 0 = Array l `liftM` G.replicateM n a
| otherwise = return empty
where n = shapeSize l
{-# INLINE replicateM #-}
generateM :: (Monad m, Shape f, Vector v a) => Layout f -> (f Int -> m a) -> m (Array v f a)
generateM l f = Array l `liftM` unstreamM (bundleGenerateM l f)
{-# INLINE generateM #-}
linearGenerateM :: (Monad m, Shape f, Vector v a) => Layout f -> (Int -> m a) -> m (Array v f a)
linearGenerateM l f
| n > 0 = Array l `liftM` G.generateM n f
| otherwise = return empty
where n = shapeSize l
{-# INLINE linearGenerateM #-}
map :: (Vector v a, Vector v b) => (a -> b) -> Array v f a -> Array v f b
map f (Array l a) = Array l (G.map f a)
{-# INLINE map #-}
imap :: (Shape f, Vector v a, Vector v b) => (f Int -> a -> b) -> Array v f a -> Array v f b
imap f (Array l v) =
Array l $ (G.unstream . Bundle.inplace (Stream.zipWith f (streamIndexes l)) id . G.stream) v
{-# INLINE imap #-}
(//) :: (G.Vector v a, Shape f) => Array v f a -> [(f Int, a)] -> Array v f a
Array l v // xs = Array l $ v G.// over (each . _1) (shapeToIndex l) xs
accum :: (Shape f, Vector v a)
=> (a -> b -> a)
-> Array v f a
-> [(f Int, b)]
-> Array v f a
accum f (Array l v) us = Array l $ G.accum f v (over (mapped . _1) (shapeToIndex l) us)
{-# INLINE accum #-}
unstreamM :: (Monad m, Vector v a) => Bundle.MBundle m u a -> m (v a)
{-# INLINE [1] unstreamM #-}
unstreamM s = do
xs <- MBundle.toList s
return $ G.unstream $ Bundle.unsafeFromList (MBundle.size s) xs
unstreamPrimM :: (PrimMonad m, Vector v a) => Bundle.MBundle m u a -> m (v a)
{-# INLINE [1] unstreamPrimM #-}
unstreamPrimM s = GM.munstream s >>= G.unsafeFreeze
unstreamPrimM_IO :: Vector v a => Bundle.MBundle IO u a -> IO (v a)
{-# INLINE unstreamPrimM_IO #-}
unstreamPrimM_IO = unstreamPrimM
unstreamPrimM_ST :: Vector v a => Bundle.MBundle (ST s) u a -> ST s (v a)
{-# INLINE unstreamPrimM_ST #-}
unstreamPrimM_ST = unstreamPrimM
{-# RULES
"unstreamM[IO]" unstreamM = unstreamPrimM_IO
"unstreamM[ST]" unstreamM = unstreamPrimM_ST #-}
streamGenerate :: (Monad m, Shape f) => Layout f -> (f Int -> a) -> Stream m a
streamGenerate l f = streamGenerateM l (return . f)
{-# INLINE streamGenerate #-}
streamGenerateM :: (Monad m, Shape f) => Layout f -> (f Int -> m a) -> Stream m a
streamGenerateM l f = l `seq` Stream step (if eq1 l zero then Nothing else Just zero)
where
{-# INLINE [0] step #-}
step (Just i) = do
x <- f i
return $ Yield x (shapeStep l i)
step Nothing = return Done
{-# INLINE [1] streamGenerateM #-}
unsafeStreamSub :: (Monad m, Shape f, G.Vector v a) => Layout f -> Array v f a -> Stream m a
unsafeStreamSub l2 (Array l1 v) = streamGenerateM l2 $ \x -> G.basicUnsafeIndexM v (shapeToIndex l1 x)
{-# INLINE unsafeStreamSub #-}
streamSub :: (Monad m, Shape f, G.Vector v a) => Layout f -> Array v f a -> Stream m a
streamSub l2 arr@(Array l1 _) = unsafeStreamSub (shapeIntersect l1 l2) arr
{-# INLINE streamSub #-}
streamIndexes :: (Monad m, Shape f) => Layout f -> Stream m (f Int)
streamIndexes l = Stream step (if eq1 l zero then Nothing else Just zero)
where
{-# INLINE [0] step #-}
step (Just i) = return $ Yield i (shapeStep l i)
step Nothing = return Done
{-# INLINE [1] streamIndexes #-}
bundleGenerate :: (Monad m, Shape f) => Layout f -> (f Int -> a) -> MBundle m v a
bundleGenerate l f = bundleGenerateM l (return . f)
{-# INLINE bundleGenerate #-}
bundleGenerateM :: (Monad m, Shape f) => Layout f -> (f Int -> m a) -> MBundle m v a
bundleGenerateM l f = MBundle.fromStream (streamGenerateM l f) (Exact (shapeSize l))
{-# INLINE [1] bundleGenerateM #-}
bundleIndexes :: (Monad m, Shape f) => Layout f -> MBundle m v (f Int)
bundleIndexes l = MBundle.fromStream (streamIndexes l) (Exact (shapeSize l))
{-# INLINE [1] bundleIndexes #-}
zip :: (Shape f, Vector v a, Vector v b, Vector v (a,b))
=> Array v f a
-> Array v f b
-> Array v f (a,b)
zip = zipWith (,)
zip3 :: (Shape f, Vector v a, Vector v b, Vector v c, Vector v (a,b,c))
=> Array v f a
-> Array v f b
-> Array v f c
-> Array v f (a,b,c)
zip3 = zipWith3 (,,)
zipWith :: (Shape f, Vector v a, Vector v b, Vector v c)
=> (a -> b -> c)
-> Array v f a
-> Array v f b
-> Array v f c
zipWith f a1@(Array l1 v1) a2@(Array l2 v2)
| eq1 l1 l1 = Array l1 $ G.zipWith f v1 v2
| otherwise = Array l' $ G.unstream $
MBundle.fromStream (Stream.zipWith f (streamSub l' a1) (streamSub l' a2)) (Exact (shapeSize l'))
where l' = shapeIntersect l1 l2
{-# INLINE zipWith #-}
zipWith3 :: (Shape f, Vector v a, Vector v b, Vector v c, Vector v d)
=> (a -> b -> c -> d)
-> Array v f a
-> Array v f b
-> Array v f c
-> Array v f d
zipWith3 f a1@(Array l1 v1) a2@(Array l2 v2) a3@(Array l3 v3)
| eq1 l1 l2 &&
eq1 l2 l3 = Array l1 $ G.zipWith3 f v1 v2 v3
| otherwise = Array l' $ G.unstream $
MBundle.fromStream (Stream.zipWith3 f (streamSub l' a1) (streamSub l' a2) (streamSub l' a3)) (Exact (shapeSize l'))
where l' = shapeIntersect (shapeIntersect l1 l2) l3
{-# INLINE zipWith3 #-}
izipWith :: (Shape f, Vector v a, Vector v b, Vector v c)
=> (f Int -> a -> b -> c)
-> Array v f a
-> Array v f b
-> Array v f c
izipWith f a1@(Array l1 v1) a2@(Array l2 v2)
| eq1 l1 l2 = Array l1 $ G.unstream $ Bundle.zipWith3 f (bundleIndexes l1) (G.stream v1) (G.stream v2)
| otherwise = Array l' $ G.unstream $
MBundle.fromStream (Stream.zipWith3 f (streamIndexes l') (streamSub l' a1) (streamSub l' a2)) (Exact (shapeSize l'))
where l' = shapeIntersect l1 l2
{-# INLINE izipWith #-}
izipWith3 :: (Shape f, Vector v a, Vector v b, Vector v c, Vector v d)
=> (f Int -> a -> b -> c -> d)
-> Array v f a
-> Array v f b
-> Array v f c
-> Array v f d
izipWith3 f a1@(Array l1 v1) a2@(Array l2 v2) a3@(Array l3 v3)
| eq1 l1 l2 = Array l1 $ G.unstream $ Bundle.zipWith4 f (bundleIndexes l1) (G.stream v1) (G.stream v2) (G.stream v3)
| otherwise =
Array l' $ G.unstream $ MBundle.fromStream
(Stream.zipWith4 f (streamIndexes l') (streamSub l' a1) (streamSub l' a2) (streamSub l' a3)) (Exact (shapeSize l'))
where l' = shapeIntersect (shapeIntersect l1 l2) l3
{-# INLINE izipWith3 #-}
rows :: (Vector v a, Vector w b)
=> IndexedTraversal Int (Array v V2 a) (Array w V2 b) (v a) (w b)
rows f (Array l@(V2 x y) v) = Array l . G.concat <$> go 0 0 where
go i a | i >= x = pure []
| otherwise = (:) <$> indexed f i (G.slice a y v) <*> go (i+1) (a+y)
{-# INLINE rows #-}
ixRow :: Vector v a => Int -> IndexedTraversal' Int (Array v V2 a) (v a)
ixRow i f m@(Array (l@(V2 x y)) v)
| y >= 0 && i < x = Array l . G.unsafeUpd v . L.zip [a..] . G.toList . G.take y <$> indexed f i (G.slice a y v)
| otherwise = pure m
where a = i * y
{-# INLINE ixRow #-}
columns :: (Vector v a, Vector w b)
=> IndexedTraversal Int (Array v V2 a) (Array w V2 b) (v a) (w b)
columns f m@(Array l@(V2 _ y) _) = transposeConcat l <$> go 0 where
go j | j >= y = pure []
| otherwise = (:) <$> indexed f j (getColumn m j) <*> go (j+1)
{-# INLINE columns #-}
ixColumn :: Vector v a => Int -> IndexedTraversal' Int (Array v V2 a) (v a)
ixColumn j f m@(Array (l@(V2 _ y)) v)
| j >= 0 && j < y = Array l . G.unsafeUpd v . L.zip js . G.toList . G.take y <$> indexed f j (getColumn m j)
| otherwise = pure m
where js = [j, j + y .. ]
{-# INLINE ixColumn #-}
getColumn :: Vector v a => Array v V2 a -> Int -> v a
getColumn (Array (V2 x y) v) j = G.generate x $ \i -> G.unsafeIndex v (i * y + j)
{-# INLINE getColumn #-}
transposeConcat :: Vector v a => V2 Int -> [v a] -> Array v V2 a
transposeConcat (V2 _ y) vs = Array (V2 x' y) $ G.create $ do
mv <- GM.new (x'*y)
iforM_ vs $ \j v ->
F.for_ [0..x'-1] $ \i ->
GM.write mv (i*y + j) (v G.! i)
return mv
where x' = minimum $ fmap G.length vs
{-# INLINE transposeConcat #-}
ixPlane :: Vector v a
=> ALens' (V3 Int) (V2 Int)
-> Int
-> IndexedTraversal' Int (Array v V3 a) (Array v V2 a)
ixPlane l32 i f a@(Array l v)
| i < 0 || i >= k = pure a
| otherwise = Array l . (v G.//) . L.zip is . toListOf values
<$> indexed f i (getPlane l32 i a)
where
is = toListOf (cloneLens l32 . shapeIndexes . to (\x -> shapeToIndex l $ pure i & l32 #~ x)) l
k = F.sum $ l & l32 #~ 0
planes :: (Vector v a, Vector w b)
=> ALens' (V3 Int) (V2 Int)
-> IndexedTraversal Int (Array v V3 a) (Array w V3 b) (Array v V2 a) (Array w V2 b)
planes l32 f a@(Array l _) = concatPlanes l l32 <$> go 0 where
go i | i >= k = pure []
| otherwise = (:) <$> indexed f i (getPlane l32 i a) <*> go (i+1)
k = F.sum $ l & l32 #~ 0
{-# INLINE planes #-}
concatPlanes :: Vector v a => V3 Int -> ALens' (V3 Int) (V2 Int) -> [Array v V2 a] -> Array v V3 a
concatPlanes l l32 as = create $ do
arr <- M.new l
iforM_ as $ \i m ->
iforMOf_ values m $ \x a -> do
let w = pure i & l32 #~ x
M.write arr w a
return arr
getPlane :: Vector v a => ALens' (V3 Int) (V2 Int) -> Int -> Array v V3 a -> Array v V2 a
getPlane l32 i a = generate (a ^# layout . l32) $ \x -> a ! (pure i & l32 #~ x)
flattenPlane :: (Vector v a, Vector w b)
=> ALens' (V3 Int) (V2 Int)
-> (v a -> b)
-> Array v V3 a
-> Array w V2 b
flattenPlane l32 f a@(Array l _) = generate l' $ \x -> f (getVector x)
where
getVector x = G.generate n $ \i -> a ! (pure i & l32 #~ x)
n = F.sum $ l & l32 #~ 0
l' = l ^# l32
{-# INLINE flattenPlane #-}
unsafeOrdinals :: (Vector v a, Shape f) => [f Int] -> IndexedTraversal' (f Int) (Array v f a) a
unsafeOrdinals is f (Array l v) = Array l . (v G.//) <$> traverse g is
where g x = let i = shapeToIndex l x in (,) i <$> indexed f x (G.unsafeIndex v i)
{-# INLINE [0] unsafeOrdinals #-}
setOrdinals :: (Indexable (f Int) p, Vector v a, Shape f) => [f Int] -> p a a -> Array v f a -> Array v f a
setOrdinals is f (Array l v) = Array l $ G.unsafeUpd v (fmap g is)
where g x = let i = shapeToIndex l x in (,) i $ indexed f x (G.unsafeIndex v i)
{-# INLINE setOrdinals #-}
{-# RULES
"unsafeOrdinals/setOrdinals" forall (is :: [f Int]).
unsafeOrdinals is = sets (setOrdinals is)
:: Vector v a => ASetter' (Array v f a) a;
"unsafeOrdinalts/isetOrdintals" forall (is :: [f Int]).
unsafeOrdinals is = sets (setOrdinals is)
:: Vector v a => AnIndexedSetter' (f Int) (Array v f a) a
#-}
freeze :: (PrimMonad m, Vector v a)
=> MArray (G.Mutable v) f (PrimState m) a -> m (Array v f a)
freeze (MArray l mv) = Array l `liftM` G.freeze mv
{-# INLINE freeze #-}
thaw :: (PrimMonad m, Vector v a)
=> Array v f a -> m (MArray (G.Mutable v) f (PrimState m) a)
thaw (Array l v) = MArray l `liftM` G.thaw v
{-# INLINE thaw #-}
delayed :: (Vector v a, Vector w b, Shape f, Shape g)
=> Iso (Array v f a) (Array w g b) (Delayed f a) (Delayed g b)
delayed = iso delay manifest
{-# INLINE delayed #-}
seqDelayed :: (Vector v a, Vector w b, Shape f, Shape g)
=> Iso (Array v f a) (Array w g b) (Delayed f a) (Delayed g b)
seqDelayed = iso delay seqManifest
{-# INLINE seqDelayed #-}
seqManifest :: (Vector v a, Shape f) => Delayed f a -> Array v f a
seqManifest (Delayed l f) = generate l f
{-# INLINE seqManifest #-}
affirm :: (Shape f, U.Unbox a) => Delayed f a -> Delayed f a
affirm = delay . (manifest :: (U.Unbox a, Shape f) => Delayed f a -> UArray f a)
{-# INLINE affirm #-}
seqAffirm :: (Shape f, U.Unbox a) => Delayed f a -> Delayed f a
seqAffirm = delay . (seqManifest :: (U.Unbox a, Shape f) => Delayed f a -> UArray f a)
{-# INLINE seqAffirm #-}
focusOn :: f Int -> Delayed f a -> Focused f a
focusOn = Focused
{-# INLINE focusOn #-}
unfocus :: Focused f a -> Delayed f a
unfocus (Focused _ d) = d
{-# INLINE unfocus #-}
unfocused :: IndexedLens (f Int) (Focused f a) (Focused f b) (Delayed f a) (Delayed f b)
unfocused f (Focused x d) = Focused x <$> indexed f x d
{-# INLINE unfocused #-}
extendFocus :: Shape f => (Focused f a -> b) -> Delayed f a -> Delayed f b
extendFocus f = unfocus . extend f . focusOn zero
{-# INLINE extendFocus #-}
locale :: ComonadStore s w => Lens' (w a) s
locale f w = (`seek` w) <$> f (pos w)
{-# INLINE locale #-}
shiftFocus :: Applicative f => f Int -> Focused f a -> Focused f a
shiftFocus dx (Focused x d@(Delayed l _)) = Focused x' d
where
x' = f <$> l <*> x <*> dx
f k i di
| i' < 0 = k + i'
| i' >= k = i' - k
| otherwise = i'
where i' = i + di
{-# INLINE shiftFocus #-}
data Boundary
= Clamp
| Mirror
| Wrap
deriving (Show, Read, Typeable)
peekB :: Shape f => Boundary -> f Int -> Focused f a -> a
peekB = \b x -> peeksB b (const x)
{-# INLINE peekB #-}
peekRelativeB :: Shape f => Boundary -> f Int -> Focused f a -> a
peekRelativeB = \b i -> peeksB b (^+^ i)
{-# INLINE peekRelativeB #-}
peeksB :: Shape f => Boundary -> (f Int -> f Int) -> Focused f a -> a
peeksB = \case
Clamp -> clampPeeks
Wrap -> wrapPeeks
Mirror -> mirrorPeeks
{-# INLINE peeksB #-}
wrapPeeks :: Shape f => (f Int -> f Int) -> Focused f a -> a
wrapPeeks f (Focused x (Delayed l ixF)) = ixF $! wrapIndex l (f x)
{-# INLINE wrapPeeks #-}
wrapIndex :: Shape f => Layout f -> f Int -> f Int
wrapIndex !l !x = liftI2 f l x where
f n i
| i < 0 = n + i
| i < n = i
| otherwise = i - n
{-# INLINE wrapIndex #-}
clampPeeks :: Shape f => (f Int -> f Int) -> Focused f a -> a
clampPeeks f (Focused x (Delayed l ixF)) = ixF $! clampIndex l (f x)
{-# INLINE clampPeeks #-}
clampIndex :: Shape f => Layout f -> f Int -> f Int
clampIndex !l !x = liftI2 f l x where
f n i
| i < 0 = 0
| i >= n = n - 1
| otherwise = i
{-# INLINE clampIndex #-}
mirrorPeeks :: Shape f => (f Int -> f Int) -> Focused f a -> a
mirrorPeeks f (Focused x (Delayed l ixF)) = ixF $! mirrorIndex l (f x)
{-# INLINE mirrorPeeks #-}
mirrorIndex :: Shape f => Layout f -> f Int -> f Int
mirrorIndex !l !x = liftI2 f l x where
f n i
| i < 0 = - i
| i < n = i
| otherwise = i - n
{-# INLINE mirrorIndex #-}