diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright Tony Day (c) 2016
+
+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 Tony Day 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/online.cabal b/online.cabal
new file mode 100644
--- /dev/null
+++ b/online.cabal
@@ -0,0 +1,85 @@
+name: online
+version: 0.2.0
+synopsis:
+  online statistics
+description:
+  transformation of statistics to online algorithms
+homepage:
+  https://github.com/tonyday567/online
+license:
+  BSD3
+license-file:
+  LICENSE
+author:
+  Tony Day
+maintainer:
+  tonyday567@gmail.com
+copyright:
+  Tony Day
+category:
+  statistics            
+build-type:
+  Simple
+cabal-version:
+  >=1.10
+extra-source-files:
+  readme.md
+  stack.yaml
+
+library
+  default-language:
+    Haskell2010
+  ghc-options:
+    -Wall
+  hs-source-dirs:
+    src
+  exposed-modules:
+    Online,
+    Online.Stats,
+    Online.StatsL1,
+    Online.Quantiles
+  build-depends:
+    base >= 4.7 && < 5,
+    protolude,
+    foldl,
+    vector,
+    tdigest,
+    numhask,
+    vector-algorithms
+  default-extensions:
+    NoImplicitPrelude,
+    UnicodeSyntax,
+    BangPatterns,
+    BinaryLiterals,
+    DeriveFoldable,
+    DeriveFunctor,
+    DeriveGeneric,
+    DeriveTraversable,
+    DisambiguateRecordFields,
+    EmptyCase,
+    FlexibleContexts,
+    FlexibleInstances,
+    FunctionalDependencies,
+    GADTSyntax,
+    InstanceSigs,
+    KindSignatures,
+    LambdaCase,
+    MonadComprehensions,
+    MultiParamTypeClasses,
+    MultiWayIf,
+    NegativeLiterals,
+    OverloadedStrings,
+    ParallelListComp,
+    PartialTypeSignatures,
+    PatternSynonyms,
+    RankNTypes,
+    RecordWildCards,
+    RecursiveDo,
+    ScopedTypeVariables,
+    TupleSections,
+    TypeFamilies,
+    TypeOperators
+
+source-repository head
+  type:     git
+  location: https://github.com/tonyday567/online
diff --git a/readme.md b/readme.md
new file mode 100644
--- /dev/null
+++ b/readme.md
@@ -0,0 +1,50 @@
+[online](https://github.com/tonyday567/online)
+===
+
+[![Build Status](https://travis-ci.org/tonyday567/online.svg)](https://travis-ci.org/tonyday567/online) [![Hackage](https://img.shields.io/hackage/v/online.svg)](https://hackage.haskell.org/package/online) [![lts](https://www.stackage.org/package/online/badge/lts)](http://stackage.org/lts/package/online) [![nightly](https://www.stackage.org/package/online/badge/nightly)](http://stackage.org/nightly/package/online)
+
+online turns a statistic (in haskell this can usually be thought of as a fold of a foldable) into an online algorithm.
+
+motivation
+===
+
+Imagine a data stream, like an ordered indexed container or a
+time-series of measurements. An exponential moving average can be
+calculated as a repeated iteration over a stream of xs:
+
+$$ ema_t = ema_{t-1} * 0.9 + x_t * 0.1 $$
+
+The 0.1 is akin to the learning rate in machine learning, or 0.9 can be
+thought of as a decaying or a rate of forgetting. An exponential moving
+average learns about what the value of x has been lately, where lately
+is, on average, about 1/0.1 = 10 x's ago. All very neat.
+
+The first bit of neat is speed. There's 2 times and a plus. The next is
+space: an ema is representing the recent xs in a size as big as a single
+x. Compare that with a simple moving average where you have to keep the
+history of the last n xs around to keep up (just try it).
+
+It's so neat, it's probably a viable monoidal category all by itself.
+
+online
+======
+
+Haskell allows us to abstract the compound ideas in an ema and create
+polymorphic routines over a wide variety of statistics, so that they all
+retain these properties of speed, space and rigour.
+
+    av xs = L.fold (online identity (.* 0.9)) xs
+    -- av [0..10] == 6.030559401413827
+    -- av [0..100] == 91.00241448887785
+
+`online identity (.* 0.9)` is how you express an ema with a decay rate
+of 0.9.
+
+online works for any statistic. Here's the construction of standard
+deviation using applicative style:
+
+    std :: Double -> L.Fold Double Double
+    std r = (\s ss -> sqrt (ss - s**2)) <$> ma r <*> sqma r
+      where
+        ma r = online identity (.*r)
+        sqma r = online (**2) (.*r)
diff --git a/src/Online.hs b/src/Online.hs
new file mode 100644
--- /dev/null
+++ b/src/Online.hs
@@ -0,0 +1,8 @@
+-- | online library
+module Online (
+    module X
+) where
+
+import Online.Stats as X
+import Online.StatsL1 as X
+import Online.Quantiles as X
diff --git a/src/Online/Quantiles.hs b/src/Online/Quantiles.hs
new file mode 100644
--- /dev/null
+++ b/src/Online/Quantiles.hs
@@ -0,0 +1,117 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE ExistentialQuantification #-}
+{-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-type-defaults #-}
+
+module Online.Quantiles where
+
+import NumHask.Prelude
+import qualified Control.Foldl as L
+import Data.TDigest
+import Data.TDigest.Internal
+import qualified Data.Vector.Unboxed as VU
+import qualified Data.Vector.Algorithms.Heap as VHeap
+import Data.List.NonEmpty (NonEmpty)
+import Data.TDigest.Postprocess()
+
+-- | a raw non-online tdigest fold
+tDigest :: L.Fold Double (TDigest 25)
+tDigest = L.Fold step begin done
+  where
+    step x a = insert a x
+    begin = tdigest ([]::[Double]) :: TDigest 25
+    done = identity
+
+-- | non-online version
+tDigestQuantiles :: [Double] -> L.Fold Double [Double]
+tDigestQuantiles qs = L.Fold step begin done
+  where
+    step x a = insert a x
+    begin = tdigest ([]::[Double]) :: TDigest 25
+    done x = fromMaybe nan . (`quantile` compress x) <$> qs
+
+-- | non-online version
+tDigestHist :: L.Fold Double (Maybe (NonEmpty HistBin))
+tDigestHist = L.Fold step begin done
+  where
+    step x a = insert a x
+    begin = tdigest ([]::[Double]) :: TDigest 25
+    done x = histogram . compress $ x
+
+data OnlineTDigest = OnlineTDigest { td :: TDigest 25, tdN :: Int, tdRate :: Double } deriving (Show)
+
+emptyOnlineTDigest :: Double -> OnlineTDigest
+emptyOnlineTDigest r = OnlineTDigest (emptyTDigest :: TDigest n) 0 r
+
+-- | decaying quantiles based on the tdigest library
+onlineQuantiles :: Double -> [Double] -> L.Fold Double [Double]
+onlineQuantiles r qs = L.Fold step begin done
+  where
+    step x a = onlineInsert a x
+    begin = emptyOnlineTDigest r
+    done x = fromMaybe nan . (`quantile` t) <$> qs
+      where
+        (OnlineTDigest t _ _) = onlineForceCompress x
+
+median :: Double -> L.Fold Double Double
+median r = L.Fold step begin done
+  where
+    step x a = onlineInsert a x
+    begin = emptyOnlineTDigest r
+    done x = fromMaybe nan (quantile 0.5 t)
+        where
+          (OnlineTDigest t _ _) = onlineForceCompress x
+
+onlineInsert' :: Double -> OnlineTDigest -> OnlineTDigest
+onlineInsert' x (OnlineTDigest td n r) = OnlineTDigest (insertCentroid (x, r^^(-(fromIntegral $ n+1))) td) (n+1) r
+
+onlineInsert :: Double -> OnlineTDigest -> OnlineTDigest
+onlineInsert x otd = onlineCompress (onlineInsert' x otd)
+
+onlineCompress :: OnlineTDigest -> OnlineTDigest
+onlineCompress otd@(OnlineTDigest Nil _ _ ) = otd
+onlineCompress otd@(OnlineTDigest t _ _)
+    | Data.TDigest.Internal.size t > relMaxSize * compression && Data.TDigest.Internal.size t > absMaxSize
+        = onlineForceCompress otd
+    | otherwise
+        = otd
+  where
+    compression = 25
+
+onlineForceCompress :: OnlineTDigest -> OnlineTDigest
+onlineForceCompress otd@(OnlineTDigest Nil _ _ ) = otd
+onlineForceCompress (OnlineTDigest t n r) = OnlineTDigest t' 0 r
+  where
+    t' = NumHask.Prelude.foldl' (flip insertCentroid) emptyTDigest $
+         (\(m,w) -> (m, w*(r^^fromIntegral n))) . fst <$> VU.toList centroids
+    -- Centroids are shuffled based on space
+    centroids :: VU.Vector (Centroid, Double)
+    centroids = runST $ do
+        v <- toMVector t
+        -- sort by cumulative weight
+        VHeap.sortBy (comparing snd) v
+        f <- VU.unsafeFreeze v
+        pure f
+
+onlineDigitize :: Double -> [Double] -> L.Fold Double Int
+onlineDigitize r qs = L.Fold step begin done
+  where
+    step (x,_) a = (onlineInsert a x, a)
+    begin = (emptyOnlineTDigest r, nan)
+    done (x,l) = bucket' qs' l
+      where
+        qs' = fromMaybe nan . (`quantile` t) <$> qs
+        (OnlineTDigest t _ _) = onlineForceCompress x
+        bucket' xs l' = L.fold L.sum $ (\x' -> if x'>l' then 0 else 1) <$> xs
+
+-- | decaying histogram based on the tdigest library
+onlineDigestHist :: Double -> L.Fold Double (Maybe (NonEmpty HistBin))
+onlineDigestHist r = L.Fold step begin done
+  where
+    step x a = onlineInsert a x
+    begin = emptyOnlineTDigest r
+    done x = histogram . compress $ t
+      where
+        (OnlineTDigest t _ _) = onlineForceCompress x
diff --git a/src/Online/Stats.hs b/src/Online/Stats.hs
new file mode 100644
--- /dev/null
+++ b/src/Online/Stats.hs
@@ -0,0 +1,140 @@
+{-# LANGUAGE ExistentialQuantification #-}
+{-# LANGUAGE GADTs #-}
+
+module Online.Stats (
+    -- * convert a statistic to online
+    Averager,
+    online,
+
+    -- * common statistics
+    av,
+
+    -- * online statistics
+    ma,
+    absma,
+    sqma,
+    std,
+    cov,
+    corr,
+    corrGauss,
+    beta,
+    alpha,
+    autocorr
+  ) where
+
+import Protolude
+import qualified Control.Foldl as L
+import Control.Foldl (Fold(..))
+
+-- | Most common statistics are averages.
+newtype Averager a b = Averager { _averager :: (a, b)}
+
+instance (Monoid a, Monoid b) => Monoid (Averager a b) where
+    mempty = Averager (mempty, mempty)
+    mappend (Averager (s,c)) (Averager (s',c')) = Averager (mappend s s', mappend c c')
+
+-- | online takes a function and turns it into a `Control.Foldl.Fold` where the step is an incremental update of the (isomorphic) statistic.
+online :: (Fractional b) => (a -> b) -> (b -> b) -> Fold a b
+online f g = Fold step begin extract
+  where
+  begin = Averager (0, 0)
+  step (Averager (s,c)) a = Averager (g $ s+f a,g $ c+1)
+  extract (Averager (s,c)) = s/c
+
+-- | average
+av :: (Fractional a) => Fold a a
+av = Fold step begin extract
+  where
+  begin = Averager (0, 0)
+  step (Averager (s,c)) a = Averager (s+a,c+1)
+  extract (Averager (s,c)) = s/c
+{-# INLINABLE av #-}
+
+-- | moving average
+ma :: (Fractional a) => a -> Fold a a
+ma r = online identity (* r)
+{-# INLINABLE ma #-}
+
+-- | absolute average
+absma :: (Fractional a) => a -> Fold a a
+absma r = online abs (* r)
+{-# INLINABLE absma #-}
+
+-- | average square
+sqma :: (Fractional a) => a -> Fold a a
+sqma r = online (\x -> x*x) (* r)
+{-# INLINABLE sqma #-}
+
+-- | standard deviation
+std :: (Floating a) => a -> Fold a a
+std r = (\s ss -> sqrt (ss - s**2)) <$> ma r <*> sqma r
+{-# INLINABLE std #-}
+
+-- | the covariance of a tuple
+-- given an underlying central tendency fold
+cov :: (Floating a) => Fold a a -> Fold (a,a) a
+cov m =
+    (\xy x' y' -> xy - x' * y') <$>
+    L.premap (uncurry (*)) m <*>
+    L.premap fst m <*>
+    L.premap snd m
+{-# INLINABLE cov #-}
+
+-- | correlation of a tuple, specialised to Guassian
+corrGauss :: (Floating a) => a -> Fold (a,a) a
+corrGauss r = (\cov' stdx stdy -> cov' / (stdx * stdy)) <$> cov (ma r) <*> L.premap fst (std r) <*> L.premap snd (std r)
+{-# INLINABLE corrGauss #-}
+
+-- | a generalised version of correlation of a tuple
+corr :: (Floating a) => Fold a a -> Fold a a -> Fold (a,a) a
+corr central deviation =
+    (\cov' stdx stdy -> cov' / (stdx * stdy)) <$>
+    cov central <*>
+    L.premap fst deviation <*>
+    L.premap snd deviation
+{-# INLINABLE corr #-}
+
+-- | the beta in a simple linear regression of a tuple
+-- given an underlying central tendency fold
+beta :: (Floating a) => Fold a a -> Fold (a,a) a
+beta m =
+    (\xy x' y' x2 -> (xy - x'*y')/(x2 - x'*x')) <$>
+    L.premap (uncurry (*)) m <*>
+    L.premap fst m <*>
+    L.premap snd m <*>
+    L.premap (\(x,_) -> x*x) m
+{-# INLINABLE beta #-}
+
+-- | the alpha of a tuple
+alpha :: (Floating a) => Fold a a -> Fold (a,a) a
+alpha m =
+    (\y b x -> y - b * x) <$>
+    L.premap fst m <*> beta m <*> L.premap snd m
+{-# INLINABLE alpha #-}
+
+{-| autocorrelation is a slippery concept.  This method starts with the concept that there is an underlying random error process (e), and autocorrelation is a process on top of that ie for a one-step correlation relationship.
+
+value@t = e@t + k * e@t-1
+
+where k is the autocorrelation.
+
+There are thus two online rates needed: one for the average being considered to be the dependent variable, and one for the online of the correlation calculation between the most recent value and the moving average. For example,
+
+>>> L.fold (autocorr 0 1)
+
+would estimate the one-step autocorrelation relationship of the previous value and the current value over the entire sample set. 
+
+-}
+autocorr :: (Floating a, RealFloat a) => Fold a a -> Fold (a,a) a -> Fold a a
+autocorr central corrf =
+    case central of
+        (Fold mStep mBegin mDone) ->
+            case corrf of
+                (Fold dStep dBegin dDone) ->
+                    let begin = (mBegin, dBegin)
+                        step (mAcc,dAcc) a = (mStep mAcc a,
+                            if isNaN (mDone mAcc)
+                            then dAcc
+                            else dStep dAcc (mDone mAcc, a))
+                        done = dDone . snd in
+                    Fold step begin done
diff --git a/src/Online/StatsL1.hs b/src/Online/StatsL1.hs
new file mode 100644
--- /dev/null
+++ b/src/Online/StatsL1.hs
@@ -0,0 +1,112 @@
+{-# LANGUAGE ExistentialQuantification #-}
+{-# LANGUAGE GADTs #-}
+
+module Online.StatsL1 (
+    -- * convert a statistic to an online median stat equivalent to L1
+    Medianer(..),
+    onlineL1,
+    onlineL1',
+
+    -- * common statistics
+    avL1,
+
+    -- * online statistics
+    maL1,
+    absmaL1,
+    covL1,
+    corrL1,
+    betaL1,
+    alphaL1,
+    autocorrL1
+  ) where
+
+import Protolude
+import qualified Control.Foldl as L
+import Control.Foldl (Fold(..))
+
+-- | A rough Median.
+-- The average absolute value of the stat is used to callibrate estimate drift towards the medium
+data Medianer a b = Medianer { medAbsSum :: a, medCount :: b, medianEst :: a}
+
+-- | onlineL1' takes a function and turns it into a `Control.Foldl.Fold` where the step is an incremental update of an (isomorphic) median statistic.
+onlineL1' :: (Ord b, Fractional b) => b -> b -> (a -> b) -> (b -> b) -> Fold a (b,b)
+onlineL1' i d f g = Fold step begin extract
+  where
+  begin = Medianer 0 0 0
+  step (Medianer s c m) a = Medianer (g $ s+abs (f a)) (g $ c+1) ((1-d) * (m + s' * i * s/c') + d * f a)
+    where
+        c' = if c == 0 then 1 else c
+        s'
+            | f a > m = 1
+            | f a < m = -1
+            | otherwise = 0
+  extract (Medianer s c m) = (s/c,m)
+
+-- | onlineL1 takes a function and turns it into a `Control.Foldl.Fold` where the step is an incremental update of an (isomorphic) median statistic.
+onlineL1 :: (Ord b, Fractional b) => b -> b -> (a -> b) -> (b -> b) -> Fold a b
+onlineL1 i d f g = snd <$> onlineL1' i d f g
+
+
+-- | averageL1
+avL1 :: (Ord a, Fractional a) => a -> Fold a a
+avL1 i = Fold step begin extract
+  where
+  begin = Medianer 0 0 0
+  step (Medianer s c m) a = Medianer (s+a) (c+1) (m + s' * i * s/c)
+    where
+        s'
+            | a > m = 1
+            | a < m = -1
+            | otherwise = 0
+  extract (Medianer _ _ m) = m
+{-# INLINABLE avL1 #-}
+
+-- | moving median
+maL1 :: (Ord a, Fractional a) => a -> a -> a -> Fold a a
+maL1 i d r = onlineL1 i d identity (*r)
+{-# INLINABLE maL1 #-}
+
+-- | moving absolute deviation
+absmaL1 :: (Ord a, Fractional a) => a -> a -> a -> Fold a a
+absmaL1 i d r = fst <$> onlineL1' i d identity (*r)
+{-# INLINABLE absmaL1 #-}
+
+-- | covariance of a tuple
+covL1 :: (Ord a, Fractional a) => a -> a -> a -> Fold (a,a) a
+covL1 i d r = (\xy xbar ybar -> xy - xbar * ybar) <$> onlineL1 i d (uncurry (*)) (*r) <*> onlineL1 i d fst (*r) <*> onlineL1 i d snd (*r)
+{-# INLINABLE covL1 #-}
+
+-- | correlation of a tuple
+corrL1 :: (Ord a, Floating a) => a -> a ->  a -> Fold (a,a) a
+corrL1 i d r = (\cov' stdx stdy -> cov' / (stdx * stdy)) <$> covL1 i d r <*> L.premap fst (absmaL1 i d r) <*> L.premap snd (absmaL1 i d r)
+{-# INLINABLE corrL1 #-}
+
+-- | the beta in a simple linear regression of a tuple
+betaL1 :: (Ord a, Floating a) => a -> a ->  a -> Fold (a,a) a
+betaL1 i d r =
+    (\xy x' y' x2 -> (xy - x'*y')/(x2 - x'*x')) <$>
+    L.premap (uncurry (*)) (maL1 i d r) <*>
+    L.premap fst (maL1 i d r) <*>
+    L.premap snd (maL1 i d r) <*>
+    L.premap (\(x,_) -> x*x) (maL1 i d r)
+{-# INLINABLE betaL1 #-}
+
+-- | the alpha in a simple linear regression of `snd` on `fst`
+alphaL1 :: (Ord a, Floating a) => a -> a ->  a -> Fold (a,a) a
+alphaL1 i d r = (\y b x -> y - b * x) <$> L.premap fst (maL1 i d r) <*> betaL1 i d r <*> L.premap snd (maL1 i d r)
+{-# INLINABLE alphaL1 #-}
+
+autocorrL1 :: (Floating a, RealFloat a) => a -> a ->  a -> a -> Fold a a
+autocorrL1 i d maR corrR = 
+    case maL1 i d maR of
+        (Fold maStep maBegin maDone) ->
+            case corrL1 i d corrR of
+                (Fold corrStep corrBegin corrDone) ->
+                    let begin = (maBegin, corrBegin)
+                        step (maAcc,corrAcc) a = (maStep maAcc a,
+                            if isNaN (maDone maAcc)
+                            then corrAcc
+                            else corrStep corrAcc (maDone maAcc, a)) 
+                        done = corrDone . snd in
+                    Fold step begin done
+{-# INLINABLE autocorrL1 #-}
diff --git a/stack.yaml b/stack.yaml
new file mode 100644
--- /dev/null
+++ b/stack.yaml
@@ -0,0 +1,7 @@
+resolver: lts-8.23
+
+packages:
+  - '.'
+
+extra-deps:
+  - numhask-0.0.7
