packages feed

contiguous-0.6.4.0: src/Data/Primitive/Contiguous/Class.hs

{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeFamilyDependencies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UnboxedTuples #-}
{-# LANGUAGE UnliftedNewtypes #-}

-- | The 'Contiguous' typeclass parameterises over a contiguous array type.
-- It provides the core primitives necessary to implement the common API in "Data.Primitive.Contiguous".
--   This allows us to have a common API to a number of contiguous
--   array types and their mutable counterparts.

module Data.Primitive.Contiguous.Class
  ( Contiguous(..)
  , Slice(..)
  , MutableSlice(..)
  , ContiguousU(..)
  , Always
  ) where


import Data.Primitive.Contiguous.Shim
import Data.Primitive hiding (fromList,fromListN)
import Data.Primitive.Unlifted.Array
import Prelude hiding (length,map,all,any,foldr,foldMap,traverse,read,filter,replicate,null,reverse,foldl,foldr,zip,zipWith,scanl,(<$),elem,maximum,minimum,mapM,mapM_,sequence,sequence_)


import Control.DeepSeq (NFData)
import Control.Monad.Primitive (PrimState, PrimMonad(..))
import Control.Monad.ST (runST,ST)
import Control.Monad.ST.Run (runPrimArrayST,runSmallArrayST,runUnliftedArrayST,runArrayST)
import Data.Kind (Type)
import Data.Primitive.Unlifted.Class (PrimUnlifted)
import GHC.Exts (ArrayArray#,Constraint,sizeofByteArray#,sizeofArray#,sizeofArrayArray#)
import GHC.Exts (SmallMutableArray#,MutableArray#,MutableArrayArray#)
import GHC.Exts (SmallArray#,Array#)
import GHC.Exts (TYPE)
import Data.Primitive.Unlifted.Array (MutableUnliftedArray,UnliftedArray)
import Data.Primitive.Unlifted.Array (MutableUnliftedArray_(MutableUnliftedArray),UnliftedArray_(UnliftedArray))
import Data.Primitive.Unlifted.Array.Primops (MutableUnliftedArray#(MutableUnliftedArray#),UnliftedArray#(UnliftedArray#))

import qualified Control.DeepSeq as DS
import qualified Data.Primitive.Unlifted.Class as Class
import qualified GHC.Exts as Exts

-- In GHC 9.2 the UnliftedRep constructor of RuntimeRep was removed
-- and replaced with a type synonym
#if __GLASGOW_HASKELL__  >= 902
import GHC.Exts (UnliftedRep)
#else
import GHC.Exts (RuntimeRep(UnliftedRep))
type UnliftedRep = 'UnliftedRep
#endif


-- | Slices of immutable arrays: packages an offset and length with a backing array.
--
-- @since 0.6.0
data Slice arr a = Slice
  { offset :: {-# UNPACK #-} !Int
  , length :: {-# UNPACK #-} !Int
  , base :: !(Unlifted arr a)
  }

-- | Slices of mutable arrays: packages an offset and length with a mutable backing array.
--
-- @since 0.6.0
data MutableSlice arr s a = MutableSlice
  { offsetMut :: {-# UNPACK #-} !Int
  , lengthMut :: {-# UNPACK #-} !Int
  , baseMut :: !(UnliftedMut arr s a)
  }

-- | The 'Contiguous' typeclass as an interface to a multitude of
-- contiguous structures.
--
-- Some functions do not make sense on slices; for those, see 'ContiguousU'.
class Contiguous (arr :: Type -> Type) where
  -- | The Mutable counterpart to the array.
  type family Mutable arr = (r :: Type -> Type -> Type) | r -> arr
  -- | The constraint needed to store elements in the array.
  type family Element arr :: Type -> Constraint
  -- | The slice type of this array.
  -- The slice of a raw array type @t@ should be 'Slice t',
  -- whereas the slice of a slice should be the same slice type.
  --
  -- @since 0.6.0
  type family Sliced arr :: Type -> Type
  -- | The mutable slice type of this array.
  -- The mutable slice of a raw array type @t@ should be 'MutableSlice t',
  -- whereas the mutable slice of a mutable slice should be the same slice type.
  --
  -- @since 0.6.0
  type family MutableSliced arr :: Type -> Type -> Type


  ------ Construction ------
  -- | Allocate a new mutable array of the given size.
  new :: (PrimMonad m, Element arr b) => Int -> m (Mutable arr (PrimState m) b)
  -- | @'replicateMut' n x@ is a mutable array of length @n@ with @x@ the
  -- value of every element.
  replicateMut :: (PrimMonad m, Element arr b)
    => Int -- length
    -> b -- fill element
    -> m (Mutable arr (PrimState m) b)
  -- | Resize an array without growing it.
  --
  -- @since 0.6.0
  shrink :: (PrimMonad m, Element arr a)
    => Mutable arr (PrimState m) a
    -> Int -- ^ new length
    -> m (Mutable arr (PrimState m) a)
  default shrink ::
       ( ContiguousU arr
       , PrimMonad m, Element arr a)
    => Mutable arr (PrimState m) a -> Int -> m (Mutable arr (PrimState m) a)
  {-# INLINE shrink #-}
  shrink = resize
  -- | The empty array.
  empty :: arr a
  -- | Create a singleton array.
  singleton :: Element arr a => a -> arr a
  -- | Create a doubleton array.
  doubleton :: Element arr a => a -> a -> arr a
  -- | Create a tripleton array.
  tripleton :: Element arr a => a -> a -> a -> arr a
  -- | Create a quadrupleton array.
  quadrupleton :: Element arr a => a -> a -> a -> a -> arr a
  -- | Create a quintupleton array.
  quintupleton :: Element arr a => a -> a -> a -> a -> a -> arr a
  -- | Create a sextupleton array.
  sextupleton :: Element arr a => a -> a -> a -> a -> a -> a -> arr a

  ------ Access and Update ------
  -- | Index into an array at the given index.
  index :: Element arr b => arr b -> Int -> b
  -- | Index into an array at the given index, yielding an unboxed one-tuple of the element.
  index# :: Element arr b => arr b -> Int -> (# b #)
  -- | Indexing in a monad.
  --
  --   The monad allows operations to be strict in the array
  --   when necessary. Suppose array copying is implemented like this:
  --
  --   > copy mv v = ... write mv i (v ! i) ...
  --
  --   For lazy arrays, @v ! i@ would not be not be evaluated,
  --   which means that @mv@ would unnecessarily retain a reference
  --   to @v@ in each element written.
  --
  --   With 'indexM', copying can be implemented like this instead:
  --
  --   > copy mv v = ... do
  --   >   x <- indexM v i
  --   >   write mv i x
  --
  --   Here, no references to @v@ are retained because indexing
  --   (but /not/ the elements) is evaluated eagerly.
  indexM :: (Element arr b, Monad m) => arr b -> Int -> m b
  -- | Read a mutable array at the given index.
  read :: (PrimMonad m, Element arr b)
       => Mutable arr (PrimState m) b -> Int -> m b
  -- | Write to a mutable array at the given index.
  write :: (PrimMonad m, Element arr b)
        => Mutable arr (PrimState m) b -> Int -> b -> m ()

  ------ Properties ------
  -- | Test whether the array is empty.
  null :: arr b -> Bool
  -- | The size of the array
  size :: Element arr b => arr b -> Int
  -- | The size of the mutable array
  sizeMut :: (PrimMonad m, Element arr b)
    => Mutable arr (PrimState m) b -> m Int
  -- | Test the two arrays for equality.
  equals :: (Element arr b, Eq b) => arr b -> arr b -> Bool
  -- | Test the two mutable arrays for pointer equality.
  --   Does not check equality of elements.
  equalsMut :: Mutable arr s a -> Mutable arr s a -> Bool

  ------ Conversion ------
  -- | Create a 'Slice' of an array.
  --
  -- @O(1)@.
  --
  -- @since 0.6.0
  slice :: (Element arr a)
    => arr a -- base array
    -> Int -- offset
    -> Int -- length
    -> Sliced arr a
  -- | Create a 'MutableSlice' of a mutable array.
  --
  -- @O(1)@.
  --
  -- @since 0.6.0
  sliceMut :: (Element arr a)
    => Mutable arr s a -- base array
    -> Int -- offset
    -> Int -- length
    -> MutableSliced arr s a
  -- | Create a 'Slice' that covers the entire array.
  --
  -- @since 0.6.0
  toSlice :: (Element arr a) => arr a -> Sliced arr a
  -- | Create a 'MutableSlice' that covers the entire array.
  --
  -- @since 0.6.0
  toSliceMut :: (PrimMonad m, Element arr a)
    => Mutable arr (PrimState m) a
    -> m (MutableSliced arr (PrimState m) a)
  -- | Clone a slice of an array.
  clone :: Element arr b
    => Sliced arr b -- ^ slice to copy
    -> arr b
  default clone ::
       ( Sliced arr ~ Slice arr, ContiguousU arr
       , Element arr b)
    => Sliced arr b -> arr b
  {-# INLINE clone #-}
  clone Slice{offset,length,base} = clone_ (lift base) offset length
  -- | Clone a slice of an array without using the 'Slice' type.
  -- These methods are required to implement 'Contiguous (Slice arr)' for any `Contiguous arr`;
  -- they are not really meant for direct use.
  --
  -- @since 0.6.0
  clone_ :: Element arr a => arr a -> Int -> Int -> arr a
  -- | Clone a slice of a mutable array.
  cloneMut :: (PrimMonad m, Element arr b)
    => MutableSliced arr (PrimState m) b -- ^ Array to copy a slice of
    -> m (Mutable arr (PrimState m) b)
  default cloneMut ::
       ( MutableSliced arr ~ MutableSlice arr, ContiguousU arr
       , PrimMonad m, Element arr b)
    => MutableSliced arr (PrimState m) b -> m (Mutable arr (PrimState m) b)
  {-# INLINE cloneMut #-}
  cloneMut MutableSlice{offsetMut,lengthMut,baseMut}
    = cloneMut_ (liftMut baseMut) offsetMut lengthMut
  -- | Clone a slice of a mutable array without using the 'MutableSlice' type.
  -- These methods are required to implement 'Contiguous (Slice arr)' for any `Contiguous arr`;
  -- they are not really meant for direct use.
  --
  -- @since 0.6.0
  cloneMut_ :: (PrimMonad m, Element arr b)
    => Mutable arr (PrimState m) b -- ^ Array to copy a slice of
    -> Int -- ^ offset
    -> Int -- ^ length
    -> m (Mutable arr (PrimState m) b)
  -- | Turn a mutable array slice an immutable array by copying.
  --
  -- @since 0.6.0
  freeze :: (PrimMonad m, Element arr a)
    => MutableSliced arr (PrimState m) a
    -> m (arr a)
  default freeze ::
       ( MutableSliced arr ~ MutableSlice arr, ContiguousU arr
       , PrimMonad m, Element arr a)
    => MutableSliced arr (PrimState m) a -> m (arr a)
  {-# INLINE freeze #-}
  freeze MutableSlice{offsetMut,lengthMut,baseMut}
    = freeze_ (liftMut baseMut) offsetMut lengthMut
  -- | Turn a slice of a mutable array into an immutable one with copying,
  -- without using the 'MutableSlice' type.
  -- These methods are required to implement 'Contiguous (Slice arr)' for any `Contiguous arr`;
  -- they are not really meant for direct use.
  --
  -- @since 0.6.0
  freeze_ :: (PrimMonad m, Element arr b)
    => Mutable arr (PrimState m) b
    -> Int -- ^ offset
    -> Int -- ^ length
    -> m (arr b)
  -- | Turn a mutable array into an immutable one without copying.
  --   The mutable array should not be used after this conversion.
  unsafeFreeze :: (PrimMonad m, Element arr b)
    => Mutable arr (PrimState m) b
    -> m (arr b)
  unsafeFreeze xs = unsafeShrinkAndFreeze xs =<< sizeMut xs
  {-# INLINE unsafeFreeze #-}
  unsafeShrinkAndFreeze :: (PrimMonad m, Element arr a)
    => Mutable arr (PrimState m) a
    -> Int -- ^ final size
    -> m (arr a)
  default unsafeShrinkAndFreeze ::
       ( ContiguousU arr
       , PrimMonad m, Element arr a)
    => Mutable arr (PrimState m) a -> Int -> m (arr a)
  {-# INLINE unsafeShrinkAndFreeze #-}
  unsafeShrinkAndFreeze arr0 len' =
    resize arr0 len' >>= unsafeFreeze
  -- | Copy a slice of an immutable array into a new mutable array.
  thaw :: (PrimMonad m, Element arr b)
    => Sliced arr b
    -> m (Mutable arr (PrimState m) b)
  default thaw ::
       ( Sliced arr ~ Slice arr, ContiguousU arr
       , PrimMonad m, Element arr b)
    => Sliced arr b
    -> m (Mutable arr (PrimState m) b)
  {-# INLINE thaw #-}
  thaw Slice{offset,length,base} = thaw_ (lift base) offset length
  -- | Copy a slice of an immutable array into a new mutable array without using the 'Slice' type.
  -- These methods are required to implement 'Contiguous (Slice arr)' for any `Contiguous arr`;
  -- they are not really meant for direct use.
  --
  -- @since 0.6.0
  thaw_ :: (PrimMonad m, Element arr b)
    => arr b
    -> Int -- ^ offset into the array
    -> Int -- ^ length of the slice
    -> m (Mutable arr (PrimState m) b)

  ------ Copy Operations ------
  -- | Copy a slice of an array into a mutable array.
  copy :: (PrimMonad m, Element arr b)
    => Mutable arr (PrimState m) b -- ^ destination array
    -> Int -- ^ offset into destination array
    -> Sliced arr b -- ^ source slice
    -> m ()
  default copy ::
      ( Sliced arr ~ Slice arr, ContiguousU arr
      , PrimMonad m, Element arr b)
    => Mutable arr (PrimState m) b -> Int -> Sliced arr b -> m ()
  {-# INLINE copy #-}
  copy dst dstOff Slice{offset,length,base} = copy_ dst dstOff (lift base) offset length
  -- | Copy a slice of an array into a mutable array without using the 'Slice' type.
  -- These methods are required to implement 'Contiguous (Slice arr)' for any `Contiguous arr`;
  -- they are not really meant for direct use.
  --
  -- @since 0.6.0
  copy_ :: (PrimMonad m, Element arr b)
    => Mutable arr (PrimState m) b -- ^ destination array
    -> Int -- ^ offset into destination array
    -> arr b -- ^ source array
    -> Int -- ^ offset into source array
    -> Int -- ^ number of elements to copy
    -> m ()
  -- | Copy a slice of a mutable array into another mutable array.
  --   In the case that the destination and source arrays are the
  --   same, the regions may overlap.
  copyMut :: (PrimMonad m, Element arr b)
    => Mutable arr (PrimState m) b -- ^ destination array
    -> Int -- ^ offset into destination array
    -> MutableSliced arr (PrimState m) b -- ^ source slice
    -> m ()
  default copyMut ::
       ( MutableSliced arr ~ MutableSlice arr, ContiguousU arr
       , PrimMonad m, Element arr b)
    => Mutable arr (PrimState m) b -> Int -> MutableSliced arr (PrimState m) b -> m ()
  {-# INLINE copyMut #-}
  copyMut dst dstOff MutableSlice{offsetMut,lengthMut,baseMut}
    = copyMut_ dst dstOff (liftMut baseMut) offsetMut lengthMut
  -- | Copy a slice of a mutable array into another mutable array without using the 'Slice' type.
  -- These methods are required to implement 'Contiguous (Slice arr)' for any `Contiguous arr`;
  -- they are not really meant for direct use.
  --
  -- @since 0.6.0
  copyMut_ :: (PrimMonad m, Element arr b)
    => Mutable arr (PrimState m) b -- ^ destination array
    -> Int -- ^ offset into destination array
    -> Mutable arr (PrimState m) b -- ^ source array
    -> Int -- ^ offset into source array
    -> Int -- ^ number of elements to copy
    -> m ()
  -- | Copy a slice of an array and then insert an element into that array.
  --
  -- The default implementation performs a memset which would be unnecessary
  -- except that the garbage collector might trace the uninitialized array.
  --
  -- Was previously @insertSlicing@
  -- @since 0.6.0
  insertAt :: (Element arr b)
    => arr b -- ^ slice to copy from
    -> Int -- ^ index in the output array to insert at
    -> b -- ^ element to insert
    -> arr b
  default insertAt ::
       (Element arr b, ContiguousU arr)
    => arr b -> Int -> b -> arr b
  insertAt src i x = run $ do
    dst <- replicateMut (size src + 1) x
    copy dst 0 (slice src 0 i)
    copy dst (i + 1) (slice src i (size src - i))
    unsafeFreeze dst
  {-# inline insertAt #-}

  ------ Reduction ------
  -- | Reduce the array and all of its elements to WHNF.
  rnf :: (NFData a, Element arr a) => arr a -> ()
  -- | Run an effectful computation that produces an array.
  run :: (forall s. ST s (arr a)) -> arr a

-- | The 'ContiguousU' typeclass is an extension of the 'Contiguous' typeclass,
-- but includes operations that make sense only on unsliced contiguous structures.
--
-- @since 0.6.0
class (Contiguous arr) => ContiguousU arr where
  -- | The unifted version of the immutable array type (i.e. eliminates an indirection through a thunk).
  type Unlifted arr = (r :: Type -> TYPE UnliftedRep) | r -> arr
  -- | The unifted version of the mutable array type (i.e. eliminates an indirection through a thunk).
  type UnliftedMut arr = (r :: Type -> Type -> TYPE UnliftedRep) | r -> arr
  -- | Resize an array into one with the given size.
  resize :: (PrimMonad m, Element arr b)
         => Mutable arr (PrimState m) b
         -> Int
         -> m (Mutable arr (PrimState m) b)
  -- | Unlift an array (i.e. point to the data without an intervening thunk).
  --
  -- @since 0.6.0
  unlift :: arr b -> Unlifted arr b
  -- | Unlift a mutable array (i.e. point to the data without an intervening thunk).
  --
  -- @since 0.6.0
  unliftMut :: Mutable arr s b -> UnliftedMut arr s b
  -- | Lift an array (i.e. point to the data through an intervening thunk).
  --
  -- @since 0.6.0
  lift :: Unlifted arr b -> arr b
  -- | Lift a mutable array (i.e. point to the data through an intervening thunk).
  --
  -- @since 0.6.0
  liftMut :: UnliftedMut arr s b -> Mutable arr s b


-- | A typeclass that is satisfied by all types. This is used
-- used to provide a fake constraint for 'Array' and 'SmallArray'.
class Always a where {}
instance Always a where {}

instance (ContiguousU arr) => Contiguous (Slice arr) where
  type Mutable (Slice arr) = MutableSlice arr
  type Element (Slice arr) = Element arr
  type Sliced (Slice arr) = Slice arr
  type MutableSliced (Slice arr) = MutableSlice arr
  ------ Construction ------
  {-# INLINE new #-}
  new len = do
    baseMut <- new len
    pure MutableSlice{offsetMut=0,lengthMut=len,baseMut=unliftMut baseMut}
  {-# INLINE replicateMut #-}
  replicateMut len x = do
    baseMut <- replicateMut len x
    pure MutableSlice{offsetMut=0,lengthMut=len,baseMut=unliftMut baseMut}
  {-# INLINE shrink #-}
  shrink xs len' = pure $ case compare len' (lengthMut xs) of
    LT -> xs{lengthMut=len'}
    EQ -> xs
    GT -> errorWithoutStackTrace "Data.Primitive.Contiguous.Class.shrink: passed a larger than existing size"
  {-# INLINE empty #-}
  empty = Slice{offset=0,length=0,base=unlift empty}
  {-# INLINE singleton #-}
  singleton a = Slice{offset=0,length=1,base=unlift $ singleton a}
  {-# INLINE doubleton #-}
  doubleton a b = Slice{offset=0,length=2,base=unlift $ doubleton a b}
  {-# INLINE tripleton #-}
  tripleton a b c = Slice{offset=0,length=3,base=unlift $ tripleton a b c}
  {-# INLINE quadrupleton #-}
  quadrupleton a b c d = Slice{offset=0,length=4,base=unlift $ quadrupleton a b c d}
  {-# INLINE quintupleton #-}
  quintupleton a b c d e = Slice{offset=0,length=5,base=unlift $ quintupleton a b c d e}
  {-# INLINE sextupleton #-}
  sextupleton a b c d e f = Slice{offset=0,length=6,base=unlift $ sextupleton a b c d e f}

  ------ Access and Update ------
  {-# INLINE index #-}
  index Slice{offset,base} i = index (lift base) (offset + i)
  {-# INLINE index# #-}
  index# Slice{offset,base} i = index# (lift base) (offset + i)
  {-# INLINE indexM #-}
  indexM Slice{offset,base} i = indexM (lift base) (offset + i)
  {-# INLINE read #-}
  read MutableSlice{offsetMut,baseMut} i = read (liftMut baseMut) (offsetMut + i)
  {-# INLINE write #-}
  write MutableSlice{offsetMut,baseMut} i = write (liftMut baseMut) (offsetMut + i)

  ------ Properties ------
  {-# INLINE null #-}
  null Slice{length} = length == 0
  {-# INLINE size #-}
  size Slice{length} = length
  {-# INLINE sizeMut #-}
  sizeMut MutableSlice{lengthMut} = pure lengthMut
  {-# INLINE equals #-}
  equals Slice{offset=oA,length=lenA,base=a}
         Slice{offset=oB,length=lenB,base=b}
    = lenA == lenB && loop 0 oA oB
    where
    loop !i !iA !iB =
      if i == lenA then True
      else index (lift a) iA == index (lift b) iB && loop (i+1) (iA+1) (iB+1)
  {-# INLINE equalsMut #-}
  equalsMut MutableSlice{offsetMut=offA,lengthMut=lenA,baseMut=a}
                MutableSlice{offsetMut=offB,lengthMut=lenB,baseMut=b}
    =  liftMut a `equalsMut` liftMut b
    && offA == offB
    && lenA == lenB

  ------ Conversion ------
  {-# INLINE slice #-}
  slice Slice{offset,base} off' len' = Slice
    { offset = offset + off'
    , length = len'
    , base
    }
  {-# INLINE sliceMut #-}
  sliceMut MutableSlice{offsetMut,baseMut} off' len' = MutableSlice
    { offsetMut = offsetMut + off'
    , lengthMut = len'
    , baseMut
    }
  {-# INLINE clone #-}
  clone = id
  {-# INLINE clone_ #-}
  clone_ Slice{offset,base} off' len' =
    Slice{offset=offset+off',length=len',base}
  {-# INLINE cloneMut #-}
  cloneMut xs@MutableSlice{lengthMut} = cloneMut_ xs 0 lengthMut
  {-# INLINE cloneMut_ #-}
  cloneMut_ MutableSlice{offsetMut,baseMut} off' len' = do
    baseMut' <- cloneMut_ (liftMut baseMut) (offsetMut + off') len'
    pure MutableSlice{offsetMut=0,lengthMut=len',baseMut=unliftMut baseMut'}
  {-# INLINE freeze #-}
  freeze xs@MutableSlice{lengthMut}
    = freeze_ xs 0 lengthMut
  {-# INLINE freeze_ #-}
  freeze_ MutableSlice{offsetMut,baseMut} off' len' = do
    base <- freeze_ (liftMut baseMut) (offsetMut + off') len'
    pure Slice{offset=0,length=len',base=unlift base}
  {-# INLINE unsafeShrinkAndFreeze #-}
  unsafeShrinkAndFreeze MutableSlice{offsetMut=0,lengthMut,baseMut} len' = do
    shrunk <- if lengthMut /= len'
      then resize (liftMut baseMut) len'
      else pure (liftMut baseMut)
    base <- unsafeFreeze shrunk
    pure Slice{offset=0,length=len',base=unlift base}
  unsafeShrinkAndFreeze MutableSlice{offsetMut,baseMut} len' = do
    base <- freeze_ (liftMut baseMut) offsetMut len'
    pure Slice{offset=0,length=len',base=unlift base}
  {-# INLINE thaw #-}
  thaw xs@Slice{length} = thaw_ xs 0 length
  {-# INLINE thaw_ #-}
  thaw_ Slice{offset,base} off' len' = do
    baseMut <- thaw_ (lift base) (offset + off') len'
    pure MutableSlice{offsetMut=0,lengthMut=len',baseMut=unliftMut baseMut}
  {-# INLINE toSlice #-}
  toSlice = id
  {-# INLINE toSliceMut #-}
  toSliceMut = pure

  ------ Copy Operations ------
  {-# INLINE copy #-}
  copy dst dstOff src@Slice{length} = copy_ dst dstOff src 0 length
  {-# INLINE copy_ #-}
  copy_ MutableSlice{offsetMut,baseMut} dstOff Slice{offset,base} off' len =
    copy_ (liftMut baseMut) (offsetMut + dstOff) (lift base) (offset + off') len
  {-# INLINE copyMut #-}
  copyMut dst dstOff src@MutableSlice{lengthMut} = copyMut_ dst dstOff src 0 lengthMut
  {-# INLINE copyMut_ #-}
  copyMut_ MutableSlice{offsetMut=dstOff,baseMut=dst} dstOff'
           MutableSlice{offsetMut=srcOff,baseMut=src} srcOff' len =
    copyMut_ (liftMut dst) (dstOff + dstOff') (liftMut src) (srcOff + srcOff') len
  {-# INLINE insertAt #-}
  insertAt Slice{offset,length,base} i x = run $ do
    dst <- replicateMut (length + 1) x
    copy_ dst 0 (lift base) offset i
    copy_ dst (i + 1) (lift base) (offset + i) (length - i)
    base' <- unsafeFreeze dst
    pure Slice{offset=0,length=length+1,base=unlift base'}

  ------ Reduction ------
  {-# INLINE rnf #-}
  rnf !arr@Slice{length} =
    let go !ix = if ix < length
          then
            let !(# x #) = index# arr ix
             in DS.rnf x `seq` go (ix + 1)
          else ()
     in go 0
  {-# INLINE run #-}
  run = runST


instance Contiguous SmallArray where
  type Mutable SmallArray = SmallMutableArray
  type Element SmallArray = Always
  type Sliced SmallArray = Slice SmallArray
  type MutableSliced SmallArray = MutableSlice SmallArray
  {-# INLINE new #-}
  new n = newSmallArray n errorThunk
  {-# INLINE empty #-}
  empty = mempty
  {-# INLINE index #-}
  index = indexSmallArray
  {-# INLINE indexM #-}
  indexM = indexSmallArrayM
  {-# INLINE index# #-}
  index# = indexSmallArray##
  {-# INLINE read #-}
  read = readSmallArray
  {-# INLINE write #-}
  write = writeSmallArray
  {-# INLINE null #-}
  null a = case sizeofSmallArray a of
    0 -> True
    _ -> False
  {-# INLINE slice #-}
  slice base offset length = Slice{offset,length,base=unlift base}
  {-# INLINE sliceMut #-}
  sliceMut baseMut offsetMut lengthMut = MutableSlice{offsetMut,lengthMut,baseMut=unliftMut baseMut}
  {-# INLINE toSlice #-}
  toSlice base = Slice{offset=0,length=size base,base=unlift base}
  {-# INLINE toSliceMut #-}
  toSliceMut baseMut = do
    lengthMut <- sizeMut baseMut
    pure MutableSlice{offsetMut=0,lengthMut,baseMut=unliftMut baseMut}
  {-# INLINE freeze_ #-}
  freeze_ = freezeSmallArray
  {-# INLINE unsafeFreeze #-}
  unsafeFreeze = unsafeFreezeSmallArray
  {-# INLINE size #-}
  size = sizeofSmallArray
  {-# INLINE sizeMut #-}
  sizeMut = (\x -> pure $! sizeofSmallMutableArray x)
  {-# INLINE thaw_ #-}
  thaw_ = thawSmallArray
  {-# INLINE equals #-}
  equals = (==)
  {-# INLINE equalsMut #-}
  equalsMut = (==)
  {-# INLINE singleton #-}
  singleton a = runST $ do
    marr <- newSmallArray 1 a
    unsafeFreezeSmallArray marr
  {-# INLINE doubleton #-}
  doubleton a b = runST $ do
    m <- newSmallArray 2 a
    writeSmallArray m 1 b
    unsafeFreezeSmallArray m
  {-# INLINE tripleton #-}
  tripleton a b c = runST $ do
    m <- newSmallArray 3 a
    writeSmallArray m 1 b
    writeSmallArray m 2 c
    unsafeFreezeSmallArray m
  {-# INLINE quadrupleton #-}
  quadrupleton a b c d = runST $ do
    m <- newSmallArray 4 a
    writeSmallArray m 1 b
    writeSmallArray m 2 c
    writeSmallArray m 3 d
    unsafeFreezeSmallArray m
  {-# INLINE quintupleton #-}
  quintupleton a b c d e = runST $ do
    m <- newSmallArray 5 a
    writeSmallArray m 1 b
    writeSmallArray m 2 c
    writeSmallArray m 3 d
    writeSmallArray m 4 e
    unsafeFreezeSmallArray m
  {-# INLINE sextupleton #-}
  sextupleton a b c d e f = runST $ do
    m <- newSmallArray 6 a
    writeSmallArray m 1 b
    writeSmallArray m 2 c
    writeSmallArray m 3 d
    writeSmallArray m 4 e
    writeSmallArray m 5 f
    unsafeFreezeSmallArray m
  {-# INLINE rnf #-}
  rnf !ary =
    let !sz = sizeofSmallArray ary
        go !ix = if ix < sz
          then
            let !(# x #) = indexSmallArray## ary ix
             in DS.rnf x `seq` go (ix + 1)
          else ()
     in go 0
  {-# INLINE clone_ #-}
  clone_ = cloneSmallArray
  {-# INLINE cloneMut_ #-}
  cloneMut_ = cloneSmallMutableArray
  {-# INLINE copy_ #-}
  copy_ = copySmallArray
  {-# INLINE copyMut_ #-}
  copyMut_ = copySmallMutableArray
  {-# INLINE replicateMut #-}
  replicateMut = replicateSmallMutableArray
  {-# INLINE run #-}
  run = runSmallArrayST

instance ContiguousU SmallArray where
  type Unlifted SmallArray = SmallArray#
  type UnliftedMut SmallArray = SmallMutableArray#
  {-# INLINE resize #-}
  resize = resizeSmallArray
  {-# INLINE unlift #-}
  unlift (SmallArray x) = x
  {-# INLINE unliftMut #-}
  unliftMut (SmallMutableArray x) = x
  {-# INLINE lift #-}
  lift x = SmallArray x
  {-# INLINE liftMut #-}
  liftMut x = SmallMutableArray x


instance Contiguous PrimArray where
  type Mutable PrimArray = MutablePrimArray
  type Element PrimArray = Prim
  type Sliced PrimArray = Slice PrimArray
  type MutableSliced PrimArray = MutableSlice PrimArray
  {-# INLINE empty #-}
  empty = mempty
  {-# INLINE new #-}
  new = newPrimArray
  {-# INLINE replicateMut #-}
  replicateMut = replicateMutablePrimArray
  {-# INLINE index #-}
  index = indexPrimArray
  {-# INLINE index# #-}
  index# arr ix = (# indexPrimArray arr ix #)
  {-# INLINE indexM #-}
  indexM arr ix = pure (indexPrimArray arr ix)
  {-# INLINE read #-}
  read = readPrimArray
  {-# INLINE write #-}
  write = writePrimArray
  {-# INLINE size #-}
  size = sizeofPrimArray
  {-# INLINE sizeMut #-}
  sizeMut = getSizeofMutablePrimArray
  {-# INLINE slice #-}
  slice base offset length = Slice{offset,length,base=unlift base}
  {-# INLINE sliceMut #-}
  sliceMut baseMut offsetMut lengthMut = MutableSlice{offsetMut,lengthMut,baseMut=unliftMut baseMut}
  {-# INLINE toSlice #-}
  toSlice base = Slice{offset=0,length=size base,base=unlift base}
  {-# INLINE toSliceMut #-}
  toSliceMut baseMut = do
    lengthMut <- sizeMut baseMut
    pure MutableSlice{offsetMut=0,lengthMut,baseMut=unliftMut baseMut}
  {-# INLINE freeze_ #-}
  freeze_ = freezePrimArrayShim
  {-# INLINE unsafeFreeze #-}
  unsafeFreeze = unsafeFreezePrimArray
  {-# INLINE thaw_ #-}
  thaw_ = thawPrimArray
  {-# INLINE copy_ #-}
  copy_ = copyPrimArray
  {-# INLINE copyMut_ #-}
  copyMut_ = copyMutablePrimArray
  {-# INLINE clone_ #-}
  clone_ = clonePrimArrayShim
  {-# INLINE cloneMut_ #-}
  cloneMut_ = cloneMutablePrimArrayShim
  {-# INLINE equals #-}
  equals = (==)
  {-# INLINE null #-}
  null (PrimArray a) = case sizeofByteArray# a of
    0# -> True
    _ -> False
  {-# INLINE equalsMut #-}
  equalsMut = sameMutablePrimArray
  {-# INLINE rnf #-}
  rnf (PrimArray !_) = ()
  {-# INLINE singleton #-}
  singleton a = runPrimArrayST $ do
    marr <- newPrimArray 1
    writePrimArray marr 0 a
    unsafeFreezePrimArray marr
  {-# INLINE doubleton #-}
  doubleton a b = runPrimArrayST $ do
    m <- newPrimArray 2
    writePrimArray m 0 a
    writePrimArray m 1 b
    unsafeFreezePrimArray m
  {-# INLINE tripleton #-}
  tripleton a b c = runPrimArrayST $ do
    m <- newPrimArray 3
    writePrimArray m 0 a
    writePrimArray m 1 b
    writePrimArray m 2 c
    unsafeFreezePrimArray m
  {-# INLINE quadrupleton #-}
  quadrupleton a b c d = runPrimArrayST $ do
    m <- newPrimArray 4
    writePrimArray m 0 a
    writePrimArray m 1 b
    writePrimArray m 2 c
    writePrimArray m 3 d
    unsafeFreezePrimArray m
  {-# INLINE quintupleton #-}
  quintupleton a b c d e = runPrimArrayST $ do
    m <- newPrimArray 5
    writePrimArray m 0 a
    writePrimArray m 1 b
    writePrimArray m 2 c
    writePrimArray m 3 d
    writePrimArray m 4 e
    unsafeFreezePrimArray m
  {-# INLINE sextupleton #-}
  sextupleton a b c d e f = runPrimArrayST $ do
    m <- newPrimArray 6
    writePrimArray m 0 a
    writePrimArray m 1 b
    writePrimArray m 2 c
    writePrimArray m 3 d
    writePrimArray m 4 e
    writePrimArray m 5 f
    unsafeFreezePrimArray m
  {-# INLINE insertAt #-}
  insertAt src i x = runPrimArrayST $ do
    dst <- new (size src + 1)
    copy dst 0 (slice src 0 i)
    write dst i x
    copy dst (i + 1) (slice src i (size src - i))
    unsafeFreeze dst
  {-# INLINE run #-}
  run = runPrimArrayST

newtype PrimArray# a = PrimArray# ByteArray#
newtype MutablePrimArray# s a = MutablePrimArray# (MutableByteArray# s)
instance ContiguousU PrimArray where
  type Unlifted PrimArray = PrimArray#
  type UnliftedMut PrimArray = MutablePrimArray#
  {-# INLINE resize #-}
  resize = resizeMutablePrimArray
  {-# INLINE unlift #-}
  unlift (PrimArray x) = PrimArray# x
  {-# INLINE unliftMut #-}
  unliftMut (MutablePrimArray x) = MutablePrimArray# x
  {-# INLINE lift #-}
  lift (PrimArray# x) = PrimArray x
  {-# INLINE liftMut #-}
  liftMut (MutablePrimArray# x) = MutablePrimArray x


instance Contiguous Array where
  type Mutable Array = MutableArray
  type Element Array = Always
  type Sliced Array = Slice Array
  type MutableSliced Array = MutableSlice Array
  {-# INLINE empty #-}
  empty = mempty
  {-# INLINE new #-}
  new n = newArray n errorThunk
  {-# INLINE replicateMut #-}
  replicateMut = newArray
  {-# INLINE index #-}
  index = indexArray
  {-# INLINE index# #-}
  index# = indexArray##
  {-# INLINE indexM #-}
  indexM = indexArrayM
  {-# INLINE read #-}
  read = readArray
  {-# INLINE write #-}
  write = writeArray
  {-# INLINE size #-}
  size = sizeofArray
  {-# INLINE sizeMut #-}
  sizeMut = (\x -> pure $! sizeofMutableArray x)
  {-# INLINE slice #-}
  slice base offset length = Slice{offset,length,base=unlift base}
  {-# INLINE sliceMut #-}
  sliceMut baseMut offsetMut lengthMut = MutableSlice{offsetMut,lengthMut,baseMut=unliftMut baseMut}
  {-# INLINE toSlice #-}
  toSlice base = Slice{offset=0,length=size base,base=unlift base}
  {-# INLINE toSliceMut #-}
  toSliceMut baseMut = do
    lengthMut <- sizeMut baseMut
    pure MutableSlice{offsetMut=0,lengthMut,baseMut=unliftMut baseMut}
  {-# INLINE freeze_ #-}
  freeze_ = freezeArray
  {-# INLINE unsafeFreeze #-}
  unsafeFreeze = unsafeFreezeArray
  {-# INLINE thaw_ #-}
  thaw_ = thawArray
  {-# INLINE copy_ #-}
  copy_ = copyArray
  {-# INLINE copyMut_ #-}
  copyMut_ = copyMutableArray
  {-# INLINE clone #-}
  clone Slice{offset,length,base} = clone_ (lift base) offset length
  {-# INLINE clone_ #-}
  clone_ = cloneArray
  {-# INLINE cloneMut_ #-}
  cloneMut_ = cloneMutableArray
  {-# INLINE equals #-}
  equals = (==)
  {-# INLINE null #-}
  null (Array a) = case sizeofArray# a of
    0# -> True
    _ -> False
  {-# INLINE equalsMut #-}
  equalsMut = sameMutableArray
  {-# INLINE rnf #-}
  rnf !ary =
    let !sz = sizeofArray ary
        go !i
          | i == sz = ()
          | otherwise =
              let !(# x #) = indexArray## ary i
               in DS.rnf x `seq` go (i+1)
     in go 0
  {-# INLINE singleton #-}
  singleton a = runArrayST (newArray 1 a >>= unsafeFreezeArray)
  {-# INLINE doubleton #-}
  doubleton a b = runArrayST $ do
    m <- newArray 2 a
    writeArray m 1 b
    unsafeFreezeArray m
  {-# INLINE tripleton #-}
  tripleton a b c = runArrayST $ do
    m <- newArray 3 a
    writeArray m 1 b
    writeArray m 2 c
    unsafeFreezeArray m
  {-# INLINE quadrupleton #-}
  quadrupleton a b c d = runArrayST $ do
    m <- newArray 4 a
    writeArray m 1 b
    writeArray m 2 c
    writeArray m 3 d
    unsafeFreezeArray m
  {-# INLINE quintupleton #-}
  quintupleton a b c d e = runArrayST $ do
    m <- newArray 5 a
    writeArray m 1 b
    writeArray m 2 c
    writeArray m 3 d
    writeArray m 4 e
    unsafeFreezeArray m
  {-# INLINE sextupleton #-}
  sextupleton a b c d e f = runArrayST $ do
    m <- newArray 6 a
    writeArray m 1 b
    writeArray m 2 c
    writeArray m 3 d
    writeArray m 4 e
    writeArray m 5 f
    unsafeFreezeArray m
  {-# INLINE run #-}
  run = runArrayST

instance ContiguousU Array where
  type Unlifted Array = Array#
  type UnliftedMut Array = MutableArray#
  {-# INLINE resize #-}
  resize = resizeArray
  {-# INLINE unlift #-}
  unlift (Array x) = x
  {-# INLINE unliftMut #-}
  unliftMut (MutableArray x) = x
  {-# INLINE lift #-}
  lift x = Array x
  {-# INLINE liftMut #-}
  liftMut x = MutableArray x

class (Class.Unlifted a ~ u, PrimUnlifted a) => PrimUnliftsInto (u :: TYPE ('Exts.BoxedRep 'Exts.Unlifted)) (a :: Type) where
instance (Class.Unlifted a ~ u, PrimUnlifted a) => PrimUnliftsInto u a

instance Contiguous (UnliftedArray_ unlifted_a) where
  type Mutable (UnliftedArray_ unlifted_a) = MutableUnliftedArray_ unlifted_a
  type Element (UnliftedArray_ unlifted_a) = PrimUnliftsInto unlifted_a
  type Sliced (UnliftedArray_ unlifted_a) = Slice (UnliftedArray_ unlifted_a)
  type MutableSliced (UnliftedArray_ unlifted_a) = MutableSlice (UnliftedArray_ unlifted_a)
  {-# INLINE empty #-}
  empty = emptyUnliftedArray
  {-# INLINE new #-}
  new = unsafeNewUnliftedArray
  {-# INLINE replicateMut #-}
  replicateMut = newUnliftedArray
  {-# INLINE index #-}
  index = indexUnliftedArray
  {-# INLINE index# #-}
  index# arr ix = (# indexUnliftedArray arr ix #)
  {-# INLINE indexM #-}
  indexM arr ix = pure (indexUnliftedArray arr ix)
  {-# INLINE read #-}
  read = readUnliftedArray
  {-# INLINE write #-}
  write = writeUnliftedArray
  {-# INLINE size #-}
  size = sizeofUnliftedArray
  {-# INLINE sizeMut #-}
  sizeMut = pure . sizeofMutableUnliftedArray
  {-# INLINE slice #-}
  slice base offset length = Slice{offset,length,base=unlift base}
  {-# INLINE sliceMut #-}
  sliceMut baseMut offsetMut lengthMut = MutableSlice{offsetMut,lengthMut,baseMut=unliftMut baseMut}
  {-# INLINE freeze_ #-}
  freeze_ = freezeUnliftedArray
  {-# INLINE unsafeFreeze #-}
  unsafeFreeze = unsafeFreezeUnliftedArray
  {-# INLINE toSlice #-}
  toSlice base = Slice{offset=0,length=size base,base=unlift base}
  {-# INLINE toSliceMut #-}
  toSliceMut baseMut = do
    lengthMut <- sizeMut baseMut
    pure MutableSlice{offsetMut=0,lengthMut,baseMut=unliftMut baseMut}
  {-# INLINE thaw_ #-}
  thaw_ = thawUnliftedArray
  {-# INLINE copy_ #-}
  copy_ = copyUnliftedArray
  {-# INLINE copyMut_ #-}
  copyMut_ = copyMutableUnliftedArray
  {-# INLINE clone_ #-}
  clone_ = cloneUnliftedArray
  {-# INLINE cloneMut_ #-}
  cloneMut_ = cloneMutableUnliftedArray
  {-# INLINE equals #-}
  equals = (==)
  {-# INLINE null #-}
  null (UnliftedArray (UnliftedArray# a)) = case Exts.sizeofArray# a of
    0# -> True
    _ -> False
  {-# INLINE equalsMut #-}
  equalsMut = sameMutableUnliftedArray
  {-# INLINE rnf #-}
  rnf !ary =
    let !sz = sizeofUnliftedArray ary
        go !i
          | i == sz = ()
          | otherwise =
              let x = indexUnliftedArray ary i
               in DS.rnf x `seq` go (i+1)
     in go 0
  {-# INLINE singleton #-}
  singleton a = runUnliftedArrayST (newUnliftedArray 1 a >>= unsafeFreezeUnliftedArray)
  {-# INLINE doubleton #-}
  doubleton a b = runUnliftedArrayST $ do
    m <- newUnliftedArray 2 a
    writeUnliftedArray m 1 b
    unsafeFreezeUnliftedArray m
  {-# INLINE tripleton #-}
  tripleton a b c = runUnliftedArrayST $ do
    m <- newUnliftedArray 3 a
    writeUnliftedArray m 1 b
    writeUnliftedArray m 2 c
    unsafeFreezeUnliftedArray m
  {-# INLINE quadrupleton #-}
  quadrupleton a b c d = runUnliftedArrayST $ do
    m <- newUnliftedArray 4 a
    writeUnliftedArray m 1 b
    writeUnliftedArray m 2 c
    writeUnliftedArray m 3 d
    unsafeFreezeUnliftedArray m
  {-# INLINE quintupleton #-}
  quintupleton a b c d e = runUnliftedArrayST $ do
    m <- newUnliftedArray 5 a
    writeUnliftedArray m 1 b
    writeUnliftedArray m 2 c
    writeUnliftedArray m 3 d
    writeUnliftedArray m 4 e
    unsafeFreezeUnliftedArray m
  {-# INLINE sextupleton #-}
  sextupleton a b c d e f = runUnliftedArrayST $ do
    m <- newUnliftedArray 6 a
    writeUnliftedArray m 1 b
    writeUnliftedArray m 2 c
    writeUnliftedArray m 3 d
    writeUnliftedArray m 4 e
    writeUnliftedArray m 5 f
    unsafeFreezeUnliftedArray m
  {-# INLINE run #-}
  run = runUnliftedArrayST

newtype UnliftedArray## (u :: TYPE UnliftedRep) (a :: Type) =
  UnliftedArray## (Exts.Array# u)
newtype MutableUnliftedArray## (u :: TYPE UnliftedRep) s (a :: Type) =
  MutableUnliftedArray## (Exts.MutableArray# s u)

instance ContiguousU (UnliftedArray_ unlifted_a) where
  type Unlifted (UnliftedArray_ unlifted_a) = UnliftedArray## unlifted_a
  type UnliftedMut (UnliftedArray_ unlifted_a) = MutableUnliftedArray## unlifted_a
  {-# INLINE resize #-}
  resize = resizeUnliftedArray
  {-# INLINE unlift #-}
  unlift (UnliftedArray (UnliftedArray# x)) = UnliftedArray## x
  {-# INLINE unliftMut #-}
  unliftMut (MutableUnliftedArray (MutableUnliftedArray# x)) = MutableUnliftedArray## x
  {-# INLINE lift #-}
  lift (UnliftedArray## x) = UnliftedArray (UnliftedArray# x)
  {-# INLINE liftMut #-}
  liftMut (MutableUnliftedArray## x) = MutableUnliftedArray (MutableUnliftedArray# x)