packages feed

median-stream 0.2.0.0 → 0.3.0.0

raw patch · 5 files changed

+172/−13 lines, 5 filesdep +randomdep +timedep ~basePVP ok

version bump matches the API change (PVP)

Dependencies added: random, time

Dependency ranges changed: base

API changes (from Hackage documentation)

+ Data.MedianStream: fromList :: (Real a, Eq a) => [a] -> MedianStream a
+ Data.MedianStream: insertList :: (Real a, Eq a) => MedianStream a -> [a] -> MedianStream a

Files

+ bench/Bench.hs view
@@ -0,0 +1,87 @@+import Control.Monad+import Data.MedianStream+import Data.Time.Clock+import System.Random+import Utils++main :: IO ()+main = benchmarks++benchmarks :: IO ()+benchmarks =+  forM_ ns $ \n -> do+    randomList <- take n . randoms <$> newStdGen+    putStrLn $ "\nBENCHMARKING: " ++ show n ++ " elements."+    benchBoth randomList n+  where ns = [10, 100, 1000, 10000, 100000, 200000, 500000]++benchBoth :: [Int] -> Int -> IO ()+benchBoth ls n = do+  putStrLn "Naive Median"+  benchNaiveMedian ls n+  putStrLn "Median Stream"+  benchMedianStream ls n++benchMedianStream :: [Int] -> Int -> IO ()+benchMedianStream ls size = do+  putStrLn "------------"+  putStrLn $ "First " ++ show inc ++ " elements."+  let (ls1, ls2) = splitAt inc ls+  let ms1        = fromList ls1+  timeOpMS ms1+  putStrLn $ "Next " ++ show inc ++ " elements."+  let (ls3, ls4) = splitAt inc ls2+  let ms2        = insertList ms1 ls3+  timeOpMS ms2+  putStrLn $ "Third " ++ show inc ++ " elements."+  let (ls5, ls6) = splitAt inc ls4+  let ms3        = insertList ms2 ls5+  timeOpMS ms3+  putStrLn $ "Last  " ++ show inc ++ " elements."+  let (ls7, ls8) = splitAt inc ls6+  let ms4        = insertList ms3 ls7+  timeOpMS ms4+  putStrLn ""+  where+    inc = size `div` 4++benchNaiveMedian :: [Int] -> Int -> IO ()+benchNaiveMedian ls size = do+  putStrLn "------------"+  putStrLn $ "First " ++ show inc ++ " elements."+  let (ls1, ls2) = splitAt inc ls+  timeOp medianOf ls1+  putStrLn $ "Next " ++ show inc ++ " elements."+  let (ls3, ls4) = splitAt inc ls2+  let ns1        = ls1 ++ ls3+  timeOp medianOf ns1+  putStrLn $ "Third " ++ show inc ++ " elements."+  let (ls5, ls6) = splitAt inc ls4+  let ns2        = ns1 ++ ls5+  timeOp medianOf ns2+  putStrLn $ "Last  " ++ show inc ++ " elements."+  let (ls7, ls8) = splitAt inc ls6+  let ns3        = ns2 ++ ls7+  timeOp medianOf ns3+  where+    inc = size `div` 4++timeOpMS :: MedianStream a -> IO ()+timeOpMS ms = do+  start <- getCurrentTime+  logMedian $ median ms+  end   <- getCurrentTime+  putStrLn $ "Elapsed: " ++ show (diffUTCTime end start)+  where+    logMedian Nothing  = return ()+    logMedian (Just m) = putStrLn $ "Median: " ++ show m++timeOp :: ([Int] -> Maybe Double) -> [Int] -> IO ()+timeOp fn ls = do+  start <- getCurrentTime+  logMedian $ fn ls+  end   <- getCurrentTime+  putStrLn $ "Elapsed: " ++ show (diffUTCTime end start)+  where+    logMedian Nothing  = return ()+    logMedian (Just m) = putStrLn $ "Median: " ++ show m
median-stream.cabal view
@@ -1,5 +1,5 @@ name:                median-stream-version:             0.2.0.0+version:             0.3.0.0 synopsis:            Constant-time queries for the median of a stream of numeric                      data. description:         Uses the two-heap approach to support O(lg n) insertions@@ -23,11 +23,39 @@  test-suite median-stream-test   type:                exitcode-stdio-1.0+  other-modules:       Utils   hs-source-dirs:      test+                     , utils   main-is:             Spec.hs   build-depends:       base                      , median-stream                      , QuickCheck+  ghc-options:         -threaded -rtsopts -with-rtsopts=-N+  default-language:    Haskell2010++benchmark median-stream-bench-optimized+  type:                exitcode-stdio-1.0+  other-modules:       Utils+  hs-source-dirs:      bench+                     , utils+  main-is:             Bench.hs+  build-depends:       base+                     , time+                     , median-stream+                     , random+  ghc-options:         -threaded -rtsopts -with-rtsopts=-N -O3+  default-language:    Haskell2010++benchmark median-stream-bench-non-optimized+  type:                exitcode-stdio-1.0+  other-modules:       Utils+  hs-source-dirs:      bench+                     , utils+  main-is:             Bench.hs+  build-depends:       base+                     , time+                     , median-stream+                     , random   ghc-options:         -threaded -rtsopts -with-rtsopts=-N   default-language:    Haskell2010 
src/Data/MedianStream.hs view
@@ -19,10 +19,13 @@ , insert , median , size+, fromList+, insertList ) where  import Data.Heap (MaxHeap, MinHeap, Heap) import qualified Data.Heap as Heap hiding (MaxHeap, MinHeap, Heap)+import Data.List (foldl') import Data.Maybe (fromJust)  type Left a  = MaxHeap a@@ -76,6 +79,16 @@ -- Complexity: O(1) size :: MedianStream a -> Int size (MedianStream lh rh) = Heap.size lh + Heap.size rh++-- | Creates a MedianStream from a list of input elements.+-- Complexity: O(nlgn)+fromList :: (Real a, Eq a) => [a] -> MedianStream a+fromList = insertList empty++-- | Adds a list of input elements to an existing MedianStream+-- Complexity: O(nlgn)+insertList :: (Real a, Eq a) => MedianStream a -> [a] -> MedianStream a+insertList = foldl' (+>)  popAndSwap :: (Heap.HeapItem pol1 a,                Heap.HeapItem pol2 a)
test/Spec.hs view
@@ -3,6 +3,7 @@ import Data.List hiding (insert) import Data.MedianStream import Test.QuickCheck+import Utils  prop_medianStreamOnInts :: [Int] -> Bool prop_medianStreamOnInts xs = medianOf xs == median (foldr insert empty xs)@@ -22,19 +23,29 @@ prop_medianStreamOnDoublesV3 :: [Double] -> Bool prop_medianStreamOnDoublesV3 xs = medianOf xs == median (foldl' (+>) empty xs) -medianOf :: (Real a, Eq a) => [a] -> Maybe Double-medianOf []  = Nothing-medianOf [x] = Just . fromRational $ toRational x-medianOf xs  =-  let sorted = sort xs-      len    = length xs-    in case even len of-      True  -> let [x, y] = take 2 $ drop (len `div` 2 - 1) sorted-                in Just $ average x y-      False -> medianOf . take 1 $ drop (len `div` 2) sorted+prop_medianStreamOnIntsFromList :: [Int] -> Bool+prop_medianStreamOnIntsFromList xs = medianOf xs == median (fromList xs) -average :: (Real a) => a -> a -> Double-average x l = (/2) . fromRational $ toRational (x + l)+prop_medianStreamOnDoublesFromList :: [Double] -> Bool+prop_medianStreamOnDoublesFromList xs = medianOf xs == median (fromList xs)++prop_medianStreamOnIntsInsertList :: [Int] -> Bool+prop_medianStreamOnIntsInsertList xs =+  let (fh, sh) = halves xs+      ms       = fromList fh+    in medianOf fh         == median ms &&+       medianOf (fh ++ sh) == median (insertList ms sh)++prop_medianStreamOnDoublesInsertList :: [Double] -> Bool+prop_medianStreamOnDoublesInsertList xs =+  let (fh, sh) = halves xs+      ms       = fromList fh+    in medianOf fh         == median ms &&+       medianOf (fh ++ sh) == median (insertList ms sh)++halves :: [a] -> ([a], [a])+halves ls = splitAt mid ls+  where mid = length ls `div` 2  return [] runTests = $quickCheckAll
+ utils/Utils.hs view
@@ -0,0 +1,20 @@+module Utils+(+  medianOf+) where++import Data.List (sort)++medianOf :: (Real a, Eq a) => [a] -> Maybe Double+medianOf []  = Nothing+medianOf [x] = Just . fromRational $ toRational x+medianOf xs  =+  let sorted = sort xs+      len    = length xs+    in case even len of+      True  -> let [x, y] = take 2 $ drop (len `div` 2 - 1) sorted+                in Just $ average x y+      False -> medianOf . take 1 $ drop (len `div` 2) sorted++average :: (Real a) => a -> a -> Double+average x l = (/2) . fromRational $ toRational (x + l)