diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2012, John W. Lato
+
+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 John W. Lato nor the names of other
+      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
+OWNER 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.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/iter-stats.cabal b/iter-stats.cabal
new file mode 100644
--- /dev/null
+++ b/iter-stats.cabal
@@ -0,0 +1,54 @@
+-- Initial iter-stats.cabal generated by cabal init.  For further 
+-- documentation, see http://haskell.org/cabal/users-guide/
+
+name:                iter-stats
+version:             0.1.0.1
+synopsis:            iteratees for statistical processing
+description:         efficient statistical values of data streams
+homepage:            https://github.com/JohnLato/iter-stats
+license:             BSD3
+license-file:        LICENSE
+author:              John W. Lato
+maintainer:          jwlato@gmail.com
+copyright:           John W. Lato, 2012
+category:            Math
+build-type:          Simple
+cabal-version:       >=1.8
+
+library
+  exposed-modules:     Statistics.Iteratee,
+                       Statistics.Iteratee.Compat,
+                       Statistics.Iteratee.Sample,
+                       Statistics.Iteratee.Uniform
+  -- other-modules:       
+  build-depends:       base >=4.5 && < 4.7,
+                       heap == 1.*,
+                       iteratee >=0.8,
+                       ListLike >=3,
+                       mtl ==2.1.*
+  hs-source-dirs:      src
+
+Test-suite iter-stats-tests
+  Hs-source-dirs: src tests
+  Main-is:        TestSuite.hs
+  Type:           exitcode-stdio-1.0
+
+  Other-modules:
+    Statistics.Iteratee.Tests
+
+  Build-depends:
+    HUnit                      >= 1.2 && < 1.3,
+    statistics,
+    test-framework             >= 0.4 && < 0.9,
+    test-framework-hunit       >= 0.2 && < 0.4,
+    test-framework-quickcheck2 >= 0.2 && < 0.4,
+    vector                     >= 0.9,
+    base,
+    heap,
+    iteratee,
+    ListLike,
+    mtl
+
+source-repository head
+  type:                git
+  location:            git://github.com/JohnLato/iter-stats.git
diff --git a/src/Statistics/Iteratee.hs b/src/Statistics/Iteratee.hs
new file mode 100644
--- /dev/null
+++ b/src/Statistics/Iteratee.hs
@@ -0,0 +1,6 @@
+module Statistics.Iteratee (
+  module S
+) where
+
+import Statistics.Iteratee.Sample as S
+import Statistics.Iteratee.Uniform as S
diff --git a/src/Statistics/Iteratee/Compat.hs b/src/Statistics/Iteratee/Compat.hs
new file mode 100644
--- /dev/null
+++ b/src/Statistics/Iteratee/Compat.hs
@@ -0,0 +1,17 @@
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE ConstraintKinds #-}
+
+module Statistics.Iteratee.Compat (
+  ListLikey
+)
+
+where
+
+import Data.Iteratee as I
+import Data.ListLike (ListLike)
+
+#if MIN_VERSION_iteratee(0,9,0)
+type ListLikey s el = (ListLike s el)
+#else
+type ListLikey s el = (ListLike s el, Nullable s)
+#endif
diff --git a/src/Statistics/Iteratee/Sample.hs b/src/Statistics/Iteratee/Sample.hs
new file mode 100644
--- /dev/null
+++ b/src/Statistics/Iteratee/Sample.hs
@@ -0,0 +1,102 @@
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE NoMonomorphismRestriction #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE ConstraintKinds #-}
+
+{-# OPTIONS -Wall #-}
+module Statistics.Iteratee.Sample (
+  minMaxNBy
+, range
+, mean
+, harmonicMean
+, variance
+, stdDev
+) where
+
+import Statistics.Iteratee.Compat
+import Control.Arrow
+import Control.Monad
+import Data.Iteratee as I
+
+import Data.Heap as Heap
+
+--  | /O(n)/ minMaxNBy. Calculate the 'n' highest and lowest elements of the
+--  stream, according to the given priority function.
+--  Returns /([minimum],[maximum]/ with the /(minimum,maximum)/
+--  elements listed first.
+minMaxNBy
+  :: forall m prio s el. (Monad m, Ord prio, ListLikey s el)
+  => Int
+  -> (el -> prio)
+  -> Iteratee s m ([(prio,el)],[(prio,el)])
+minMaxNBy ns prio = finalize `liftM` I.foldl' step (Heap.empty,Heap.empty)
+  where
+    finalize :: (MaxPrioHeap prio el, MinPrioHeap prio el)
+             -> ([(prio,el)],[(prio,el)])
+    finalize = Heap.toDescList *** Heap.toDescList
+    addHeap val = Heap.insert (prio val, val)
+    step :: (MaxPrioHeap prio el, MinPrioHeap prio el) -> el
+            -> (MaxPrioHeap prio el, MinPrioHeap prio el)
+    step (!mins,!maxes) val = let sz = Heap.size mins
+                                  adj hp = if sz >= ns
+                                             then Heap.drop 1 hp
+                                             else hp
+                              in (adj $ addHeap val mins
+                                 ,adj $ addHeap val maxes)
+{-# INLINE minMaxNBy #-}
+
+-- | /O(n)/ Range. The difference between the largest and smallest elements of
+-- a stream.
+range :: (Monad m, ListLikey s el, Num el, Ord el)
+      => Iteratee s m el
+range = finalize `liftM` minMaxNBy 1 id
+  where
+    finalize ([mins],[maxes]) = snd maxes - snd mins
+    finalize _ = 0
+{-# INLINE range #-}
+
+-- | /O(n)/ Arithmetic mean.  Uses Welford's algorithm.
+mean :: forall s m el. (Fractional el, Monad m, ListLikey s el)
+     => Iteratee s m el
+mean = fst `liftM` I.foldl' step (0,0)
+  where
+    step :: (el,Integer) -> el -> (el,Integer)
+    step (!m,!n) x = let m' = m + (x-m) / fromIntegral n'
+                         n' = n + 1
+                     in  (m',n')
+{-# INLINE mean #-}
+
+-- | /O(n)/ Harmonic mean.
+harmonicMean :: (Fractional el, Monad m, ListLikey s el) => Iteratee s m el
+harmonicMean = finalize `liftM` I.foldl' step (0,0 :: Integer)
+  where
+    finalize (m,n) = fromIntegral n / m
+    step (!m,!n) val = (m+(1/val),n+1)
+{-# INLINE harmonicMean #-}
+
+-- | /O(n)/ variance, using Knuth's algorithm.
+var :: (Fractional el, Integral t, Monad m, ListLikey s el)
+    => Iteratee s m (t, el, el)
+var = I.foldl' step (0,0,0)
+  where
+    step (!n,!m,!s) x = let n' = n+1
+                            m' = m+d/fromIntegral n'
+                            s' = s+d* (x-m')
+                            d  = x-m
+                        in (n',m',s')
+{-# INLINE var #-}
+
+-- | /O(n)/ Maximum likelihood estimate of a sample's variance, using Knuth's
+--   algorithm.
+variance :: (Fractional b, Monad m, ListLikey s b) => Iteratee s m b
+variance = finalize `liftM` var
+  where
+    finalize (n,_,s)
+      | n > 1 = s / fromInteger n
+      | otherwise = 0
+{-# INLINE variance #-}
+
+-- | /O(n) Standard deviation, using Knuth's algorithm.
+stdDev :: (Floating b, Monad m, Functor m, ListLikey s b) => Iteratee s m b
+stdDev = sqrt `liftM` variance
+{-# INLINE stdDev #-}
diff --git a/src/Statistics/Iteratee/Uniform.hs b/src/Statistics/Iteratee/Uniform.hs
new file mode 100644
--- /dev/null
+++ b/src/Statistics/Iteratee/Uniform.hs
@@ -0,0 +1,67 @@
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE ConstraintKinds #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE NoMonomorphismRestriction #-}
+
+{-# OPTIONS -Wall #-}
+-- | some functions for working with uniformly-sampled data
+module Statistics.Iteratee.Uniform (
+  someRollingFunction
+, movingAverage
+) where
+
+import Statistics.Iteratee.Compat
+import Statistics.Iteratee.Sample
+
+import Control.Monad.Identity
+import Data.Iteratee as I
+import Data.ListLike (ListLike)
+
+#if MIN_VERSION_iteratee(0,9,0)
+#else
+import qualified Data.ListLike as LL
+#endif
+
+#if MIN_VERSION_iteratee(0,9,0)
+roll'
+    :: (Monad m, ListLike s el)
+    => Int  -- ^ length of chunk (t)
+    -> Int  -- ^ amount to consume (d)
+    -> Iteratee s m [s]
+roll' = roll
+#else
+roll'
+    :: (Monad m, Nullable s, ListLike s el)
+    => Int  -- ^ length of chunk (t)
+    -> Int  -- ^ amount to consume (d)
+    -> Iteratee s m [s]
+roll' t d
+  | t > d  = liftI (go LL.empty)
+  | otherwise = error "Iteratee.roll: (t <= d).  Reverse the args?"
+    where
+        go prev (Chunk vec) =
+                let withPrev = prev `LL.append` vec
+                in if LL.length withPrev > t
+                    then idone [LL.take t withPrev] (Chunk $ LL.drop d withPrev)
+                    else liftI (go withPrev)
+        go prev e = idone [prev] e
+#endif
+
+someRollingFunction
+    :: (Monad m, ListLikey s el)
+    => Int
+    -> (s -> summary)
+    -> Enumeratee s [summary] m a
+someRollingFunction count mkSummary =
+    convStream (roll' count 1)
+    ><> mapStream mkSummary
+{-# INLINABLE someRollingFunction #-}
+
+movingAverage
+    :: (Fractional el, Monad m, ListLikey s el)
+    => Int
+    -> Enumeratee s [el] m a
+movingAverage n = someRollingFunction n chunkMean
+  where
+    chunkMean = runIdentity . (run <=< flip enumPure1Chunk mean)
+{-# INLINABLE movingAverage #-}
diff --git a/tests/Statistics/Iteratee/Tests.hs b/tests/Statistics/Iteratee/Tests.hs
new file mode 100644
--- /dev/null
+++ b/tests/Statistics/Iteratee/Tests.hs
@@ -0,0 +1,39 @@
+module Statistics.Iteratee.Tests
+
+where
+
+import Data.Iteratee as I
+import Statistics.Iteratee as Si
+import Statistics.Sample as St
+import Test.Framework
+import Test.Framework.Providers.QuickCheck2 (testProperty)
+
+import Control.Monad.Identity
+import qualified Data.Vector.Unboxed as V
+
+tests =
+  [ testGroup "Sample" $ map mkUnProp uns
+  ]
+
+unsProp :: (Eq a)
+        => (V.Vector Double -> a)
+        -> (Iteratee [Double] Identity a)
+        -> [Double]
+        -> Bool
+unsProp vec iter xs = if null xs then True
+    else vec (V.fromList xs) == (runIdentity $ run =<< enumPure1Chunk xs iter)
+    -- we're using Eq for doubles, which is always a bad idea...
+
+    -- also not checking empty vectors, because in some cases (range,
+    -- harmonicMean) we get NaN's or other funky values.
+
+mkUnProp (lbl, st, si) = testProperty lbl $ unsProp st si
+
+-- unary properties
+uns =
+  [ ("mean", St.mean, Si.mean)
+  , ("range", St.range, Si.range)
+  , ("harmonic_mean", St.harmonicMean, Si.harmonicMean)
+  , ("variance", St.fastVariance, Si.variance)
+  , ("std_dev", St.fastStdDev,   Si.stdDev)
+  ]
diff --git a/tests/TestSuite.hs b/tests/TestSuite.hs
new file mode 100644
--- /dev/null
+++ b/tests/TestSuite.hs
@@ -0,0 +1,6 @@
+import Test.Framework (defaultMain)
+
+import qualified Statistics.Iteratee.Tests as T
+
+main :: IO ()
+main = defaultMain T.tests
