{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}

module Scientist.Duration
  ( Duration
  , measureDuration
  , durationToSeconds
  ) where

import Prelude

import Control.Monad.IO.Class (MonadIO(..))
import Data.Fixed (Fixed(..), Nano, showFixed)
import qualified System.Clock as Clock

-- | Time elapsed in seconds up to nanosecond precision
--
-- >> 1.005
-- 1.005s
newtype Duration = Duration
  { Duration -> Nano
_unDuration :: Nano
  }
  deriving stock (Duration -> Duration -> Bool
(Duration -> Duration -> Bool)
-> (Duration -> Duration -> Bool) -> Eq Duration
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Duration -> Duration -> Bool
$c/= :: Duration -> Duration -> Bool
== :: Duration -> Duration -> Bool
$c== :: Duration -> Duration -> Bool
Eq)
  deriving newtype (Int -> Duration
Duration -> Int
Duration -> [Duration]
Duration -> Duration
Duration -> Duration -> [Duration]
Duration -> Duration -> Duration -> [Duration]
(Duration -> Duration)
-> (Duration -> Duration)
-> (Int -> Duration)
-> (Duration -> Int)
-> (Duration -> [Duration])
-> (Duration -> Duration -> [Duration])
-> (Duration -> Duration -> [Duration])
-> (Duration -> Duration -> Duration -> [Duration])
-> Enum Duration
forall a.
(a -> a)
-> (a -> a)
-> (Int -> a)
-> (a -> Int)
-> (a -> [a])
-> (a -> a -> [a])
-> (a -> a -> [a])
-> (a -> a -> a -> [a])
-> Enum a
enumFromThenTo :: Duration -> Duration -> Duration -> [Duration]
$cenumFromThenTo :: Duration -> Duration -> Duration -> [Duration]
enumFromTo :: Duration -> Duration -> [Duration]
$cenumFromTo :: Duration -> Duration -> [Duration]
enumFromThen :: Duration -> Duration -> [Duration]
$cenumFromThen :: Duration -> Duration -> [Duration]
enumFrom :: Duration -> [Duration]
$cenumFrom :: Duration -> [Duration]
fromEnum :: Duration -> Int
$cfromEnum :: Duration -> Int
toEnum :: Int -> Duration
$ctoEnum :: Int -> Duration
pred :: Duration -> Duration
$cpred :: Duration -> Duration
succ :: Duration -> Duration
$csucc :: Duration -> Duration
Enum, Eq Duration
Eq Duration
-> (Duration -> Duration -> Ordering)
-> (Duration -> Duration -> Bool)
-> (Duration -> Duration -> Bool)
-> (Duration -> Duration -> Bool)
-> (Duration -> Duration -> Bool)
-> (Duration -> Duration -> Duration)
-> (Duration -> Duration -> Duration)
-> Ord Duration
Duration -> Duration -> Bool
Duration -> Duration -> Ordering
Duration -> Duration -> Duration
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: Duration -> Duration -> Duration
$cmin :: Duration -> Duration -> Duration
max :: Duration -> Duration -> Duration
$cmax :: Duration -> Duration -> Duration
>= :: Duration -> Duration -> Bool
$c>= :: Duration -> Duration -> Bool
> :: Duration -> Duration -> Bool
$c> :: Duration -> Duration -> Bool
<= :: Duration -> Duration -> Bool
$c<= :: Duration -> Duration -> Bool
< :: Duration -> Duration -> Bool
$c< :: Duration -> Duration -> Bool
compare :: Duration -> Duration -> Ordering
$ccompare :: Duration -> Duration -> Ordering
$cp1Ord :: Eq Duration
Ord, Integer -> Duration
Duration -> Duration
Duration -> Duration -> Duration
(Duration -> Duration -> Duration)
-> (Duration -> Duration -> Duration)
-> (Duration -> Duration -> Duration)
-> (Duration -> Duration)
-> (Duration -> Duration)
-> (Duration -> Duration)
-> (Integer -> Duration)
-> Num Duration
forall a.
(a -> a -> a)
-> (a -> a -> a)
-> (a -> a -> a)
-> (a -> a)
-> (a -> a)
-> (a -> a)
-> (Integer -> a)
-> Num a
fromInteger :: Integer -> Duration
$cfromInteger :: Integer -> Duration
signum :: Duration -> Duration
$csignum :: Duration -> Duration
abs :: Duration -> Duration
$cabs :: Duration -> Duration
negate :: Duration -> Duration
$cnegate :: Duration -> Duration
* :: Duration -> Duration -> Duration
$c* :: Duration -> Duration -> Duration
- :: Duration -> Duration -> Duration
$c- :: Duration -> Duration -> Duration
+ :: Duration -> Duration -> Duration
$c+ :: Duration -> Duration -> Duration
Num, Num Duration
Ord Duration
Num Duration
-> Ord Duration -> (Duration -> Rational) -> Real Duration
Duration -> Rational
forall a. Num a -> Ord a -> (a -> Rational) -> Real a
toRational :: Duration -> Rational
$ctoRational :: Duration -> Rational
$cp2Real :: Ord Duration
$cp1Real :: Num Duration
Real, Num Duration
Num Duration
-> (Duration -> Duration -> Duration)
-> (Duration -> Duration)
-> (Rational -> Duration)
-> Fractional Duration
Rational -> Duration
Duration -> Duration
Duration -> Duration -> Duration
forall a.
Num a
-> (a -> a -> a) -> (a -> a) -> (Rational -> a) -> Fractional a
fromRational :: Rational -> Duration
$cfromRational :: Rational -> Duration
recip :: Duration -> Duration
$crecip :: Duration -> Duration
/ :: Duration -> Duration -> Duration
$c/ :: Duration -> Duration -> Duration
$cp1Fractional :: Num Duration
Fractional, Fractional Duration
Real Duration
Real Duration
-> Fractional Duration
-> (forall b. Integral b => Duration -> (b, Duration))
-> (forall b. Integral b => Duration -> b)
-> (forall b. Integral b => Duration -> b)
-> (forall b. Integral b => Duration -> b)
-> (forall b. Integral b => Duration -> b)
-> RealFrac Duration
Duration -> b
Duration -> b
Duration -> b
Duration -> b
Duration -> (b, Duration)
forall b. Integral b => Duration -> b
forall b. Integral b => Duration -> (b, Duration)
forall a.
Real a
-> Fractional a
-> (forall b. Integral b => a -> (b, a))
-> (forall b. Integral b => a -> b)
-> (forall b. Integral b => a -> b)
-> (forall b. Integral b => a -> b)
-> (forall b. Integral b => a -> b)
-> RealFrac a
floor :: Duration -> b
$cfloor :: forall b. Integral b => Duration -> b
ceiling :: Duration -> b
$cceiling :: forall b. Integral b => Duration -> b
round :: Duration -> b
$cround :: forall b. Integral b => Duration -> b
truncate :: Duration -> b
$ctruncate :: forall b. Integral b => Duration -> b
properFraction :: Duration -> (b, Duration)
$cproperFraction :: forall b. Integral b => Duration -> (b, Duration)
$cp2RealFrac :: Fractional Duration
$cp1RealFrac :: Real Duration
RealFrac)

