{-# LANGUAGE BangPatterns #-} -- | -- Module : Data.Histogram.ST -- Copyright : Copyright (c) 2009, Alexey Khudyakov -- License : BSD3 -- Maintainer : Alexey Khudyakov -- Stability : experimental -- -- Mutable histograms. module Data.Histogram.ST ( -- * Mutable histograms MHistogram , newMHistogram , fill -- , fillMonoidAccum , unsafeFreezeHist , freezeHist ) where import Control.Monad import Control.Monad.Primitive import qualified Data.Vector.Generic as G import qualified Data.Vector.Generic.Mutable as M import Data.Histogram.Generic ---------------------------------------------------------------- -- Mutable histograms ---------------------------------------------------------------- -- | Mutable histogram. data MHistogram s v bin a = MHistogram {-# UNPACK #-} !Int -- Number of bins !bin -- Binning !(v s a) -- Bin contents. Underflows are stored at the -- n'th index and overflow are in the n+1 -- | Create new mutable histogram. All bins are set to zero element as -- passed to function. newMHistogram :: (PrimMonad m, Bin bin, M.MVector v a) => a -> bin -> m (MHistogram (PrimState m) v bin a) newMHistogram zero bin = do let n = nBins bin -- NOTE: replicate will create vector of zero length requested -- length is negative. Thus if number of bins is negative buffer -- will be shorter than 2. And it's assumed that there's always at -- least 2 bin. Consequently it could lead to memory corruption. when (n < 0) $ error "Data.Histogram.ST.newMHistogram: negative number of bins" a <- M.replicate (n + 2) zero return $ MHistogram n bin a {-# INLINE newMHistogram #-} -- | Generic fill. It could be seen as left fold with multiple -- accumulators where accumulator is chosen by @BinValue bin@. fill :: (PrimMonad m, M.MVector v a, Bin bin) => MHistogram (PrimState m) v bin a -- ^ Mutable histogram to put value to -> BinValue bin -- ^ Value being binned -> (a -> b -> a) -- ^ Fold function -> b -- ^ Value being put into histogram -> m () fill (MHistogram n bin arr) !x f val = do a <- M.unsafeRead arr ix M.unsafeWrite arr ix $! f a val where i = toIndex bin x ix | i < 0 = n | i >= n = n+1 | otherwise = i {-# INLINE fill #-} -- | Create immutable histogram from mutable one. This operation is -- unsafe! Accumulator mustn't be used after that unsafeFreezeHist :: (PrimMonad m, G.Vector v a, Bin bin) => MHistogram (PrimState m) (G.Mutable v) bin a -> m (Histogram v bin a) unsafeFreezeHist (MHistogram n bin arr) = do u <- M.unsafeRead arr n o <- M.unsafeRead arr (n+1) a <- G.unsafeFreeze $ M.slice 0 n arr return $ histogramUO bin (Just (u,o)) a {-# INLINE unsafeFreezeHist #-} -- | Create immutable histogram from mutable one. freezeHist :: (PrimMonad m, G.Vector v a, Bin bin) => MHistogram (PrimState m) (G.Mutable v) bin a -> m (Histogram v bin a) freezeHist (MHistogram n bin arr) = do u <- M.unsafeRead arr n o <- M.unsafeRead arr (n+1) a <- G.freeze $ M.slice 0 n arr return $ histogramUO bin (Just (u,o)) a {-# INLINE freezeHist #-}