module TimerWheel.Internal.Nanoseconds
  ( Nanoseconds (..),
    fromSeconds,
    fromNonNegativeSeconds,
    div,
    unsafeMinus,
    sleep,
  )
where

import Control.Concurrent (threadDelay)
import Data.Fixed (Fixed (..))
import TimerWheel.Internal.Prelude hiding (div)
import qualified Prelude

-- Some positive number of nanoseconds
newtype Nanoseconds = Nanoseconds {Nanoseconds -> Word64
unNanoseconds :: Word64}
  deriving stock (Nanoseconds -> Nanoseconds -> Bool
(Nanoseconds -> Nanoseconds -> Bool)
-> (Nanoseconds -> Nanoseconds -> Bool) -> Eq Nanoseconds
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Nanoseconds -> Nanoseconds -> Bool
== :: Nanoseconds -> Nanoseconds -> Bool
$c/= :: Nanoseconds -> Nanoseconds -> Bool
/= :: Nanoseconds -> Nanoseconds -> Bool
Eq, Eq Nanoseconds
Eq Nanoseconds =>
(Nanoseconds -> Nanoseconds -> Ordering)
-> (Nanoseconds -> Nanoseconds -> Bool)
-> (Nanoseconds -> Nanoseconds -> Bool)
-> (Nanoseconds -> Nanoseconds -> Bool)
-> (Nanoseconds -> Nanoseconds -> Bool)
-> (Nanoseconds -> Nanoseconds -> Nanoseconds)
-> (Nanoseconds -> Nanoseconds -> Nanoseconds)
-> Ord Nanoseconds
Nanoseconds -> Nanoseconds -> Bool
Nanoseconds -> Nanoseconds -> Ordering
Nanoseconds -> Nanoseconds -> Nanoseconds
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
$ccompare :: Nanoseconds -> Nanoseconds -> Ordering
compare :: Nanoseconds -> Nanoseconds -> Ordering
$c< :: Nanoseconds -> Nanoseconds -> Bool
< :: Nanoseconds -> Nanoseconds -> Bool
$c<= :: Nanoseconds -> Nanoseconds -> Bool
<= :: Nanoseconds -> Nanoseconds -> Bool
$c> :: Nanoseconds -> Nanoseconds -> Bool
> :: Nanoseconds -> Nanoseconds -> Bool
$c>= :: Nanoseconds -> Nanoseconds -> Bool
>= :: Nanoseconds -> Nanoseconds -> Bool
$cmax :: Nanoseconds -> Nanoseconds -> Nanoseconds
max :: Nanoseconds -> Nanoseconds -> Nanoseconds
$cmin :: Nanoseconds -> Nanoseconds -> Nanoseconds
min :: Nanoseconds -> Nanoseconds -> Nanoseconds
Ord)

-- | Convert a number of seconds into a number of nanoseconds.
--
-- Negative values are converted to 0.
fromSeconds :: Seconds -> Nanoseconds
fromSeconds :: Seconds -> Nanoseconds
fromSeconds =
  Seconds -> Nanoseconds
fromNonNegativeSeconds (Seconds -> Nanoseconds)
-> (Seconds -> Seconds) -> Seconds -> Nanoseconds
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Seconds -> Seconds -> Seconds
forall a. Ord a => a -> a -> a
max Seconds
0

-- | Like 'fromNonNegativeSeconds', but with an unchecked precondition: the given seconds is non-negative.
--
-- What you get for your troubles: one puny fewer int comparisons.
fromNonNegativeSeconds :: Seconds -> Nanoseconds
fromNonNegativeSeconds :: Seconds -> Nanoseconds
fromNonNegativeSeconds Seconds
seconds =
  Word64 -> Nanoseconds
Nanoseconds (forall a b. Coercible a b => a -> b
forall a b. Coercible a b => a -> b
coerce @(Integer -> Word64) Integer -> Word64
forall a b. (Integral a, Num b) => a -> b
fromIntegral Seconds
seconds)

div :: Nanoseconds -> Nanoseconds -> Nanoseconds
div :: Nanoseconds -> Nanoseconds -> Nanoseconds
div =
  (Word64 -> Word64 -> Word64)
-> Nanoseconds -> Nanoseconds -> Nanoseconds
forall a b. Coercible a b => a -> b
coerce (forall a. Integral a => a -> a -> a
Prelude.div @Word64)

-- `unsafeMinus n m` subtracts `m` from `n`, but does something wild if `m` is bigger than `n`
unsafeMinus :: Nanoseconds -> Nanoseconds -> Nanoseconds
unsafeMinus :: Nanoseconds -> Nanoseconds -> Nanoseconds
unsafeMinus =
  (Word64 -> Word64 -> Word64)
-> Nanoseconds -> Nanoseconds -> Nanoseconds
forall a b. Coercible a b => a -> b
coerce ((-) @Word64)

sleep :: Nanoseconds -> IO ()
sleep :: Nanoseconds -> IO ()
sleep (Nanoseconds Word64
nanos) =
  Int -> IO ()
threadDelay (Word64 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Word64
nanos Word64 -> Word64 -> Word64
forall a. Integral a => a -> a -> a
`Prelude.div` Word64
1000))