packages feed

dunai-test 0.10.1 → 0.11.0

raw patch · 7 files changed

+276/−231 lines, 7 filesdep ~dunaiPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: dunai

API changes (from Hackage documentation)

- FRP.Dunai.QuickCheck: generateConstantStream :: (Int -> DTime -> Gen a) -> (DTime, Int) -> Gen (SignalSampleStream a)
- FRP.Dunai.QuickCheck: generateDSNormal :: DTime -> DTime -> Maybe DTime -> Maybe DTime -> Gen DTime
- FRP.Dunai.QuickCheck: generateDelta :: Maybe DTime -> Maybe DTime -> Gen DTime
- FRP.Dunai.QuickCheck: generateDeltas :: Distribution -> Range -> Length -> Gen DTime
- FRP.Dunai.QuickCheck: generateStreamLenDT :: (Maybe DTime, Maybe DTime) -> Maybe (Either Int DTime) -> Gen (DTime, Int)
- FRP.Dunai.QuickCheck: timeStampsUntil :: DTime -> Gen [DTime]
- FRP.Dunai.QuickCheck: timeStampsUntilWith :: Gen DTime -> DTime -> Gen [DTime]
- FRP.Dunai.QuickCheck: vectorOfWith :: Int -> (Int -> Gen a) -> Gen [a]
- FRP.Dunai.Stream: sClipAfterTime :: (Ord t, Num t) => t -> [(t, b)] -> [(t, b)]
+ FRP.Dunai.Stream: sClipAfterTime :: DTime -> SignalSampleStream a -> SignalSampleStream a

Files

