packages feed

massiv-1.0.5.0: src/Data/Massiv/Array/Manifest/Boxed.hs

{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}

-- |
-- Module      : Data.Massiv.Array.Manifest.Boxed
-- Copyright   : (c) Alexey Kuleshevich 2018-2022
-- License     : BSD3
-- Maintainer  : Alexey Kuleshevich <lehins@yandex.ru>
-- Stability   : experimental
-- Portability : non-portable
module Data.Massiv.Array.Manifest.Boxed (
  B (..),
  BL (..),
  BN (..),
  N,
  pattern N,
  Array (..),
  MArray (..),
  wrapLazyArray,
  unwrapLazyArray,
  unwrapNormalForm,
  evalNormalForm,
  unwrapArray,
  evalArray,
  toLazyArray,
  evalLazyArray,
  forceLazyArray,
  unwrapMutableArray,
  unwrapMutableLazyArray,
  evalMutableArray,
  unwrapNormalFormArray,
  evalNormalFormArray,
  unwrapNormalFormMutableArray,
  evalNormalFormMutableArray,
  toBoxedVector,
  toBoxedMVector,
  fromBoxedVector,
  fromBoxedMVector,
  evalBoxedVector,
  evalBoxedMVector,
  evalNormalBoxedVector,
  evalNormalBoxedMVector,
  coerceBoxedArray,
  coerceNormalBoxedArray,
  seqArray,
  deepseqArray,
) where

import Control.DeepSeq (NFData (..), deepseq)
import Control.Exception
import Control.Monad ((>=>))
import Control.Monad.Primitive
import qualified Data.Foldable as F (Foldable (..))
import Data.Massiv.Array.Delayed.Pull (D)
import Data.Massiv.Array.Delayed.Push (DL)
import Data.Massiv.Array.Delayed.Stream (DS)
import Data.Massiv.Array.Manifest.Internal (compute, computeAs)
import Data.Massiv.Array.Manifest.List as L
import Data.Massiv.Array.Mutable
import Data.Massiv.Array.Ops.Fold
import Data.Massiv.Array.Ops.Fold.Internal
import Data.Massiv.Array.Ops.Map (traverseA)
import Data.Massiv.Core.Common
import Data.Massiv.Core.List
import Data.Massiv.Core.Operations
import Data.Massiv.Vector.Stream as S (isteps, steps)
import qualified Data.Primitive.Array as A
import qualified Data.Vector as VB
import qualified Data.Vector.Mutable as MVB
import GHC.Exts as GHC
import System.IO.Unsafe (unsafePerformIO)
import Prelude hiding (mapM, replicate)
#if !MIN_VERSION_vector(0,13,0)
import Unsafe.Coerce (unsafeCoerce)
#endif

#include "massiv.h"

----------------
-- Boxed Lazy --
----------------

-- | Array representation for Boxed elements. This data structure is lazy with
-- respect to its elements.
--
-- ====__Example__
--
-- Memoized version of a factorial that relies on laziness. Note that
-- computing memoized factorial of a million would likely overflow memory.
--
-- >>> import Data.Massiv.Array as A
-- >>> :{
-- mkMemoFactorial :: Int -> (Int -> Integer)
-- mkMemoFactorial n =
--   let arr = makeVectorR BL Seq (Sz1 n) fact
--       fact i | i == 0 = 1
--              | otherwise = (arr ! (i - 1)) * toInteger i
--   in (arr !)
-- :}
--
-- >>> let fact = mkMemoFactorial 1000001
-- >>> fact 50
-- 30414093201713378043612608166064768844377641568960512000000000000
-- >>> length $ show $ fact 5000
-- 16326
data BL = BL deriving (Show)

