-- |
-- Utility for construction of vectors when the size is not known ahead.
module VectorExtras.Accumulator
  ( Accumulator,
    init,
    add,
    addList,
    addFoldable,

    -- * Execution
    toVector,
    toReverseVector,
  )
where

import Data.Vector.Generic (Vector, fromListN)
import qualified VectorExtras.Basics.Generic as Basics
import VectorExtras.Prelude hiding (fromListN, init, length)

-- |
-- Finalise the accumulator as vector.
{-# INLINE toVector #-}
toVector :: (Vector v a) => Accumulator a -> v a
toVector :: forall (v :: * -> *) a. Vector v a => Accumulator a -> v a
toVector (Accumulator Int
size [a]
list) =
  forall (v :: * -> *) a. Vector v a => Int -> [a] -> v a
Basics.fromReverseListN Int
size [a]
list

-- |
-- Finalise the accumulator as vector in reverse order.
{-# INLINE toReverseVector #-}
toReverseVector :: (Vector v a) => Accumulator a -> v a
toReverseVector :: forall (v :: * -> *) a. Vector v a => Accumulator a -> v a
toReverseVector (Accumulator Int
size [a]
list) =
  forall (v :: * -> *) a. Vector v a => Int -> [a] -> v a
fromListN Int
size [a]
list

-- |
-- Constructor of vectors optimised for appending elements one by one,
-- providing for \(\mathcal{O}(n)\) complexity of the whole construction process.
--
-- Very useful as accumulator in folds.
--
-- Under the hood it is the size counter and a reverse list of elements.
-- When executed, a vector of the according size gets allocated and
-- gets populated with the elements in reverse order
-- (starting from the last element).
data Accumulator a
  = Accumulator !Int ![a]

-- | Create an empty accumulator.
init :: Accumulator a
init :: forall a. Accumulator a
init = forall a. Int -> [a] -> Accumulator a
Accumulator Int
0 []

-- |
-- Add an element to the accumulator.
{-# INLINE add #-}
add :: a -> Accumulator a -> Accumulator a
add :: forall a. a -> Accumulator a -> Accumulator a
add a
head (Accumulator Int
size [a]
tail) =
  forall a. Int -> [a] -> Accumulator a
Accumulator (forall a. Enum a => a -> a
succ Int
size) (a
head forall a. a -> [a] -> [a]
: [a]
tail)

-- |
-- Add a list of elements to the accumulator.
{-# INLINE addList #-}
addList :: [a] -> Accumulator a -> Accumulator a
addList :: forall a. [a] -> Accumulator a -> Accumulator a
addList = forall (f :: * -> *) a.
Foldable f =>
f a -> Accumulator a -> Accumulator a
addFoldable

-- |
-- Add a foldable of elements to the accumulator.
{-# INLINE addFoldable #-}
addFoldable :: (Foldable f) => f a -> Accumulator a -> Accumulator a
addFoldable :: forall (f :: * -> *) a.
Foldable f =>
f a -> Accumulator a -> Accumulator a
addFoldable f a
foldable Accumulator a
acc =
  forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' (\Accumulator a
acc a
a -> forall a. a -> Accumulator a -> Accumulator a
add a
a Accumulator a
acc) Accumulator a
acc f a
foldable