module Foundation.Array.Chunked.Unboxed
( ChunkedUArray
) where
import Data.Typeable
import Control.Arrow ((***))
import Basement.BoxedArray (Array)
import qualified Basement.BoxedArray as A
import Basement.Exception
import Basement.UArray (UArray)
import qualified Basement.UArray as U
import Basement.Compat.Bifunctor
import Basement.Compat.Semigroup
import Basement.Compat.Base
import Basement.Types.OffsetSize
import Basement.PrimType
import GHC.ST
import Foundation.Numerical
import Foundation.Primitive
import qualified Foundation.Collection as C
newtype ChunkedUArray ty = ChunkedUArray (Array (UArray ty))
deriving (Show, Ord, Typeable)
instance PrimType ty => Eq (ChunkedUArray ty) where
(==) = equal
instance NormalForm (ChunkedUArray ty) where
toNormalForm (ChunkedUArray spine) = toNormalForm spine
instance Semigroup (ChunkedUArray a) where
(<>) = append
instance Monoid (ChunkedUArray a) where
mempty = empty
mappend = append
mconcat = concat
type instance C.Element (ChunkedUArray ty) = ty
instance PrimType ty => IsList (ChunkedUArray ty) where
type Item (ChunkedUArray ty) = ty
fromList = vFromList
toList = vToList
instance PrimType ty => C.Foldable (ChunkedUArray ty) where
foldl' = foldl'
foldr = foldr
instance PrimType ty => C.Collection (ChunkedUArray ty) where
null = null
length = length
elem = elem
minimum = minimum
maximum = maximum
all p (ChunkedUArray cua) = A.all (U.all p) cua
any p (ChunkedUArray cua) = A.any (U.any p) cua
instance PrimType ty => C.Sequential (ChunkedUArray ty) where
take = take
drop = drop
splitAt = splitAt
revTake = revTake
revDrop = revDrop
splitOn = splitOn
break = break
breakEnd = breakEnd
intersperse = intersperse
filter = filter
reverse = reverse
unsnoc = unsnoc
uncons = uncons
snoc = snoc
cons = cons
find = find
sortBy = sortBy
singleton = fromList . (:[])
replicate n = fromList . C.replicate n
instance PrimType ty => C.IndexedCollection (ChunkedUArray ty) where
(!) l n
| isOutOfBound n (length l) = Nothing
| otherwise = Just $ index l n
findIndex predicate c = loop 0
where
!len = length c
loop i
| i .==# len = Nothing
| otherwise =
if predicate (unsafeIndex c i) then Just i else Nothing
empty :: ChunkedUArray ty
empty = ChunkedUArray A.empty
append :: ChunkedUArray ty -> ChunkedUArray ty -> ChunkedUArray ty
append (ChunkedUArray a1) (ChunkedUArray a2) = ChunkedUArray (mappend a1 a2)
concat :: [ChunkedUArray ty] -> ChunkedUArray ty
concat x = ChunkedUArray (mconcat $ fmap (\(ChunkedUArray spine) -> spine) x)
vFromList :: PrimType ty => [ty] -> ChunkedUArray ty
vFromList l = ChunkedUArray $ A.singleton $ fromList l
vToList :: PrimType ty => ChunkedUArray ty -> [ty]
vToList (ChunkedUArray a) = mconcat $ toList $ toList <$> a
null :: PrimType ty => ChunkedUArray ty -> Bool
null (ChunkedUArray array) =
C.null array || allNulls 0
where
!len = A.length array
allNulls !idx
| idx .==# len = True
| otherwise = C.null (array `A.unsafeIndex` idx) && allNulls (idx + 1)
length :: PrimType ty => ChunkedUArray ty -> CountOf ty
length (ChunkedUArray array) = C.foldl' (\acc l -> acc + U.length l) 0 array
elem :: PrimType ty => ty -> ChunkedUArray ty -> Bool
elem el (ChunkedUArray array) = loop 0
where
!len = A.length array
loop i
| i .==# len = False
| otherwise =
case C.elem el (A.unsafeIndex array i) of
True -> True
False -> loop (i+1)
foldl' :: PrimType ty => (a -> ty -> a) -> a -> ChunkedUArray ty -> a
foldl' f initialAcc (ChunkedUArray cua) = A.foldl' (U.foldl' f) initialAcc cua
foldr :: PrimType ty => (ty -> a -> a) -> a -> ChunkedUArray ty -> a
foldr f initialAcc (ChunkedUArray cua) = A.foldr (flip $ U.foldr f) initialAcc cua
minimum :: (Ord ty, PrimType ty) => C.NonEmpty (ChunkedUArray ty) -> ty
minimum cua = foldl' min (unsafeIndex cua' 0) (drop 1 cua')
where
cua' = C.getNonEmpty cua
maximum :: (Ord ty, PrimType ty) => C.NonEmpty (ChunkedUArray ty) -> ty
maximum cua = foldl' max (unsafeIndex cua' 0) (drop 1 cua')
where
cua' = C.getNonEmpty cua
equal :: PrimType ty => ChunkedUArray ty -> ChunkedUArray ty -> Bool
equal ca1 ca2 =
len1 == len2 && go 0
where
len1 = length ca1
len2 = length ca2
go !x
| x .==# len1 = True
| otherwise = (ca1 `unsafeIndex` x == ca2 `unsafeIndex` x) && go (x + 1)
findPos :: PrimType ty => Offset ty -> ChunkedUArray ty -> Maybe (Offset (UArray ty), Offset ty)
findPos absOfs (ChunkedUArray array)
| A.null array = Nothing
| otherwise = loop absOfs 0
where
!len = A.length array
loop relOfs outerI
| outerI .==# len = Nothing
| relOfs == 0 = Just (outerI, 0)
| otherwise =
let !innera = A.unsafeIndex array outerI
!innerLen = U.length innera
in case removeArraySize relOfs innerLen of
Nothing -> Just (outerI, relOfs)
Just relOfs' -> loop relOfs' (outerI + 1)
splitChunk :: Offset (UArray ty) -> ChunkedUArray ty -> (ChunkedUArray ty, ChunkedUArray ty)
splitChunk ofs (ChunkedUArray c) = (ChunkedUArray *** ChunkedUArray) $ A.splitAt (offsetAsSize ofs) c
take :: PrimType ty => CountOf ty -> ChunkedUArray ty -> ChunkedUArray ty
take n c@(ChunkedUArray spine)
| n <= 0 = empty
| otherwise =
case findPos (sizeAsOffset n) c of
Nothing -> c
Just (ofs, 0) -> ChunkedUArray (A.take (offsetAsSize ofs) spine)
Just (ofs, r) ->
let uarr = A.unsafeIndex spine ofs
in ChunkedUArray (A.take (offsetAsSize ofs) spine `A.snoc` U.take (offsetAsSize r) uarr)
drop :: PrimType ty => CountOf ty -> ChunkedUArray ty -> ChunkedUArray ty
drop n c@(ChunkedUArray spine)
| n <= 0 = c
| otherwise =
case findPos (sizeAsOffset n) c of
Nothing -> empty
Just (ofs, 0) -> ChunkedUArray (A.drop (offsetAsSize ofs) spine)
Just (ofs, r) ->
let uarr = A.unsafeIndex spine ofs
in ChunkedUArray (U.drop (offsetAsSize r) uarr `A.cons` A.drop (offsetAsSize ofs+1) spine)
splitAt :: PrimType ty => CountOf ty -> ChunkedUArray ty -> (ChunkedUArray ty, ChunkedUArray ty)
splitAt n c@(ChunkedUArray spine)
| n <= 0 = (empty, c)
| otherwise =
case findPos (sizeAsOffset n) c of
Nothing -> (c, empty)
Just (ofs, 0) -> splitChunk ofs c
Just (ofs, offsetAsSize -> r) ->
let uarr = A.unsafeIndex spine ofs
in ( ChunkedUArray (A.take (offsetAsSize ofs) spine `A.snoc` U.take r uarr)
, ChunkedUArray (U.drop r uarr `A.cons` A.drop (offsetAsSize ofs+1) spine)
)
revTake :: PrimType ty => CountOf ty -> ChunkedUArray ty -> ChunkedUArray ty
revTake n c = case length c n of
Nothing -> c
Just elems -> drop elems c
revDrop :: PrimType ty => CountOf ty -> ChunkedUArray ty -> ChunkedUArray ty
revDrop n c = case length c n of
Nothing -> empty
Just keepElems -> take keepElems c
splitOn :: PrimType ty => (ty -> Bool) -> ChunkedUArray ty -> [ChunkedUArray ty]
splitOn p = fmap fromList . C.splitOn p . toList
break :: PrimType ty => (ty -> Bool) -> ChunkedUArray ty -> (ChunkedUArray ty, ChunkedUArray ty)
break p = bimap fromList fromList . C.break p . toList
breakEnd :: PrimType ty => (ty -> Bool) -> ChunkedUArray ty -> (ChunkedUArray ty, ChunkedUArray ty)
breakEnd p = bimap fromList fromList . C.breakEnd p . toList
intersperse :: PrimType ty => ty -> ChunkedUArray ty -> ChunkedUArray ty
intersperse el = fromList . C.intersperse el . toList
reverse :: PrimType ty => ChunkedUArray ty -> ChunkedUArray ty
reverse = fromList . C.reverse . toList
filter :: PrimType ty => (ty -> Bool) -> ChunkedUArray ty -> ChunkedUArray ty
filter p = fromList . C.filter p . toList
unsnoc :: PrimType ty => ChunkedUArray ty -> Maybe (ChunkedUArray ty, ty)
unsnoc v = first fromList <$> (C.unsnoc $ toList v)
uncons :: PrimType ty => ChunkedUArray ty -> Maybe (ty, ChunkedUArray ty)
uncons v = second fromList <$> (C.uncons $ toList v)
cons :: PrimType ty => ty -> ChunkedUArray ty -> ChunkedUArray ty
cons el (ChunkedUArray inner) = ChunkedUArray $ runST $ do
let newLen = C.length inner + 1
newArray <- A.new newLen
let single = fromList [el]
A.unsafeWrite newArray 0 single
A.unsafeCopyAtRO newArray (Offset 1) inner (Offset 0) (C.length inner)
A.unsafeFreeze newArray
snoc :: PrimType ty => ChunkedUArray ty -> ty -> ChunkedUArray ty
snoc (ChunkedUArray spine) el = ChunkedUArray $ runST $ do
newArray <- A.new (A.length spine + 1)
let single = U.singleton el
A.unsafeCopyAtRO newArray (Offset 0) spine (Offset 0) (C.length spine)
A.unsafeWrite newArray (sizeAsOffset $ A.length spine) single
A.unsafeFreeze newArray
find :: PrimType ty => (ty -> Bool) -> ChunkedUArray ty -> Maybe ty
find fn v = loop 0
where
len = length v
loop !idx
| idx .==# len = Nothing
| otherwise =
let currentElem = v `unsafeIndex` idx
in case fn currentElem of
True -> Just currentElem
False -> loop (idx + 1)
sortBy :: PrimType ty => (ty -> ty -> Ordering) -> ChunkedUArray ty -> ChunkedUArray ty
sortBy p = fromList . C.sortBy p . toList
index :: PrimType ty => ChunkedUArray ty -> Offset ty -> ty
index array n
| isOutOfBound n len = outOfBound OOB_Index n len
| otherwise = unsafeIndex array n
where len = length array
unsafeIndex :: PrimType ty => ChunkedUArray ty -> Offset ty -> ty
unsafeIndex (ChunkedUArray array) idx = go (A.unsafeIndex array 0) 0 idx
where
go u globalIndex 0 = case C.null u of
True -> go (A.unsafeIndex array (globalIndex + 1)) (globalIndex + 1) 0
False -> U.unsafeIndex u 0
go u !globalIndex !i
| C.null u = go (A.unsafeIndex array (globalIndex + 1)) (globalIndex + 1) i
| otherwise =
case removeArraySize i (U.length u) of
Just i' -> go (A.unsafeIndex array (globalIndex + 1)) (globalIndex + 1) i'
Nothing -> U.unsafeIndex u i
removeArraySize :: Offset ty -> CountOf ty -> Maybe (Offset ty)
removeArraySize (Offset ty) (CountOf s)
| ty >= s = Just (Offset (ty s))
| otherwise = Nothing