data instance Array BL ix e = BLArray
  { blComp :: !Comp
  , blSize :: !(Sz ix)
  , blOffset :: {-# UNPACK #-} !Int
  , blData :: {-# UNPACK #-} !(A.Array e)
  }

data instance MArray s BL ix e
  = MBLArray !(Sz ix) {-# UNPACK #-} !Int {-# UNPACK #-} !(A.MutableArray s e)

instance (Ragged L ix e, Show e) => Show (Array BL ix e) where
  showsPrec = showsArrayPrec id
  showList = showArrayList

instance (Ragged L ix e, Show e) => Show (Array DL ix e) where
  showsPrec = showsArrayPrec (computeAs BL)
  showList = showArrayList

instance Show e => Show (Array DS Ix1 e) where
  showsPrec = showsArrayPrec (computeAs BL)
  showList = showArrayList

instance (Index ix, NFData e) => NFData (Array BL ix e) where
  rnf = (`deepseqArray` ())
  {-# INLINE rnf #-}

instance (Index ix, Eq e) => Eq (Array BL ix e) where
  (==) = eqArrays (==)
  {-# INLINE (==) #-}

instance (Index ix, Ord e) => Ord (Array BL ix e) where
  compare = compareArrays compare
  {-# INLINE compare #-}

instance Strategy BL where
  setComp c arr = arr{blComp = c}
  {-# INLINE setComp #-}
  getComp = blComp
  {-# INLINE getComp #-}
  repr = BL

instance Source BL e where
  unsafeLinearIndex (BLArray _ _sz o a) i =
    indexAssert "BL.unsafeLinearIndex" (SafeSz . A.sizeofArray) A.indexArray a (i + o)
  {-# INLINE unsafeLinearIndex #-}

  unsafeOuterSlice (BLArray c _ o a) szL i = BLArray c szL (i * totalElem szL + o) a
  {-# INLINE unsafeOuterSlice #-}

  unsafeLinearSlice i k (BLArray c _ o a) = BLArray c k (o + i) a
  {-# INLINE unsafeLinearSlice #-}

instance Manifest BL e where
  unsafeLinearIndexM (BLArray _ _sz o a) i =
    indexAssert "BL.unsafeLinearIndexM" (SafeSz . A.sizeofArray) A.indexArray a (i + o)
  {-# INLINE unsafeLinearIndexM #-}

  sizeOfMArray (MBLArray sz _ _) = sz
  {-# INLINE sizeOfMArray #-}

  unsafeResizeMArray sz (MBLArray _ off marr) = MBLArray sz off marr
  {-# INLINE unsafeResizeMArray #-}

  unsafeLinearSliceMArray i k (MBLArray _ o a) = MBLArray k (i + o) a
  {-# INLINE unsafeLinearSliceMArray #-}

  unsafeThaw (BLArray _ sz o a) = MBLArray sz o <$> A.unsafeThawArray a
  {-# INLINE unsafeThaw #-}

  unsafeFreeze comp (MBLArray sz o ma) = BLArray comp sz o <$> A.unsafeFreezeArray ma
  {-# INLINE unsafeFreeze #-}

  unsafeNew sz = MBLArray sz 0 <$> A.newArray (totalElem sz) uninitialized
  {-# INLINE unsafeNew #-}

  initialize _ = return ()
  {-# INLINE initialize #-}

  newMArray sz e = MBLArray sz 0 <$> A.newArray (totalElem sz) e
  {-# INLINE newMArray #-}

  unsafeLinearRead (MBLArray _ o ma) i =
    indexAssert "B.unsafeLinearRead" (SafeSz . A.sizeofMutableArray) A.readArray ma (i + o)
  {-# INLINE unsafeLinearRead #-}

  unsafeLinearWrite (MBLArray _sz o ma) i =
    indexAssert "B.unsafeLinearWrite" (SafeSz . A.sizeofMutableArray) A.writeArray ma (i + o)
  {-# INLINE unsafeLinearWrite #-}

instance Size BL where
  size = blSize
  {-# INLINE size #-}
  unsafeResize !sz !arr = arr{blSize = sz}
  {-# INLINE unsafeResize #-}

instance Index ix => Shape BL ix where
  maxLinearSize = Just . SafeSz . elemsCount
  {-# INLINE maxLinearSize #-}

instance Index ix => Load BL ix e where
  makeArray comp sz f = compute (makeArray comp sz f :: Array D ix e)
  {-# INLINE makeArray #-}

  makeArrayLinear !comp !sz f = unsafePerformIO $ generateArrayLinear comp sz (pure . f)
  {-# INLINE makeArrayLinear #-}

  replicate comp sz e = runST (newMArray sz e >>= unsafeFreeze comp)
  {-# INLINE replicate #-}

  iterArrayLinearST_ !scheduler !arr =
    splitLinearlyWith_ scheduler (elemsCount arr) (unsafeLinearIndex arr)
  {-# INLINE iterArrayLinearST_ #-}

instance Index ix => StrideLoad BL ix e

instance Index ix => Stream BL ix e where
  toStream = S.steps
  {-# INLINE toStream #-}
  toStreamIx = S.isteps
  {-# INLINE toStreamIx #-}

-- | Row-major sequential folding over a Boxed array.
instance Index ix => Foldable (Array BL ix) where
  fold = fold
  {-# INLINE fold #-}
  foldMap = foldMono
  {-# INLINE foldMap #-}
  foldl = lazyFoldlS
  {-# INLINE foldl #-}
  foldl' = foldlS
  {-# INLINE foldl' #-}
  foldr = foldrFB
  {-# INLINE foldr #-}
  foldr' = foldrS
  {-# INLINE foldr' #-}
  null (BLArray _ sz _ _) = totalElem sz == 0
  {-# INLINE null #-}
  length = totalElem . size
  {-# INLINE length #-}
  toList arr = build (\c n -> foldrFB c n arr)
  {-# INLINE toList #-}

instance Index ix => Functor (Array BL ix) where
  fmap f arr = makeArrayLinear (blComp arr) (blSize arr) (f . unsafeLinearIndex arr)
  {-# INLINE fmap #-}
  (<$) e arr = replicate (getComp arr) (size arr) e
  {-# INLINE (<$) #-}

instance Index ix => Traversable (Array BL ix) where
  traverse = traverseA
  {-# INLINE traverse #-}

instance (IsList (Array L ix e), Ragged L ix e) => IsList (Array BL ix e) where
  type Item (Array BL ix e) = Item (Array L ix e)
  fromList = L.fromLists' Seq
  {-# INLINE fromList #-}
  toList = GHC.toList . toListArray
  {-# INLINE toList #-}

instance Num e => FoldNumeric BL e where
  unsafeDotProduct = defaultUnsafeDotProduct
  {-# INLINE unsafeDotProduct #-}
  powerSumArray = defaultPowerSumArray
  {-# INLINE powerSumArray #-}
  foldArray = defaultFoldArray
  {-# INLINE foldArray #-}

instance Num e => Numeric BL e where
  unsafeLiftArray = defaultUnsafeLiftArray
  {-# INLINE unsafeLiftArray #-}
  unsafeLiftArray2 = defaultUnsafeLiftArray2
  {-# INLINE unsafeLiftArray2 #-}

------------------
-- Boxed Strict --
------------------

-- | Array representation for Boxed elements. Its elements are strict to Weak
-- Head Normal Form (WHNF) only.
data B = B deriving (Show)

newtype instance Array B ix e = BArray (Array BL ix e)

newtype instance MArray s B ix e = MBArray (MArray s BL ix e)

instance (Ragged L ix e, Show e) => Show (Array B ix e) where
  showsPrec = showsArrayPrec id
  showList = showArrayList

instance (Index ix, NFData e) => NFData (Array B ix e) where
  rnf = (`deepseqArray` ()) . coerce
  {-# INLINE rnf #-}

instance (Index ix, Eq e) => Eq (Array B ix e) where
  (==) = eqArrays (==)
  {-# INLINE (==) #-}

instance (Index ix, Ord e) => Ord (Array B ix e) where
  compare = compareArrays compare
  {-# INLINE compare #-}

instance Source B e where
  unsafeLinearIndex arr = unsafeLinearIndex (toLazyArray arr)
  {-# INLINE unsafeLinearIndex #-}

  unsafeLinearSlice i k arr = coerce (unsafeLinearSlice i k (toLazyArray arr))
  {-# INLINE unsafeLinearSlice #-}

  unsafeOuterSlice arr i = coerce (unsafeOuterSlice (toLazyArray arr) i)
  {-# INLINE unsafeOuterSlice #-}

instance Strategy B where
  getComp = blComp . coerce
  {-# INLINE getComp #-}
  setComp c arr = coerceBoxedArray (coerce arr){blComp = c}
  {-# INLINE setComp #-}
  repr = B

instance Index ix => Shape B ix where
  maxLinearSize = Just . SafeSz . elemsCount
  {-# INLINE maxLinearSize #-}

instance Size B where
  size = blSize . coerce
  {-# INLINE size #-}
  unsafeResize sz = coerce (\arr -> arr{blSize = sz})
  {-# INLINE unsafeResize #-}

instance Manifest B e where
  unsafeLinearIndexM = coerce unsafeLinearIndexM
  {-# INLINE unsafeLinearIndexM #-}

  sizeOfMArray = sizeOfMArray . coerce
  {-# INLINE sizeOfMArray #-}

  unsafeResizeMArray sz = MBArray . unsafeResizeMArray sz . coerce
  {-# INLINE unsafeResizeMArray #-}

  unsafeLinearSliceMArray i k = MBArray . unsafeLinearSliceMArray i k . coerce
  {-# INLINE unsafeLinearSliceMArray #-}

  unsafeThaw arr = MBArray <$> unsafeThaw (coerce arr)
  {-# INLINE unsafeThaw #-}

  unsafeFreeze comp marr = BArray <$> unsafeFreeze comp (coerce marr)
  {-# INLINE unsafeFreeze #-}

  unsafeNew sz = MBArray <$> unsafeNew sz
  {-# INLINE unsafeNew #-}

  initialize _ = return ()
  {-# INLINE initialize #-}

  newMArray sz !e = MBArray <$> newMArray sz e
  {-# INLINE newMArray #-}

  unsafeLinearRead ma = unsafeLinearRead (coerce ma)
  {-# INLINE unsafeLinearRead #-}

  unsafeLinearWrite ma i e = e `seq` unsafeLinearWrite (coerce ma) i e
  {-# INLINE unsafeLinearWrite #-}

instance Index ix => Load B ix e where
  makeArray comp sz f = compute (makeArray comp sz f :: Array D ix e)
  {-# INLINE makeArray #-}

  makeArrayLinear !comp !sz f = unsafePerformIO $ generateArrayLinear comp sz (pure . f)
  {-# INLINE makeArrayLinear #-}

  replicate comp sz e = runST (newMArray sz e >>= unsafeFreeze comp)
  {-# INLINE replicate #-}

  iterArrayLinearST_ scheduler = coerce (iterArrayLinearST_ scheduler)
  {-# INLINE iterArrayLinearST_ #-}

instance Index ix => StrideLoad B ix e

instance Index ix => Stream B ix e where
  toStream = S.steps
  {-# INLINE toStream #-}
  toStreamIx = S.isteps
  {-# INLINE toStreamIx #-}

-- | Row-major sequential folding over a Boxed array.
instance Index ix => Foldable (Array B ix) where
  fold = fold
  {-# INLINE fold #-}
  foldMap = foldMono
  {-# INLINE foldMap #-}
  foldl = lazyFoldlS
  {-# INLINE foldl #-}
  foldl' = foldlS
  {-# INLINE foldl' #-}
  foldr = foldrFB
  {-# INLINE foldr #-}
  foldr' = foldrS
  {-# INLINE foldr' #-}
  null arr = totalElem (size arr) == 0
  {-# INLINE null #-}
  length = totalElem . size
  {-# INLINE length #-}
  toList arr = build (\c n -> foldrFB c n arr)
  {-# INLINE toList #-}

instance Index ix => Functor (Array B ix) where
  fmap f arr = makeArrayLinear (getComp arr) (size arr) (f . unsafeLinearIndex arr)
  {-# INLINE fmap #-}
  (<$) !e arr = replicate (getComp arr) (size arr) e
  {-# INLINE (<$) #-}

instance Index ix => Traversable (Array B ix) where
  traverse = traverseA
  {-# INLINE traverse #-}

instance (IsList (Array L ix e), Ragged L ix e) => IsList (Array B ix e) where
  type Item (Array B ix e) = Item (Array L ix e)
  fromList = L.fromLists' Seq
  {-# INLINE fromList #-}
  toList = GHC.toList . toListArray
  {-# INLINE toList #-}

instance Num e => FoldNumeric B e where
  unsafeDotProduct = defaultUnsafeDotProduct
  {-# INLINE unsafeDotProduct #-}
  powerSumArray = defaultPowerSumArray
  {-# INLINE powerSumArray #-}
  foldArray = defaultFoldArray
  {-# INLINE foldArray #-}

instance Num e => Numeric B e where
  unsafeLiftArray = defaultUnsafeLiftArray
  {-# INLINE unsafeLiftArray #-}
  unsafeLiftArray2 = defaultUnsafeLiftArray2
  {-# INLINE unsafeLiftArray2 #-}

-----------------------
-- Boxed Normal Form --
-----------------------

-- | Array representation for Boxed elements. Its elements are always in Normal
-- Form (NF), therefore `NFData` instance is required.
data BN = BN deriving (Show)

-- | Type and pattern `N` have been added for backwards compatibility and will be replaced
-- in the future in favor of `BN`.
--
-- /Deprecated/ - since 1.0.0
type N = BN

pattern N :: N
pattern N = BN

{-# COMPLETE N #-}

{-# DEPRECATED N "In favor of more consistently named `BN`" #-}

newtype instance Array BN ix e = BNArray (Array BL ix e)

newtype instance MArray s BN ix e = MBNArray (MArray s BL ix e)

instance (Ragged L ix e, Show e, NFData e) => Show (Array BN ix e) where
  showsPrec = showsArrayPrec coerce
  showList = showArrayList

-- | /O(1)/ - `BN` is already in normal form
instance NFData (Array BN ix e) where
  rnf = (`seq` ())
  {-# INLINE rnf #-}

instance (Index ix, NFData e, Eq e) => Eq (Array BN ix e) where
  (==) = eqArrays (==)
  {-# INLINE (==) #-}

instance (Index ix, NFData e, Ord e) => Ord (Array BN ix e) where
  compare = compareArrays compare
  {-# INLINE compare #-}

instance Strategy BN where
  setComp c = coerce (setComp c)
  {-# INLINE setComp #-}
  getComp = blComp . coerce
  {-# INLINE getComp #-}
  repr = BN

instance NFData e => Source BN e where
  unsafeLinearIndex (BNArray arr) = unsafeLinearIndex arr
  {-# INLINE unsafeLinearIndex #-}
  unsafeLinearSlice i k (BNArray a) = coerce (unsafeLinearSlice i k a)
  {-# INLINE unsafeLinearSlice #-}
  unsafeOuterSlice (BNArray a) i = coerce (unsafeOuterSlice a i)
  {-# INLINE unsafeOuterSlice #-}

instance Index ix => Shape BN ix where
  maxLinearSize = Just . SafeSz . elemsCount
  {-# INLINE maxLinearSize #-}

instance Size BN where
  size = blSize . coerce
  {-# INLINE size #-}

  unsafeResize !sz = coerce . unsafeResize sz . coerce
  {-# INLINE unsafeResize #-}

instance NFData e => Manifest BN e where
  unsafeLinearIndexM arr = unsafeLinearIndexM (coerce arr)
  {-# INLINE unsafeLinearIndexM #-}

  sizeOfMArray = sizeOfMArray . coerce
  {-# INLINE sizeOfMArray #-}

  unsafeResizeMArray sz = coerce . unsafeResizeMArray sz . coerce
  {-# INLINE unsafeResizeMArray #-}

  unsafeLinearSliceMArray i k = MBNArray . unsafeLinearSliceMArray i k . coerce
  {-# INLINE unsafeLinearSliceMArray #-}

  unsafeThaw arr = MBNArray <$> unsafeThaw (coerce arr)
  {-# INLINE unsafeThaw #-}

  unsafeFreeze comp marr = BNArray <$> unsafeFreeze comp (coerce marr)
  {-# INLINE unsafeFreeze #-}

  unsafeNew sz = MBNArray <$> unsafeNew sz
  {-# INLINE unsafeNew #-}

  initialize _ = return ()
  {-# INLINE initialize #-}

  newMArray sz e = e `deepseq` (MBNArray <$> newMArray sz e)
  {-# INLINE newMArray #-}

  unsafeLinearRead ma = unsafeLinearRead (coerce ma)
  {-# INLINE unsafeLinearRead #-}

  unsafeLinearWrite ma i e = e `deepseq` unsafeLinearWrite (coerce ma) i e
  {-# INLINE unsafeLinearWrite #-}

instance (Index ix, NFData e) => Load BN ix e where
  makeArray comp sz f = compute (makeArray comp sz f :: Array D ix e)
  {-# INLINE makeArray #-}
  makeArrayLinear !comp !sz f = unsafePerformIO $ generateArrayLinear comp sz (pure . f)
  {-# INLINE makeArrayLinear #-}
  replicate comp sz e = runST (newMArray sz e >>= unsafeFreeze comp)
  {-# INLINE replicate #-}
  iterArrayLinearST_ !scheduler !arr =
    splitLinearlyWith_ scheduler (elemsCount arr) (unsafeLinearIndex arr)
  {-# INLINE iterArrayLinearST_ #-}

instance (Index ix, NFData e) => StrideLoad BN ix e

instance (Index ix, NFData e) => Stream BN ix e where
  toStream = toStream . coerce
  {-# INLINE toStream #-}
  toStreamIx = toStreamIx . coerce
  {-# INLINE toStreamIx #-}

instance (NFData e, IsList (Array L ix e), Ragged L ix e) => IsList (Array BN ix e) where
  type Item (Array BN ix e) = Item (Array L ix e)
  fromList = L.fromLists' Seq
  {-# INLINE fromList #-}
  toList = GHC.toList . toListArray
  {-# INLINE toList #-}

instance (NFData e, Num e) => FoldNumeric BN e where
  unsafeDotProduct = defaultUnsafeDotProduct
  {-# INLINE unsafeDotProduct #-}
  powerSumArray = defaultPowerSumArray
  {-# INLINE powerSumArray #-}
  foldArray = defaultFoldArray
  {-# INLINE foldArray #-}

instance (NFData e, Num e) => Numeric BN e where
  unsafeLiftArray = defaultUnsafeLiftArray
  {-# INLINE unsafeLiftArray #-}
  unsafeLiftArray2 = defaultUnsafeLiftArray2
  {-# INLINE unsafeLiftArray2 #-}

----------------------
-- Helper functions --
----------------------

uninitialized :: a
uninitialized = throw Uninitialized

---------------------
-- WHNF conversion --
---------------------

-- | /O(1)/ - Unwrap boxed array. This will discard any possible slicing that has been
-- applied to the array.
--
-- @since 0.2.1
unwrapArray :: Array B ix e -> A.Array e
unwrapArray = blData . coerce
{-# INLINE unwrapArray #-}

-- | /O(n)/ - Wrap a boxed array and evaluate all elements to a WHNF.
--
-- @since 0.2.1
evalArray
  :: Comp
  -- ^ Computation strategy
  -> A.Array e
  -- ^ Lazy boxed array from @primitive@ package.
  -> Vector B e
evalArray comp a = evalLazyArray $ setComp comp $ wrapLazyArray a
{-# INLINE evalArray #-}

-- | /O(1)/ - Unwrap boxed array. This will discard any possible slicing that has been
-- applied to the array.
--
-- @since 0.6.0
unwrapLazyArray :: Array BL ix e -> A.Array e
unwrapLazyArray = blData
{-# INLINE unwrapLazyArray #-}

-- | /O(1)/ - Wrap a boxed array.
--
-- @since 0.6.0
wrapLazyArray :: A.Array e -> Vector BL e
wrapLazyArray a = BLArray Seq (SafeSz (A.sizeofArray a)) 0 a
{-# INLINE wrapLazyArray #-}

-- | /O(1)/ - Cast a strict boxed array into a lazy boxed array.
--
-- @since 0.6.0
toLazyArray :: Array B ix e -> Array BL ix e
toLazyArray = coerce
{-# INLINE toLazyArray #-}

-- | /O(n)/ - Evaluate all elements of a boxed lazy array to weak head normal form
--
-- @since 0.6.0
evalLazyArray :: Index ix => Array BL ix e -> Array B ix e
evalLazyArray arr = arr `seqArray` BArray arr
{-# INLINE evalLazyArray #-}

-- | /O(n)/ - Evaluate all elements of a boxed lazy array to normal form
--
-- @since 0.6.0
forceLazyArray :: (NFData e, Index ix) => Array BL ix e -> Array N ix e
forceLazyArray arr = arr `deepseqArray` BNArray arr
{-# INLINE forceLazyArray #-}

-- | /O(1)/ - Unwrap mutable boxed array. This will discard any possible slicing that has been
-- applied to the array.
--
-- @since 0.2.1
unwrapMutableArray :: MArray s B ix e -> A.MutableArray s e
unwrapMutableArray (MBArray (MBLArray _ _ marr)) = marr
{-# INLINE unwrapMutableArray #-}

-- | /O(1)/ - Unwrap mutable boxed lazy array. This will discard any possible slicing that has been
-- applied to the array.
--
-- @since 0.6.0
unwrapMutableLazyArray :: MArray s BL ix e -> A.MutableArray s e
unwrapMutableLazyArray (MBLArray _ _ marr) = marr
{-# INLINE unwrapMutableLazyArray #-}

-- | /O(n)/ - Wrap mutable boxed array and evaluate all elements to WHNF.
--
-- @since 0.2.1
evalMutableArray
  :: PrimMonad m
  => A.MutableArray (PrimState m) e
  -- ^ Mutable array that will get wrapped
  -> m (MArray (PrimState m) B Ix1 e)
evalMutableArray = fmap MBArray . fromMutableArraySeq seq
{-# INLINE evalMutableArray #-}

-------------------
-- NF conversion --
-------------------

-- | /O(1)/ - Unwrap a fully evaluated boxed array. This will discard any possible slicing
-- that has been applied to the array.
--
-- @since 0.2.1
unwrapNormalFormArray :: Array N ix e -> A.Array e
unwrapNormalFormArray = blData . coerce
{-# INLINE unwrapNormalFormArray #-}

-- | /O(n)/ - Wrap a boxed array and evaluate all elements to a Normal Form (NF).
--
-- @since 0.2.1
evalNormalFormArray
  :: NFData e
  => Comp
  -- ^ Computation strategy
  -> A.Array e
  -- ^ Lazy boxed array
  -> Array N Ix1 e
evalNormalFormArray comp = forceLazyArray . setComp comp . wrapLazyArray
{-# INLINE evalNormalFormArray #-}

-- | /O(1)/ - Unwrap a fully evaluated mutable boxed array. This will discard any possible
-- slicing that has been applied to the array.
--
-- @since 0.2.1
unwrapNormalFormMutableArray :: MArray s N ix e -> A.MutableArray s e
unwrapNormalFormMutableArray = unwrapMutableLazyArray . coerce
{-# INLINE unwrapNormalFormMutableArray #-}

-- | /O(n)/ - Wrap mutable boxed array and evaluate all elements to NF.
--
-- @since 0.2.1
evalNormalFormMutableArray
  :: (PrimMonad m, NFData e)
  => A.MutableArray (PrimState m) e
  -> m (MArray (PrimState m) N Ix1 e)
evalNormalFormMutableArray marr = MBNArray <$> fromMutableArraySeq deepseq marr
{-# INLINE evalNormalFormMutableArray #-}

----------------------
-- Helper functions --
----------------------

fromMutableArraySeq
  :: PrimMonad m
  => (e -> m () -> m a)
  -> A.MutableArray (PrimState m) e
  -> m (MArray (PrimState m) BL Ix1 e)
fromMutableArraySeq with ma = do
  let !sz = A.sizeofMutableArray ma
  loopA_ 0 (< sz) (+ 1) (A.readArray ma >=> (`with` return ()))
  return $! MBLArray (SafeSz sz) 0 ma
{-# INLINE fromMutableArraySeq #-}

seqArray :: Index ix => Array BL ix a -> t -> t
seqArray !arr t = foldlInternal (flip seq) () (flip seq) () arr `seq` t
{-# INLINE seqArray #-}

deepseqArray :: (NFData a, Index ix) => Array BL ix a -> t -> t
deepseqArray !arr t = foldlInternal (flip deepseq) () (flip seq) () arr `seq` t
{-# INLINE deepseqArray #-}

-- | /O(1)/ - Converts array from `N` to `B` representation.
--
-- @since 0.5.0
unwrapNormalForm :: Array N ix e -> Array B ix e
unwrapNormalForm = coerce
{-# INLINE unwrapNormalForm #-}

-- | /O(n)/ - Compute all elements of a boxed array to NF (normal form)
--
-- @since 0.5.0
evalNormalForm :: (Index ix, NFData e) => Array B ix e -> Array N ix e
evalNormalForm (BArray arr) = arr `deepseqArray` BNArray arr
{-# INLINE evalNormalForm #-}

{- FOURMOLU_DISABLE -}
-- | /O(1)/ - Converts a boxed `Array` into a `VB.Vector` without touching any
-- elements.
--
-- @since 0.5.0
{-# INLINE toBoxedVector #-}
toBoxedVector :: Index ix => Array BL ix a -> VB.Vector a
toBoxedVector BLArray{blOffset = off, blSize = sz, blData = arr } =
#if MIN_VERSION_vector(0,13,0)
  VB.unsafeFromArraySlice arr off (totalElem sz)
#elif MIN_VERSION_vector(0,12,2)
  VB.unsafeTake (totalElem sz) (VB.unsafeDrop off (VB.fromArray arr))
#else
  fromVectorCast $ VectorCast off (totalElem sz) arr

fromVectorCast :: VectorCast a -> VB.Vector a
fromVectorCast = unsafeCoerce
#endif
{- FOURMOLU_ENABLE -}

-- | /O(1)/ - Converts a boxed `MArray` into a `MVB.MVector`.
--
-- @since 0.5.0
toBoxedMVector :: Index ix => MArray s BL ix a -> MVB.MVector s a
toBoxedMVector (MBLArray sz o marr) = MVB.MVector o (totalElem sz) marr
{-# INLINE toBoxedMVector #-}

-- | /O(n)/ - Convert a boxed vector and evaluate all elements to WHNF. Computation
-- strategy will be respected during evaluation
--
-- @since 0.5.0
evalBoxedVector :: Comp -> VB.Vector a -> Array B Ix1 a
evalBoxedVector comp = evalLazyArray . setComp comp . fromBoxedVector
{-# INLINE evalBoxedVector #-}

-- | /O(n)/ - Convert mutable boxed vector and evaluate all elements to WHNF
-- sequentially. Both keep pointing to the same memory
--
-- @since 0.5.0
evalBoxedMVector :: PrimMonad m => MVB.MVector (PrimState m) a -> m (MArray (PrimState m) B Ix1 a)
evalBoxedMVector (MVB.MVector o k ma) =
  let marr = MBArray (MBLArray (SafeSz k) o ma)
   in marr <$ loopA_ o (< k) (+ 1) (A.readArray ma >=> (`seq` pure ()))
{-# INLINE evalBoxedMVector #-}

-- | /O(1)/ - Cast a boxed vector without touching any elements.
--
-- @since 0.6.0
fromBoxedVector :: VB.Vector a -> Vector BL a
{-# INLINE fromBoxedVector #-}
fromBoxedVector v =
  BLArray{blComp = Seq, blSize = SafeSz n, blOffset = offset, blData = arr}
  where
#if MIN_VERSION_vector(0,13,0)
    (arr, offset, n) = VB.toArraySlice v
#else
    VectorCast offset n arr = toVectorCast v

-- This internal type is needed to get into the internals of a boxed vector,
-- since it is not possible until vector-0.13 version.
data VectorCast a =
  VectorCast {-# UNPACK #-}!Int {-# UNPACK #-}!Int {-# UNPACK #-}!(A.Array a)

toVectorCast :: VB.Vector a -> VectorCast a
toVectorCast = unsafeCoerce
#endif

-- | /O(1)/ - Convert mutable boxed vector to a lazy mutable boxed array. Both keep
-- pointing to the same memory
--
-- @since 0.6.0
fromBoxedMVector :: MVB.MVector s a -> MArray s BL Ix1 a
fromBoxedMVector (MVB.MVector o k ma) = MBLArray (SafeSz k) o ma
{-# INLINE fromBoxedMVector #-}

-- | /O(1)/ - Cast a boxed lazy array. It is unsafe because it can violate the invariant
-- that all elements of `N` array are in NF.
--
-- @since 0.6.0
coerceNormalBoxedArray :: Array BL ix e -> Array N ix e
coerceNormalBoxedArray = coerce
{-# INLINE coerceNormalBoxedArray #-}

-- | /O(1)/ - Cast a boxed lazy array. It is unsafe because it can violate the invariant
-- that all elements of `B` array are in WHNF.
--
-- @since 0.6.0
coerceBoxedArray :: Array BL ix e -> Array B ix e
coerceBoxedArray = coerce
{-# INLINE coerceBoxedArray #-}

-- | /O(n)/ - Convert mutable boxed vector and evaluate all elements to WHNF
-- sequentially. Both keep pointing to the same memory
--
-- @since 0.5.0
evalNormalBoxedMVector
  :: (NFData a, PrimMonad m) => MVB.MVector (PrimState m) a -> m (MArray (PrimState m) N Ix1 a)
evalNormalBoxedMVector (MVB.MVector o k ma) =
  let marr = MBNArray (MBLArray (SafeSz k) o ma)
   in marr <$ loopA_ o (< k) (+ 1) (A.readArray ma >=> pure . rnf)
{-# INLINE evalNormalBoxedMVector #-}

-- | /O(n)/ - Convert a boxed vector and evaluate all elements to WHNF. Computation
-- strategy will be respected during evaluation
--
-- @since 0.5.0
evalNormalBoxedVector :: NFData a => Comp -> VB.Vector a -> Array N Ix1 a
evalNormalBoxedVector comp v =
  runST $ do
    MVB.MVector o k ma <- VB.unsafeThaw v
    forceLazyArray <$> unsafeFreeze comp (MBLArray (SafeSz k) o ma)
{-# INLINE evalNormalBoxedVector #-}