packages feed

median-stream (empty) → 0.1.0.0

raw patch · 5 files changed

+200/−0 lines, 5 filesdep +QuickCheckdep +basedep +heapsetup-changed

Dependencies added: QuickCheck, base, heap, median-stream

Files

+ LICENSE view
@@ -0,0 +1,29 @@+BSD 3-Clause License++Copyright (c) 2016, Joe Canero+All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++* Redistributions of source code must retain the above copyright notice, this+  list of conditions and the following disclaimer.++* Redistributions in binary form must reproduce the above copyright notice,+  this list of conditions and the following disclaimer in the documentation+  and/or other materials provided with the distribution.++* Neither the name of the copyright holder nor the names of its+  contributors may be used to endorse or promote products derived from+  this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ median-stream.cabal view
@@ -0,0 +1,36 @@+name:                median-stream+version:             0.1.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+                     and O(1) queries for the median.+homepage:            https://github.com/caneroj1/median-stream#readme+license:             BSD3+license-file:        LICENSE+author:              Joe Canero+maintainer:          jmc41493@gmail.com+copyright:           Copyright: (c) 2016 Joe Canero+category:            Data+build-type:          Simple+cabal-version:       >=1.10++library+  hs-source-dirs:      src+  exposed-modules:     Data.MedianStream+  build-depends:       base >= 4.7 && < 5+                     , heap+  default-language:    Haskell2010++test-suite median-stream-test+  type:                exitcode-stdio-1.0+  hs-source-dirs:      test+  main-is:             Spec.hs+  build-depends:       base+                     , median-stream+                     , QuickCheck+  ghc-options:         -threaded -rtsopts -with-rtsopts=-N+  default-language:    Haskell2010++source-repository head+  type:     git+  location: https://github.com/caneroj1/median-stream
+ src/Data/MedianStream.hs view
@@ -0,0 +1,91 @@+{-|+Module      : Data.MedianStream+Description : Main module+Copyright   : (c) Joseph Canero, 2016+License     : BSD-3+Maintainer  : jmc41493@gmail.com+Stability   : experimental+Portability : POSIX+-}++{-# LANGUAGE GADTs #-}++module Data.MedianStream+(+  MedianStream+, (+>)+, (<+)+, empty+, insert+, median+, size+) where++import Data.Heap (MaxHeap, MinHeap, Heap)+import qualified Data.Heap as Heap hiding (MaxHeap, MinHeap, Heap)+import Data.Maybe (fromJust)++type Left a  = MaxHeap a+type Right a = MinHeap a++-- | A MedianStream is a data type that can be inserted into and queried+-- to get a median of a stream of numeric values.+data MedianStream a where+  MedianStream :: (Real a, Eq a) => Left a -> Right a -> MedianStream a++-- Infix wrapper around insert with the MedianStream on the left.+-- Complexity: O(lgn)+(+>) :: MedianStream a -> a -> MedianStream a+(+>) ms a = insert a ms++-- Infix wrapper around insert with the MedianStream on the right.+-- Complexity: O(lgn)+(<+) :: a -> MedianStream a -> MedianStream a+(<+) = insert++-- Create an empty MedianStream with no values.+-- Complexity: O(1)+empty :: (Real a, Eq a) => MedianStream a+empty = MedianStream Heap.empty Heap.empty++-- Insert a new numeric value into the median stream.+-- Complexity: O(lgn)+insert :: a -> MedianStream a -> MedianStream a+insert a ms@(MedianStream lh rh)+  | even $ size ms = oddMedianStream+  | otherwise      = evenMedianStream+    where+      oddMedianStream+        | maybe False (a >=) $ Heap.viewHead rh =+          uncurry MedianStream $ popAndSwap lh rh a+        | otherwise = MedianStream (Heap.insert a lh) rh+      evenMedianStream+        | maybe False (a < ) $ Heap.viewHead lh =+          uncurry (flip MedianStream) $ popAndSwap rh lh a+        | otherwise = MedianStream lh (Heap.insert a rh)++median :: MedianStream a -> Maybe Double+median ms@(MedianStream lh rh)+  | even $ size ms = average <$> Heap.viewHead lh <*> Heap.viewHead rh+  | otherwise      = (fromRational . toRational) <$> Heap.viewHead lh++-- Returns the number of elements in the MedianStream.+-- Complexity: O(1)+size :: MedianStream a -> Int+size (MedianStream lh rh) = Heap.size lh + Heap.size rh++popAndSwap :: (Heap.HeapItem pol1 a,+               Heap.HeapItem pol2 a)+           => Heap pol1 a+           -> Heap pol2 a+           -> a+           -> (Heap pol1 a, Heap pol2 a)+popAndSwap lh rh a =+  let+    ([top], srh) = Heap.splitAt 1 rh+    nlh          = Heap.insert top lh+    nrh          = Heap.insert a   srh+    in (nlh, nrh)++average :: (Real a) => a -> a -> Double+average x l = (/2 ) . fromRational $ toRational (x + l)
+ test/Spec.hs view
@@ -0,0 +1,42 @@+{-# LANGUAGE TemplateHaskell #-}++import Data.List hiding (insert)+import Data.MedianStream+import Test.QuickCheck++prop_medianStreamOnInts :: [Int] -> Bool+prop_medianStreamOnInts xs = medianOf xs == median (foldr insert empty xs)++prop_medianStreamOnDoubles :: [Double] -> Bool+prop_medianStreamOnDoubles xs = medianOf xs == median (foldr insert empty xs)++prop_medianStreamOnIntsV2 :: [Int] -> Bool+prop_medianStreamOnIntsV2 xs = medianOf xs == median (foldr (<+) empty xs)++prop_medianStreamOnDoublesV2 :: [Double] -> Bool+prop_medianStreamOnDoublesV2 xs = medianOf xs == median (foldr (<+) empty xs)++prop_medianStreamOnIntsV3 :: [Int] -> Bool+prop_medianStreamOnIntsV3 xs = medianOf xs == median (foldl' (+>) empty xs)++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++average :: (Real a) => a -> a -> Double+average x l = (/2) . fromRational $ toRational (x + l)++return []+runTests = $quickCheckAll++main = runTests