instance Show Duration where
  show :: Duration -> String
show (Duration Nano
x) = Bool -> Nano -> String
forall k (a :: k). HasResolution a => Bool -> Fixed a -> String
showFixed Bool
True Nano
x String -> ShowS
forall a. Semigroup a => a -> a -> a
<> String
"s"

measureDuration :: MonadIO m => m a -> m (a, Duration)
measureDuration :: m a -> m (a, Duration)
measureDuration m a
f = do
  TimeSpec
begin <- IO TimeSpec -> m TimeSpec
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO IO TimeSpec
getTime
  (,) (a -> Duration -> (a, Duration))
-> m a -> m (Duration -> (a, Duration))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> m a
f m (Duration -> (a, Duration)) -> m Duration -> m (a, Duration)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> IO Duration -> m Duration
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO
    (Integer -> Duration
fromNanoSecs (Integer -> Duration)
-> (TimeSpec -> Integer) -> TimeSpec -> Duration
forall b c a. (b -> c) -> (a -> b) -> a -> c
. TimeSpec -> Integer
Clock.toNanoSecs (TimeSpec -> Integer)
-> (TimeSpec -> TimeSpec) -> TimeSpec -> Integer
forall b c a. (b -> c) -> (a -> b) -> a -> c
. TimeSpec -> TimeSpec -> TimeSpec
forall a. Num a => a -> a -> a
subtract TimeSpec
begin (TimeSpec -> Duration) -> IO TimeSpec -> IO Duration
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO TimeSpec
getTime)
  where getTime :: IO TimeSpec
getTime = Clock -> IO TimeSpec
Clock.getTime Clock
Clock.Monotonic

-- | Convert from duration to seconds
--
-- >> toSecs 0.000001
-- 0.000001
durationToSeconds :: Duration -> Double
durationToSeconds :: Duration -> Double
durationToSeconds = Duration -> Double
forall a b. (Real a, Fractional b) => a -> b
realToFrac

-- | Convert to duration from nanoseconds
--
-- >> fromNanoSecs 1000
-- 0.000001s
fromNanoSecs :: Integer -> Duration
fromNanoSecs :: Integer -> Duration
fromNanoSecs = Nano -> Duration
Duration (Nano -> Duration) -> (Integer -> Nano) -> Integer -> Duration
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Integer -> Nano
forall k (a :: k). Integer -> Fixed a
MkFixed