CHANGELOG view
@@ -1,3 +1,7 @@+2023-04-21 Ivan Perez <ivan.perez@keera.co.uk>+        * Version bump (0.11.0) (#358).+        * Conformance with style guide (#348).+ 2023-02-21 Ivan Perez <ivan.perez@keera.co.uk>         * Version bump (0.10.1) (#345). 
dunai-test.cabal view
@@ -30,7 +30,7 @@ build-type:    Simple  name:          dunai-test-version:       0.10.1+version:       0.11.0 author:        Ivan Perez maintainer:    ivan.perez@keera.co.uk homepage:      https://github.com/ivanperez-keera/dunai@@ -74,7 +74,7 @@    build-depends:       base >= 4 && < 5-    , dunai >= 0.5 && < 0.11+    , dunai >= 0.5 && < 0.12     , normaldistribution     , QuickCheck 
src/FRP/Dunai/Debug.hs view
@@ -6,9 +6,9 @@ -- Debug FRP networks by inspecting their behaviour inside. module FRP.Dunai.Debug where -import Debug.Trace+-- External imports import Data.MonadicStreamFunction hiding (trace)-import System.IO.Unsafe+import Debug.Trace                (trace)  -- ** Debugging @@ -16,7 +16,7 @@ -- 'trace'. traceMSF :: Monad m          => Show a-        => MSF m a a+         => MSF m a a traceMSF = traceMSFWith show  -- | Monadic Stream Function that prints the value passing through using
src/FRP/Dunai/LTLFuture.hs view
@@ -23,15 +23,18 @@     )   where +-- External imports #if !MIN_VERSION_base(4,8,0)-import Control.Applicative (Applicative, (<$>), (<*>), pure)+import Control.Applicative (Applicative, pure, (<$>), (<*>)) #endif -import Control.Monad.Trans.MSF.Reader-import Data.MonadicStreamFunction+import Control.Monad.Trans.MSF.Reader          (ReaderT, readerS, runReaderS)+import Data.MonadicStreamFunction              (MSF) import Data.MonadicStreamFunction.InternalCore (unMSF)-import FRP.Dunai.Stream +-- Internal imports+import FRP.Dunai.Stream (DTime, SignalSampleStream)+ -- * Temporal Logics based on SFs  -- | Type representing future-time linear temporal logic with until and next.@@ -67,29 +70,31 @@ -- -- Returns 'True' if the temporal proposition is currently true. evalT :: (Functor m, Applicative m, Monad m)-      => TPred (ReaderT DTime m) a -> SignalSampleStream a -> m Bool-evalT (Prop sf)       [] = return False-evalT (And t1 t2)     [] = (&&) <$> evalT t1 [] <*> evalT t2 []-evalT (Or  t1 t2)     [] = (||) <$> evalT t1 [] <*> evalT t2 []-evalT (Not t1)        [] = not  <$> evalT t1 []-evalT (Implies t1 t2) [] = (||) <$> (not <$> evalT t1 []) <*> evalT t2 []-evalT (Always t1)     [] = return True-evalT (Eventually t1) [] = return False-evalT (Next t1)       [] = return False-evalT (Until t1 t2)   [] = (||) <$> evalT t1 [] <*> evalT t2 []+      => TPred (ReaderT DTime m) a+      -> SignalSampleStream a+      -> m Bool+evalT (Prop _sf)      []     = return False+evalT (And t1 t2)     []     = (&&) <$> evalT t1 [] <*> evalT t2 []+evalT (Or  t1 t2)     []     = (||) <$> evalT t1 [] <*> evalT t2 []+evalT (Not t1)        []     = not  <$> evalT t1 []+evalT (Implies t1 t2) []     = (||) <$> (not <$> evalT t1 []) <*> evalT t2 []+evalT (Always _t)     []     = return True+evalT (Eventually _t) []     = return False+evalT (Next _t)       []     = return False+evalT (Until t1 t2)   []     = (||) <$> evalT t1 [] <*> evalT t2 [] evalT op              (x:xs) = do   (r, op') <- stepF op x   case (r, xs) of     (Def x,    _) -> return x     (SoFar x, []) -> return x-    (SoFar x, xs) -> evalT op' xs+    (SoFar _, xs) -> evalT op' xs  -- ** Multi-valued temporal evaluation  -- | Multi-valued logic result data MultiRes-    = Def Bool    -- ^ Definite value known-    | SoFar Bool  -- ^ Value so far, but could change+  = Def Bool   -- ^ Definite value known+  | SoFar Bool -- ^ Value so far, but could change  -- | Multi-valued implementation of @and@. andM :: MultiRes -> MultiRes -> MultiRes@@ -97,10 +102,9 @@ andM _             (Def False)   = Def False andM (Def True)    x             = x andM x             (Def True)    = x-andM (SoFar False) (SoFar x)     = SoFar False-andM (SoFar x)     (SoFar False) = SoFar False+andM (SoFar False) (SoFar _)     = SoFar False+andM (SoFar _)     (SoFar False) = SoFar False andM (SoFar True)  (SoFar x)     = SoFar x-andM (SoFar x)     (SoFar True)  = SoFar x  -- | Multi-valued implementation of @or@. orM :: MultiRes -> MultiRes -> MultiRes@@ -108,10 +112,9 @@ orM _             (Def False)   = Def False orM (Def True)    x             = x orM x             (Def True)    = x-orM (SoFar False) (SoFar x)     = SoFar False-orM (SoFar x)     (SoFar False) = SoFar False+orM (SoFar False) (SoFar _)     = SoFar False+orM (SoFar _)     (SoFar False) = SoFar False orM (SoFar True)  (SoFar x)     = SoFar x-orM (SoFar x)     (SoFar True)  = SoFar x  -- | Perform one step of evaluation of a temporal predicate. stepF :: (Applicative m, Monad m)@@ -119,16 +122,16 @@       -> (DTime, a)       -> m (MultiRes, TPred (ReaderT DTime m) a) -stepF (Prop sf) x  = do+stepF (Prop sf) x = do   (b, sf') <- unMSF (runReaderS sf) x   return (Def b, Prop (readerS sf'))  stepF (Always sf) x = do   (b, sf') <- stepF sf x   case b of-    Def True    -> pure (SoFar True, Always sf')-    Def False   -> pure (Def False, Always sf')-    SoFar True  -> pure (SoFar True, Always sf')+    Def True    -> pure (SoFar True,  Always sf')+    Def False   -> pure (Def False,   Always sf')+    SoFar True  -> pure (SoFar True,  Always sf')     SoFar False -> pure (SoFar False, Always sf')  stepF (Eventually sf) x = do@@ -142,7 +145,7 @@ stepF (Not sf) x = do   (b, sf') <- stepF sf x   case b of-    Def x   -> pure (Def (not x), Not sf')+    Def x   -> pure (Def (not x),   Not sf')     SoFar x -> pure (SoFar (not x), Not sf')  stepF (And sf1 sf2) x = do
src/FRP/Dunai/LTLPast.hs view
@@ -1,6 +1,11 @@ {-# LANGUAGE Arrows #-}--- | Past-time LTL using MSFs.+-- |+-- Copyright  : (c) Ivan Perez, 2017+-- License    : BSD3+-- Maintainer : ivan.perez@keera.co.uk --+-- Past-time LTL using MSFs.+-- -- This module provides ways of defining past-, discrete-time temporal -- predicates with MSFs. --@@ -9,9 +14,10 @@ -- (Past-time LTL as MSF combinators). module FRP.Dunai.LTLPast where -import Control.Monad.Trans.MSF.Maybe-import Data.Maybe-import Data.MonadicStreamFunction+-- External imports+import Control.Monad.Trans.MSF.Maybe (MaybeT, catchMaybe, inMaybeT)+import Data.MonadicStreamFunction    (MSF, arr, feedback, iPre, liftTransS,+                                      returnA, (&&&), (>>>), (^>>))  -- * Past-time linear temporal logic using MSFs. @@ -31,23 +37,23 @@  -- | Output True when the second input is True or the first one is False. impliesSF :: Monad m => MSF m (Bool, Bool) Bool-impliesSF = arr $ \(i,p) -> not i || p+impliesSF = arr $ \(i, p) -> not i || p  -- ** Temporal MSFs  -- | Output True when every input up until the current time has been True. ----- This corresponds to Historically, or the past-time version of Globally--- or Always.+-- This corresponds to Historically, or the past-time version of Globally or+-- Always. sofarSF :: Monad m => MSF m Bool Bool-sofarSF = feedback True $ arr $ \(n,o) -> let n' = o && n in (n', n')+sofarSF = feedback True $ arr $ \(n, o) -> let n' = o && n in (n', n')  -- | Output True when at least one input up until the current time has been -- True. -- -- This corresponds to Ever, or the past-time version of Eventually. everSF :: Monad m => MSF m Bool Bool-everSF = feedback False $ arr $ \(n,o) -> let n' = o || n in (n', n')+everSF = feedback False $ arr $ \(n, o) -> let n' = o || n in (n', n')  -- | Output True if the first element has always been True, or the second has -- been True ever since the first one became False.@@ -60,7 +66,7 @@      untilMaybeB :: Monad m => MSF m a (b, Bool) -> MSF (MaybeT m) a b     untilMaybeB msf = proc a -> do-      (b,c) <- liftTransS msf -< a+      (b, c) <- liftTransS msf -< a       inMaybeT -< if c then Nothing else Just b      cond ((i, u), o) = ((n, o && u), n)@@ -73,22 +79,6 @@ lastSF :: Monad m => MSF m Bool Bool lastSF = iPre False --- data UnclearResult = Possibly Bool | Definitely Bool------ causally :: SF a Bool -> SF a UnclearResult--- causally = (>>> arr Definitely)------ data TSF a = NonCausal (SF a UnclearResult)---            | Causal    (SF a Bool)------ evalTSF :: TSF a -> SignalSampleStream a -> Bool--- evalTSF (Causal sf)    ss = firstSample $ fst $ evalSF sf ss--- evalTSF (NonCausal sf) ss = clarifyResult $ lastSample $ fst $ evalSF sf ss------ clarifyResult :: UnclearResult -> Bool--- clarifyResult (Possibly x)   = x--- clarifyResult (Definitely x) = x- -- * Past-time linear temporal logic as MSF combinators.  -- | A signal predicate is an MSF whose output is a Boolean value.@@ -118,16 +108,15 @@ -- | Output True at a time if the input has always been True up until that -- time. ----- This corresponds to Historically, or the past-time version of Globally--- or Always.+-- This corresponds to Historically, or the past-time version of Globally or+-- Always. history' :: Monad m => SPred m a -> SPred m a history' sf = feedback True $ proc (a, last) -> do   b <- sf -< a   let cur = last && b   returnA -< (cur, cur) --- | Output True at a time if the input has ever been True up until that--- time.+-- | Output True at a time if the input has ever been True up until that time. -- -- This corresponds to Ever, or the past-time version of Eventually. ever' :: Monad m => SPred m a -> SPred m a
src/FRP/Dunai/QuickCheck.hs view
@@ -1,85 +1,53 @@ {-# LANGUAGE CPP                 #-}-{-# LANGUAGE MultiWayIf          #-} {-# LANGUAGE ScopedTypeVariables #-}--module FRP.Dunai.QuickCheck where---- Examples accompanying the ICFP 2017 paper.+-- |+-- Copyright  : (c) Ivan Perez, 2017-2023+-- License    : BSD3+-- Maintainer : ivan.perez@keera.co.uk ----- Changes with respect to the paper:+-- QuickCheck generators for input streams. ----- - The signature of ballTrulyFalling' in the paper was SF () Double. It's---   been changed to the intended meaning: TPred ()+-- Random stream generation can be customized usin three parameters:+--+-- - The distribution for the random time deltas ('Distribution').+-- - The maximum and minimum bounds for the time deltas ('Range').+-- - The maximum stream length ('Length').+--+-- The main function to generate streams is 'generateStream'. The specific time+-- deltas can be customized further using 'generateStreamWith'. Some helper+-- functions are provided to facilitate testing.+module FRP.Dunai.QuickCheck+    (+      -- * Random stream generation+      generateStream+    , generateStreamWith --- - The function uniDistStreamMaxDT had the wrong type and the name on the---   paper was: uniDistStream. This has been fixed.+      -- ** Parameters used to generate random input streams+    , Distribution(..)+    , Range+    , Length +      -- ** Helpers for common cases+    , uniDistStream+    , uniDistStreamMaxDT+    , fixedDelayStream+    , fixedDelayStreamWith+    )+  where++-- External imports #if !MIN_VERSION_base(4,8,0)-import Control.Applicative ((<$>), pure)+import Control.Applicative (pure, (<$>)) #endif -import Data.Random.Normal-import Data.MonadicStreamFunction-import FRP.Dunai.Stream-import Test.QuickCheck-import Test.QuickCheck.Gen---- * Random stream generation---- ** Parameters used to generate random input streams--data Distribution = DistConstant-                  | DistNormal (DTime, DTime)-                  | DistRandom--type Range = (Maybe DTime, Maybe DTime)--type Length = Maybe (Either Int DTime)---- ** Time delta generation---- | Generate a random delta according to some required specifications.-generateDeltas :: Distribution -> Range -> Length -> Gen DTime-generateDeltas DistConstant            (mn, mx) len = generateDelta mn mx-generateDeltas DistRandom              (mn, mx) len = generateDelta mn mx-generateDeltas (DistNormal (avg, dev)) (mn, mx) len =-  generateDSNormal avg dev mn mx---- | Generate one random delta, possibly within a range.-generateDelta :: Maybe DTime -> Maybe DTime -> Gen DTime-generateDelta (Just x)  (Just y)  = choose (x, y)-generateDelta (Just x)  Nothing   = (x+) . getPositive <$> arbitrary-generateDelta Nothing   (Just y)  = choose (2.2251e-308, y)-generateDelta Nothing   Nothing   = getPositive <$> arbitrary---- | Generate a random delta following a normal distribution,---   and possibly within a given range.-generateDSNormal :: DTime -> DTime -> Maybe DTime -> Maybe DTime -> Gen DTime-generateDSNormal avg stddev m n = suchThat gen (\x -> mx x && mn x)-  where-    gen = MkGen (\r _ -> let (x,_) = normal' (avg, stddev) r in x)-    mn  = maybe (const True) (<=) m-    mx  = maybe (const True) (>=) n---- | Generate random samples up until a max time.-timeStampsUntil :: DTime -> Gen [DTime]-timeStampsUntil = timeStampsUntilWith arbitrary+import Data.Random.Normal  (normal')+import Test.QuickCheck     (Arbitrary, arbitrary, getPositive)+import Test.QuickCheck.Gen (Gen (MkGen), choose, suchThat) --- | Generate random samples up until a max time, with a given time delta---   generation function.-timeStampsUntilWith :: Gen DTime -> DTime -> Gen [DTime]-timeStampsUntilWith arb = timeStampsUntilWith' arb []-  where-    -- Generate random samples up until a max time, with a given time delta-    -- generation function, and an initial suffix of time deltas.-    timeStampsUntilWith' :: Gen DTime -> [DTime] -> DTime -> Gen [DTime]-    timeStampsUntilWith' arb acc ds-      | ds < 0    = return acc-      | otherwise = do d <- arb-                       let acc' = acc `seq` (d:acc)-                       acc' `seq` timeStampsUntilWith' arb acc' (ds - d)+-- Internal imports+import FRP.Dunai.Stream (DTime, SignalSampleStream, groupDeltas) --- ** Random stream generation+-- * Random stream generation  -- | Generate random stream. generateStream :: Arbitrary a@@ -95,64 +63,40 @@                    -> Range                    -> Length                    -> Gen (SignalSampleStream a)--generateStreamWith arb DistConstant range  len     =+generateStreamWith arb DistConstant range len =   generateConstantStream arb =<< generateStreamLenDT range len--generateStreamWith arb DistRandom   (m, n) Nothing = do-  l <- arbitrary-  x <- arb 0 0-  ds <- vectorOfWith l (\_ -> generateDelta m n)-  let f n = arb n (ds!!(n-1))-  xs <- vectorOfWith l f-  return $ groupDeltas (x:xs) ds--generateStreamWith arb DistRandom (m, n) (Just (Left l)) = do-  x <- arb 0 0-  ds <- vectorOfWith l (\_ -> generateDelta m n)-  let f n = arb n (ds!!(n-1))-  xs <- vectorOfWith l f-  return $ groupDeltas (x:xs) ds+generateStreamWith arb dist (m, n) len = do+    ds <- generateDeltas len+    let l = length ds+    let f n = arb n (ds !! (n - 1))+    xs <- vectorOfWith l f -generateStreamWith arb DistRandom (m, n) (Just (Right maxds)) = do-  ds <- timeStampsUntilWith (generateDelta m n) maxds-  let l = length ds-  x  <- arb 0 0-  let f n = arb n (ds!!(n-1))-  xs <- vectorOfWith l f-  return $ groupDeltas (x:xs) ds+    x <- arb 0 0+    return $ groupDeltas (x:xs) ds -generateStreamWith arb (DistNormal (avg, stddev)) (m, n) Nothing = do-  l <- arbitrary-  x <- arb 0 0-  ds <- vectorOfWith l (\_ -> generateDSNormal avg stddev m n)-  let f n = arb n (ds!!(n-1))-  xs <- vectorOfWith l f-  return $ groupDeltas (x:xs) ds+  where -generateStreamWith arb (DistNormal (avg, stddev)) (m, n) (Just (Left l)) = do-  x <- arb 0 0-  ds <- vectorOfWith l (\_ -> generateDSNormal avg stddev m n)-  let f n = arb n (ds!!(n-1))-  xs <- vectorOfWith l f-  return $ groupDeltas (x:xs) ds+    deltaF :: Gen DTime+    deltaF = case dist of+               DistRandom -> generateDelta m n+               DistNormal (avg, stddev) -> generateDSNormal avg stddev m n+               _ -> error "dunai-test: generateStreamWith" -generateStreamWith arb (DistNormal (avg, stddev)) (m, n) (Just (Right maxds)) = do-  ds <- timeStampsUntilWith (generateDSNormal avg stddev m n) maxds-  let l = length ds-  x <- arb 0 0-  let f n = arb n (ds!!(n-1))-  xs <- vectorOfWith l f-  return $ groupDeltas (x:xs) ds+    generateDeltas :: Length -> Gen [DTime]+    generateDeltas Nothing              = do l <- arbitrary+                                             vectorOfWith l (\_ -> deltaF)+    generateDeltas (Just (Left l))      = vectorOfWith l (\_ -> deltaF)+    generateDeltas (Just (Right maxds)) = timeStampsUntilWith deltaF maxds  -- | Generate arbitrary stream with fixed length and constant delta. generateConstantStream :: (Int -> DTime -> Gen a)                        -> (DTime, Int)                        -> Gen (SignalSampleStream a) generateConstantStream arb (x, length) = do-  ys <- vectorOfWith length (`arb` x)-  let ds = repeat x-  return $ groupDeltas ys ds+    ys <- vectorOfWith length (`arb` x)+    return $ groupDeltas ys ds+  where+    ds = repeat x  -- | Generate arbitrary stream generateStreamLenDT :: (Maybe DTime, Maybe DTime)@@ -166,16 +110,53 @@          Just (Right ds) -> pure (floor (ds / x))   return (x, l) --- generateStreamLenDT (Just x,  Just y)  (Just (Left l))   = (,) <$> choose (x, y)        <*> pure l--- generateStreamLenDT (Just x,  Nothing) (Just (Left l))   = (,) <$> ((x+) <$> arbitrary) <*> pure l--- generateStreamLenDT (Nothing, Just y)  (Just (Left l))   = (,) <$> choose (0, y)        <*> pure l--- generateStreamLenDT (Just x,  _)       (Just (Right ts)) = (,) <$> pure x               <*> pure (floor (ts / x))--- generateStreamLenDT (Just x,  _)       Nothing           = (,) <$> pure x               <*> arbitrary--- generateStreamLenDT (Nothing, Nothing) Nothing           = (,) <$> arbitrary            <*> arbitrary--- generateStreamLenDT (Nothing, Nothing) (Just (Left l))   = (,) <$> arbitrary            <*> pure l--- generateStreamLenDT (Nothing, Nothing) (Just (Right ds)) = f2  <$> arbitrary---   where---     f2 l = (ds / fromIntegral l, l)+-- ** Time delta generation++-- | Generate one random delta, possibly within a range.+generateDelta :: Maybe DTime -> Maybe DTime -> Gen DTime+generateDelta (Just x) (Just y) = choose (x, y)+generateDelta (Just x) Nothing  = (x +) . getPositive <$> arbitrary+generateDelta Nothing  (Just y) = choose (2.2251e-308, y)+generateDelta Nothing  Nothing  = getPositive <$> arbitrary++-- | Generate a random delta following a normal distribution, and possibly+-- within a given range.+generateDSNormal :: DTime -> DTime -> Maybe DTime -> Maybe DTime -> Gen DTime+generateDSNormal avg stddev m n = suchThat gen (\x -> mx x && mn x)+  where+    gen = MkGen (\r _ -> fst $ normal' (avg, stddev) r)+    mn  = maybe (const True) (<=) m+    mx  = maybe (const True) (>=) n++-- | Generate random samples up until a max time, with a given time delta+-- generation function.+timeStampsUntilWith :: Gen DTime -> DTime -> Gen [DTime]+timeStampsUntilWith arb = timeStampsUntilWith' arb []+  where+    -- Generate random samples up until a max time, with a given time delta+    -- generation function, and an initial suffix of time deltas.+    timeStampsUntilWith' :: Gen DTime -> [DTime] -> DTime -> Gen [DTime]+    timeStampsUntilWith' arb acc ds+      | ds < 0    = return acc+      | otherwise = do d <- arb+                       let acc' = acc `seq` (d:acc)+                       acc' `seq` timeStampsUntilWith' arb acc' (ds - d)++-- ** Parameters used to generate random input streams++-- | Distributions used for time delta (DT) generation.+data Distribution+  = DistConstant              -- ^ Constant DT for the whole stream.+  | DistNormal (DTime, DTime) -- ^ Variable DT following normal distribution,+                              --   with an average and a standard deviation.+  | DistRandom                -- ^ Completely random (positive) DT.++-- | Upper and lower bounds of time deltas for random DT generation.+type Range = (Maybe DTime, Maybe DTime)++-- | Optional maximum length for a stream, given as a time, or a number of+-- samples.+type Length = Maybe (Either Int DTime)  -- ** Helpers for common cases 
src/FRP/Dunai/Stream.hs view
@@ -1,22 +1,47 @@-{-# LANGUAGE MultiWayIf #-}+-- |+-- Copyright  : (c) Ivan Perez, 2017-2023+-- License    : BSD3+-- Maintainer : ivan.perez@keera.co.uk+--+-- Streams and stream manipulation API.+--+-- The evaluation of Dunai MSFs, especially for testing purposes, needs the+-- generation of suitable input streams.+--+-- While some streams can be generated randomly using QuickCheck, it is+-- sometimes useful to be able to preprend or adapt an input stream. It is also+-- useful to debug programs when you have recorded input streams using Haskell+-- Titan.+--+-- This module defines types for input streams, as well as an API to create,+-- examine and combine streams. It also provides evaluation functions that are+-- needed to apply an MSF to a stream and obtain an output stream and a+-- continuation MSF. module FRP.Dunai.Stream where -import Data.MonadicStreamFunction+-- External imports+import Control.Monad.Trans.MSF.Reader          (ReaderT, readerS, runReaderS)+import Data.MonadicStreamFunction              (MSF) import Data.MonadicStreamFunction.InternalCore (unMSF)-import Control.Monad.Trans.MSF.Reader  -- * Types++-- | A stream of samples, with their sampling times. type SignalSampleStream a = SampleStream (DTime, a)++-- | A stream of samples, with no sampling time. type SampleStream a = [a]-type DTime    = Double +-- | DTime is the time type for lengths of sample intervals. Conceptually,+-- DTime = R+ = { x in R | x > 0 }.+type DTime = Double  -- ** Creation  -- | Group a series of samples with a series of time deltas. -----   The first sample will have no delta. Unused samples and deltas will be---   dropped.+-- The first sample will have no delta. Unused samples and deltas will be+-- dropped. groupDeltas :: [a] -> [DTime] -> SignalSampleStream a groupDeltas xs ds = zip (0:ds) xs @@ -26,9 +51,11 @@ samples :: SignalSampleStream a -> [a] samples = map snd +-- | Return the first sample in a signal sample stream. firstSample :: SignalSampleStream a -> a firstSample = head . samples +-- | Return the last sample in a signal sample stream. lastSample :: SignalSampleStream a -> a lastSample = last . samples @@ -36,74 +63,115 @@  -- ** Merging -sMerge :: (a -> a -> a) -> SignalSampleStream a -> SignalSampleStream a -> SignalSampleStream a-sMerge f []              xs2             = xs2-sMerge f xs1             []              = xs1-sMerge f ((dt1, x1):xs1) ((dt2, x2):xs2)+-- | Merge two streams, using an auxiliary function to merge samples that fall+-- at the exact same sampling time.+sMerge :: (a -> a -> a)+       -> SignalSampleStream a+       -> SignalSampleStream a+       -> SignalSampleStream a+sMerge _ []                xs2               = xs2+sMerge _ xs1               []                = xs1+sMerge f ((dt1, x1) : xs1) ((dt2, x2) : xs2)   | dt1 == dt2 = (dt1, f x1 x2) : sMerge f xs1 xs2-  | dt1 <  dt2 = (dt1, x1) : sMerge f xs1 ((dt2-dt1, x2):xs2)-  | otherwise  = (dt2, x2) : sMerge f ((dt1-dt2, x1):xs1) xs2+  | dt1 <  dt2 = (dt1, x1) : sMerge f xs1 ((dt2 - dt1, x2) : xs2)+  | otherwise  = (dt2, x2) : sMerge f ((dt1 - dt2, x1) : xs1) xs2  -- ** Concatenating +-- | Concatenate two sample streams, separating them by a given time delta. sConcat :: SignalSampleStream a -> SignalSampleStream a -> SignalSampleStream a sConcat xs1 xs2 = xs1 ++ xs2  -- ** Refining++-- | Refine a signal sample stream by establishing the maximum time delta.+--+-- If two samples are separated by a time delta bigger than the given max DT,+-- the former is replicated as many times as necessary. sRefine :: DTime -> a -> SignalSampleStream a -> SignalSampleStream a-sRefine maxDT _ [] = []-sRefine maxDT a0 ((dt, a):as)-  | dt > maxDT = (maxDT, a0) : sRefine maxDT a0 ((dt - maxDT, a):as)+sRefine _     _  []             = []+sRefine maxDT a0 ((dt, a) : as)+  | dt > maxDT = (maxDT, a0) : sRefine maxDT a0 ((dt - maxDT, a) : as)   | otherwise  = (dt, a) : sRefine maxDT a as -refineWith :: (a -> a -> a) -> DTime -> a -> SignalSampleStream a -> SignalSampleStream a-refineWith interpolate maxDT _  [] = []-refineWith interpolate maxDT a0 ((dt, a):as)-  | dt > maxDT = let a' = interpolate a0 a-                 in (maxDT, interpolate a0 a) : refineWith interpolate maxDT a' ((dt - maxDT, a):as)-  | otherwise  = (dt, a) : refineWith interpolate maxDT a as+-- | Refine a stream by establishing the maximum time delta.+--+-- If two samples are separated by a time delta bigger than the given max DT,+-- the auxiliary interpolation function is used to determine the intermediate+-- sample.+refineWith :: (a -> a -> a)+           -> DTime+           -> a+           -> SignalSampleStream a+           -> SignalSampleStream a+refineWith _           _     _  []             = []+refineWith interpolate maxDT a0 ((dt, a) : as)+    | dt > maxDT+    = (maxDT, interpolate a0 a) :+        refineWith interpolate maxDT a' ((dt - maxDT, a) : as)+    | otherwise+    = (dt, a) : refineWith interpolate maxDT a as+  where+    a' = interpolate a0 a  -- ** Clipping (dropping samples) +-- | Clip a signal sample stream at a given number of samples. sClipAfterFrame :: Int -> SignalSampleStream a -> SignalSampleStream a sClipAfterFrame = take -sClipAfterTime dt [] = []-sClipAfterTime dt ((dt',x):xs)+-- | Clip a signal sample stream after a certain (non-zero) time.+sClipAfterTime :: DTime -> SignalSampleStream a -> SignalSampleStream a+sClipAfterTime _  []              = []+sClipAfterTime dt ((dt', x) : xs)   | dt < dt'  = []   | otherwise = (dt', x) : sClipAfterTime (dt - dt') xs +-- | Drop the first n samples of a signal sample stream. The time deltas are+-- not re-calculated. sClipBeforeFrame :: Int -> SignalSampleStream a -> SignalSampleStream a sClipBeforeFrame 0 xs@(_:_) = xs-sClipBeforeFrame n xs@[x]   = xs-sClipBeforeFrame n xs       = sClipBeforeFrame (n-1) xs+sClipBeforeFrame _ xs@[_]   = xs+sClipBeforeFrame n xs       = sClipBeforeFrame (n - 1) xs -sClipBeforeTime  :: DTime -> SignalSampleStream a -> SignalSampleStream a+-- | Drop the first samples of a signal sample stream up to a given time. The+-- time deltas are not re-calculated to match the original stream.+sClipBeforeTime :: DTime -> SignalSampleStream a -> SignalSampleStream a sClipBeforeTime dt xs-  | dt <= 0   = xs-  | otherwise = case xs of-                  [x]              -> xs-                  (_:(dt',x'):xs') -> if | dt < dt'  -> ((dt'- dt, x'):xs')-                                         | otherwise -> sClipBeforeTime (dt - dt') ((0,x'):xs')-+    | dt <= 0        = xs+    | length xs == 1 = xs+    | dt < dt'       = (dt' - dt, x') : xs'+    | otherwise      = sClipBeforeTime (dt - dt') ((0, x') : xs')+  where+    (_ : (dt', x') : xs') = xs +-- | Evaluate an SF with a 'SignalSampleStream', obtaining an output stream and+-- a continuation.+--+-- You should never use this for actual execution in your applications, only+-- for testing. evalSF :: Monad m        => MSF (ReaderT DTime m) a b        -> SignalSampleStream a        -> m (SampleStream b, MSF (ReaderT DTime m) a b) evalSF fsf as = do-  let msf'' = runReaderS fsf-  (ss, msf') <- evalMSF msf'' as-  return (ss, readerS msf')-+    (ss, msf') <- evalMSF msf'' as+    return (ss, readerS msf')+  where+    msf'' = runReaderS fsf +-- | Evaluate an MSF with a 'SampleStream', obtaining an output stream and a+-- continuation.+--+-- You should never use this for actual execution in your applications, only+-- for testing. evalMSF :: Monad m         => MSF m a b-       -> SampleStream a-       -> m (SampleStream b, MSF m a b)-evalMSF fsf [] = return ([], fsf)+        -> SampleStream a+        -> m (SampleStream b, MSF m a b)+evalMSF fsf []     = return ([], fsf) evalMSF fsf (a:as) = do   (b, fsf')   <- unMSF fsf a   (bs, fsf'') <- evalMSF fsf' as-  let outputStrm  = b : bs+  let outputStrm = b : bs   return (outputStrm, fsf'')