{-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE DeriveGeneric #-} -------------------------------------------------------------------- -- | -- Copyright : (c) Edward Kmett 2013 -- License : BSD3 -- Maintainer: Edward Kmett -- Stability : experimental -- Portability: non-portable -- -------------------------------------------------------------------- module Data.Approximate.Type ( Approximate(Approximate) , HasApproximate(..) , exact , zero , one , withMin, withMax ) where import Control.Applicative import Control.DeepSeq import Control.Lens import Control.Monad import Data.Binary as Binary import Data.Bytes.Serial as Bytes import Data.Copointed import Data.Data import Data.Functor.Apply import Data.Functor.Classes import Data.Hashable (Hashable(..)) import Data.Hashable.Lifted (Hashable1(..)) import Data.Monoid import Data.Pointed import Data.SafeCopy import Data.Serialize as Serialize import qualified Data.Vector.Generic as G import qualified Data.Vector.Generic.Mutable as M import qualified Data.Vector.Unboxed as U import Data.Vector.Unboxed (Unbox) import GHC.Generics import Numeric.Log -- | An approximate number, with a likely interval, an expected value and a lower bound on the @log@ of probability that the answer falls in the interval. -- -- /NB:/ The probabilities associated with confidence are stored in the @log@ domain. data Approximate a = Approximate { _confidence :: {-# UNPACK #-} !(Log Double) , _lo, _estimate, _hi :: a } deriving (Eq,Show,Read,Data,Generic) makeClassy ''Approximate instance Binary a => Binary (Approximate a) where put (Approximate p l m h) = Binary.put p >> Binary.put l >> Binary.put m >> Binary.put h get = Approximate <$> Binary.get <*> Binary.get <*> Binary.get <*> Binary.get instance Serialize a => Serialize (Approximate a) where put (Approximate p l m h) = Serialize.put p >> Serialize.put l >> Serialize.put m >> Serialize.put h get = Approximate <$> Serialize.get <*> Serialize.get <*> Serialize.get <*> Serialize.get instance (Serialize a, Typeable a) => SafeCopy (Approximate a) where -- safecopy-0.10.0 changed its default implementations for these methods. -- The implementations below are copied from the pre-0.10.0 defaults. errorTypeName _ = "" getCopy = contain Serialize.get putCopy = contain . Serialize.put instance Eq1 Approximate where liftEq eq (Approximate p la ma ha) (Approximate q lb mb hb) = p == q && eq la lb && eq ma mb && eq ha hb instance Hashable a => Hashable (Approximate a) instance Hashable1 Approximate where liftHashWithSalt h s (Approximate c low est high) = hashWithSalt s c `h` low `h` est `h` high instance Serial a => Serial (Approximate a) instance Serial1 Approximate where serializeWith f (Approximate p l m h) = serialize p >> f l >> f m >> f h deserializeWith m = Approximate <$> deserialize <*> m <*> m <*> m -- instance Storable a => Storable (Approximate a) where -- sizeOf _ = sizeOf (undefined :: Double) + sizeOf (undefined :: a) * 3 --? instance Unbox a => Unbox (Approximate a) newtype instance U.MVector s (Approximate a) = MV_Approximate (U.MVector s (Log Double,a,a,a)) newtype instance U.Vector (Approximate a) = V_Approximate (U.Vector (Log Double,a,a,a)) instance Unbox a => M.MVector U.MVector (Approximate a) where basicLength (MV_Approximate v) = M.basicLength v {-# INLINE basicLength #-} basicUnsafeSlice i n (MV_Approximate v) = MV_Approximate $ M.basicUnsafeSlice i n v {-# INLINE basicUnsafeSlice #-} basicOverlaps (MV_Approximate v1) (MV_Approximate v2) = M.basicOverlaps v1 v2 {-# INLINE basicOverlaps #-} basicUnsafeNew n = MV_Approximate `liftM` M.basicUnsafeNew n {-# INLINE basicUnsafeNew #-} basicUnsafeReplicate n (Approximate p l m h) = MV_Approximate `liftM` M.basicUnsafeReplicate n (p,l,m,h) {-# INLINE basicUnsafeReplicate #-} basicUnsafeRead (MV_Approximate v) i = (\(p,l,m,h) -> Approximate p l m h) `liftM` M.basicUnsafeRead v i {-# INLINE basicUnsafeRead #-} basicUnsafeWrite (MV_Approximate v) i (Approximate p l m h) = M.basicUnsafeWrite v i (p,l,m,h) {-# INLINE basicUnsafeWrite #-} basicClear (MV_Approximate v) = M.basicClear v {-# INLINE basicClear #-} basicSet (MV_Approximate v) (Approximate p l m h) = M.basicSet v (p,l,m,h) {-# INLINE basicSet #-} basicUnsafeCopy (MV_Approximate v1) (MV_Approximate v2) = M.basicUnsafeCopy v1 v2 {-# INLINE basicUnsafeCopy #-} basicUnsafeMove (MV_Approximate v1) (MV_Approximate v2) = M.basicUnsafeMove v1 v2 {-# INLINE basicUnsafeMove #-} basicUnsafeGrow (MV_Approximate v) n = MV_Approximate `liftM` M.basicUnsafeGrow v n {-# INLINE basicUnsafeGrow #-} basicInitialize (MV_Approximate v) = M.basicInitialize v {-# INLINE basicInitialize #-} instance Unbox a => G.Vector U.Vector (Approximate a) where basicUnsafeFreeze (MV_Approximate v) = V_Approximate `liftM` G.basicUnsafeFreeze v {-# INLINE basicUnsafeFreeze #-} basicUnsafeThaw (V_Approximate v) = MV_Approximate `liftM` G.basicUnsafeThaw v {-# INLINE basicUnsafeThaw #-} basicLength (V_Approximate v) = G.basicLength v {-# INLINE basicLength #-} basicUnsafeSlice i n (V_Approximate v) = V_Approximate $ G.basicUnsafeSlice i n v {-# INLINE basicUnsafeSlice #-} basicUnsafeIndexM (V_Approximate v) i = (\(p,l,m,h) -> Approximate p l m h) `liftM` G.basicUnsafeIndexM v i {-# INLINE basicUnsafeIndexM #-} basicUnsafeCopy (MV_Approximate mv) (V_Approximate v) = G.basicUnsafeCopy mv v {-# INLINE basicUnsafeCopy #-} elemseq _ (Approximate p l m h) z = G.elemseq (undefined :: U.Vector (Log Double)) p $ G.elemseq (undefined :: U.Vector a) l $ G.elemseq (undefined :: U.Vector a) m $ G.elemseq (undefined :: U.Vector a) h z {-# INLINE elemseq #-} instance NFData a => NFData (Approximate a) where rnf (Approximate _ l m h) = rnf l `seq` rnf m `seq` rnf h instance Functor Approximate where fmap f (Approximate p l m h) = Approximate p (f l) (f m) (f h) {-# INLINE fmap #-} instance Foldable Approximate where foldMap f (Approximate _ l m h) = f l `mappend` f m `mappend` f h {-# INLINE foldMap #-} instance Traversable Approximate where traverse f (Approximate p l m h) = Approximate p <$> f l <*> f m <*> f h {-# INLINE traverse #-} instance Copointed Approximate where copoint (Approximate _ _ a _) = a {-# INLINE copoint #-} instance Pointed Approximate where point a = Approximate 1 a a a {-# INLINE point #-} instance Apply Approximate where Approximate p lf mf hf <.> Approximate q la ma ha = Approximate (p * q) (lf la) (mf ma) (hf ha) {-# INLINE (<.>) #-} instance Applicative Approximate where pure a = Approximate 1 a a a {-# INLINE pure #-} Approximate p lf mf hf <*> Approximate q la ma ha = Approximate (p * q) (lf la) (mf ma) (hf ha) {-# INLINE (<*>) #-} withMin :: Ord a => a -> Approximate a -> Approximate a withMin b r@(Approximate p l m h) | b <= l = r | otherwise = Approximate p b (max b m) (max b h) {-# INLINE withMin #-} withMax :: Ord a => a -> Approximate a -> Approximate a withMax b r@(Approximate p l m h) | h <= b = r | otherwise = Approximate p (min l b) (min m b) b {-# INLINE withMax #-} instance (Ord a, Num a) => Num (Approximate a) where (+) = liftA2 (+) {-# INLINE (+) #-} m * n | is zero n || is one m = m | is zero m || is one n = n | otherwise = Approximate (m^.confidence * n^.confidence) (Prelude.minimum extrema) (m^.estimate * n^.estimate) (Prelude.maximum extrema) where extrema = (*) <$> [m^.lo,m^.hi] <*> [n^.lo,n^.hi] {-# INLINE (*) #-} negate (Approximate p l m h) = Approximate p (-h) (-m) (-l) {-# INLINE negate #-} Approximate p la ma ha - Approximate q lb mb hb = Approximate (p * q) (la - hb) (ma - mb) (ha - lb) {-# INLINE (-) #-} abs (Approximate p la ma ha) = Approximate p (min lb hb) (abs ma) (max lb hb) where lb = abs la hb = abs ha {-# INLINE abs #-} signum = fmap signum {-# INLINE signum #-} fromInteger = pure . fromInteger {-# INLINE fromInteger #-} exact :: Eq a => Prism' (Approximate a) a exact = prism pure $ \ s -> case s of Approximate (Exp lp) a b c | lp == 0 && a == c -> Right b _ -> Left s {-# INLINE exact #-} zero :: (Num a, Eq a) => Prism' (Approximate a) () zero = exact.only 0 {-# INLINE zero #-} one :: (Num a, Eq a) => Prism' (Approximate a) () one = exact.only 1 {-# INLINE one #-} is :: Getting Any s a -> s -> Bool is = has {-# INLINE is #-}