monoid-statistics 1.0.1.0 → 1.1.0
raw patch · 9 files changed
+1105/−389 lines, 9 filesdep +criteriondep +doctestdep +exceptionsdep ~basedep ~math-functionsPVP ok
version bump matches the API change (PVP)
Dependencies added: criterion, doctest, exceptions, mwc-random, tasty-expected-failure, tasty-hunit
Dependency ranges changed: base, math-functions
API changes (from Hackage documentation)
- Data.Monoid.Statistics.Class: instance (GHC.Num.Num a, a Data.Type.Equality.~ a') => Data.Monoid.Statistics.Class.StatMonoid (Data.Semigroup.Internal.Product a) a'
- Data.Monoid.Statistics.Class: instance (GHC.Num.Num a, a Data.Type.Equality.~ a') => Data.Monoid.Statistics.Class.StatMonoid (Data.Semigroup.Internal.Sum a) a'
- Data.Monoid.Statistics.Numeric: MeanKahan :: !Int -> !KahanSum -> MeanKahan
- Data.Monoid.Statistics.Numeric: WelfordMean :: !Int -> !Double -> WelfordMean
- Data.Monoid.Statistics.Numeric: asMeanKahan :: MeanKahan -> MeanKahan
- Data.Monoid.Statistics.Numeric: asWelfordMean :: WelfordMean -> WelfordMean
- Data.Monoid.Statistics.Numeric: calcCount :: CalcCount m => m -> Int
- Data.Monoid.Statistics.Numeric: calcMean :: CalcMean m => m -> Maybe Double
- Data.Monoid.Statistics.Numeric: calcStddev :: CalcVariance m => m -> Maybe Double
- Data.Monoid.Statistics.Numeric: calcStddevML :: CalcVariance m => m -> Maybe Double
- Data.Monoid.Statistics.Numeric: calcVariance :: CalcVariance m => m -> Maybe Double
- Data.Monoid.Statistics.Numeric: calcVarianceML :: CalcVariance m => m -> Maybe Double
- Data.Monoid.Statistics.Numeric: class CalcCount m
- Data.Monoid.Statistics.Numeric: class CalcMean m
- Data.Monoid.Statistics.Numeric: class CalcVariance m
- Data.Monoid.Statistics.Numeric: data MeanKahan
- Data.Monoid.Statistics.Numeric: data WelfordMean
- Data.Monoid.Statistics.Numeric: instance (GHC.Classes.Ord a, a Data.Type.Equality.~ a') => Data.Monoid.Statistics.Class.StatMonoid (Data.Monoid.Statistics.Numeric.Max a) a'
- Data.Monoid.Statistics.Numeric: instance (GHC.Classes.Ord a, a Data.Type.Equality.~ a') => Data.Monoid.Statistics.Class.StatMonoid (Data.Monoid.Statistics.Numeric.Min a) a'
- Data.Monoid.Statistics.Numeric: instance (a Data.Type.Equality.~ GHC.Types.Double) => Data.Monoid.Statistics.Class.StatMonoid Data.Monoid.Statistics.Numeric.MaxD a
- Data.Monoid.Statistics.Numeric: instance (a Data.Type.Equality.~ GHC.Types.Double) => Data.Monoid.Statistics.Class.StatMonoid Data.Monoid.Statistics.Numeric.MinD a
- Data.Monoid.Statistics.Numeric: instance Data.Data.Data Data.Monoid.Statistics.Numeric.MeanKahan
- Data.Monoid.Statistics.Numeric: instance Data.Data.Data Data.Monoid.Statistics.Numeric.WelfordMean
- Data.Monoid.Statistics.Numeric: instance Data.Monoid.Statistics.Numeric.CalcCount (Data.Monoid.Statistics.Numeric.CountG GHC.Types.Int)
- Data.Monoid.Statistics.Numeric: instance Data.Monoid.Statistics.Numeric.CalcCount Data.Monoid.Statistics.Numeric.MeanKBN
- Data.Monoid.Statistics.Numeric: instance Data.Monoid.Statistics.Numeric.CalcCount Data.Monoid.Statistics.Numeric.MeanKahan
- Data.Monoid.Statistics.Numeric: instance Data.Monoid.Statistics.Numeric.CalcCount Data.Monoid.Statistics.Numeric.Variance
- Data.Monoid.Statistics.Numeric: instance Data.Monoid.Statistics.Numeric.CalcCount Data.Monoid.Statistics.Numeric.WelfordMean
- Data.Monoid.Statistics.Numeric: instance Data.Monoid.Statistics.Numeric.CalcMean Data.Monoid.Statistics.Numeric.MeanKBN
- Data.Monoid.Statistics.Numeric: instance Data.Monoid.Statistics.Numeric.CalcMean Data.Monoid.Statistics.Numeric.MeanKahan
- Data.Monoid.Statistics.Numeric: instance Data.Monoid.Statistics.Numeric.CalcMean Data.Monoid.Statistics.Numeric.Variance
- Data.Monoid.Statistics.Numeric: instance Data.Monoid.Statistics.Numeric.CalcMean Data.Monoid.Statistics.Numeric.WelfordMean
- Data.Monoid.Statistics.Numeric: instance Data.Monoid.Statistics.Numeric.CalcVariance Data.Monoid.Statistics.Numeric.Variance
- Data.Monoid.Statistics.Numeric: instance Data.Vector.Generic.Base.Vector Data.Vector.Unboxed.Base.Vector Data.Monoid.Statistics.Numeric.WelfordMean
- Data.Monoid.Statistics.Numeric: instance Data.Vector.Generic.Mutable.Base.MVector Data.Vector.Unboxed.Base.MVector Data.Monoid.Statistics.Numeric.WelfordMean
- Data.Monoid.Statistics.Numeric: instance Data.Vector.Unboxed.Base.Unbox Data.Monoid.Statistics.Numeric.WelfordMean
- Data.Monoid.Statistics.Numeric: instance GHC.Base.Monoid Data.Monoid.Statistics.Numeric.MeanKahan
- Data.Monoid.Statistics.Numeric: instance GHC.Base.Monoid Data.Monoid.Statistics.Numeric.WelfordMean
- Data.Monoid.Statistics.Numeric: instance GHC.Base.Semigroup Data.Monoid.Statistics.Numeric.MeanKahan
- Data.Monoid.Statistics.Numeric: instance GHC.Base.Semigroup Data.Monoid.Statistics.Numeric.WelfordMean
- Data.Monoid.Statistics.Numeric: instance GHC.Classes.Eq Data.Monoid.Statistics.Numeric.MeanKahan
- Data.Monoid.Statistics.Numeric: instance GHC.Classes.Eq Data.Monoid.Statistics.Numeric.WelfordMean
- Data.Monoid.Statistics.Numeric: instance GHC.Generics.Generic Data.Monoid.Statistics.Numeric.MeanKahan
- Data.Monoid.Statistics.Numeric: instance GHC.Generics.Generic Data.Monoid.Statistics.Numeric.WelfordMean
- Data.Monoid.Statistics.Numeric: instance GHC.Real.Real a => Data.Monoid.Statistics.Class.StatMonoid Data.Monoid.Statistics.Numeric.MeanKahan a
- Data.Monoid.Statistics.Numeric: instance GHC.Real.Real a => Data.Monoid.Statistics.Class.StatMonoid Data.Monoid.Statistics.Numeric.WelfordMean a
- Data.Monoid.Statistics.Numeric: instance GHC.Show.Show Data.Monoid.Statistics.Numeric.MeanKahan
- Data.Monoid.Statistics.Numeric: instance GHC.Show.Show Data.Monoid.Statistics.Numeric.WelfordMean
+ Data.Monoid.Statistics.Class: CalcViaHas :: a -> CalcViaHas a
+ Data.Monoid.Statistics.Class: EmptySample :: String -> SampleError
+ Data.Monoid.Statistics.Class: InvalidSample :: String -> String -> SampleError
+ Data.Monoid.Statistics.Class: Partial :: a -> Partial a
+ Data.Monoid.Statistics.Class: calcCount :: CalcCount a => a -> Int
+ Data.Monoid.Statistics.Class: calcMean :: (CalcMean a, MonadThrow m) => a -> m Double
+ Data.Monoid.Statistics.Class: calcStddev :: (CalcVariance a, MonadThrow m) => a -> m Double
+ Data.Monoid.Statistics.Class: calcStddevML :: (CalcVariance a, MonadThrow m) => a -> m Double
+ Data.Monoid.Statistics.Class: calcVariance :: (CalcVariance a, MonadThrow m) => a -> m Double
+ Data.Monoid.Statistics.Class: calcVarianceML :: (CalcVariance a, MonadThrow m) => a -> m Double
+ Data.Monoid.Statistics.Class: class CalcCount a
+ Data.Monoid.Statistics.Class: class CalcMean a
+ Data.Monoid.Statistics.Class: class CalcVariance a
+ Data.Monoid.Statistics.Class: class CalcMean a => HasMean a
+ Data.Monoid.Statistics.Class: class CalcVariance a => HasVariance a
+ Data.Monoid.Statistics.Class: data SampleError
+ Data.Monoid.Statistics.Class: getMean :: HasMean a => a -> Double
+ Data.Monoid.Statistics.Class: getStddev :: HasVariance a => a -> Double
+ Data.Monoid.Statistics.Class: getStddevML :: HasVariance a => a -> Double
+ Data.Monoid.Statistics.Class: getVariance :: HasVariance a => a -> Double
+ Data.Monoid.Statistics.Class: getVarianceML :: HasVariance a => a -> Double
+ Data.Monoid.Statistics.Class: instance (Data.Monoid.Statistics.Class.StatMonoid a x, Data.Monoid.Statistics.Class.StatMonoid b y) => Data.Monoid.Statistics.Class.StatMonoid (Data.Monoid.Statistics.Class.PPair a b) (x, y)
+ Data.Monoid.Statistics.Class: instance (Data.Monoid.Statistics.Class.StatMonoid m1 a, Data.Monoid.Statistics.Class.StatMonoid m2 a) => Data.Monoid.Statistics.Class.StatMonoid (m1, m2) a
+ Data.Monoid.Statistics.Class: instance (Data.Monoid.Statistics.Class.StatMonoid m1 a, Data.Monoid.Statistics.Class.StatMonoid m2 a, Data.Monoid.Statistics.Class.StatMonoid m3 a) => Data.Monoid.Statistics.Class.StatMonoid (m1, m2, m3) a
+ Data.Monoid.Statistics.Class: instance (Data.Monoid.Statistics.Class.StatMonoid m1 a, Data.Monoid.Statistics.Class.StatMonoid m2 a, Data.Monoid.Statistics.Class.StatMonoid m3 a, Data.Monoid.Statistics.Class.StatMonoid m4 a) => Data.Monoid.Statistics.Class.StatMonoid (m1, m2, m3, m4) a
+ Data.Monoid.Statistics.Class: instance (GHC.Base.Monoid a, GHC.Base.Monoid b) => GHC.Base.Monoid (Data.Monoid.Statistics.Class.PPair a b)
+ Data.Monoid.Statistics.Class: instance (GHC.Base.Semigroup a, GHC.Base.Semigroup b) => GHC.Base.Semigroup (Data.Monoid.Statistics.Class.PPair a b)
+ Data.Monoid.Statistics.Class: instance (GHC.Num.Num a, a GHC.Types.~ a') => Data.Monoid.Statistics.Class.StatMonoid (Data.Semigroup.Internal.Product a) a'
+ Data.Monoid.Statistics.Class: instance (GHC.Num.Num a, a GHC.Types.~ a') => Data.Monoid.Statistics.Class.StatMonoid (Data.Semigroup.Internal.Sum a) a'
+ Data.Monoid.Statistics.Class: instance Control.Monad.Catch.MonadThrow Data.Monoid.Statistics.Class.Partial
+ Data.Monoid.Statistics.Class: instance Data.Data.Data a => Data.Data.Data (Data.Monoid.Statistics.Class.Partial a)
+ Data.Monoid.Statistics.Class: instance Data.Monoid.Statistics.Class.HasMean a => Data.Monoid.Statistics.Class.CalcMean (Data.Monoid.Statistics.Class.CalcViaHas a)
+ Data.Monoid.Statistics.Class: instance Data.Monoid.Statistics.Class.HasMean a => Data.Monoid.Statistics.Class.HasMean (Data.Monoid.Statistics.Class.CalcViaHas a)
+ Data.Monoid.Statistics.Class: instance Data.Monoid.Statistics.Class.HasVariance a => Data.Monoid.Statistics.Class.CalcVariance (Data.Monoid.Statistics.Class.CalcViaHas a)
+ Data.Monoid.Statistics.Class: instance Data.Monoid.Statistics.Class.HasVariance a => Data.Monoid.Statistics.Class.HasVariance (Data.Monoid.Statistics.Class.CalcViaHas a)
+ Data.Monoid.Statistics.Class: instance GHC.Base.Applicative Data.Monoid.Statistics.Class.Partial
+ Data.Monoid.Statistics.Class: instance GHC.Base.Functor Data.Monoid.Statistics.Class.Partial
+ Data.Monoid.Statistics.Class: instance GHC.Base.Monad Data.Monoid.Statistics.Class.Partial
+ Data.Monoid.Statistics.Class: instance GHC.Classes.Eq a => GHC.Classes.Eq (Data.Monoid.Statistics.Class.Partial a)
+ Data.Monoid.Statistics.Class: instance GHC.Classes.Ord a => GHC.Classes.Ord (Data.Monoid.Statistics.Class.Partial a)
+ Data.Monoid.Statistics.Class: instance GHC.Exception.Type.Exception Data.Monoid.Statistics.Class.SampleError
+ Data.Monoid.Statistics.Class: instance GHC.Generics.Generic (Data.Monoid.Statistics.Class.Partial a)
+ Data.Monoid.Statistics.Class: instance GHC.Read.Read a => GHC.Read.Read (Data.Monoid.Statistics.Class.Partial a)
+ Data.Monoid.Statistics.Class: instance GHC.Real.Real a => Data.Monoid.Statistics.Class.StatMonoid Numeric.Sum.KB2Sum a
+ Data.Monoid.Statistics.Class: instance GHC.Show.Show Data.Monoid.Statistics.Class.SampleError
+ Data.Monoid.Statistics.Class: instance GHC.Show.Show a => GHC.Show.Show (Data.Monoid.Statistics.Class.Partial a)
+ Data.Monoid.Statistics.Class: newtype CalcViaHas a
+ Data.Monoid.Statistics.Class: newtype Partial a
+ Data.Monoid.Statistics.Class: partial :: HasCallStack => Partial a -> a
+ Data.Monoid.Statistics.Extra: MeanKB2 :: !Int -> {-# UNPACK #-} !KB2Sum -> MeanKB2
+ Data.Monoid.Statistics.Extra: MeanKahan :: !Int -> !KahanSum -> MeanKahan
+ Data.Monoid.Statistics.Extra: WelfordMean :: !Int -> !Double -> WelfordMean
+ Data.Monoid.Statistics.Extra: asMeanKB2 :: MeanKB2 -> MeanKB2
+ Data.Monoid.Statistics.Extra: asMeanKahan :: MeanKahan -> MeanKahan
+ Data.Monoid.Statistics.Extra: asWelfordMean :: WelfordMean -> WelfordMean
+ Data.Monoid.Statistics.Extra: data MeanKB2
+ Data.Monoid.Statistics.Extra: data MeanKahan
+ Data.Monoid.Statistics.Extra: data WelfordMean
+ Data.Monoid.Statistics.Extra: instance Data.Data.Data Data.Monoid.Statistics.Extra.MeanKahan
+ Data.Monoid.Statistics.Extra: instance Data.Data.Data Data.Monoid.Statistics.Extra.WelfordMean
+ Data.Monoid.Statistics.Extra: instance Data.Monoid.Statistics.Class.CalcCount Data.Monoid.Statistics.Extra.MeanKahan
+ Data.Monoid.Statistics.Extra: instance Data.Monoid.Statistics.Class.CalcCount Data.Monoid.Statistics.Extra.WelfordMean
+ Data.Monoid.Statistics.Extra: instance Data.Monoid.Statistics.Class.CalcMean Data.Monoid.Statistics.Extra.MeanKB2
+ Data.Monoid.Statistics.Extra: instance Data.Monoid.Statistics.Class.CalcMean Data.Monoid.Statistics.Extra.MeanKahan
+ Data.Monoid.Statistics.Extra: instance Data.Monoid.Statistics.Class.CalcMean Data.Monoid.Statistics.Extra.WelfordMean
+ Data.Monoid.Statistics.Extra: instance Data.Vector.Generic.Base.Vector Data.Vector.Unboxed.Base.Vector Data.Monoid.Statistics.Extra.MeanKahan
+ Data.Monoid.Statistics.Extra: instance Data.Vector.Generic.Base.Vector Data.Vector.Unboxed.Base.Vector Data.Monoid.Statistics.Extra.WelfordMean
+ Data.Monoid.Statistics.Extra: instance Data.Vector.Generic.Mutable.Base.MVector Data.Vector.Unboxed.Base.MVector Data.Monoid.Statistics.Extra.MeanKahan
+ Data.Monoid.Statistics.Extra: instance Data.Vector.Generic.Mutable.Base.MVector Data.Vector.Unboxed.Base.MVector Data.Monoid.Statistics.Extra.WelfordMean
+ Data.Monoid.Statistics.Extra: instance Data.Vector.Unboxed.Base.Unbox Data.Monoid.Statistics.Extra.MeanKahan
+ Data.Monoid.Statistics.Extra: instance Data.Vector.Unboxed.Base.Unbox Data.Monoid.Statistics.Extra.WelfordMean
+ Data.Monoid.Statistics.Extra: instance GHC.Base.Monoid Data.Monoid.Statistics.Extra.MeanKB2
+ Data.Monoid.Statistics.Extra: instance GHC.Base.Monoid Data.Monoid.Statistics.Extra.MeanKahan
+ Data.Monoid.Statistics.Extra: instance GHC.Base.Monoid Data.Monoid.Statistics.Extra.WelfordMean
+ Data.Monoid.Statistics.Extra: instance GHC.Base.Semigroup Data.Monoid.Statistics.Extra.MeanKB2
+ Data.Monoid.Statistics.Extra: instance GHC.Base.Semigroup Data.Monoid.Statistics.Extra.MeanKahan
+ Data.Monoid.Statistics.Extra: instance GHC.Base.Semigroup Data.Monoid.Statistics.Extra.WelfordMean
+ Data.Monoid.Statistics.Extra: instance GHC.Classes.Eq Data.Monoid.Statistics.Extra.MeanKB2
+ Data.Monoid.Statistics.Extra: instance GHC.Classes.Eq Data.Monoid.Statistics.Extra.MeanKahan
+ Data.Monoid.Statistics.Extra: instance GHC.Classes.Eq Data.Monoid.Statistics.Extra.WelfordMean
+ Data.Monoid.Statistics.Extra: instance GHC.Generics.Generic Data.Monoid.Statistics.Extra.MeanKahan
+ Data.Monoid.Statistics.Extra: instance GHC.Generics.Generic Data.Monoid.Statistics.Extra.WelfordMean
+ Data.Monoid.Statistics.Extra: instance GHC.Real.Real a => Data.Monoid.Statistics.Class.StatMonoid Data.Monoid.Statistics.Extra.MeanKB2 a
+ Data.Monoid.Statistics.Extra: instance GHC.Real.Real a => Data.Monoid.Statistics.Class.StatMonoid Data.Monoid.Statistics.Extra.MeanKahan a
+ Data.Monoid.Statistics.Extra: instance GHC.Real.Real a => Data.Monoid.Statistics.Class.StatMonoid Data.Monoid.Statistics.Extra.WelfordMean a
+ Data.Monoid.Statistics.Extra: instance GHC.Show.Show Data.Monoid.Statistics.Extra.MeanKB2
+ Data.Monoid.Statistics.Extra: instance GHC.Show.Show Data.Monoid.Statistics.Extra.MeanKahan
+ Data.Monoid.Statistics.Extra: instance GHC.Show.Show Data.Monoid.Statistics.Extra.WelfordMean
+ Data.Monoid.Statistics.Numeric: MeanNaive :: !Int -> !Double -> MeanNaive
+ Data.Monoid.Statistics.Numeric: WMeanKBN :: {-# UNPACK #-} !KBNSum -> {-# UNPACK #-} !KBNSum -> WMeanKBN
+ Data.Monoid.Statistics.Numeric: WMeanNaive :: !Double -> !Double -> WMeanNaive
+ Data.Monoid.Statistics.Numeric: Weighted :: w -> a -> Weighted w a
+ Data.Monoid.Statistics.Numeric: asMean :: Mean -> Mean
+ Data.Monoid.Statistics.Numeric: asMeanNaive :: MeanNaive -> MeanNaive
+ Data.Monoid.Statistics.Numeric: asWMean :: WMean -> WMean
+ Data.Monoid.Statistics.Numeric: asWMeanKBN :: WMeanKBN -> WMeanKBN
+ Data.Monoid.Statistics.Numeric: asWMeanNaive :: WMeanNaive -> WMeanNaive
+ Data.Monoid.Statistics.Numeric: data MeanNaive
+ Data.Monoid.Statistics.Numeric: data WMeanKBN
+ Data.Monoid.Statistics.Numeric: data WMeanNaive
+ Data.Monoid.Statistics.Numeric: data Weighted w a
+ Data.Monoid.Statistics.Numeric: instance (Data.Data.Data w, Data.Data.Data a) => Data.Data.Data (Data.Monoid.Statistics.Numeric.Weighted w a)
+ Data.Monoid.Statistics.Numeric: instance (Data.Vector.Unboxed.Base.Unbox w, Data.Vector.Unboxed.Base.Unbox a) => Data.Vector.Generic.Base.Vector Data.Vector.Unboxed.Base.Vector (Data.Monoid.Statistics.Numeric.Weighted w a)
+ Data.Monoid.Statistics.Numeric: instance (Data.Vector.Unboxed.Base.Unbox w, Data.Vector.Unboxed.Base.Unbox a) => Data.Vector.Generic.Mutable.Base.MVector Data.Vector.Unboxed.Base.MVector (Data.Monoid.Statistics.Numeric.Weighted w a)
+ Data.Monoid.Statistics.Numeric: instance (Data.Vector.Unboxed.Base.Unbox w, Data.Vector.Unboxed.Base.Unbox a) => Data.Vector.Unboxed.Base.Unbox (Data.Monoid.Statistics.Numeric.Weighted w a)
+ Data.Monoid.Statistics.Numeric: instance (GHC.Classes.Eq w, GHC.Classes.Eq a) => GHC.Classes.Eq (Data.Monoid.Statistics.Numeric.Weighted w a)
+ Data.Monoid.Statistics.Numeric: instance (GHC.Classes.Ord a, a GHC.Types.~ a') => Data.Monoid.Statistics.Class.StatMonoid (Data.Monoid.Statistics.Numeric.Max a) a'
+ Data.Monoid.Statistics.Numeric: instance (GHC.Classes.Ord a, a GHC.Types.~ a') => Data.Monoid.Statistics.Class.StatMonoid (Data.Monoid.Statistics.Numeric.Min a) a'
+ Data.Monoid.Statistics.Numeric: instance (GHC.Classes.Ord w, GHC.Classes.Ord a) => GHC.Classes.Ord (Data.Monoid.Statistics.Numeric.Weighted w a)
+ Data.Monoid.Statistics.Numeric: instance (GHC.Real.Real w, GHC.Real.Real a) => Data.Monoid.Statistics.Class.StatMonoid Data.Monoid.Statistics.Numeric.WMeanKBN (Data.Monoid.Statistics.Numeric.Weighted w a)
+ Data.Monoid.Statistics.Numeric: instance (GHC.Real.Real w, GHC.Real.Real a) => Data.Monoid.Statistics.Class.StatMonoid Data.Monoid.Statistics.Numeric.WMeanNaive (Data.Monoid.Statistics.Numeric.Weighted w a)
+ Data.Monoid.Statistics.Numeric: instance (GHC.Show.Show w, GHC.Show.Show a) => GHC.Show.Show (Data.Monoid.Statistics.Numeric.Weighted w a)
+ Data.Monoid.Statistics.Numeric: instance (a GHC.Types.~ GHC.Types.Double) => Data.Monoid.Statistics.Class.StatMonoid Data.Monoid.Statistics.Numeric.MaxD a
+ Data.Monoid.Statistics.Numeric: instance (a GHC.Types.~ GHC.Types.Double) => Data.Monoid.Statistics.Class.StatMonoid Data.Monoid.Statistics.Numeric.MinD a
+ Data.Monoid.Statistics.Numeric: instance Data.Data.Data Data.Monoid.Statistics.Numeric.MeanNaive
+ Data.Monoid.Statistics.Numeric: instance Data.Data.Data Data.Monoid.Statistics.Numeric.WMeanKBN
+ Data.Monoid.Statistics.Numeric: instance Data.Data.Data Data.Monoid.Statistics.Numeric.WMeanNaive
+ Data.Monoid.Statistics.Numeric: instance Data.Foldable.Foldable (Data.Monoid.Statistics.Numeric.Weighted w)
+ Data.Monoid.Statistics.Numeric: instance Data.Monoid.Statistics.Class.CalcCount (Data.Monoid.Statistics.Numeric.CountG GHC.Types.Int)
+ Data.Monoid.Statistics.Numeric: instance Data.Monoid.Statistics.Class.CalcCount Data.Monoid.Statistics.Numeric.MeanKBN
+ Data.Monoid.Statistics.Numeric: instance Data.Monoid.Statistics.Class.CalcCount Data.Monoid.Statistics.Numeric.MeanNaive
+ Data.Monoid.Statistics.Numeric: instance Data.Monoid.Statistics.Class.CalcCount Data.Monoid.Statistics.Numeric.Variance
+ Data.Monoid.Statistics.Numeric: instance Data.Monoid.Statistics.Class.CalcMean Data.Monoid.Statistics.Numeric.MeanKBN
+ Data.Monoid.Statistics.Numeric: instance Data.Monoid.Statistics.Class.CalcMean Data.Monoid.Statistics.Numeric.MeanNaive
+ Data.Monoid.Statistics.Numeric: instance Data.Monoid.Statistics.Class.CalcMean Data.Monoid.Statistics.Numeric.Variance
+ Data.Monoid.Statistics.Numeric: instance Data.Monoid.Statistics.Class.CalcMean Data.Monoid.Statistics.Numeric.WMeanKBN
+ Data.Monoid.Statistics.Numeric: instance Data.Monoid.Statistics.Class.CalcMean Data.Monoid.Statistics.Numeric.WMeanNaive
+ Data.Monoid.Statistics.Numeric: instance Data.Monoid.Statistics.Class.CalcVariance Data.Monoid.Statistics.Numeric.Variance
+ Data.Monoid.Statistics.Numeric: instance Data.Traversable.Traversable (Data.Monoid.Statistics.Numeric.Weighted w)
+ Data.Monoid.Statistics.Numeric: instance GHC.Base.Functor (Data.Monoid.Statistics.Numeric.Weighted w)
+ Data.Monoid.Statistics.Numeric: instance GHC.Base.Monoid Data.Monoid.Statistics.Numeric.MeanNaive
+ Data.Monoid.Statistics.Numeric: instance GHC.Base.Monoid Data.Monoid.Statistics.Numeric.WMeanKBN
+ Data.Monoid.Statistics.Numeric: instance GHC.Base.Monoid Data.Monoid.Statistics.Numeric.WMeanNaive
+ Data.Monoid.Statistics.Numeric: instance GHC.Base.Semigroup Data.Monoid.Statistics.Numeric.MeanNaive
+ Data.Monoid.Statistics.Numeric: instance GHC.Base.Semigroup Data.Monoid.Statistics.Numeric.WMeanKBN
+ Data.Monoid.Statistics.Numeric: instance GHC.Base.Semigroup Data.Monoid.Statistics.Numeric.WMeanNaive
+ Data.Monoid.Statistics.Numeric: instance GHC.Classes.Eq Data.Monoid.Statistics.Numeric.MeanNaive
+ Data.Monoid.Statistics.Numeric: instance GHC.Classes.Eq Data.Monoid.Statistics.Numeric.WMeanKBN
+ Data.Monoid.Statistics.Numeric: instance GHC.Classes.Eq Data.Monoid.Statistics.Numeric.WMeanNaive
+ Data.Monoid.Statistics.Numeric: instance GHC.Generics.Generic (Data.Monoid.Statistics.Numeric.Weighted w a)
+ Data.Monoid.Statistics.Numeric: instance GHC.Generics.Generic Data.Monoid.Statistics.Numeric.MeanNaive
+ Data.Monoid.Statistics.Numeric: instance GHC.Generics.Generic Data.Monoid.Statistics.Numeric.WMeanKBN
+ Data.Monoid.Statistics.Numeric: instance GHC.Generics.Generic Data.Monoid.Statistics.Numeric.WMeanNaive
+ Data.Monoid.Statistics.Numeric: instance GHC.Real.Real a => Data.Monoid.Statistics.Class.StatMonoid Data.Monoid.Statistics.Numeric.MeanNaive a
+ Data.Monoid.Statistics.Numeric: instance GHC.Show.Show Data.Monoid.Statistics.Numeric.MeanNaive
+ Data.Monoid.Statistics.Numeric: instance GHC.Show.Show Data.Monoid.Statistics.Numeric.WMeanKBN
+ Data.Monoid.Statistics.Numeric: instance GHC.Show.Show Data.Monoid.Statistics.Numeric.WMeanNaive
+ Data.Monoid.Statistics.Numeric: type Mean = MeanKBN
+ Data.Monoid.Statistics.Numeric: type WMean = WMeanKBN
- Data.Monoid.Statistics.Class: reduceSample :: (Foldable f, StatMonoid m a) => f a -> m
+ Data.Monoid.Statistics.Class: reduceSample :: forall m a f. (StatMonoid m a, Foldable f) => f a -> m
- Data.Monoid.Statistics.Class: reduceSampleVec :: (Vector v a, StatMonoid m a) => v a -> m
+ Data.Monoid.Statistics.Class: reduceSampleVec :: forall m a v. (StatMonoid m a, Vector v a) => v a -> m
- Data.Monoid.Statistics.Numeric: MeanKBN :: !Int -> !KBNSum -> MeanKBN
+ Data.Monoid.Statistics.Numeric: MeanKBN :: !Int -> {-# UNPACK #-} !KBNSum -> MeanKBN
Files
- Changelog.md +32/−0
- Data/Monoid/Statistics.hs +1/−0
- Data/Monoid/Statistics/Class.hs +307/−34
- Data/Monoid/Statistics/Extra.hs +175/−0
- Data/Monoid/Statistics/Numeric.hs +216/−215
- bench/Main.hs +68/−0
- monoid-statistics.cabal +53/−14
- tests/Main.hs +247/−126
- tests/doctests.hs +6/−0
+ Changelog.md view
@@ -0,0 +1,32 @@+# Changes in 1.1.0.0++- Type classes `CalcMean` and `CalcVar` are generalized to use `MonadThrow` to+ signal failure instead of using `Maybe` only++- Functions for computing standard deviation are placed into type+ classes. Sometimes we have standard deviation at hand, if distribution is+ parameterized by it for example.++- `Mean` now type synonym for `MeanKBN`.++- `WelfordMean` and `KahanMean` are moved to `D.M.S.Extra` module.++- Support for calculating weighted mean.++- `StatMonoid` instances for up to 4-tuples.++- `Max` now works correctly (#2).++- `PPair` for use in parallel computation is added.+++# Changes in 1.0.0.0++- Type class definition changed: now it has both `addValue :: m → a → m` and+ `singletonMonoid :: a → m`++- `Mean` renamed as `WelfordMean`++- `Unbox` instances added for all data types.++- `BinomAcc` added.
Data/Monoid/Statistics.hs view
@@ -12,3 +12,4 @@ import Data.Monoid.Statistics.Class import Data.Monoid.Statistics.Numeric+
Data/Monoid/Statistics/Class.hs view
@@ -1,14 +1,17 @@-{-# LANGUAGE BangPatterns #-}-{-# LANGUAGE CPP #-}-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE TypeFamilies #-}----{-# OPTIONS_GHC -fno-warn-orphans #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE DefaultSignatures #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveFoldable #-}+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DeriveTraversable #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TypeFamilies #-} -- | -- Module : Data.Monoid.Statistics -- Copyright : Copyright (c) 2010,2017, Alexey Khudyakov <alexey.skladnoy@gmail.com>@@ -17,31 +20,43 @@ -- Stability : experimental -- module Data.Monoid.Statistics.Class- ( -- * Type class and helpers+ ( -- * Monoid Type class and helpers StatMonoid(..) , reduceSample , reduceSampleVec+ -- * Ad-hoc type classes for select statistics+ -- $adhoc+ , CalcCount(..)+ , CalcMean(..)+ , HasMean(..)+ , CalcVariance(..)+ , HasVariance(..)+ , CalcViaHas(..)+ -- * Exception handling+ , Partial(..)+ , partial+ , SampleError(..) -- * Data types , Pair(..) ) where -import Data.Data (Typeable,Data)-#if MIN_VERSION_base(4,9,0)-import qualified Data.Semigroup as SG (Semigroup(..))-#endif-import Data.Monoid (Monoid(..),(<>),Sum(..),Product(..))+import Control.Exception+import Control.Monad.Catch (MonadThrow(..))+import Data.Data (Typeable,Data)+import Data.Monoid import Data.Vector.Unboxed (Unbox) import Data.Vector.Unboxed.Deriving (derivingUnbox) import qualified Data.Foldable as F import qualified Data.Vector.Generic as G import Numeric.Sum+import GHC.Stack (HasCallStack) import GHC.Generics (Generic) + -- | This type class is used to express parallelizable constant space--- algorithms for calculation of statistics. By definitions--- /statistic/ is some measure of sample which doesn't depend on--- order of elements (for example: mean, sum, number of elements,--- variance, etc).+-- algorithms for calculation of statistics. /Statistic/ is function+-- of type @[a]→b@ which does not depend on order of elements. (for+-- example: mean, sum, number of elements, variance, etc). -- -- For many statistics it's possible to possible to construct -- constant space algorithm which is expressed as fold. Additionally@@ -49,7 +64,7 @@ -- fold accumulator to get statistic for union of two samples. -- -- Thus for such algorithm we have value which corresponds to empty--- sample, merge function which which corresponds to merging of two+-- sample, function which which corresponds to merging of two -- samples, and single step of fold. Last one allows to evaluate -- statistic given data sample and first two form a monoid and allow -- parallelization: split data into parts, build estimate for each@@ -57,7 +72,7 @@ -- -- Instance must satisfy following laws. If floating point -- arithmetics is used then equality should be understood as--- approximate. +-- approximate. -- -- > 1. addValue (addValue y mempty) x == addValue mempty x <> addValue mempty y -- > 2. x <> y == y <> x@@ -73,17 +88,66 @@ {-# MINIMAL addValue | singletonMonoid #-} -- | Calculate statistic over 'Foldable'. It's implemented in terms of--- foldl'.-reduceSample :: (F.Foldable f, StatMonoid m a) => f a -> m+-- foldl'. Note that in cases when accumulator is immediately+-- consumed by polymorphic function such as 'callMeam' its type+-- becomes ambiguous. @TypeApplication@ then could be used to+-- disambiguate.+--+-- >>> reduceSample @Mean [1,2,3,4]+-- MeanKBN 4 (KBNSum 10.0 0.0)+-- >>> calcMean $ reduceSample @Mean [1,2,3,4] :: Maybe Double+-- Just 2.5+reduceSample :: forall m a f. (StatMonoid m a, F.Foldable f) => f a -> m reduceSample = F.foldl' addValue mempty --- | Calculate statistic over vector. It's implemented in terms of--- foldl'.-reduceSampleVec :: (G.Vector v a, StatMonoid m a) => v a -> m+-- | Calculate statistic over vector. Works in same was as+-- 'reduceSample' but works for vectors.+reduceSampleVec :: forall m a v. (StatMonoid m a, G.Vector v a) => v a -> m reduceSampleVec = G.foldl' addValue mempty {-# INLINE reduceSampleVec #-} +instance ( StatMonoid m1 a+ , StatMonoid m2 a+ ) => StatMonoid (m1,m2) a where+ addValue (!m1, !m2) a =+ let !m1' = addValue m1 a+ !m2' = addValue m2 a+ in (m1', m2')+ singletonMonoid a = ( singletonMonoid a+ , singletonMonoid a+ ) +instance ( StatMonoid m1 a+ , StatMonoid m2 a+ , StatMonoid m3 a+ ) => StatMonoid (m1,m2,m3) a where+ addValue (!m1, !m2, !m3) a =+ let !m1' = addValue m1 a+ !m2' = addValue m2 a+ !m3' = addValue m3 a+ in (m1', m2', m3')+ singletonMonoid a = ( singletonMonoid a+ , singletonMonoid a+ , singletonMonoid a+ )++instance ( StatMonoid m1 a+ , StatMonoid m2 a+ , StatMonoid m3 a+ , StatMonoid m4 a+ ) => StatMonoid (m1,m2,m3,m4) a where+ addValue (!m1, !m2, !m3, !m4) a =+ let !m1' = addValue m1 a+ !m2' = addValue m2 a+ !m3' = addValue m3 a+ !m4' = addValue m4 a+ in (m1', m2', m3', m4')+ singletonMonoid a = ( singletonMonoid a+ , singletonMonoid a+ , singletonMonoid a+ , singletonMonoid a+ )+ instance (Num a, a ~ a') => StatMonoid (Sum a) a' where singletonMonoid = Sum @@ -98,8 +162,189 @@ addValue m x = add m (realToFrac x) {-# INLINE addValue #-} +instance Real a => StatMonoid KB2Sum a where+ addValue m x = add m (realToFrac x)+ {-# INLINE addValue #-} + ----------------------------------------------------------------+-- Ad-hoc type class+----------------------------------------------------------------++-- $adhoc+--+-- Type classes defined here allows to extract common statistics from+-- estimators. it's assumed that quantities in question are already+-- computed so extraction is cheap.+--+--+-- ==== Error handling+--+-- Computation of statistics may fail. For example mean is not defined+-- for an empty sample. @Maybe@ could be seen as easy way to handle+-- this situation. But in many cases most convenient way to handle+-- failure is to throw an exception. So failure is encoded by using+-- polymorphic function of type @MonadThrow m ⇒ a → m X@.+--+-- Maybe types has instance, such as 'Maybe', 'Either'+-- 'Control.Exception.SomeException', 'IO' and most transformers+-- wrapping it. Notably this library defines 'Partial' monad which+-- allows to convert failures to exception in pure setting.+--+-- >>> calcMean $ reduceSample @Mean []+-- *** Exception: EmptySample "Data.Monoid.Statistics.Numeric.MeanKBN: calcMean"+--+-- >>> calcMean $ reduceSample @Mean [] :: Maybe Double+-- Nothing+--+-- >>> import Control.Exception+-- >>> calcMean $ reduceSample @Mean [] :: Either SomeException Double+-- Left (EmptySample "Data.Monoid.Statistics.Numeric.MeanKBN: calcMean")+--+-- Last example uses IO+--+-- >>> calcMean $ reduceSample @Mean []+-- *** Exception: EmptySample "Data.Monoid.Statistics.Numeric.MeanKBN: calcMean"+--+--+-- ==== Deriving instances+--+-- Type classes come in two variants, one that allow failure and one+-- for use in cases when quantity is always defined. This is not the+-- case for estimators, but true for distributions and intended for+-- such use cases. In that case 'CalcViaHas' could be used to derive+-- necessary instances.+--+-- >>> :{+-- data NormalDist = NormalDist !Double !Double+-- deriving (CalcMean,CalcVariance) via CalcViaHas NormalDist+-- instance HasMean NormalDist where+-- getMean (NormalDist mu _) = mu+-- instance HasVariance NormalDist where+-- getVariance (NormalDist _ s) = s+-- getVarianceML (NormalDist _ s) = s+-- :}+++-- | Value from which we can efficiently extract number of elements in+-- sample it represents.+class CalcCount a where+ -- | /Assumed O(1)/. Number of elements in sample.+ calcCount :: a -> Int++-- | Value from which we can efficiently calculate mean of sample or+-- distribution.+class CalcMean a where+ -- | /Assumed O(1)/ Returns @Nothing@ if there isn't enough data to+ -- make estimate or distribution doesn't have defined mean.+ --+ -- \[ \bar{x} = \frac{1}{N}\sum_{i=1}^N{x_i} \]+ calcMean :: MonadThrow m => a -> m Double++-- | Same as 'CalcMean' but should never fail+class CalcMean a => HasMean a where+ getMean :: a -> Double+++-- | Values from which we can efficiently compute estimate of sample+-- variance or distribution variance. It has two methods: one which+-- applies bias correction to estimate and another that returns+-- maximul likelyhood estimate. For distribution they should return+-- same value.+class CalcVariance a where+ -- | /Assumed O(1)/ Calculate unbiased estimate of variance:+ --+ -- \[ \sigma^2 = \frac{1}{N-1}\sum_{i=1}^N(x_i - \bar{x})^2 \]+ calcVariance :: MonadThrow m => a -> m Double+ calcVariance = fmap (\x->x*x) . calcStddev+ -- | /Assumed O(1)/ Calculate maximum likelihood estimate of variance:+ --+ -- \[ \sigma^2 = \frac{1}{N}\sum_{i=1}^N(x_i - \bar{x})^2 \]+ calcVarianceML :: MonadThrow m => a -> m Double+ calcVarianceML = fmap (\x->x*x) . calcStddevML+ -- | Calculate sample standard deviation from unbiased estimation of+ -- variance.+ calcStddev :: MonadThrow m => a -> m Double+ calcStddev = fmap sqrt . calcVariance+ -- | Calculate sample standard deviation from maximum likelihood+ -- estimation of variance.+ calcStddevML :: (MonadThrow m) => a -> m Double+ calcStddevML = fmap sqrt . calcVarianceML+ {-# MINIMAL (calcVariance,calcVarianceML) | (calcStddev,calcStddevML) #-}++-- | Same as 'CalcVariance' but never fails+class CalcVariance a => HasVariance a where+ getVariance :: a -> Double+ getVariance = (\x -> x*x) . getStddev+ getVarianceML :: a -> Double+ getVarianceML = (\x -> x*x) . getStddevML+ getStddev :: a -> Double+ getStddev = sqrt . getVariance+ getStddevML :: a -> Double+ getStddevML = sqrt . getVarianceML+ {-# MINIMAL (getVariance,getVarianceML) | (getStddev,getStddevML) #-}+++++newtype CalcViaHas a = CalcViaHas a+ deriving newtype (HasMean, HasVariance)++instance HasMean a => CalcMean (CalcViaHas a) where+ calcMean = pure . getMean++instance HasVariance a => CalcVariance (CalcViaHas a) where+ calcVariance = pure . getVariance+ calcVarianceML = pure . getVarianceML++----------------------------------------------------------------+-- Exceptions+----------------------------------------------------------------++-- | Identity monad which is used to encode partial functions for+-- 'MonadThrow' based error handling. Its @MonadThrow@ instance+-- just throws normal exception.+newtype Partial a = Partial a+ deriving (Show, Read, Eq, Ord, Typeable, Data, Generic)++-- | Convert error to IO exception. This way one could for example+-- convert case when some statistics is not defined to an exception:+--+-- >>> calcMean $ reduceSample @Mean []+-- *** Exception: EmptySample "Data.Monoid.Statistics.Numeric.MeanKBN: calcMean"+partial :: HasCallStack => Partial a -> a+partial (Partial x) = x++instance Functor Partial where+ fmap f (Partial a) = Partial (f a)++instance Applicative Partial where+ pure = Partial+ Partial f <*> Partial a = Partial (f a)+ (!_) *> a = a+ a <* (!_) = a+instance Monad Partial where+ return = pure+ Partial a >>= f = f a+ (>>) = (*>)++instance MonadThrow Partial where+ throwM = throw++-- | Exception which is thrown when we can't compute some value+data SampleError+ = EmptySample String+ -- ^ @EmptySample function@: We're trying to compute quantity that+ -- is undefined for empty sample.+ | InvalidSample String String+ -- ^ @InvalidSample function descripton@ quantity in question could+ -- not be computed for some other reason+ deriving Show++instance Exception SampleError+++---------------------------------------------------------------- -- Generic monoids ---------------------------------------------------------------- @@ -107,16 +352,15 @@ data Pair a b = Pair !a !b deriving (Show,Eq,Ord,Typeable,Data,Generic) -#if MIN_VERSION_base(4,9,0)-instance (SG.Semigroup a, SG.Semigroup b) => SG.Semigroup (Pair a b) where- Pair x y <> Pair x' y' = Pair (x SG.<> x') (y SG.<> y')-#endif+instance (Semigroup a, Semigroup b) => Semigroup (Pair a b) where+ Pair x y <> Pair x' y' = Pair (x <> x') (y <> y')+ {-# INLINABLE (<>) #-} instance (Monoid a, Monoid b) => Monoid (Pair a b) where mempty = Pair mempty mempty- mappend (Pair x y) (Pair x' y') = Pair (x <> x') (y <> y')+ mappend = (<>) {-# INLINABLE mempty #-}- {-# INLINE mappend #-}+ {-# INLINABLE mappend #-} instance (StatMonoid a x, StatMonoid b x) => StatMonoid (Pair a b) x where addValue (Pair a b) !x = Pair (addValue a x) (addValue b x)@@ -124,7 +368,36 @@ {-# INLINE addValue #-} {-# INLINE singletonMonoid #-} ++-- | Strict pair for parallel accumulation+data PPair a b = PPair !a !b++instance (Semigroup a, Semigroup b) => Semigroup (PPair a b) where+ PPair x y <> PPair x' y' = PPair (x <> x') (y <> y')+ {-# INLINABLE (<>) #-}++instance (Monoid a, Monoid b) => Monoid (PPair a b) where+ mempty = PPair mempty mempty+ mappend = (<>)+ {-# INLINABLE mempty #-}+ {-# INLINABLE mappend #-}++instance (StatMonoid a x, StatMonoid b y) => StatMonoid (PPair a b) (x,y) where+ addValue (PPair a b) (!x,!y) = PPair (addValue a x) (addValue b y)+ singletonMonoid (!x,!y) = PPair (singletonMonoid x) (singletonMonoid y)+ {-# INLINE addValue #-}+ {-# INLINE singletonMonoid #-}+++++ derivingUnbox "Pair" [t| forall a b. (Unbox a, Unbox b) => Pair a b -> (a,b) |] [| \(Pair a b) -> (a,b) |] [| \(a,b) -> Pair a b |]++-- $setup+--+-- >>> :set -XDerivingVia+-- >>> import Data.Monoid.Statistics.Numeric
+ Data/Monoid/Statistics/Extra.hs view
@@ -0,0 +1,175 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TypeFamilies #-}+-- |+-- Monoids for calculating various statistics in constant space. This+-- module contains algorithms that should be generally avoided unless+-- there's specific reason to use them.+module Data.Monoid.Statistics.Extra (+ -- * Mean+ WelfordMean(..)+ , asWelfordMean+ , MeanKahan(..)+ , asMeanKahan+ , MeanKB2(..)+ , asMeanKB2+ -- $references+ ) where++import Control.Monad.Catch (MonadThrow(..))+import Data.Data (Typeable,Data)+import Data.Vector.Unboxed.Deriving (derivingUnbox)+import Numeric.Sum+import GHC.Generics (Generic)++import Data.Monoid.Statistics.Class++++----------------------------------------------------------------+-- Mean+----------------------------------------------------------------+++-- | Incremental calculation of mean which uses second-order+-- compensated Kahan-Babuška summation. In most cases+-- 'Data.Monoid.Statistics.Numeric.KBNSum' should provide enough+-- precision.+data MeanKB2 = MeanKB2 !Int {-# UNPACK #-} !KB2Sum+ deriving (Show,Eq)++asMeanKB2 :: MeanKB2 -> MeanKB2+asMeanKB2 = id++instance Semigroup MeanKB2 where+ MeanKB2 0 _ <> m = m+ m <> MeanKB2 0 _ = m+ MeanKB2 n1 s1 <> MeanKB2 n2 s2 = MeanKB2 (n1+n2) (s1 <> s2)++instance Monoid MeanKB2 where+ mempty = MeanKB2 0 mempty+ mappend = (<>)++instance Real a => StatMonoid MeanKB2 a where+ addValue (MeanKB2 n m) x = MeanKB2 (n+1) (addValue m x)++instance CalcMean MeanKB2 where+ calcMean (MeanKB2 0 _) = throwM $ EmptySample "Data.Monoid.Statistics.Extra.MeanKB2"+ calcMean (MeanKB2 n s) = return $! kb2 s / fromIntegral n++++-- | Incremental calculation of mean. Sum of elements is calculated+-- using compensated Kahan summation. It's provided only for sake of+-- completeness. 'Data.Monoid.Statistics.Numeric.KBNSum' should be used+-- instead.+data MeanKahan = MeanKahan !Int !KahanSum+ deriving (Show,Eq,Typeable,Data,Generic)++asMeanKahan :: MeanKahan -> MeanKahan+asMeanKahan = id+++instance Semigroup MeanKahan where+ MeanKahan 0 _ <> m = m+ m <> MeanKahan 0 _ = m+ MeanKahan n1 s1 <> MeanKahan n2 s2 = MeanKahan (n1+n2) (s1 <> s2)+ {-# INLINE (<>) #-}++instance Monoid MeanKahan where+ mempty = MeanKahan 0 mempty+ mappend = (<>)++instance Real a => StatMonoid MeanKahan a where+ addValue (MeanKahan n m) x = MeanKahan (n+1) (addValue m x)++instance CalcCount MeanKahan where+ calcCount (MeanKahan n _) = n+instance CalcMean MeanKahan where+ calcMean (MeanKahan 0 _) = throwM $ EmptySample "Data.Monoid.Statistics.Extra.WelfordMean"+ calcMean (MeanKahan n s) = return (kahan s / fromIntegral n)+++-- | Incremental calculation of mean. Note that this algorithm doesn't+-- offer better numeric precision than plain summation. Its only+-- advantage is protection against double overflow:+--+-- >>> calcMean $ reduceSample @MeanKBN (replicate 100 1e308) :: Maybe Double+-- Just NaN+-- >>> calcMean $ reduceSample @WelfordMean (replicate 100 1e308) :: Maybe Double+-- Just 1.0e308+--+-- Unless this feature is needed 'Data.Monoid.Statistics.Numeric.KBNSum'+-- should be used. Algorithm is due to Welford [Welford1962]+--+-- \[ s_n = s_{n-1} + \frac{x_n - s_{n-1}}{n} \]+data WelfordMean = WelfordMean !Int -- Number of entries+ !Double -- Current mean+ deriving (Show,Eq,Typeable,Data,Generic)++-- | Type restricted 'id'+asWelfordMean :: WelfordMean -> WelfordMean+asWelfordMean = id++instance Semigroup WelfordMean where+ WelfordMean 0 _ <> m = m+ m <> WelfordMean 0 _ = m+ WelfordMean n x <> WelfordMean k y+ = WelfordMean (n + k) ((x*n' + y*k') / (n' + k'))+ where+ n' = fromIntegral n+ k' = fromIntegral k+ {-# INLINE (<>) #-}++instance Monoid WelfordMean where+ mempty = WelfordMean 0 0+ mappend = (<>)+ {-# INLINE mempty #-}+ {-# INLINE mappend #-}++instance Real a => StatMonoid WelfordMean a where+ addValue (WelfordMean n m) !x+ = WelfordMean n' (m + (realToFrac x - m) / fromIntegral n')+ where+ n' = n+1+ {-# INLINE addValue #-}++instance CalcCount WelfordMean where+ calcCount (WelfordMean n _) = n+instance CalcMean WelfordMean where+ calcMean (WelfordMean 0 _) = throwM $ EmptySample "Data.Monoid.Statistics.Extra.WelfordMean"+ calcMean (WelfordMean _ m) = return m++++----------------------------------------------------------------+-- Unboxed instances+----------------------------------------------------------------++derivingUnbox "MeanKahan"+ [t| MeanKahan -> (Int,Double,Double) |]+ [| \(MeanKahan a (KahanSum b c)) -> (a,b,c) |]+ [| \(a,b,c) -> MeanKahan a (KahanSum b c) |]++derivingUnbox "WelfordMean"+ [t| WelfordMean -> (Int,Double) |]+ [| \(WelfordMean a b) -> (a,b) |]+ [| \(a,b) -> WelfordMean a b |]+++-- $references+--+-- * [Welford1962] Welford, B.P. (1962) Note on a method for+-- calculating corrected sums of squares and+-- products. /Technometrics/+-- 4(3):419-420. <http://www.jstor.org/stable/1266577>++-- $setup+--+-- >>> :set -XTypeApplications+-- >>> import Data.Monoid.Statistics.Numeric
Data/Monoid/Statistics/Numeric.hs view
@@ -1,26 +1,39 @@ {-# LANGUAGE BangPatterns #-}-{-# LANGUAGE CPP #-} {-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveFoldable #-} {-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DeriveTraversable #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE ViewPatterns #-}+-- |+-- Monoids for calculating various statistics in constant space module Data.Monoid.Statistics.Numeric ( -- * Mean & Variance -- ** Number of elements CountG(..) , Count , asCount- -- ** Mean+ -- ** Mean algorithms+ -- ** Default algorithms+ , Mean+ , asMean+ , WMean+ , asWMean+ -- *** Mean+ , MeanNaive(..)+ , asMeanNaive , MeanKBN(..) , asMeanKBN- , WelfordMean(..)- , asWelfordMean- , MeanKahan(..)- , asMeanKahan+ -- *** Weighted mean+ , WMeanNaive(..)+ , asWMeanNaive+ , WMeanKBN(..)+ , asWMeanKBN -- ** Variance , Variance(..) , asVariance@@ -32,27 +45,22 @@ -- * Binomial trials , BinomAcc(..) , asBinomAcc- -- * Accessors- , CalcCount(..)- , CalcMean(..)- , CalcVariance(..)- , calcStddev- , calcStddevML+ -- * Rest+ , Weighted(..) -- * References -- $references ) where -import Data.Monoid ((<>))-import Data.Monoid.Statistics.Class-#if MIN_VERSION_base(4,9,0)-import qualified Data.Semigroup as SG (Semigroup(..))-#endif+import Control.Monad.Catch (MonadThrow(..)) import Data.Data (Typeable,Data) import Data.Vector.Unboxed (Unbox) import Data.Vector.Unboxed.Deriving (derivingUnbox) import Numeric.Sum import GHC.Generics (Generic) +import Data.Monoid.Statistics.Class++ ---------------------------------------------------------------- -- Statistical monoids ----------------------------------------------------------------@@ -67,144 +75,188 @@ asCount :: CountG a -> CountG a asCount = id -#if MIN_VERSION_base(4,9,0)-instance Integral a => SG.Semigroup (CountG a) where- (<>) = mappend-#endif+instance Integral a => Semigroup (CountG a) where+ CountG i <> CountG j = CountG (i + j) instance Integral a => Monoid (CountG a) where- mempty = CountG 0- CountG i `mappend` CountG j = CountG (i + j)- {-# INLINE mempty #-}- {-# INLINE mappend #-}+ mempty = CountG 0+ mappend = (<>) instance (Integral a) => StatMonoid (CountG a) b where singletonMonoid _ = CountG 1 addValue (CountG n) _ = CountG (n + 1)- {-# INLINE singletonMonoid #-}- {-# INLINE addValue #-} + instance CalcCount (CountG Int) where calcCount = calcCountN- {-# INLINE calcCount #-} ---------------------------------------------------------------- --- | Incremental calculation of mean. Sum of elements is calculated--- using compensated Kahan summation.-data MeanKahan = MeanKahan !Int !KahanSum+-- | Type alias for currently recommended algorithms for calculation+-- of mean. It should be default choice+type Mean = MeanKBN++asMean :: Mean -> Mean+asMean = id++-- | Type alias for currently recommended algorithms for calculation+-- of weighted mean. It should be default choice+type WMean = WMeanKBN++asWMean :: WMean -> WMean+asWMean = id+++----------------------------------------------------------------++-- | Incremental calculation of mean. It tracks separately number of+-- elements and running sum. Note that summation of floating point+-- numbers loses precision and genrally use 'MeanKBN' is+-- recommended.+data MeanNaive = MeanNaive !Int !Double deriving (Show,Eq,Typeable,Data,Generic) -asMeanKahan :: MeanKahan -> MeanKahan-asMeanKahan = id+asMeanNaive :: MeanNaive -> MeanNaive+asMeanNaive = id -#if MIN_VERSION_base(4,9,0)-instance SG.Semigroup MeanKahan where- (<>) = mappend-#endif -instance Monoid MeanKahan where- mempty = MeanKahan 0 mempty- MeanKahan 0 _ `mappend` m = m- m `mappend` MeanKahan 0 _ = m- MeanKahan n1 s1 `mappend` MeanKahan n2 s2 = MeanKahan (n1+n2) (s1<>s2)+instance Semigroup MeanNaive where+ MeanNaive 0 _ <> m = m+ m <> MeanNaive 0 _ = m+ MeanNaive n1 s1 <> MeanNaive n2 s2 = MeanNaive (n1+n2) (s1 + s2) -instance Real a => StatMonoid MeanKahan a where- addValue (MeanKahan n m) x = MeanKahan (n+1) (addValue m x)+instance Monoid MeanNaive where+ mempty = MeanNaive 0 0+ mappend = (<>) -instance CalcCount MeanKahan where- calcCount (MeanKahan n _) = n-instance CalcMean MeanKahan where- calcMean (MeanKahan 0 _) = Nothing- calcMean (MeanKahan n s) = Just (kahan s / fromIntegral n)+instance Real a => StatMonoid MeanNaive a where+ addValue (MeanNaive n m) x = MeanNaive (n+1) (m + realToFrac x)+ {-# INLINE addValue #-} +instance CalcCount MeanNaive where+ calcCount (MeanNaive n _) = n+instance CalcMean MeanNaive where+ calcMean (MeanNaive 0 _) = throwM $ EmptySample "Data.Monoid.Statistics.Numeric.MeanNaive: calcMean"+ calcMean (MeanNaive n s) = return (s / fromIntegral n) --- | Incremental calculation of mean. Sum of elements is calculated--- using Kahan-Babuška-Neumaier summation.-data MeanKBN = MeanKBN !Int !KBNSum+----------------------------------------------------------------++-- | Incremental calculation of mean. It tracks separately number of+-- elements and running sum. It uses algorithm for compensated+-- summation which works with mantissa of double size at cost of+-- doing more operations. This means that it's usually possible to+-- compute sum (and therefore mean) within 1 ulp.+data MeanKBN = MeanKBN !Int {-# UNPACK #-} !KBNSum deriving (Show,Eq,Typeable,Data,Generic) asMeanKBN :: MeanKBN -> MeanKBN asMeanKBN = id -#if MIN_VERSION_base(4,9,0)-instance SG.Semigroup MeanKBN where- (<>) = mappend-#endif -instance Monoid MeanKBN where- mempty = MeanKBN 0 mempty- MeanKBN 0 _ `mappend` m = m- m `mappend` MeanKBN 0 _ = m- MeanKBN n1 s1 `mappend` MeanKBN n2 s2 = MeanKBN (n1+n2) (s1<>s2)+instance Semigroup MeanKBN where+ MeanKBN 0 _ <> m = m+ m <> MeanKBN 0 _ = m+ MeanKBN n1 s1 <> MeanKBN n2 s2 = MeanKBN (n1+n2) (s1 <> s2) +instance Monoid MeanKBN where+ mempty = MeanKBN 0 mempty+ mappend = (<>)+ instance Real a => StatMonoid MeanKBN a where addValue (MeanKBN n m) x = MeanKBN (n+1) (addValue m x)+ {-# INLINE addValue #-} instance CalcCount MeanKBN where calcCount (MeanKBN n _) = n instance CalcMean MeanKBN where- calcMean (MeanKBN 0 _) = Nothing- calcMean (MeanKBN n s) = Just (kbn s / fromIntegral n)+ calcMean (MeanKBN 0 _) = throwM $ EmptySample "Data.Monoid.Statistics.Numeric.MeanKBN: calcMean"+ calcMean (MeanKBN n s) = return (kbn s / fromIntegral n) +---------------------------------------------------------------- --- | Incremental calculation of mean. One of algorithm's advantage is--- protection against double overflow:------ > λ> calcMean $ asMeanKBN $ reduceSample (replicate 100 1e308)--- > Just NaN--- > λ> calcMean $ asWelfordMean $ reduceSample (replicate 100 1e308)--- > Just 1.0e308------ Algorithm is due to Welford [Welford1962]-data WelfordMean = WelfordMean !Int -- Number of entries- !Double -- Current mean+-- | Incremental calculation of weighed mean.+data WMeanNaive = WMeanNaive+ !Double -- Weight+ !Double -- Weighted sum deriving (Show,Eq,Typeable,Data,Generic) --- | Type restricted 'id'-asWelfordMean :: WelfordMean -> WelfordMean-asWelfordMean = id+asWMeanNaive :: WMeanNaive -> WMeanNaive+asWMeanNaive = id -#if MIN_VERSION_base(4,9,0)-instance SG.Semigroup WelfordMean where- (<>) = mappend-#endif -instance Monoid WelfordMean where- mempty = WelfordMean 0 0- mappend (WelfordMean 0 _) m = m- mappend m (WelfordMean 0 _) = m- mappend (WelfordMean n x) (WelfordMean k y)- = WelfordMean (n + k) ((x*n' + y*k') / (n' + k'))- where- n' = fromIntegral n- k' = fromIntegral k- {-# INLINE mempty #-}- {-# INLINE mappend #-}+instance Semigroup WMeanNaive where+ WMeanNaive w1 s1 <> WMeanNaive w2 s2 = WMeanNaive (w1 + w2) (s1 + s2) --- | \[ s_n = s_{n-1} + \frac{x_n - s_{n-1}}{n} \]-instance Real a => StatMonoid WelfordMean a where- addValue (WelfordMean n m) !x- = WelfordMean n' (m + (realToFrac x - m) / fromIntegral n')+instance Monoid WMeanNaive where+ mempty = WMeanNaive 0 0+ mappend = (<>)++instance (Real w, Real a) => StatMonoid WMeanNaive (Weighted w a) where+ addValue (WMeanNaive n s) (Weighted w a)+ = WMeanNaive (n + w') (s + (w' * a')) where- n' = n+1+ w' = realToFrac w+ a' = realToFrac a {-# INLINE addValue #-} -instance CalcCount WelfordMean where- calcCount (WelfordMean n _) = n-instance CalcMean WelfordMean where- calcMean (WelfordMean 0 _) = Nothing- calcMean (WelfordMean _ m) = Just m+instance CalcMean WMeanNaive where+ calcMean (WMeanNaive w s)+ | w <= 0 = throwM $ EmptySample "Data.Monoid.Statistics.Numeric.WMeanNaive: calcMean"+ | otherwise = return (s / w) +---------------------------------------------------------------- +-- | Incremental calculation of weighed mean. Sum of both weights and+-- elements is calculated using Kahan-Babuška-Neumaier summation.+data WMeanKBN = WMeanKBN+ {-# UNPACK #-} !KBNSum -- Weight+ {-# UNPACK #-} !KBNSum -- Weighted sum+ deriving (Show,Eq,Typeable,Data,Generic) +asWMeanKBN :: WMeanKBN -> WMeanKBN+asWMeanKBN = id+++instance Semigroup WMeanKBN where+ WMeanKBN n1 s1 <> WMeanKBN n2 s2 = WMeanKBN (n1 <> n2) (s1 <> s2)++instance Monoid WMeanKBN where+ mempty = WMeanKBN mempty mempty+ mappend = (<>)++instance (Real w, Real a) => StatMonoid WMeanKBN (Weighted w a) where+ addValue (WMeanKBN n m) (Weighted w a)+ = WMeanKBN (add n w') (add m (w' * a'))+ where+ w' = realToFrac w :: Double+ a' = realToFrac a :: Double+ {-# INLINE addValue #-}++instance CalcMean WMeanKBN where+ calcMean (WMeanKBN (kbn -> w) (kbn -> s))+ | w <= 0 = throwM $ EmptySample "Data.Monoid.Statistics.Numeric.WMeanKBN: calcMean"+ | otherwise = return (s / w)++ ---------------------------------------------------------------- --- | Incremental algorithms for calculation the standard deviation.+-- | This is algorithm for estimation of mean and variance of sample+-- which uses modified Welford algorithm. It uses KBN summation and+-- provides approximately 2 additional decimal digits+data VarWelfordKBN = VarWelfordKBN+ {-# UNPACK #-} !Int -- Number of elements in the sample+ {-# UNPACK #-} !KBNSum -- Current sum of elements of sample+ {-# UNPACK #-} !KBNSum -- Current sum of squares of deviations from current mean++asVarWelfordKBN :: VarWelfordKBN -> VarWelfordKBN+asVarWelfordKBN = id+++-- | Incremental algorithms for calculation the standard deviation [Chan1979]. data Variance = Variance {-# UNPACK #-} !Int -- Number of elements in the sample {-# UNPACK #-} !Double -- Current sum of elements of sample {-# UNPACK #-} !Double -- Current sum of squares of deviations from current mean@@ -213,17 +265,9 @@ -- | Type restricted 'id ' asVariance :: Variance -> Variance asVariance = id-{-# INLINE asVariance #-} -#if MIN_VERSION_base(4,9,0)-instance SG.Semigroup Variance where- (<>) = mappend-#endif---- | Iterative algorithm for calculation of variance [Chan1979]-instance Monoid Variance where- mempty = Variance 0 0 0- mappend (Variance n1 ta sa) (Variance n2 tb sb)+instance Semigroup Variance where+ Variance n1 ta sa <> Variance n2 tb sb = Variance (n1+n2) (ta+tb) sumsq where na = fromIntegral n1@@ -232,10 +276,18 @@ sumsq | n1 == 0 = sb | n2 == 0 = sa | otherwise = sa + sb + nom / ((na + nb) * na * nb)- {-# INLINE mempty #-}- {-# INLINE mappend #-} +instance Monoid Variance where+ mempty = Variance 0 0 0+ mappend = (<>)+ instance Real a => StatMonoid Variance a where+ addValue (Variance 0 _ _) x = singletonMonoid x+ addValue (Variance n t s) (realToFrac -> x)+ = Variance (n + 1) (t + x) (s + sqr (t - n' * x) / (n' * (n'+1)))+ where+ n' = fromIntegral n+ {-# INLINE addValue #-} singletonMonoid x = Variance 1 (realToFrac x) 0 {-# INLINE singletonMonoid #-} @@ -243,17 +295,20 @@ calcCount (Variance n _ _) = n instance CalcMean Variance where- calcMean (Variance 0 _ _) = Nothing- calcMean (Variance n s _) = Just (s / fromIntegral n)+ calcMean (Variance 0 _ _) = throwM $ EmptySample "Data.Monoid.Statistics.Numeric.Variance: calcMean"+ calcMean (Variance n s _) = return (s / fromIntegral n) instance CalcVariance Variance where calcVariance (Variance n _ s)- | n < 2 = Nothing- | otherwise = Just $! s / fromIntegral (n - 1)+ | n < 2 = throwM $ InvalidSample+ "Data.Monoid.Statistics.Numeric.Variance: calcVariance"+ "Need at least 2 elements"+ | otherwise = return $! s / fromIntegral (n - 1) calcVarianceML (Variance n _ s)- | n < 1 = Nothing- | otherwise = Just $! s / fromIntegral n-+ | n < 1 = throwM $ InvalidSample+ "Data.Monoid.Statistics.Numeric.Variance: calcVarianceML"+ "Need at least 1 element"+ | otherwise = return $! s / fromIntegral n @@ -263,36 +318,33 @@ newtype Min a = Min { calcMin :: Maybe a } deriving (Show,Eq,Ord,Typeable,Data,Generic) -#if MIN_VERSION_base(4,9,0)-instance Ord a => SG.Semigroup (Min a) where- (<>) = mappend-#endif+instance Ord a => Semigroup (Min a) where+ Min (Just a) <> Min (Just b) = Min (Just $! min a b)+ Min a <> Min Nothing = Min a+ Min Nothing <> Min b = Min b instance Ord a => Monoid (Min a) where- mempty = Min Nothing- Min (Just a) `mappend` Min (Just b) = Min (Just $! min a b)- Min a `mappend` Min Nothing = Min a- Min Nothing `mappend` Min b = Min b+ mempty = Min Nothing+ mappend = (<>) instance (Ord a, a ~ a') => StatMonoid (Min a) a' where singletonMonoid a = Min (Just a) + ---------------------------------------------------------------- -- | Calculate maximum of sample newtype Max a = Max { calcMax :: Maybe a } deriving (Show,Eq,Ord,Typeable,Data,Generic) -#if MIN_VERSION_base(4,9,0)-instance Ord a => SG.Semigroup (Max a) where- (<>) = mappend-#endif+instance Ord a => Semigroup (Max a) where+ Max (Just a) <> Max (Just b) = Max (Just $! max a b)+ Max a <> Max Nothing = Max a+ Max Nothing <> Max b = Max b instance Ord a => Monoid (Max a) where- mempty = Max Nothing- Max (Just a) `mappend` Max (Just b) = Max (Just $! min a b)- Max a `mappend` Max Nothing = Max a- Max Nothing `mappend` Max b = Max b+ mempty = Max Nothing+ mappend = (<>) instance (Ord a, a ~ a') => StatMonoid (Max a) a' where singletonMonoid a = Max (Just a)@@ -310,21 +362,17 @@ | isNaN a && isNaN b = True | otherwise = a == b -#if MIN_VERSION_base(4,9,0)-instance SG.Semigroup MinD where- (<>) = mappend-#endif---- N.B. forall (x :: Double) (x <= NaN) == False-instance Monoid MinD where- mempty = MinD (0/0)- mappend (MinD x) (MinD y)+instance Semigroup MinD where+ MinD x <> MinD y | isNaN x = MinD y | isNaN y = MinD x | otherwise = MinD (min x y)- {-# INLINE mempty #-}- {-# INLINE mappend #-} +-- N.B. forall (x :: Double) (x <= NaN) == False+instance Monoid MinD where+ mempty = MinD (0/0)+ mappend = (<>)+ instance a ~ Double => StatMonoid MinD a where singletonMonoid = MinD @@ -340,20 +388,16 @@ | isNaN a && isNaN b = True | otherwise = a == b -#if MIN_VERSION_base(4,9,0)-instance SG.Semigroup MaxD where- (<>) = mappend-#endif--instance Monoid MaxD where- mempty = MaxD (0/0)- mappend (MaxD x) (MaxD y)+instance Semigroup MaxD where+ MaxD x <> MaxD y | isNaN x = MaxD y | isNaN y = MaxD x | otherwise = MaxD (max x y)- {-# INLINE mempty #-}- {-# INLINE mappend #-} +instance Monoid MaxD where+ mempty = MaxD (0/0)+ mappend = (<>)+ instance a ~ Double => StatMonoid MaxD a where singletonMonoid = MaxD @@ -370,64 +414,21 @@ asBinomAcc :: BinomAcc -> BinomAcc asBinomAcc = id -#if MIN_VERSION_base(4,9,0)-instance SG.Semigroup BinomAcc where- (<>) = mappend-#endif+instance Semigroup BinomAcc where+ BinomAcc n1 m1 <> BinomAcc n2 m2 = BinomAcc (n1+n2) (m1+m2) instance Monoid BinomAcc where- mempty = BinomAcc 0 0- mappend (BinomAcc n1 m1) (BinomAcc n2 m2) = BinomAcc (n1+n2) (m1+m2)+ mempty = BinomAcc 0 0+ mappend = (<>) instance StatMonoid BinomAcc Bool where addValue (BinomAcc nS nT) True = BinomAcc (nS+1) (nT+1) addValue (BinomAcc nS nT) False = BinomAcc nS (nT+1) --------------------------------------------------------------------- Ad-hoc type class--------------------------------------------------------------------- | Accumulator could be used to evaluate number of elements in--- sample.-class CalcCount m where- -- | Number of elements in sample- calcCount :: m -> Int---- | Monoids which could be used to calculate sample mean:------ \[ \bar{x} = \frac{1}{N}\sum_{i=1}^N{x_i} \]-class CalcMean m where- -- | Returns @Nothing@ if there isn't enough data to make estimate.- calcMean :: m -> Maybe Double---- | Monoids which could be used to calculate sample variance. Both--- methods return @Nothing@ if there isn't enough data to make--- estimate.-class CalcVariance m where- -- | Calculate unbiased estimate of variance:- --- -- \[ \sigma^2 = \frac{1}{N-1}\sum_{i=1}^N(x_i - \bar{x})^2 \]- calcVariance :: m -> Maybe Double- -- | Calculate maximum likelihood estimate of variance:- --- -- \[ \sigma^2 = \frac{1}{N}\sum_{i=1}^N(x_i - \bar{x})^2 \]- calcVarianceML :: m -> Maybe Double---- | Calculate sample standard deviation from unbiased estimation of--- variance:------ \[ \sigma = \sqrt{\frac{1}{N-1}\sum_{i=1}^N(x_i - \bar{x})^2 } \]-calcStddev :: CalcVariance m => m -> Maybe Double-calcStddev = fmap sqrt . calcVariance---- | Calculate sample standard deviation from maximum likelihood--- estimation of variance:------ \[ \sigma = \sqrt{\frac{1}{N}\sum_{i=1}^N(x_i - \bar{x})^2 } \]-calcStddevML :: CalcVariance m => m -> Maybe Double-calcStddevML = fmap sqrt . calcVarianceML+-- | Value @a@ weighted by weight @w@+data Weighted w a = Weighted w a+ deriving (Show,Eq,Ord,Typeable,Data,Generic,Functor,Foldable,Traversable) @@ -454,11 +455,6 @@ [| \(MeanKBN a (KBNSum b c)) -> (a,b,c) |] [| \(a,b,c) -> MeanKBN a (KBNSum b c) |] -derivingUnbox "WelfordMean"- [t| WelfordMean -> (Int,Double) |]- [| \(WelfordMean a b) -> (a,b) |]- [| \(a,b) -> WelfordMean a b |]- derivingUnbox "Variance" [t| Variance -> (Int,Double,Double) |] [| \(Variance a b c) -> (a,b,c) |]@@ -473,6 +469,11 @@ [t| MaxD -> Double |] [| calcMaxD |] [| MaxD |]++derivingUnbox "Weighted"+ [t| forall w a. (Unbox w, Unbox a) => Weighted w a -> (w,a) |]+ [| \(Weighted w a) -> (w,a) |]+ [| \(w,a) -> Weighted w a |] -- $references --
+ bench/Main.hs view
@@ -0,0 +1,68 @@+-- |+module Main where++import Control.Monad+import Control.Monad.ST (runST)++import qualified Data.Vector.Unboxed as U++import Criterion.Main+import System.Random.MWC+--+import Numeric.Sum+import Data.Monoid.Statistics+import Data.Monoid.Statistics.Extra+++sampleD10,sampleD100,sampleD1000 :: U.Vector Double+sampleD10 = runST $ U.replicateM 10 . uniform =<< create+sampleD100 = runST $ U.replicateM 100 . uniform =<< create+sampleD1000 = runST $ U.replicateM 1000 . uniform =<< create++sampleI10,sampleI100,sampleI1000 :: U.Vector Int+sampleI10 = runST $ U.replicateM 10 . uniform =<< create+sampleI100 = runST $ U.replicateM 100 . uniform =<< create+sampleI1000 = runST $ U.replicateM 1000 . uniform =<< create++main :: IO ()+main = defaultMain+ [ bgroup "Count"+ [ bench "10 D" $ whnf (reduceSampleVec :: U.Vector Double -> Count) sampleD10+ , bench "100 D" $ whnf (reduceSampleVec :: U.Vector Double -> Count) sampleD100+ , bench "1000 D" $ whnf (reduceSampleVec :: U.Vector Double -> Count) sampleD1000+ ]+ , bgroup "KBN"+ [ bench "10 D" $ whnf (reduceSampleVec :: U.Vector Double -> KBNSum) sampleD10+ , bench "100 D" $ whnf (reduceSampleVec :: U.Vector Double -> KBNSum) sampleD100+ , bench "1000 D" $ whnf (reduceSampleVec :: U.Vector Double -> KBNSum) sampleD1000+ ]+ , bgroup "Kahan"+ [ bench "10 D" $ whnf (reduceSampleVec :: U.Vector Double -> KahanSum) sampleD10+ , bench "100 D" $ whnf (reduceSampleVec :: U.Vector Double -> KahanSum) sampleD100+ , bench "1000 D" $ whnf (reduceSampleVec :: U.Vector Double -> KahanSum) sampleD1000+ ]+ , bgroup "MeanKBN"+ [ bench "10 D" $ whnf (reduceSampleVec :: U.Vector Double -> MeanKBN) sampleD10+ , bench "100 D" $ whnf (reduceSampleVec :: U.Vector Double -> MeanKBN) sampleD100+ , bench "1000 D" $ whnf (reduceSampleVec :: U.Vector Double -> MeanKBN) sampleD1000+ , bench "10 I" $ whnf (reduceSampleVec :: U.Vector Int -> MeanKBN) sampleI10+ , bench "100 I" $ whnf (reduceSampleVec :: U.Vector Int -> MeanKBN) sampleI100+ , bench "1000 I" $ whnf (reduceSampleVec :: U.Vector Int -> MeanKBN) sampleI1000+ ]+ , bgroup "WelfordMean"+ [ bench "10 D" $ whnf (reduceSampleVec :: U.Vector Double -> WelfordMean) sampleD10+ , bench "100 D" $ whnf (reduceSampleVec :: U.Vector Double -> WelfordMean) sampleD100+ , bench "1000 D" $ whnf (reduceSampleVec :: U.Vector Double -> WelfordMean) sampleD1000+ , bench "10 I" $ whnf (reduceSampleVec :: U.Vector Int -> WelfordMean) sampleI10+ , bench "100 I" $ whnf (reduceSampleVec :: U.Vector Int -> WelfordMean) sampleI100+ , bench "1000 I" $ whnf (reduceSampleVec :: U.Vector Int -> WelfordMean) sampleI1000+ ]+ , bgroup "Variance"+ [ bench "10 D" $ whnf (reduceSampleVec :: U.Vector Double -> Variance) sampleD10+ , bench "100 D" $ whnf (reduceSampleVec :: U.Vector Double -> Variance) sampleD100+ , bench "1000 D" $ whnf (reduceSampleVec :: U.Vector Double -> Variance) sampleD1000+ , bench "10 I" $ whnf (reduceSampleVec :: U.Vector Int -> Variance) sampleI10+ , bench "100 I" $ whnf (reduceSampleVec :: U.Vector Int -> Variance) sampleI100+ , bench "1000 I" $ whnf (reduceSampleVec :: U.Vector Int -> Variance) sampleI1000+ ]+ ]
monoid-statistics.cabal view
@@ -1,5 +1,5 @@ Name: monoid-statistics-Version: 1.0.1.0+Version: 1.1.0 Cabal-Version: >= 1.10 License: BSD3 License-File: LICENSE@@ -17,12 +17,15 @@ possibility to parallelize calculations. However not all statistics could be calculated this way. +Extra-Source-Files:+ Changelog.md+ tested-with:- GHC ==7.10.3- || ==8.0.2- || ==8.2.2- || ==8.4.4- || ==8.6.5+ GHC ==8.6.5+ || ==8.8.4+ || ==8.10.7+ || ==9.0.1+ || ==9.2.1 extra-source-files: README.md@@ -34,15 +37,19 @@ Library default-language: Haskell2010 ghc-options: -Wall -O2- Build-Depends: base >=4.8 && <5+ --+ Build-Depends: base >=4.12 && <5+ , exceptions >=0.10 , vector >=0.11 && <1 , vector-th-unbox >=0.2.1.6- , math-functions >=0.2.1.0+ , math-functions >=0.3+ -- Exposed-modules: Data.Monoid.Statistics Data.Monoid.Statistics.Class Data.Monoid.Statistics.Numeric+ Data.Monoid.Statistics.Extra -test-suite tests+test-suite monoid-statistics-tests default-language: Haskell2010 type: exitcode-stdio-1.0 ghc-options: -Wall -threaded@@ -51,11 +58,43 @@ if arch(i386) ghc-options: -msse2 hs-source-dirs: tests- main-is: Main.hs- other-modules:- build-depends: monoid-statistics- , base >=4.8 && <5- , math-functions >=0.2.1+ Main-is: Main.hs+ Other-Modules:+ Build-Depends: monoid-statistics+ , base >=4.9 && <5+ , math-functions >=0.3 , tasty >=0.11 , tasty-quickcheck >=0.9+ , tasty-hunit+ , tasty-expected-failure , QuickCheck++test-suite monoid-statistics-doctest+ if impl(ghcjs)+ buildable: False+ -- It seems GHC 9.0 & 9.2 chokes to death on examples with deriving via+ if impl(ghc >= 9.0) && impl(ghc < 9.1)+ buildable: False+ if impl(ghc >= 9.2) && impl(ghc < 9.3)+ buildable: False+ type: exitcode-stdio-1.0+ main-is: doctests.hs+ hs-source-dirs: tests+ default-language: Haskell2010+ build-depends:+ base >=4.9 && <5+ , doctest >=0.15 && <0.21+ , monoid-statistics -any++benchmark monoid-stat-bench+ default-language: Haskell2010+ type: exitcode-stdio-1.0+ ghc-options: -Wall -O2+ hs-source-dirs: bench+ Main-is: Main.hs+ Build-Depends: monoid-statistics+ , base >=4.9 && <5+ , vector >=0.11 && <1+ , math-functions >=0.3+ , mwc-random >=0.13+ , criterion
tests/Main.hs view
@@ -1,31 +1,72 @@-{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-} -- {-# OPTIONS_GHC -fno-warn-orphans #-}-import Data.Monoid+module Main (main) where import Data.Typeable import Numeric.Sum import Test.Tasty import Test.Tasty.QuickCheck+import Test.Tasty.HUnit+import Test.Tasty.ExpectedFailure (ignoreTest) import Data.Monoid.Statistics+import Data.Monoid.Statistics.Extra -data T a = T+----------------------------------------------------------------+-- Properties+---------------------------------------------------------------- +class MonoidProperty m where+ isAssociative, isCommutative, isMemptyDistribute, isMemptyNeutral :: Bool+ isMemptyNeutral = True+ isMemptyDistribute = True+ isAssociative = True+ isCommutative = True++instance {-# OVERLAPPABLE #-} MonoidProperty m+instance MonoidProperty MeanNaive where+ isAssociative = False+instance MonoidProperty WelfordMean where+ isAssociative = False+ isMemptyDistribute = False+instance MonoidProperty MeanKahan where+ isAssociative = False+ isCommutative = False+ isMemptyDistribute = False+instance MonoidProperty MeanKBN where+ isAssociative = False+ isCommutative = False+instance MonoidProperty Variance where+ isAssociative = False+instance MonoidProperty WMeanNaive where+ isAssociative = False+instance MonoidProperty WMeanKBN where+ isMemptyNeutral = False+ isAssociative = False+ isCommutative = False+ p_memptyIsNeutral- :: forall m. (Monoid m, Arbitrary m, Show m, Eq m)- => T m -> TestTree-p_memptyIsNeutral _- = testProperty "mempty is neutral" $ \(m :: m) ->- (m <> mempty) == m- && (mempty <> m) == m+ :: forall m. (Monoid m, MonoidProperty m, Arbitrary m, Show m, Eq m)+ => TestTree+p_memptyIsNeutral+ = (if isMemptyNeutral @m then id else ignoreTest)+ $ testProperty "mempty is neutral" $ \(m :: m) ->+ counterexample ("m <> mempty = " ++ show (m <> mempty))+ $ counterexample ("mempty <> m = " ++ show (mempty <> m))+ $ (m <> mempty) == m+ && (mempty <> m) == m p_associativity- :: forall m. (Monoid m, Arbitrary m, Show m, Eq m)- => T m -> TestTree-p_associativity _- = testProperty "associativity" $ \(a :: m) b c ->+ :: forall m. (MonoidProperty m, Monoid m, Arbitrary m, Show m, Eq m)+ => TestTree+p_associativity+ = (if isAssociative @m then id else ignoreTest)+ $ testProperty "associativity" $ \(a :: m) b c -> let val1 = (a <> b) <> c val2 = a <> (b <> c) in counterexample ("left : " ++ show val1)@@ -33,29 +74,35 @@ $ val1 == val2 p_commutativity- :: forall m. (Monoid m, Arbitrary m, Show m, Eq m)- => T m -> TestTree-p_commutativity _- = testProperty "commutativity" $ \(a :: m) b ->- (a <> b) == (b <> a)+ :: forall m. (Monoid m, MonoidProperty m, Arbitrary m, Show m, Eq m)+ => TestTree+p_commutativity+ = (if isCommutative @m then id else ignoreTest)+ $ testProperty "commutativity" $ \(a :: m) b ->+ let val1 = a <> b+ val2 = b <> a+ in counterexample ("a <> b = " ++ show val1)+ $ counterexample ("b <> a = " ++ show val2)+ $ val1 == val2 p_addValue1- :: forall a m. ( StatMonoid m a- , Arbitrary m, Show m, Eq m- , Arbitrary a, Show a, Eq a)- => T a -> T m -> TestTree-p_addValue1 _ _+ :: forall m a. ( StatMonoid m a+ , Eq m+ , Arbitrary a, Show a)+ => TestTree+p_addValue1 = testProperty "addValue x mempty == singletonMonoid" $ \(a :: a) -> singletonMonoid a == addValue (mempty :: m) a p_addValue2- :: forall a m. ( StatMonoid m a- , Arbitrary m, Show m, Eq m- , Arbitrary a, Show a, Eq a)- => T a -> T m -> TestTree-p_addValue2 _ _- = testProperty "addValue law" $ \(x :: a) (y :: a) ->+ :: forall m a. ( MonoidProperty m, StatMonoid m a+ , Show m, Eq m+ , Arbitrary a, Show a)+ => TestTree+p_addValue2+ = (if isMemptyDistribute @m then id else ignoreTest)+ $ testProperty "addValue (addValue m x) y = addValue 0 x <> addValue 0 y" $ \(x :: a) (y :: a) -> let val1 = addValue (addValue mempty y) x val2 = (addValue mempty x <> addValue (mempty :: m) y) in counterexample ("left : " ++ show val1)@@ -66,85 +113,132 @@ ---------------------------------------------------------------- -testType :: forall m. Typeable m => T m -> [T m -> TestTree] -> TestTree-testType t props = testGroup (show (typeRep (Proxy :: Proxy m)))- (fmap ($ t) props)+testMonoid+ :: forall m a.+ ( StatMonoid m a, MonoidProperty m+ , Typeable a, Typeable m, Arbitrary a, Arbitrary m, Show a, Show m, Eq m)+ => [TestTree] -> TestTree+testMonoid tests+ = testGroup (show (typeOf (undefined :: m)) ++ " <= " ++ show (typeOf (undefined :: a)))+ $ [ p_memptyIsNeutral @m+ , p_associativity @m+ , p_commutativity @m+ , p_addValue1 @m @a+ , p_addValue2 @m @a+ ]+ ++ tests +testMeanMonoid+ :: forall m.+ ( StatMonoid m Double, CalcMean m, CalcCount m, MonoidProperty m+ , Typeable m, Arbitrary m, Show m, Eq m)+ => [TestTree] -> TestTree+testMeanMonoid tests+ = testMonoid @m @Double+ $ [ testCase "Count" $ do+ let m = reduceSample @m testSample+ testSampleCount @=? calcCount m+ , testCase "Mean" $ do+ let m = reduceSample @m testSample+ Just testSampleMean @=? calcMean m+ , testCase "Mean (empty sample)" $ do+ let m = reduceSample @m @Double []+ Nothing @=? calcMean m+ ] ++ tests +testVarianceMonoid+ :: forall m.+ ( StatMonoid m Double, CalcVariance m, CalcMean m, CalcCount m, MonoidProperty m+ , Typeable m, Arbitrary m, Show m, Eq m)+ => [TestTree] -> TestTree+testVarianceMonoid tests+ = testMeanMonoid @m+ $ [ testCase "Variance (unbiased)" $ do+ let m = reduceSample @m testSample+ Just testSampleVariance @=? calcVariance m+ , testCase "Variance (ML)" $ do+ let m = reduceSample @m testSample+ Just testSampleVarianceML @=? calcVarianceML m+ ] ++ tests++testWMeanMonoid+ :: forall m.+ ( StatMonoid m (Weighted Double Double), CalcMean m, MonoidProperty m+ , Typeable m, Arbitrary m, Show m, Eq m)+ => [TestTree] -> TestTree+testWMeanMonoid tests+ = testMonoid @m @(Weighted Double Double)+ $ [ testCase "Mean" $ do+ let m = reduceSample @m testWSample+ Just testWSampleMean @=? calcMean m+ ] ++ tests++ main :: IO () main = defaultMain $ testGroup "monoid-statistics"- [ testType (T :: T (CountG Int))- [ p_memptyIsNeutral- , p_associativity- , p_commutativity- , p_addValue1 (T :: T Int)- , p_addValue2 (T :: T Int)- ]- , testType (T :: T (Min Int))- [ p_memptyIsNeutral- , p_associativity- , p_commutativity- , p_addValue1 (T :: T Int)- , p_addValue2 (T :: T Int)- ]- , testType (T :: T (Max Int))- [ p_memptyIsNeutral- , p_associativity- , p_commutativity- , p_addValue1 (T :: T Int)- , p_addValue2 (T :: T Int)- ]- , testType (T :: T MinD)- [ p_memptyIsNeutral- , p_associativity- , p_commutativity- , p_addValue1 (T :: T Double)- , p_addValue2 (T :: T Double)- ]- , testType (T :: T MaxD)- [ p_memptyIsNeutral- , p_associativity- , p_commutativity- , p_addValue1 (T :: T Double)- , p_addValue2 (T :: T Double)- ]- , testType (T :: T BinomAcc)- [ p_memptyIsNeutral- , p_associativity- , p_commutativity- , p_addValue1 (T :: T Bool)- , p_addValue2 (T :: T Bool)- ]- , testType (T :: T WelfordMean)- [ p_memptyIsNeutral- -- , p_associativity- , p_commutativity- , p_addValue1 (T :: T Double)- -- , p_addValue2 (T :: T Double)- ]- , testType (T :: T MeanKBN)- [ p_memptyIsNeutral- -- , p_associativity- -- , p_commutativity- , p_addValue1 (T :: T Double)- , p_addValue2 (T :: T Double)- ]- , testType (T :: T MeanKahan)- [ p_memptyIsNeutral- -- , p_associativity- -- , p_commutativity- , p_addValue1 (T :: T Double)- -- , p_addValue2 (T :: T Double)- ]- , testType (T :: T Variance)- [ p_memptyIsNeutral- -- , p_associativity- , p_commutativity- , p_addValue1 (T :: T Double)- , p_addValue2 (T :: T Double)- ]+ [ testMonoid @(CountG Int) @Int+ [ testCase "CountG" $ let xs = "acbdef"+ n = reduceSample xs :: Count+ in length xs @=? calcCount n+ ]+ , testMonoid @(Min Int) @Int+ [ testCase "Min []" $ let xs = []+ n = reduceSample xs :: Min Int+ in Nothing @=? calcMin n+ , testCase "Min" $ let xs = [1..10]+ n = reduceSample xs :: Min Int+ in Just (minimum xs) @=? calcMin n+ ]+ , testMonoid @(Max Int) @Int+ [ testCase "Max []" $ let xs = []+ n = reduceSample xs :: Max Int+ in Nothing @=? calcMax n+ , testCase "Max" $ let xs = [1..10]+ n = reduceSample xs :: Max Int+ in Just (maximum xs) @=? calcMax n+ ]+ , testMonoid @MinD @Double+ [ testCase "MinD" $ let xs = [1..10]+ n = reduceSample xs :: MinD+ in minimum xs @=? calcMinD n+ ]+ , testMonoid @MaxD @Double+ [ testCase "MaxD" $ let xs = [1..10]+ n = reduceSample xs :: MaxD+ in maximum xs @=? calcMaxD n++ ]+ , testMonoid @BinomAcc @Bool []+ -- Numeric accumulators+ , testMeanMonoid @MeanNaive []+ , testMeanMonoid @WelfordMean []+ , testMeanMonoid @MeanKahan []+ , testMeanMonoid @MeanKBN []+ , testWMeanMonoid @WMeanNaive []+ , testWMeanMonoid @WMeanKBN []+ , testVarianceMonoid @Variance [] ] +-- | Test sample for which we could compute statistics exactly, and+-- any reasonable algorithm should be able to return exact answer as+-- well+testSample :: [Double]+testSample = [1..10]++testWSample :: [Weighted Double Double]+testWSample = [Weighted x x | x <- [1..10]]++testSampleCount :: Int+testSampleCount = length testSample++testSampleMean,testWSampleMean :: Double+testSampleMean = 5.5+testWSampleMean = 7.0++testSampleVariance,testSampleVarianceML :: Double+testSampleVariance = 9.166666666666666+testSampleVarianceML = 8.25+ ---------------------------------------------------------------- instance (Arbitrary a, Num a, Ord a) => Arbitrary (CountG a) where@@ -153,19 +247,19 @@ return (CountG n) instance (Arbitrary a) => Arbitrary (Max a) where- arbitrary = Max <$> arbitrary+ arbitrary = fmap Max arbitrary instance (Arbitrary a) => Arbitrary (Min a) where- arbitrary = Min <$> arbitrary+ arbitrary = fmap Min arbitrary instance Arbitrary MinD where- arbitrary = frequency [ (1, pure mempty)- , (4, MinD <$> arbitrary)+ arbitrary = frequency [ (1, return mempty)+ , (4, fmap MinD arbitrary) ] instance Arbitrary MaxD where- arbitrary = frequency [ (1, pure mempty)- , (4, MaxD <$> arbitrary)+ arbitrary = frequency [ (1, return mempty)+ , (4, fmap MaxD arbitrary) ] instance Arbitrary BinomAcc where@@ -174,34 +268,61 @@ NonNegative nFail <- arbitrary return $ BinomAcc nSucc (nFail + nSucc) +instance Arbitrary MeanNaive where+ arbitrary = arbitrary >>= \x -> case x of+ NonNegative 0 -> return mempty+ NonNegative n -> do m <- arbitrary+ return (MeanNaive n m)+ instance Arbitrary WelfordMean where- arbitrary = arbitrary >>= \case+ arbitrary = arbitrary >>= \x -> case x of NonNegative 0 -> return mempty NonNegative n -> do m <- arbitrary return (WelfordMean n m) +instance Arbitrary MeanKahan where+ arbitrary = do+ n <- arbitrary+ s <- arbitraryKBN n+ return $! MeanKahan (getNonNegative n) s++instance Arbitrary MeanKBN where+ arbitrary = do+ n <- arbitrary+ s <- arbitraryKBN n+ return $! MeanKBN (getNonNegative n) s++instance Arbitrary WMeanKBN where+ arbitrary = do+ n <- arbitrary+ KBNSum w1 w2 <- arbitraryKBN n+ s <- arbitraryKBN n+ return $! WMeanKBN (KBNSum (abs w1) w2) s++instance Arbitrary WMeanNaive where+ arbitrary = do+ NonNegative w <- arbitrary+ s <- arbitrary+ return $! WMeanNaive w s+ instance Arbitrary Variance where- arbitrary = arbitrary >>= \case+ arbitrary = arbitrary >>= \x -> case x of NonNegative 0 -> return mempty NonNegative n -> do m <- arbitrary NonNegative s <- arbitrary return $ Variance n m s -instance Arbitrary MeanKBN where- arbitrary = arbitrary >>= \case- NonNegative 0 -> return mempty- NonNegative n -> do- x1 <- arbitrary- x2 <- arbitrary- x3 <- arbitrary- return $ MeanKBN n (((zero `add` x1) `add` x2) `add` x3)+instance (Arbitrary a, Arbitrary w) => Arbitrary (Weighted w a) where+ arbitrary = Weighted <$> arbitrary <*> arbitrary -instance Arbitrary MeanKahan where- arbitrary = arbitrary >>= \case- NonNegative 0 -> return mempty- NonNegative n -> do- x1 <- arbitrary- x2 <- arbitrary- x3 <- arbitrary- return $ MeanKahan n (((zero `add` x1) `add` x2) `add` x3)+arbitraryKBN :: Summation a => NonNegative Int -> Gen a+arbitraryKBN (NonNegative 0) = return zero+arbitraryKBN (NonNegative 1) = do+ x1 <- arbitrary+ return $! zero `add` x1+arbitraryKBN _ = do+ x1 <- arbitrary+ x2 <- arbitrary+ x3 <- arbitrary+ return $! ((zero `add` x1) `add` x2) `add` x3
+ tests/doctests.hs view
@@ -0,0 +1,6 @@+module Main where++import Test.DocTest (doctest)++main :: IO ()+main = doctest ["Data"]