{-# LANGUAGE TypeFamilies #-} {-# OPTIONS_GHC -Wall #-} -- | Generally the best-performing implementation in this package. -- -- 'ComboBuffer' supports amortized O(1) push and O(1) length. lookups at 'ix -- >= n/2' and 'ix=0' are O(1), while lookups at '0 <= ix < n/2' are no more -- than O(n). If you need to do many lookups in the first half, and -- particularly around n/4, another structure may be a better choice. module Data.RingBuffer.ComboBuffer ( ComboBuffer (..) ) where import Prelude hiding (length) import Data.RingBuffer.Class import Data.RingBuffer.Chord import qualified Data.Vector.Unboxed as V data ComboBuffer a = CB {-# UNPACK #-} !Int -- Half Size {-# UNPACK #-} !Int -- buffer position !(V.Vector a) -- newer vector !(V.Vector a) -- older vector (Chord a) -- updates | CBOdd {-# UNPACK #-} !Int -- Half (Size + 1) {-# UNPACK #-} !Int -- buffer position !(V.Vector a) -- newer vector !(V.Vector a) -- older vector (Chord a) -- updates deriving (Eq, Ord, Show) type instance El (ComboBuffer a) = a instance V.Unbox a => Initializable (ComboBuffer a) where {-# INLINE newInit #-} newInit = newInit' newInit' :: V.Unbox a => a -> Int -> ComboBuffer a newInit' el sz | sz <= 0 = error "Can't initialize ComboBuffer with size <= 0" | sz `rem` 2 == 0 = let half = sz `div` 2 iv = V.replicate half el in CB half 0 iv iv (emptyChord el) | otherwise = let half = (1+sz) `div` 2 iv = V.replicate half el in CBOdd half 0 iv iv (emptyChord el) {-# INLINE newInit' #-} instance V.Unbox a => RingBuffer (ComboBuffer a) where {-# INLINE length #-} length (CB sz _ _ _ _) = 2*sz length (CBOdd sz _ _ _ _) = 2*sz-1 {-# INLINE (!) #-} (!) = at {-# INLINE push #-} push = pushB {-# INLINE slice #-} slice = sliceB at :: V.Unbox a => ComboBuffer a -> Int -> a at (CB sz pos v1 v2 upds) ix | ix < 0 = error $ "ComboBuffer: index out of range: " ++ show ix | ix < pos = upds ! ix | ix < 2*sz = let ix' = ix - pos in if ix' < sz then v1 `V.unsafeIndex` ix' else v2 `V.unsafeIndex` (ix' - sz) | otherwise = error $ "ComboBuffer: index out of range: " ++ show (ix,2*sz) at (CBOdd sz pos v1 v2 upds) ix | ix < 0 = error $ "ComboBuffer: index out of range: " ++ show ix | ix < pos = upds ! ix | ix < 2*sz-1 = let ix' = ix - pos in if ix' < sz then v1 `V.unsafeIndex` ix' else v2 `V.unsafeIndex` (ix' - sz) | otherwise = error $ "ComboBuffer: index out of range: " ++ show (ix,2*sz-1) {-# INLINE at #-} sliceB :: V.Unbox a => ComboBuffer a -> Int -> Int -> [a] sliceB (CB sz pos v1 v2 upds) start num = let ix' = start - pos fstTake = sz - (ix'+num) in if ix' < sz then slice upds start num ++ V.toList (V.slice ix' (min 0 fstTake) v1) ++ V.toList (V.slice 0 (min 0 (negate fstTake)) v2) else V.toList (V.slice (ix'-sz) num v2) sliceB (CBOdd sz pos v1 v2 upds) start num = let ix' = start - pos fstTake = sz - (ix'+num) in if ix' < sz then slice upds start num ++ V.toList (V.slice ix' (min 0 fstTake) v1) ++ V.toList (V.slice 0 (min 0 (negate fstTake)) v2) else V.toList (V.slice (ix'-sz) num v2) {-# INLINE sliceB #-} pushB :: V.Unbox a => ComboBuffer a -> a -> ComboBuffer a pushB (CB sz pos v1 v2 upds) el | pos == sz-1 = CB sz 0 (cToVec $ push upds el) v1 (emptyChord el) | otherwise = CB sz (pos+1) v1 v2 (push upds el) pushB (CBOdd sz pos v1 v2 upds) el | pos == sz-1 = CBOdd sz 0 (cToVec $ push upds el) v1 (emptyChord el) | otherwise = CBOdd sz (pos+1) v1 v2 (push upds el) {-# INLINE pushB #-}