@@ -1,5 +1,5 @@
11-- |
2−-- Module : Serial.NestedFold
2+-- Module : Stream.Reduce
33-- Copyright : (c) 2018 Composewell Technologies
44-- License : BSD-3-Clause
55-- Maintainer : streamly@composewell.com
@@ -11,26 +11,37 @@
1111{-# LANGUAGE DeriveGeneric #-}
1212{-# LANGUAGE RankNTypes #-}
1313
14−module Serial.NestedFold (benchmarks) where
14+module Stream.Reduce (benchmarks) where
1515
1616import Control.DeepSeq (NFData(..))
17−import Control.Monad (when)
1817import Control.Monad.IO.Class (MonadIO(..))
1918import Data.Monoid (Sum(..))
20−import Data.Proxy (Proxy(..))
21−import Data.HashMap.Strict (HashMap)
2219import GHC.Generics (Generic)
2320import Streamly.Internal.Data.IsMap.HashMap ()
21+import Streamly.Internal.Data.Stream.Serial (SerialT)
2422
2523import qualified Streamly.Internal.Data.Refold.Type as Refold
2624import qualified Streamly.Internal.Data.Fold as FL
27−import qualified Streamly.Internal.Data.Stream.IsStream as Internal
28−import qualified Streamly.Prelude as S
25+import qualified Stream.Common as Common
26+#ifdef USE_PRELUDE
27+import Control.Monad (when)
28+import Data.Proxy (Proxy(..))
29+import Data.HashMap.Strict (HashMap)
30+import qualified Streamly.Internal.Data.Stream.IsStream as S
31+import Streamly.Prelude (fromSerial)
32+import Streamly.Benchmark.Prelude hiding
33+ ( benchIOSrc, sourceUnfoldrM, apDiscardFst, apDiscardSnd, apLiftA2
34+ , toNullAp, monadThen, toNullM, toNullM3, filterAllInM, filterAllOutM
35+ , filterSome, breakAfterSome, toListM, toListSome, transformMapM
36+ , transformComposeMapM, transformTeeMapM, transformZipMapM)
37+#else
38+import Streamly.Benchmark.Prelude (benchIO)
39+import qualified Streamly.Internal.Data.Stream as S
40+#endif
2941
3042import Gauge
31−import Streamly.Prelude (SerialT, fromSerial)
3243import Streamly.Benchmark.Common
33−import Streamly.Benchmark.Prelude
44+import Stream.Common
3445import Prelude hiding (reverse, tail)
3546
3647-------------------------------------------------------------------------------
@@ -48,13 +59,14 @@ iterateN g initial count = f count initial
4859
4960-- Iterate a transformation over a singleton stream
5061{-# INLINE iterateSingleton #-}
51−iterateSingleton :: S.MonadAsync m
62+iterateSingleton :: Monad m
5263 => (Int -> SerialT m Int -> SerialT m Int)
5364 -> Int
5465 -> Int
5566 -> SerialT m Int
5667iterateSingleton g count n = iterateN g (return n) count
5768
69+{-
5870-- XXX need to check why this is slower than the explicit recursion above, even
5971-- if the above code is written in a foldr like head recursive way. We also
6072-- need to try this with foldlM' once #150 is fixed.
@@ -62,17 +74,18 @@ iterateSingleton g count n = iterateN g (return n) count
6274-- foldrM and any related fusion issues.
6375{-# INLINE _iterateSingleton #-}
6476_iterateSingleton ::
65− S.MonadAsync m
77+ Monad m
6678 => (Int -> SerialT m Int -> SerialT m Int)
6779 -> Int
6880 -> Int
6981 -> SerialT m Int
7082_iterateSingleton g value n = S.foldrM g (return n) $ sourceIntFromTo value n
83+-}
7184
7285-- Apply transformation g count times on a stream of length len
7386{-# INLINE iterateSource #-}
7487iterateSource ::
75− S.MonadAsync m
88+ MonadAsync m
7689 => (SerialT m Int -> SerialT m Int)
7790 -> Int
7891 -> Int
@@ -94,9 +107,9 @@ o_n_space_functor value =
94107 [ bgroup "Functor"
95108 [ benchIO "(+) (n times) (baseline)" $ \i0 ->
96109 iterateN (\i acc -> acc >>= \n -> return $ i + n) (return i0) value
97− , benchIOSrc fromSerial "(<$) (n times)" $
110+ , benchIOSrc "(<$) (n times)" $
98111 iterateSingleton (<$) value
99− , benchIOSrc fromSerial "fmap (n times)" $
112+ , benchIOSrc "fmap (n times)" $
100113 iterateSingleton (fmap . (+)) value
101114 {-
102115 , benchIOSrc fromSerial "_(<$) (n times)" $
@@ -111,74 +124,92 @@ o_n_space_functor value =
111124-- Grouping transformations
112125-------------------------------------------------------------------------------
113126
127+#ifdef USE_PRELUDE
114128{-# INLINE groups #-}
115129groups :: MonadIO m => SerialT m Int -> m ()
116−groups = S.drain . S.groups FL.drain
130+groups = Common.drain . S.groups FL.drain
117131
118132-- XXX Change this test when the order of comparison is later changed
119133{-# INLINE groupsByGT #-}
120134groupsByGT :: MonadIO m => SerialT m Int -> m ()
121−groupsByGT = S.drain . S.groupsBy (>) FL.drain
135+groupsByGT = Common.drain . S.groupsBy (>) FL.drain
122136
123137{-# INLINE groupsByEq #-}
124138groupsByEq :: MonadIO m => SerialT m Int -> m ()
125−groupsByEq = S.drain . S.groupsBy (==) FL.drain
139+groupsByEq = Common.drain . S.groupsBy (==) FL.drain
126140
127141-- XXX Change this test when the order of comparison is later changed
128142{-# INLINE groupsByRollingLT #-}
129143groupsByRollingLT :: MonadIO m => SerialT m Int -> m ()
130144groupsByRollingLT =
131− S.drain . S.groupsByRolling (<) FL.drain
145+ Common.drain . S.groupsByRolling (<) FL.drain
132146
133147{-# INLINE groupsByRollingEq #-}
134148groupsByRollingEq :: MonadIO m => SerialT m Int -> m ()
135149groupsByRollingEq =
136− S.drain . S.groupsByRolling (==) FL.drain
150+ Common.drain . S.groupsByRolling (==) FL.drain
151+#endif
137152
138153{-# INLINE foldMany #-}
139154foldMany :: Monad m => SerialT m Int -> m ()
140155foldMany =
141− S.drain
142− . S.map getSum
143− . Internal.foldMany (FL.take 2 FL.mconcat)
144− . S.map Sum
156+ Common.drain
157+ . fmap getSum
158+ . S.foldMany (FL.take 2 FL.mconcat)
159+ . fmap Sum
160+
161+{-# INLINE foldManyPost #-}
162+foldManyPost :: Monad m => SerialT m Int -> m ()
163+foldManyPost =
164+ Common.drain
165+ . fmap getSum
166+ . S.foldManyPost (FL.take 2 FL.mconcat)
167+ . fmap Sum
145168
146169{-# INLINE refoldMany #-}
147170refoldMany :: Monad m => SerialT m Int -> m ()
148171refoldMany =
149− S.drain
150− . S.map getSum
151− . Internal.refoldMany (Refold.take 2 Refold.sconcat) (return mempty)
152− . S.map Sum
172+ Common.drain
173+ . fmap getSum
174+ . S.refoldMany (Refold.take 2 Refold.sconcat) (return mempty)
175+ . fmap Sum
153176
154177{-# INLINE foldIterateM #-}
155178foldIterateM :: Monad m => SerialT m Int -> m ()
156179foldIterateM =
157− S.drain
158− . S.map getSum
159− . Internal.foldIterateM
180+ Common.drain
181+ . fmap getSum
182+ . S.foldIterateM
160183 (return . FL.take 2 . FL.sconcat) (return (Sum 0))
161− . S.map Sum
184+ . fmap Sum
162185
163186{-# INLINE refoldIterateM #-}
164187refoldIterateM :: Monad m => SerialT m Int -> m ()
165188refoldIterateM =
166− S.drain
167− . S.map getSum
168− . Internal.refoldIterateM
189+ Common.drain
190+ . fmap getSum
191+ . S.refoldIterateM
169192 (Refold.take 2 Refold.sconcat) (return (Sum 0))
170− . S.map Sum
193+ . fmap Sum
171194
172195o_1_space_grouping :: Int -> [Benchmark]
173196o_1_space_grouping value =
174197 -- Buffering operations using heap proportional to group/window sizes.
175198 [ bgroup "grouping"
176− [ benchIOSink value "groups" groups
199+ [
200+#ifdef USE_PRELUDE
201+ benchIOSink value "groups" groups
177202 , benchIOSink value "groupsByGT" groupsByGT
178203 , benchIOSink value "groupsByEq" groupsByEq
179204 , benchIOSink value "groupsByRollingLT" groupsByRollingLT
180205 , benchIOSink value "groupsByRollingEq" groupsByRollingEq
181− , benchIOSink value "foldMany" foldMany
206+ ,
207+#endif
208+ -- XXX parseMany/parseIterate benchmarks are in the Parser/ParserD
209+ -- modules we can bring those here. arraysOf benchmarks are in
210+ -- Parser/ParserD/Array.Stream/FileSystem.Handle.
211+ benchIOSink value "foldMany" foldMany
212+ , benchIOSink value "foldManyPost" foldManyPost
182213 , benchIOSink value "refoldMany" refoldMany
183214 , benchIOSink value "foldIterateM" foldIterateM
184215 , benchIOSink value "refoldIterateM" refoldIterateM
@@ -195,7 +226,7 @@ reverse n = composeN n S.reverse
195226
196227{-# INLINE reverse' #-}
197228reverse' :: MonadIO m => Int -> SerialT m Int -> m ()
198−reverse' n = composeN n Internal.reverse'
229+reverse' n = composeN n S.reverse'
199230
200231o_n_heap_buffering :: Int -> [Benchmark]
201232o_n_heap_buffering value =
@@ -205,7 +236,9 @@ o_n_heap_buffering value =
205236 benchIOSink value "reverse" (reverse 1)
206237 , benchIOSink value "reverse'" (reverse' 1)
207238
239+#ifdef USE_PRELUDE
208240 , benchIOSink value "mkAsync" (mkAsync fromSerial)
241+#endif
209242 ]
210243 ]
211244
@@ -213,25 +246,26 @@ o_n_heap_buffering value =
213246-- Grouping/Splitting
214247-------------------------------------------------------------------------------
215248
249+#ifdef USE_PRELUDE
216250{-# INLINE classifySessionsOf #-}
217−classifySessionsOf :: S.MonadAsync m => (Int -> Int) -> SerialT m Int -> m ()
251+classifySessionsOf :: MonadAsync m => (Int -> Int) -> SerialT m Int -> m ()
218252classifySessionsOf getKey =
219− S.drain
220− . Internal.classifySessionsOf
253+ Common.drain
254+ . S.classifySessionsOf
221255 (const (return False)) 3 (FL.take 10 FL.sum)
222− . Internal.timestamped
223− . S.map (\x -> (getKey x, x))
256+ . S.timestamped
257+ . fmap (\x -> (getKey x, x))
224258
225259{-# INLINE classifySessionsOfHash #-}
226−classifySessionsOfHash :: S.MonadAsync m =>
260+classifySessionsOfHash :: MonadAsync m =>
227261 (Int -> Int) -> SerialT m Int -> m ()
228262classifySessionsOfHash getKey =
229− S.drain
230− . Internal.classifySessionsByGeneric
263+ Common.drain
264+ . S.classifySessionsByGeneric
231265 (Proxy :: Proxy (HashMap k))
232266 1 False (const (return False)) 3 (FL.take 10 FL.sum)
233− . Internal.timestamped
234− . S.map (\x -> (getKey x, x))
267+ . S.timestamped
268+ . fmap (\x -> (getKey x, x))
235269
236270o_n_space_grouping :: Int -> [Benchmark]
237271o_n_space_grouping value =
@@ -251,6 +285,7 @@ o_n_space_grouping value =
251285 where
252286
253287 getKey n = (`mod` n)
288+#endif
254289
255290-------------------------------------------------------------------------------
256291-- Mixed Transformation
@@ -258,15 +293,15 @@ o_n_space_grouping value =
258293
259294{-# INLINE scanMap #-}
260295scanMap :: MonadIO m => Int -> SerialT m Int -> m ()
261−scanMap n = composeN n $ S.map (subtract 1) . S.scanl' (+) 0
296+scanMap n = composeN n $ fmap (subtract 1) . Common.scanl' (+) 0
262297
263298{-# INLINE dropMap #-}
264299dropMap :: MonadIO m => Int -> SerialT m Int -> m ()
265−dropMap n = composeN n $ S.map (subtract 1) . S.drop 1
300+dropMap n = composeN n $ fmap (subtract 1) . S.drop 1
266301
267302{-# INLINE dropScan #-}
268303dropScan :: MonadIO m => Int -> SerialT m Int -> m ()
269−dropScan n = composeN n $ S.scanl' (+) 0 . S.drop 1
304+dropScan n = composeN n $ Common.scanl' (+) 0 . S.drop 1
270305
271306{-# INLINE takeDrop #-}
272307takeDrop :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
@@ -274,11 +309,11 @@ takeDrop value n = composeN n $ S.drop 1 . S.take (value + 1)
274309
275310{-# INLINE takeScan #-}
276311takeScan :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
277−takeScan value n = composeN n $ S.scanl' (+) 0 . S.take (value + 1)
312+takeScan value n = composeN n $ Common.scanl' (+) 0 . S.take (value + 1)
278313
279314{-# INLINE takeMap #-}
280315takeMap :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
281−takeMap value n = composeN n $ S.map (subtract 1) . S.take (value + 1)
316+takeMap value n = composeN n $ fmap (subtract 1) . S.take (value + 1)
282317
283318{-# INLINE filterDrop #-}
284319filterDrop :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
@@ -290,15 +325,17 @@ filterTake value n = composeN n $ S.take (value + 1) . S.filter (<= (value + 1))
290325
291326{-# INLINE filterScan #-}
292327filterScan :: MonadIO m => Int -> SerialT m Int -> m ()
293−filterScan n = composeN n $ S.scanl' (+) 0 . S.filter (<= maxBound)
328+filterScan n = composeN n $ Common.scanl' (+) 0 . S.filter (<= maxBound)
294329
330+#ifdef USE_PRELUDE
295331{-# INLINE filterScanl1 #-}
296332filterScanl1 :: MonadIO m => Int -> SerialT m Int -> m ()
297333filterScanl1 n = composeN n $ S.scanl1' (+) . S.filter (<= maxBound)
334+#endif
298335
299336{-# INLINE filterMap #-}
300337filterMap :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
301−filterMap value n = composeN n $ S.map (subtract 1) . S.filter (<= (value + 1))
338+filterMap value n = composeN n $ fmap (subtract 1) . S.filter (<= (value + 1))
302339
303340-------------------------------------------------------------------------------
304341-- Scan and fold
@@ -310,17 +347,17 @@ data Pair a b =
310347
311348{-# INLINE sumProductFold #-}
312349sumProductFold :: Monad m => SerialT m Int -> m (Int, Int)
313−sumProductFold = S.foldl' (\(s, p) x -> (s + x, p * x)) (0, 1)
350+sumProductFold = Common.foldl' (\(s, p) x -> (s + x, p * x)) (0, 1)
314351
315352{-# INLINE sumProductScan #-}
316353sumProductScan :: Monad m => SerialT m Int -> m (Pair Int Int)
317354sumProductScan =
318− S.foldl' (\(Pair _ p) (s0, x) -> Pair s0 (p * x)) (Pair 0 1) .
319− S.scanl' (\(s, _) x -> (s + x, x)) (0, 0)
355+ Common.foldl' (\(Pair _ p) (s0, x) -> Pair s0 (p * x)) (Pair 0 1) .
356+ Common.scanl' (\(s, _) x -> (s + x, x)) (0, 0)
320357
321358{-# INLINE foldl'ReduceMap #-}
322359foldl'ReduceMap :: Monad m => SerialT m Int -> m Int
323−foldl'ReduceMap = fmap (+ 1) . S.foldl' (+) 0
360+foldl'ReduceMap = fmap (+ 1) . Common.foldl' (+) 0
324361
325362o_1_space_transformations_mixed :: Int -> [Benchmark]
326363o_1_space_transformations_mixed value =
@@ -347,7 +384,9 @@ o_1_space_transformations_mixedX4 value =
347384 , benchIOSink value "filter-drop" (filterDrop value 4)
348385 , benchIOSink value "filter-take" (filterTake value 4)
349386 , benchIOSink value "filter-scan" (filterScan 4)
387+#ifdef USE_PRELUDE
350388 , benchIOSink value "filter-scanl1" (filterScanl1 4)
389+#endif
351390 , benchIOSink value "filter-map" (filterMap value 4)
352391 ]
353392 ]
@@ -358,40 +397,43 @@ o_1_space_transformations_mixedX4 value =
358397
359398-- this is quadratic
360399{-# INLINE iterateScan #-}
361−iterateScan :: S.MonadAsync m => Int -> Int -> Int -> SerialT m Int
362−iterateScan = iterateSource (S.scanl' (+) 0)
400+iterateScan :: MonadAsync m => Int -> Int -> Int -> SerialT m Int
401+iterateScan = iterateSource (Common.scanl' (+) 0)
363402
403+#ifdef USE_PRELUDE
364404-- this is quadratic
365405{-# INLINE iterateScanl1 #-}
366−iterateScanl1 :: S.MonadAsync m => Int -> Int -> Int -> SerialT m Int
406+iterateScanl1 :: MonadAsync m => Int -> Int -> Int -> SerialT m Int
367407iterateScanl1 = iterateSource (S.scanl1' (+))
408+#endif
368409
369410{-# INLINE iterateMapM #-}
370−iterateMapM :: S.MonadAsync m => Int -> Int -> Int -> SerialT m Int
411+iterateMapM :: MonadAsync m => Int -> Int -> Int -> SerialT m Int
371412iterateMapM = iterateSource (S.mapM return)
372413
373414{-# INLINE iterateFilterEven #-}
374−iterateFilterEven :: S.MonadAsync m => Int -> Int -> Int -> SerialT m Int
415+iterateFilterEven :: MonadAsync m => Int -> Int -> Int -> SerialT m Int
375416iterateFilterEven = iterateSource (S.filter even)
376417
377418{-# INLINE iterateTakeAll #-}
378−iterateTakeAll :: S.MonadAsync m => Int -> Int -> Int -> Int -> SerialT m Int
419+iterateTakeAll :: MonadAsync m => Int -> Int -> Int -> Int -> SerialT m Int
379420iterateTakeAll value = iterateSource (S.take (value + 1))
380421
381422{-# INLINE iterateDropOne #-}
382−iterateDropOne :: S.MonadAsync m => Int -> Int -> Int -> SerialT m Int
423+iterateDropOne :: MonadAsync m => Int -> Int -> Int -> SerialT m Int
383424iterateDropOne = iterateSource (S.drop 1)
384425
385426{-# INLINE iterateDropWhileFalse #-}
386−iterateDropWhileFalse :: S.MonadAsync m
427+iterateDropWhileFalse :: MonadAsync m
387428 => Int -> Int -> Int -> Int -> SerialT m Int
388429iterateDropWhileFalse value = iterateSource (S.dropWhile (> (value + 1)))
389430
390431{-# INLINE iterateDropWhileTrue #-}
391−iterateDropWhileTrue :: S.MonadAsync m
432+iterateDropWhileTrue :: MonadAsync m
392433 => Int -> Int -> Int -> Int -> SerialT m Int
393434iterateDropWhileTrue value = iterateSource (S.dropWhile (<= (value + 1)))
394435
436+#ifdef USE_PRELUDE
395437{-# INLINE tail #-}
396438tail :: Monad m => SerialT m a -> m ()
397439tail s = S.tail s >>= mapM_ tail
@@ -403,26 +445,31 @@ nullHeadTail s = do
403445 when (not r) $ do
404446 _ <- S.head s
405447 S.tail s >>= mapM_ nullHeadTail
448+#endif
406449
407450-- Head recursive operations.
408451o_n_stack_iterated :: Int -> [Benchmark]
409452o_n_stack_iterated value = by10 `seq` by100 `seq`
410453 [ bgroup "iterated"
411− [ benchIOSrc fromSerial "mapM (n/10 x 10)" $ iterateMapM by10 10
412− , benchIOSrc fromSerial "scanl' (quadratic) (n/100 x 100)" $
454+ [ benchIOSrc "mapM (n/10 x 10)" $ iterateMapM by10 10
455+ , benchIOSrc "scanl' (quadratic) (n/100 x 100)" $
413456 iterateScan by100 100
414− , benchIOSrc fromSerial "scanl1' (n/10 x 10)" $ iterateScanl1 by10 10
415− , benchIOSrc fromSerial "filterEven (n/10 x 10)" $
457+#ifdef USE_PRELUDE
458+ , benchIOSrc "scanl1' (n/10 x 10)" $ iterateScanl1 by10 10
459+#endif
460+ , benchIOSrc "filterEven (n/10 x 10)" $
416461 iterateFilterEven by10 10
417− , benchIOSrc fromSerial "takeAll (n/10 x 10)" $
462+ , benchIOSrc "takeAll (n/10 x 10)" $
418463 iterateTakeAll value by10 10
419− , benchIOSrc fromSerial "dropOne (n/10 x 10)" $ iterateDropOne by10 10
420− , benchIOSrc fromSerial "dropWhileFalse (n/10 x 10)" $
464+ , benchIOSrc "dropOne (n/10 x 10)" $ iterateDropOne by10 10
465+ , benchIOSrc "dropWhileFalse (n/10 x 10)" $
421466 iterateDropWhileFalse value by10 10
422− , benchIOSrc fromSerial "dropWhileTrue (n/10 x 10)" $
467+ , benchIOSrc "dropWhileTrue (n/10 x 10)" $
423468 iterateDropWhileTrue value by10 10
469+#ifdef USE_PRELUDE
424470 , benchIOSink value "tail" tail
425471 , benchIOSink value "nullHeadTail" nullHeadTail
472+#endif
426473 ]
427474 ]
428475
@@ -438,12 +485,12 @@ o_n_stack_iterated value = by10 `seq` by100 `seq`
438485o_1_space_pipes :: Int -> [Benchmark]
439486o_1_space_pipes value =
440487 [ bgroup "pipes"
441− [ benchIOSink value "mapM" (transformMapM fromSerial 1)
442− , benchIOSink value "compose" (transformComposeMapM fromSerial 1)
443− , benchIOSink value "tee" (transformTeeMapM fromSerial 1)
488+ [ benchIOSink value "mapM" (transformMapM 1)
489+ , benchIOSink value "compose" (transformComposeMapM 1)
490+ , benchIOSink value "tee" (transformTeeMapM 1)
444491#ifdef DEVBUILD
445492 -- XXX this take 1 GB memory to compile
446− , benchIOSink value "zip" (transformZipMapM fromSerial 1)
493+ , benchIOSink value "zip" (transformZipMapM 1)
447494#endif
448495 ]
449496 ]
@@ -451,12 +498,12 @@ o_1_space_pipes value =
451498o_1_space_pipesX4 :: Int -> [Benchmark]
452499o_1_space_pipesX4 value =
453500 [ bgroup "pipesX4"
454− [ benchIOSink value "mapM" (transformMapM fromSerial 4)
455− , benchIOSink value "compose" (transformComposeMapM fromSerial 4)
456− , benchIOSink value "tee" (transformTeeMapM fromSerial 4)
501+ [ benchIOSink value "mapM" (transformMapM 4)
502+ , benchIOSink value "compose" (transformComposeMapM 4)
503+ , benchIOSink value "tee" (transformTeeMapM 4)
457504#ifdef DEVBUILD
458505 -- XXX this take 1 GB memory to compile
459− , benchIOSink value "zip" (transformZipMapM fromSerial 4)
506+ , benchIOSink value "zip" (transformZipMapM 4)
460507#endif
461508 ]
462509 ]
@@ -481,8 +528,12 @@ benchmarks moduleName size =
481528 ]
482529 , bgroup (o_n_stack_prefix moduleName) (o_n_stack_iterated size)
483530 , bgroup (o_n_heap_prefix moduleName) $ Prelude.concat
484− [ o_n_space_grouping size
485− , o_n_space_functor size
531+ [
532+#ifdef USE_PRELUDE
533+ o_n_space_grouping size
534+ ,
535+#endif
536+ o_n_space_functor size
486537 , o_n_heap_buffering size
487538 ]
488539 ]