module VectorBuilder.Core.Builder
where

import VectorBuilder.Prelude hiding (empty, concat)
import qualified VectorBuilder.Core.Update as A
import qualified Data.Vector.Generic as B
import qualified Data.Vector.Generic.Mutable as C


-- |
-- An abstraction over the size of a vector for the process of its construction.
-- 
-- It postpones the actual construction of a vector until the execution of the builder.
data Builder element =
  Builder !Int !(A.Update element)


-- |
-- Gets the size of a Builder.
{-# INLINE size #-}
size :: Builder element -> Int
size :: Builder element -> Int
size (Builder Int
s Update element
_) = Int
s


-- * Initialisation

-- |
-- Empty builder.
{-# INLINE empty #-}
empty :: Builder element
empty :: Builder element
empty =
  Int -> Update element -> Builder element
forall element. Int -> Update element -> Builder element
Builder Int
0 Update element
forall element. Update element
A.empty

-- |
-- Builder of a single element.
{-# INLINE singleton #-}
singleton :: element -> Builder element
singleton :: element -> Builder element
singleton element
element =
  Int -> Update element -> Builder element
forall element. Int -> Update element -> Builder element
Builder Int
1 (element -> Update element
forall element. element -> Update element
A.write element
element)

-- |
-- Builder from an immutable vector of elements.
-- 
-- Supports all kinds of vectors: boxed, unboxed, primitive, storable.
{-# INLINE vector #-}
vector :: B.Vector vector element => vector element -> Builder element
vector :: vector element -> Builder element
vector vector element
vector =
  Int -> Update element -> Builder element
forall element. Int -> Update element -> Builder element
Builder (vector element -> Int
forall (v :: * -> *) a. Vector v a => v a -> Int
B.length vector element
vector) (vector element -> Update element
forall (vector :: * -> *) element.
Vector vector element =>
vector element -> Update element
A.writeMany vector element
vector)

{-# INLINE foldable #-}
foldable :: Foldable foldable => foldable element -> Builder element
foldable :: foldable element -> Builder element
foldable foldable element
foldable =
  Int -> Update element -> Builder element
forall element. Int -> Update element -> Builder element
Builder (foldable element -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length foldable element
foldable) (foldable element -> Update element
forall (foldable :: * -> *) element.
Foldable foldable =>
foldable element -> Update element
A.writeFoldable foldable element
foldable)


-- * Updates

{-# INLINE snoc #-}
snoc :: element -> Builder element -> Builder element
snoc :: element -> Builder element -> Builder element
snoc element
element (Builder Int
size Update element
update) =
  Int -> Update element -> Builder element
forall element. Int -> Update element -> Builder element
Builder (Int -> Int
forall a. Enum a => a -> a
succ Int
size) (Int -> Update element -> Update element -> Update element
forall element.
Int -> Update element -> Update element -> Update element
A.prepend Int
size Update element
update (element -> Update element
forall element. element -> Update element
A.write element
element))

{-# INLINE cons #-}
cons :: element -> Builder element -> Builder element
cons :: element -> Builder element -> Builder element
cons element
element (Builder Int
size Update element
update) =
  Int -> Update element -> Builder element
forall element. Int -> Update element -> Builder element
Builder (Int -> Int
forall a. Enum a => a -> a
succ Int
size) (Int -> Update element -> Update element -> Update element
forall element.
Int -> Update element -> Update element -> Update element
A.prepend Int
1 (element -> Update element
forall element. element -> Update element
A.write element
element) Update element
update)

{-# INLINE prepend #-}
prepend :: Builder element -> Builder element -> Builder element
prepend :: Builder element -> Builder element -> Builder element
prepend (Builder Int
leftSize Update element
leftUpdate) (Builder Int
rightSize Update element
rightUpdate) =
  Int -> Update element -> Builder element
forall element. Int -> Update element -> Builder element
Builder (Int
leftSize Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
rightSize) (Int -> Update element -> Update element -> Update element
forall element.
Int -> Update element -> Update element -> Update element
A.prepend Int
leftSize Update element
leftUpdate Update element
rightUpdate)

{-# INLINE append #-}
append :: Builder element -> Builder element -> Builder element
append :: Builder element -> Builder element -> Builder element
append =
  (Builder element -> Builder element -> Builder element)
-> Builder element -> Builder element -> Builder element
forall a b c. (a -> b -> c) -> b -> a -> c
flip Builder element -> Builder element -> Builder element
forall element.
Builder element -> Builder element -> Builder element
prepend

{-# INLINE concat #-}
concat :: Foldable foldable => foldable (Builder element) -> Builder element
concat :: foldable (Builder element) -> Builder element
concat foldable (Builder element)
builders =
  Int -> Update element -> Builder element
forall element. Int -> Update element -> Builder element
Builder
    (let
      step :: Int -> Builder element -> Int
step Int
size (Builder Int
builderSize Update element
_) = Int
size Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
builderSize
      in (Int -> Builder element -> Int)
-> Int -> foldable (Builder element) -> Int
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' Int -> Builder element -> Int
forall element. Int -> Builder element -> Int
step Int
0 foldable (Builder element)
builders)
    ((forall s (vector :: * -> * -> *).
 MVector vector element =>
 vector s element -> Int -> ST s ())
-> Update element
forall element.
(forall s (vector :: * -> * -> *).
 MVector vector element =>
 vector s element -> Int -> ST s ())
-> Update element
A.Update (\vector s element
mVector Int
offset -> (Int -> Builder element -> ST s Int)
-> Int -> foldable (Builder element) -> ST s ()
forall (t :: * -> *) (m :: * -> *) b a.
(Foldable t, Monad m) =>
(b -> a -> m b) -> b -> t a -> m ()
foldM_ (\Int
index (Builder Int
size (A.Update forall s (vector :: * -> * -> *).
MVector vector element =>
vector s element -> Int -> ST s ()
st)) ->
      vector s element -> Int -> ST s ()
forall s (vector :: * -> * -> *).
MVector vector element =>
vector s element -> Int -> ST s ()
st vector s element
mVector Int
index ST s () -> Int -> ST s Int
forall (f :: * -> *) a b. Functor f => f a -> b -> f b
$> Int
index Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
size)
      Int
offset
      foldable (Builder element)
builders))

-- * Instances

-- |
-- Provides support for /O(1)/ concatenation.
instance Semigroup (Builder element) where
  {-# INLINE (<>) #-}
  <> :: Builder element -> Builder element -> Builder element
(<>) =
    Builder element -> Builder element -> Builder element
forall element.
Builder element -> Builder element -> Builder element
prepend
  sconcat :: NonEmpty (Builder element) -> Builder element
sconcat =
    NonEmpty (Builder element) -> Builder element
forall (foldable :: * -> *) element.
Foldable foldable =>
foldable (Builder element) -> Builder element
concat

-- |
-- Provides support for /O(1)/ concatenation.
instance Monoid (Builder element) where
  {-# INLINE mempty #-}
  mempty :: Builder element
mempty =
    Builder element
forall element. Builder element
empty
  {-# INLINE mappend #-}
  mappend :: Builder element -> Builder element -> Builder element
mappend =
    Builder element -> Builder element -> Builder element
forall a. Semigroup a => a -> a -> a
(<>)
  {-# INLINE mconcat #-}
  mconcat :: [Builder element] -> Builder element
mconcat =
    [Builder element] -> Builder element
forall (foldable :: * -> *) element.
Foldable foldable =>
foldable (Builder element) -> Builder element
concat