{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE UnboxedTuples #-}
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE RoleAnnotations #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
module Data.Primitive.Array.Maybe
( MaybeArray
, MutableMaybeArray
, indexMaybeArray
, newMaybeArray
, readMaybeArray
, writeMaybeArray
, sequenceMaybeArray
, unsafeFreezeMaybeArray
, thawMaybeArray
, maybeArrayFromList
, maybeArrayFromListN
, sizeofMaybeArray
) where
import Prelude hiding (zipWith)
import Control.Applicative (Alternative(..), liftA2)
import Control.Monad (when, MonadPlus(..))
import Control.Monad.Fail (MonadFail(..))
import Control.Monad.ST (ST, runST)
import Control.Monad.Zip (MonadZip(..))
import Control.Monad.Primitive
import Data.Primitive.Array
import Data.Primitive.UnliftedArray (PrimUnlifted)
import Data.Foldable hiding (toList)
import Data.Functor.Classes
import qualified Data.Foldable as Foldable
import Data.Maybe (maybe)
import Data.Data
(Data(..), DataType, mkDataType, Constr, mkConstr, Fixity(..), constrIndex)
import Data.Primitive.Maybe.Internal
import GHC.Exts (Any,reallyUnsafePtrEquality#, Int(..), IsList(..), MutableArray#)
import Text.ParserCombinators.ReadP
import Unsafe.Coerce (unsafeCoerce)
newtype MaybeArray a = MaybeArray (Array Any)
deriving (PrimUnlifted)
newtype MutableMaybeArray s a = MutableMaybeArray (MutableArray s Any)
deriving (PrimUnlifted)
type role MaybeArray representational
type role MutableMaybeArray nominal representational
instance Functor MaybeArray where
fmap :: forall a b. (a -> b) -> MaybeArray a -> MaybeArray b
fmap f (MaybeArray arr) = MaybeArray $
createArray (sizeofArray arr) (error "impossible") $ \mb ->
let go i
| i == (sizeofArray arr) = return ()
| otherwise = do
x <- indexArrayM arr i
case unsafeToMaybe x of
Nothing -> pure ()
Just val -> writeArray mb i (toAny (f val))
go (i + 1)
in go 0
e <$ (MaybeArray a) = MaybeArray $ createArray (sizeofArray a) (toAny e) (\ !_ -> pure ())
instance Applicative MaybeArray where
pure :: a -> MaybeArray a
pure a = MaybeArray $ runArray $ newArray 1 (toAny a)
(<*>) :: MaybeArray (a -> b) -> MaybeArray a -> MaybeArray b
abm@(MaybeArray ab) <*> am@(MaybeArray a) = MaybeArray $ createArray (szab * sza) nothingSurrogate $ \mb ->
let go1 i = when (i < szab) $ do
case indexMaybeArray abm i of
Nothing -> pure ()
Just f -> go2 (i * sza) f 0
go1 (i + 1)
go2 off f j = when (j < sza) $ do
case indexMaybeArray am j of
Nothing -> pure ()
Just v -> writeArray mb (off + j) (toAny (f v))
go2 off f (j + 1)
in go1 0
where szab = sizeofArray ab; sza = sizeofArray a
MaybeArray a *> MaybeArray b = MaybeArray $ createArray (sza * szb) nothingSurrogate $ \mb ->
let go i | i < sza = copyArray mb (i * szb) b 0 szb
| otherwise = return ()
in go 0
where sza = sizeofArray a; szb = sizeofArray b
MaybeArray a <* MaybeArray b = MaybeArray $ createArray (sza*szb) nothingSurrogate $ \ma ->
let fill off i e | i < szb = writeArray ma (off+i) e >> fill off (i+1) e
| otherwise = return ()
go i | i < sza
= do x <- indexArrayM a i
fill (i*szb) 0 x >> go (i+1)
| otherwise = return ()
in go 0
where sza = sizeofArray a ; szb = sizeofArray b
instance Traversable MaybeArray where
traverse = traverseArray
traverseArray :: Applicative f
=> (a -> f b)
-> MaybeArray a
-> f (MaybeArray b)
traverseArray f = \ !(MaybeArray ary) ->
let
!len = sizeofArray ary
go !ix
| ix == len = pure $ STA $ \mary -> unsafeFreezeArray (MutableArray mary)
| otherwise = let x = indexArray ary ix
in case unsafeToMaybe x of
Nothing -> go (ix + 1)
Just v -> liftA2 (\b (STA m) -> STA $ \mary ->
writeArray (MutableArray mary) ix (toAny b) >> m mary)
(f v) (go (ix + 1))
in if len == 0
then pure mempty
else MaybeArray <$> runSTA len <$> go 0
newtype STA a = STA { _runSTA :: forall s. MutableArray# s a -> ST s (Array a) }
runSTA :: Int -> STA a -> Array a
runSTA !sz = \(STA m) -> runST $ newArray_ sz >>= \ar -> m (marray# ar)
newArray_ :: Int -> ST s (MutableArray s a)
newArray_ !n = newArray n badTraverseValue
badTraverseValue :: a
badTraverseValue = error "traverse: bad indexing"
instance Alternative MaybeArray where
empty = mempty
(<|>) = (<>)
some a | sizeofMaybeArray a == 0 = mempty
| otherwise = error "some: infinite arrays are not well defined"
many a | sizeofMaybeArray a == 0 = pure []
| otherwise = error "many: infinite arrays are not well defined"
instance MonadPlus MaybeArray where
mzero = empty
mplus = (<|>)
instance MonadFail MaybeArray where
fail _ = empty
zipWith :: (a -> b -> c) -> MaybeArray a -> MaybeArray b -> MaybeArray c
zipWith f (MaybeArray aa) (MaybeArray ab) = MaybeArray $
createArray mn nothingSurrogate $ \mc ->
let go i
| i < mn = do
x <- indexArrayM aa i
y <- indexArrayM ab i
let x' = unsafeToMaybe x
y' = unsafeToMaybe y
case x' of
Nothing -> go (i + 1)
Just va -> case y' of
Nothing -> go (i + 1)
Just vb -> writeArray mc i (toAny $ f va vb) >> go (i + 1)
| otherwise = return ()
in go 0
where mn = sizeofArray aa `min` sizeofArray ab
instance MonadZip MaybeArray where
mzip aa ab = zipWith (,) aa ab
mzipWith f aa ab = zipWith f aa ab
munzip :: forall a b. MaybeArray (a, b) -> (MaybeArray a, MaybeArray b)
munzip (MaybeArray aab) = runST $ do
let sz = sizeofArray aab
ma_ <- newArray sz nothingSurrogate :: ST s (MutableArray s Any)
mb_ <- newArray sz nothingSurrogate :: ST s (MutableArray s Any)
let go :: forall s. Int -> MutableArray s Any -> MutableArray s Any -> ST s ()
go i ma mb = if i < sz
then do
tab <- indexArrayM aab i
let (a, b) = fromAny tab
a' = unsafeToMaybe a
b' = unsafeToMaybe b
maybe (pure ()) (writeArray ma i) a'
maybe (pure ()) (writeArray mb i) b'
go (i + 1) ma mb
else return ()
go 0 ma_ mb_
(ma1, ma2) <- (,) <$> unsafeFreezeArray ma_ <*> unsafeFreezeArray mb_
return (unsafeCoerce ma1, unsafeCoerce ma2) :: ST s (MaybeArray a, MaybeArray b)
data ArrayStack a
= PushArray !(Array a) !(ArrayStack a)
| EmptyStack
instance Monad MaybeArray where
return = pure
(>>) = (*>)
(MaybeArray ary) >>= f = MaybeArray $ collect 0 EmptyStack (la - 1)
where
la = sizeofArray ary
collect sz stk i
| i < 0 = createArray sz nothingSurrogate $ fill 0 stk
| otherwise = let x = indexArray ary i
in case unsafeToMaybe x of
Nothing -> collect sz stk (i - 1)
Just v -> let (MaybeArray sb) = f v
lsb = sizeofArray sb
in if lsb == 0
then collect sz stk (i - 1)
else collect (sz + lsb) (PushArray sb stk) (i - 1)
fill _ EmptyStack _ = return ()
fill off (PushArray sb sbs) smb
| let lsb = sizeofArray sb
= copyArray smb off sb 0 lsb
*> fill (off + lsb) sbs smb
instance Foldable MaybeArray where
foldr f = \z !(MaybeArray ary) ->
let
!sz = sizeofArray ary
go i
| i == sz = z
| otherwise = let !x = indexArray ary i
in case unsafeToMaybe x of
Nothing -> z
Just val -> f val (go (i + 1))
in go 0
{-# INLINE foldr #-}
foldl f = \z !(MaybeArray ary) ->
let
go i
| i < 0 = z
| otherwise = let !x = indexArray ary i
in case unsafeToMaybe x of
Nothing -> z
Just val -> f (go (i - 1)) val
in go (sizeofArray ary - 1)
{-# INLINE foldl #-}
null (MaybeArray a) = sizeofArray a == 0
{-# INLINE null #-}
length (MaybeArray a) = sizeofArray a
{-# INLINE length #-}
sum = foldl' (+) 0
{-# INLINE sum #-}
product = foldl' (*) 1
{-# INLINE product #-}
instance Semigroup (MaybeArray a) where
(<>) :: MaybeArray a -> MaybeArray a -> MaybeArray a
MaybeArray a1 <> MaybeArray a2 = MaybeArray $
createArray (sza1 + sza2) nothingSurrogate $ \ma ->
copyArray ma 0 a1 0 sza1 >> copyArray ma sza1 a2 0 sza2
where
sza1 = sizeofArray a1; sza2 = sizeofArray a2
instance Monoid (MaybeArray a) where
mempty = MaybeArray emptyArray
mappend = (<>)
instance IsList (MaybeArray a) where
type Item (MaybeArray a) = a
fromListN = maybeArrayFromListN
fromList = maybeArrayFromList
toList = Foldable.toList
instance Eq a => Eq (MaybeArray a) where
sma1 == sma2 = maybeArrayLiftEq (==) sma1 sma2
instance Eq1 MaybeArray where
liftEq = maybeArrayLiftEq
instance Ord1 MaybeArray where
liftCompare = maybeArrayLiftCompare
maybeArrayLiftEq :: (a -> b -> Bool) -> MaybeArray a -> MaybeArray b -> Bool
maybeArrayLiftEq p (MaybeArray sa1) (MaybeArray sa2) = length sa1 == length sa2 && loop (length sa1 - 1)
where
loop i
| i < 0 = True
| otherwise = let x = unsafeToMaybe (indexArray sa1 i)
y = unsafeToMaybe (indexArray sa2 i)
in case x of
Nothing -> case y of
Nothing -> True && loop (i - 1)
_ -> False
Just x' -> case y of
Nothing -> False
Just y' -> p x' y' && loop (i - 1)
maybeArrayLiftCompare :: (a -> b -> Ordering) -> MaybeArray a -> MaybeArray b -> Ordering
maybeArrayLiftCompare elemCompare (MaybeArray a1) (MaybeArray a2) = loop 0
where
la1 = length a1
la2 = length a2
mn = la1 `min` la2
loop i
| i < mn = let x = unsafeToMaybe (indexArray a1 i)
y = unsafeToMaybe (indexArray a2 i)
in case x of
Nothing -> case y of
Nothing -> EQ `mappend` loop (i + 1)
_ -> LT
Just x' -> case y of
Nothing -> GT
Just y' -> elemCompare x' y' `mappend` loop (i + 1)
| otherwise = compare la1 la2
instance Ord a => Ord (MaybeArray a) where
compare sma1 sma2 = maybeArrayLiftCompare compare sma1 sma2
maybeArrayLiftShowsPrec :: (Int -> a -> ShowS) -> ([a] -> ShowS) -> Int -> MaybeArray a -> ShowS
maybeArrayLiftShowsPrec elemShowsPrec elemListShowsPrec p sa = showParen (p > 10) $
showString "fromListN " . shows (length sa) . showString " "
. listLiftShowsPrec elemShowsPrec elemListShowsPrec 11 (toList sa)
listLiftShowsPrec :: (Int -> a -> ShowS) -> ([a] -> ShowS) -> Int -> [a] -> ShowS
listLiftShowsPrec _ sl _ = sl
instance Show1 MaybeArray where
liftShowsPrec = maybeArrayLiftShowsPrec
instance Show a => Show (MaybeArray a) where
showsPrec p sa = maybeArrayLiftShowsPrec showsPrec showList p sa
maybeArrayLiftReadsPrec :: (Int -> ReadS a) -> ReadS [a] -> Int -> ReadS (MaybeArray a)
maybeArrayLiftReadsPrec _ listReadsPrec p = readParen (p > 10) . readP_to_S $ do
() <$ string "fromListN"
skipSpaces
n <- readS_to_P reads
skipSpaces
l <- readS_to_P listReadsPrec
return $ maybeArrayFromListN n l
instance Read1 MaybeArray where
liftReadsPrec = maybeArrayLiftReadsPrec
instance Read a => Read (MaybeArray a) where
readsPrec = maybeArrayLiftReadsPrec readsPrec readList
maybeArrayDataType :: DataType
maybeArrayDataType = mkDataType "Data.Primitive.Array.Maybe.MaybeArray" [fromListConstr]
fromListConstr :: Constr
fromListConstr = mkConstr maybeArrayDataType "fromList" [] Prefix
instance Data a => Data (MaybeArray a) where
toConstr _ = fromListConstr
dataTypeOf _ = maybeArrayDataType
gunfold k z c = case constrIndex c of
1 -> k (z fromList)
_ -> error "gunfold"
gfoldl f z m = z fromList `f` toList m
newMaybeArray :: PrimMonad m => Int -> Maybe a -> m (MutableMaybeArray (PrimState m) a)
{-# INLINE newMaybeArray #-}
newMaybeArray i ma = case ma of
Just a -> do
x <- newArray i (unsafeCoerce a)
return (MutableMaybeArray x)
Nothing -> do
x <- newArray i nothingSurrogate
return (MutableMaybeArray x)
indexMaybeArray :: MaybeArray a -> Int -> Maybe a
{-# INLINE indexMaybeArray #-}
indexMaybeArray (MaybeArray a) ix =
let (# v #) = indexArray## a ix
in unsafeToMaybe v
readMaybeArray :: PrimMonad m => MutableMaybeArray (PrimState m) a -> Int -> m (Maybe a)
{-# INLINE readMaybeArray #-}
readMaybeArray (MutableMaybeArray m) ix = do
a <- readArray m ix
return (unsafeToMaybe a)
writeMaybeArray :: PrimMonad m => MutableMaybeArray (PrimState m) a -> Int -> Maybe a -> m ()
{-# INLINE writeMaybeArray #-}
writeMaybeArray (MutableMaybeArray marr) ix ma = case ma of
Just a -> writeArray marr ix (unsafeCoerce a)
Nothing -> writeArray marr ix nothingSurrogate
sequenceMaybeArray :: MaybeArray a -> Maybe (Array a)
sequenceMaybeArray m@(MaybeArray a) =
if hasNothing m then Nothing else Just (unsafeCoerce a)
hasNothing :: MaybeArray a -> Bool
hasNothing (MaybeArray a) = go 0 where
go !ix = if ix < sizeofArray a
then
let (# v #) = indexArray## a ix
in case reallyUnsafePtrEquality# v nothingSurrogate of
0# -> go (ix + 1)
_ -> True
else False
unsafeFreezeMaybeArray :: PrimMonad m => MutableMaybeArray (PrimState m) a -> m (MaybeArray a)
{-# INLINE unsafeFreezeMaybeArray #-}
unsafeFreezeMaybeArray (MutableMaybeArray ma) = do
a <- unsafeFreezeArray ma
return (MaybeArray a)
thawMaybeArray
:: PrimMonad m
=> MaybeArray a
-> Int
-> Int
-> m (MutableMaybeArray (PrimState m) a)
thawMaybeArray (MaybeArray a) off len =
fmap MutableMaybeArray (thawArray a off len)
maybeArrayFromListN :: Int -> [a] -> MaybeArray a
maybeArrayFromListN n l = MaybeArray $
createArray n (error "uninitialized element") $ \sma ->
let go !ix [] = if ix == n
then return ()
else error "list length less than specified size"
go !ix (x : xs) = if ix < n
then do
writeArray sma ix (toAny x)
go (ix+1) xs
else error "list length greater than specified size"
in go 0 l
maybeArrayFromList :: [a] -> MaybeArray a
maybeArrayFromList l = maybeArrayFromListN (length l) l
sizeofMaybeArray :: MaybeArray a -> Int
sizeofMaybeArray (MaybeArray a) = sizeofArray a
{-# INLINE sizeofMaybeArray #-}