packages feed

seqn-0.1.0.0: src/Data/Seqn/Internal/Stream.hs

{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE ExistentialQuantification #-}

module Data.Seqn.Internal.Stream
  ( Step(..)
  , Stream(..)
  , foldr
  , ifoldr
  , isPrefixOf
  , isSubsequenceOf
  , zipWith
  ) where

import Prelude hiding (foldr, zipWith)
import Data.Functor.Classes (Eq1(..), Ord1(..), eq1, compare1)

import qualified Data.Seqn.Internal.Util as U

-- Budget stream fusion
-- The pieces here are adopted from vector-stream. See vector-stream on Hackage
-- for a more complete implementation.

-- Always benchmark and check the Core when making changes to stream stuff!

data Stream a = forall s. Stream (s -> Step s a) s

data Step s a
  = Yield !a s
  | Done

instance Eq a => Eq (Stream a) where
  (==) = eq1
  {-# INLINE (==) #-}

instance Eq1 Stream where
  liftEq f (Stream step1 s10) (Stream step2 s20) = go s10 s20
    where
      go s1 s2 = case step1 s1 of
        Yield x1 s1' -> case step2 s2 of
          Yield x2 s2' -> f x1 x2 && go s1' s2'
          Done -> False
        Done -> case step2 s2 of
          Yield _ _ -> False
          Done -> True
  {-# INLINE liftEq #-}

instance Ord a => Ord (Stream a) where
  compare = compare1
  {-# INLINE compare #-}

instance Ord1 Stream where
  liftCompare f (Stream step1 s10) (Stream step2 s20) = go s10 s20
    where
      go s1 s2 = case step1 s1 of
        Yield x1 s1' -> case step2 s2 of
          Yield x2 s2' -> f x1 x2 <> go s1' s2'
          Done -> GT
        Done -> case step2 s2 of
          Yield _ _ -> LT
          Done -> EQ
  {-# INLINE liftCompare #-}

foldr :: (a -> b -> b) -> b -> Stream a -> b
foldr f z (Stream step s0) = go s0
  where
    go s = case step s of
      Yield x s' -> f x (go s')
      Done -> z
{-# INLINE foldr #-}

ifoldr :: (Int -> a -> b -> b) -> b -> Int -> (Int -> Int) -> Stream a -> b
ifoldr f z i0 istep (Stream step s0) = go i0 s0
  where
    go !i s = case step s of
      Yield x s' -> f i x (go (istep i) s')
      Done -> z
{-# INLINE ifoldr #-}

isPrefixOf :: Eq a => Stream a -> Stream a -> Bool
isPrefixOf (Stream step1 s10) (Stream step2 s20) = go s10 s20
  where
    go s1 s2 = case step1 s1 of
      Yield x1 s1' -> case step2 s2 of
        Yield x2 s2' -> x1 == x2 && go s1' s2'
        Done -> False
      Done -> True
{-# INLINE isPrefixOf #-}

isSubsequenceOf :: Eq a => Stream a -> Stream a -> Bool
isSubsequenceOf (Stream step1 s10) (Stream step2 s20) = go1 s10 s20
  where
    go1 s1 s2 = case step1 s1 of
      Yield x s1' -> go2 x s1' s2
      Done -> True
    go2 !x s1' s2 = case step2 s2 of
      Yield y s2'
        | x == y -> go1 s1' s2'
        | otherwise -> go2 x s1' s2'
      Done -> False
{-# INLINE isSubsequenceOf #-}

zipWith :: (a -> b -> c) -> Stream a -> Stream b -> Stream c
zipWith f (Stream step1 s10) (Stream step2 s20) = Stream step (U.S2 s10 s20)
  where
    step (U.S2 s1 s2) = case step1 s1 of
      Yield x1 s1' -> case step2 s2 of
        Yield x2 s2' -> Yield (f x1 x2) (U.S2 s1' s2')
        Done -> Done
      Done -> Done
    {-# INLINE [0] step #-}
{-# INLINE zipWith #-}