on master help sign in
Implement intersectBySorted API
commit160393c8e0aed7143ab1e49c558167545353b9c2 authorRanjeet Kumar Ranjan <ranjeetr@composewell.com> dateWed 5 Jan 2022, 23:10 (5 years ago) parent4bc714f
5 files changed+111−11
benchmark/Streamly/Benchmark/Prelude/Serial/NestedStream.hs+4 src/Streamly/Internal/Data/Stream/IsStream/Top.hs+8−5 src/Streamly/Internal/Data/Stream/StreamD/Nesting.hs+54 streamly.cabal+1−1 test/Streamly/Test/Prelude/Top.hs+44−5
benchmark/Streamly/Benchmark/Prelude/Serial/NestedStream.hs+4
@@ -459,6 +459,10 @@ o_n_heap_buffering value =
459459 $ joinWith Internal.joinOuter sqrtVal sqrtVal
460460 , benchIOSrc1 "joinOuterMap"
461461 $ joinMapWith Internal.joinOuterMap sqrtVal sqrtVal
462+ , benchIOSrc1 "intersectBy"
463+ $ joinWith Internal.intersectBy sqrtVal sqrtVal
464+ , benchIOSrc1 "intersectBySorted"
465+ $ joinMapWith Internal.intersectBySorted sqrtVal sqrtVal
462466 ]
463467 ]
464468
src/Streamly/Internal/Data/Stream/IsStream/Top.hs+8−5
@@ -28,7 +28,7 @@ module Streamly.Internal.Data.Stream.IsStream.Top
2828 -- | These are not exactly set operations because streams are not
2929 -- necessarily sets, they may have duplicated elements.
3030 , intersectBy
31− , mergeIntersectBy
31+ , intersectBySorted
3232 , differenceBy
3333 , mergeDifferenceBy
3434 , unionBy
@@ -65,6 +65,7 @@ import Streamly.Internal.Data.Stream.IsStream.Common (concatM)
6565import Streamly.Internal.Data.Stream.IsStream.Type
6666 (IsStream(..), adapt, foldl', fromList)
6767import Streamly.Internal.Data.Stream.Serial (SerialT)
68+--import Streamly.Internal.Data.Stream.StreamD (fromStreamD, toStreamD)
6869import Streamly.Internal.Data.Time.Units (NanoSecond64(..), toRelTime64)
6970
7071import qualified Data.List as List
@@ -79,6 +80,7 @@ import qualified Streamly.Internal.Data.Stream.IsStream.Expand as Stream
7980import qualified Streamly.Internal.Data.Stream.IsStream.Reduce as Stream
8081import qualified Streamly.Internal.Data.Stream.IsStream.Transform as Stream
8182import qualified Streamly.Internal.Data.Stream.IsStream.Type as IsStream
83+import qualified Streamly.Internal.Data.Stream.StreamD as StreamD
8284
8385import Prelude hiding (filter, zipWith, concatMap, concat)
8486
@@ -580,11 +582,12 @@ intersectBy eq s1 s2 =
580582--
581583-- Time: O(m+n)
582584--
583−-- /Unimplemented/
584−{-# INLINE mergeIntersectBy #-}
585−mergeIntersectBy :: -- (IsStream t, Monad m) =>
585+-- /Pre-release/
586+{-# INLINE intersectBySorted #-}
587+intersectBySorted :: (IsStream t, MonadIO m, Eq a) =>
586588 (a -> a -> Ordering) -> t m a -> t m a -> t m a
587−mergeIntersectBy _eq _s1 _s2 = undefined
589+intersectBySorted eq s1 =
590+ IsStream.fromStreamD . StreamD.intersectBySorted eq (IsStream.toStreamD s1) . IsStream.toStreamD
588591
589592-- Roughly joinLeft s1 s2 = s1 `difference` s2 + s1 `intersection` s2
590593
src/Streamly/Internal/Data/Stream/StreamD/Nesting.hs+54
@@ -142,6 +142,7 @@ module Streamly.Internal.Data.Stream.StreamD.Nesting
142142 -- | Opposite to compact in ArrayStream
143143 , splitInnerBy
144144 , splitInnerBySuffix
145+ , intersectBySorted
145146 )
146147where
147148
@@ -482,6 +483,59 @@ mergeBy
482483 => (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a
483484mergeBy cmp = mergeByM (\a b -> return $ cmp a b)
484485
486+-------------------------------------------------------------------------------
487+-- Intersection of sorted streams ---------------------------------------------
488+-------------------------------------------------------------------------------
489+{-# INLINE_NORMAL intersectBySorted #-}
490+intersectBySorted
491+ :: (MonadIO m, Eq a)
492+ => (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a
493+intersectBySorted cmp (Stream stepa ta) (Stream stepb tb) =
494+ Stream step (Just ta, Just tb, Nothing, Nothing, Nothing)
495+
496+ where
497+ {-# INLINE_LATE step #-}
498+
499+ -- step 1
500+ step gst (Just sa, sb, Nothing, b, Nothing) = do
501+ r <- stepa gst sa
502+ return $ case r of
503+ Yield a sa' -> Skip (Just sa', sb, Just a, b, Nothing)
504+ Skip sa' -> Skip (Just sa', sb, Nothing, b, Nothing)
505+ Stop -> Stop
506+
507+ -- step 2
508+ step gst (sa, Just sb, a, Nothing, Nothing) = do
509+ r <- stepb gst sb
510+ return $ case r of
511+ Yield b sb' -> Skip (sa, Just sb', a, Just b, Nothing)
512+ Skip sb' -> Skip (sa, Just sb', a, Nothing, Nothing)
513+ Stop -> Stop
514+
515+ -- step 3
516+ -- both the values are available compare it
517+ step _ (sa, sb, Just a, Just b, Nothing) = do
518+ let res = cmp a b
519+ return $ case res of
520+ GT -> Skip (sa, sb, Just a, Nothing, Nothing)
521+ LT -> Skip (sa, sb, Nothing, Just b, Nothing)
522+ EQ -> Yield a (sa, sb, Nothing, Just a, Just b) -- step 4
523+
524+ -- step 4
525+ -- Matching element
526+ step gst (Just sa, Just sb, Nothing, Just _, Just b) = do
527+ r1 <- stepa gst sa
528+ return $ case r1 of
529+ Yield a' sa' -> do
530+ if a' == b -- match with prev a
531+ then Yield a' (Just sa', Just sb, Nothing, Just b, Just b) --step 1
532+ else Skip (Just sa', Just sb, Just a', Nothing, Nothing)
533+
534+ Skip sa' -> Skip (Just sa', Just sb, Nothing, Nothing, Nothing)
535+ Stop -> Stop
536+
537+ step _ (_, _, _, _, _) = return Stop
538+
485539------------------------------------------------------------------------------
486540-- Combine N Streams - unfoldMany
487541------------------------------------------------------------------------------
streamly.cabal+1−1
@@ -101,7 +101,7 @@ extra-source-files:
101101 test/Streamly/Test/Data/Array/Prim/Pinned.hs
102102 test/Streamly/Test/Data/Array/Foreign.hs
103103 test/Streamly/Test/Data/Array/Stream/Foreign.hs
104− test/Streamly/Test/Data/Parser/ParserD.hs
104+ test/Streamly/Test/Data/Parser/ParserD.hs
105105 test/Streamly/Test/FileSystem/Event.hs
106106 test/Streamly/Test/FileSystem/Event/Common.hs
107107 test/Streamly/Test/FileSystem/Event/Darwin.hs
test/Streamly/Test/Prelude/Top.hs+44−5
@@ -1,6 +1,7 @@
1−module Main (main) where
1+module Main (main)
2+ where
23
3−import Data.List (elem, nub, sort)
4+import Data.List (elem, intersect, nub, sort)
45import Data.Maybe (isNothing)
56import Test.QuickCheck
67 ( Gen
@@ -168,10 +169,45 @@ joinLeftMap =
168169 let v2 = joinLeftList ls0 ls1
169170 assert (v1 == v2)
170171
171−-------------------------------------------------------------------------------
172−-- Main
173−-------------------------------------------------------------------------------
172+intersectBy :: Property
173+intersectBy =
174+ forAll (listOf (chooseInt (min_value, max_value))) $ \ls0 ->
175+ forAll (listOf (chooseInt (min_value, max_value))) $ \ls1 ->
176+ monadicIO $ action (sort ls0) (sort ls1)
177+
178+ where
174179
180+ action ls0 ls1 = do
181+ v1 <-
182+ run
183+ $ S.toList
184+ $ Top.intersectBy
185+ (==)
186+ (S.fromList ls0)
187+ (S.fromList ls1)
188+ let v2 = intersect ls0 ls1
189+ assert (v1 == sort v2)
190+
191+intersectBySorted :: Property
192+intersectBySorted =
193+ forAll (listOf (chooseInt (min_value, max_value))) $ \ls0 ->
194+ forAll (listOf (chooseInt (min_value, max_value))) $ \ls1 ->
195+ monadicIO $ action (sort ls0) (sort ls1)
196+
197+ where
198+
199+ action ls0 ls1 = do
200+ v1 <-
201+ run
202+ $ S.toList
203+ $ Top.intersectBySorted
204+ compare
205+ (S.fromList ls0)
206+ (S.fromList ls1)
207+ let v2 = intersect ls0 ls1
208+ assert (v1 == sort v2)
209+
210+-------------------------------------------------------------------------------
175211moduleName :: String
176212moduleName = "Prelude.Top"
177213
@@ -187,3 +223,6 @@ main = hspec $ do
187223 prop "joinOuterMap" Main.joinOuterMap
188224 prop "joinLeft" Main.joinLeft
189225 prop "joinLeftMap" Main.joinLeftMap
226+ -- intersect
227+ prop "intersectBy" Main.intersectBy
228+ prop "intersectBySorted" Main.intersectBySorted