{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE UndecidableInstances #-}

module Cardano.Chain.Slotting.SlotNumber
  ( SlotNumber (..),
    addSlotCount,
    -- deprecated
    subSlotCount,
  )
where

import Cardano.Binary (FromCBOR (..), ToCBOR (..))
import Cardano.Chain.Slotting.SlotCount (SlotCount (..))
import Cardano.Prelude
import qualified Data.Aeson as Aeson
import Formatting (bprint, int)
import qualified Formatting.Buildable as B
import NoThunks.Class (NoThunks (..))
import Text.JSON.Canonical (FromJSON (..), ToJSON (..))

-- | 'SlotNumber' is an absolute slot number from the beginning of time
--
--   'SlotNumber' is held in a 'Word64'. Assuming a slot every 20 seconds, 'Word64'
--   is sufficient for slot indices for 10^13 years.
newtype SlotNumber = SlotNumber
  { SlotNumber -> Word64
unSlotNumber :: Word64
  }
  deriving (SlotNumber -> SlotNumber -> Bool
(SlotNumber -> SlotNumber -> Bool)
-> (SlotNumber -> SlotNumber -> Bool) -> Eq SlotNumber
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: SlotNumber -> SlotNumber -> Bool
$c/= :: SlotNumber -> SlotNumber -> Bool
== :: SlotNumber -> SlotNumber -> Bool
$c== :: SlotNumber -> SlotNumber -> Bool
Eq, (forall x. SlotNumber -> Rep SlotNumber x)
-> (forall x. Rep SlotNumber x -> SlotNumber) -> Generic SlotNumber
forall x. Rep SlotNumber x -> SlotNumber
forall x. SlotNumber -> Rep SlotNumber x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep SlotNumber x -> SlotNumber
$cfrom :: forall x. SlotNumber -> Rep SlotNumber x
Generic, Eq SlotNumber
Eq SlotNumber
-> (SlotNumber -> SlotNumber -> Ordering)
-> (SlotNumber -> SlotNumber -> Bool)
-> (SlotNumber -> SlotNumber -> Bool)
-> (SlotNumber -> SlotNumber -> Bool)
-> (SlotNumber -> SlotNumber -> Bool)
-> (SlotNumber -> SlotNumber -> SlotNumber)
-> (SlotNumber -> SlotNumber -> SlotNumber)
-> Ord SlotNumber
SlotNumber -> SlotNumber -> Bool
SlotNumber -> SlotNumber -> Ordering
SlotNumber -> SlotNumber -> SlotNumber
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 :: SlotNumber -> SlotNumber -> SlotNumber
$cmin :: SlotNumber -> SlotNumber -> SlotNumber
max :: SlotNumber -> SlotNumber -> SlotNumber
$cmax :: SlotNumber -> SlotNumber -> SlotNumber
>= :: SlotNumber -> SlotNumber -> Bool
$c>= :: SlotNumber -> SlotNumber -> Bool
> :: SlotNumber -> SlotNumber -> Bool
$c> :: SlotNumber -> SlotNumber -> Bool
<= :: SlotNumber -> SlotNumber -> Bool
$c<= :: SlotNumber -> SlotNumber -> Bool
< :: SlotNumber -> SlotNumber -> Bool
$c< :: SlotNumber -> SlotNumber -> Bool
compare :: SlotNumber -> SlotNumber -> Ordering
$ccompare :: SlotNumber -> SlotNumber -> Ordering
$cp1Ord :: Eq SlotNumber
Ord, Int -> SlotNumber -> ShowS
[SlotNumber] -> ShowS
SlotNumber -> String
(Int -> SlotNumber -> ShowS)
-> (SlotNumber -> String)
-> ([SlotNumber] -> ShowS)
-> Show SlotNumber
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [SlotNumber] -> ShowS
$cshowList :: [SlotNumber] -> ShowS
show :: SlotNumber -> String
$cshow :: SlotNumber -> String
showsPrec :: Int -> SlotNumber -> ShowS
$cshowsPrec :: Int -> SlotNumber -> ShowS
Show)
  deriving newtype (Integer -> SlotNumber
SlotNumber -> SlotNumber
SlotNumber -> SlotNumber -> SlotNumber
(SlotNumber -> SlotNumber -> SlotNumber)
-> (SlotNumber -> SlotNumber -> SlotNumber)
-> (SlotNumber -> SlotNumber -> SlotNumber)
-> (SlotNumber -> SlotNumber)
-> (SlotNumber -> SlotNumber)
-> (SlotNumber -> SlotNumber)
-> (Integer -> SlotNumber)
-> Num SlotNumber
forall a.
(a -> a -> a)
-> (a -> a -> a)
-> (a -> a -> a)
-> (a -> a)
-> (a -> a)
-> (a -> a)
-> (Integer -> a)
-> Num a
fromInteger :: Integer -> SlotNumber
$cfromInteger :: Integer -> SlotNumber
signum :: SlotNumber -> SlotNumber
$csignum :: SlotNumber -> SlotNumber
abs :: SlotNumber -> SlotNumber
$cabs :: SlotNumber -> SlotNumber
negate :: SlotNumber -> SlotNumber
$cnegate :: SlotNumber -> SlotNumber
* :: SlotNumber -> SlotNumber -> SlotNumber
$c* :: SlotNumber -> SlotNumber -> SlotNumber
- :: SlotNumber -> SlotNumber -> SlotNumber
$c- :: SlotNumber -> SlotNumber -> SlotNumber
+ :: SlotNumber -> SlotNumber -> SlotNumber
$c+ :: SlotNumber -> SlotNumber -> SlotNumber
Num)
  deriving anyclass (SlotNumber -> ()
(SlotNumber -> ()) -> NFData SlotNumber
forall a. (a -> ()) -> NFData a
rnf :: SlotNumber -> ()
$crnf :: SlotNumber -> ()
NFData, Context -> SlotNumber -> IO (Maybe ThunkInfo)
Proxy SlotNumber -> String
(Context -> SlotNumber -> IO (Maybe ThunkInfo))
-> (Context -> SlotNumber -> IO (Maybe ThunkInfo))
-> (Proxy SlotNumber -> String)
-> NoThunks SlotNumber
forall a.
(Context -> a -> IO (Maybe ThunkInfo))
-> (Context -> a -> IO (Maybe ThunkInfo))
-> (Proxy a -> String)
-> NoThunks a
showTypeOf :: Proxy SlotNumber -> String
$cshowTypeOf :: Proxy SlotNumber -> String
wNoThunks :: Context -> SlotNumber -> IO (Maybe ThunkInfo)
$cwNoThunks :: Context -> SlotNumber -> IO (Maybe ThunkInfo)
noThunks :: Context -> SlotNumber -> IO (Maybe ThunkInfo)
$cnoThunks :: Context -> SlotNumber -> IO (Maybe ThunkInfo)
NoThunks)

-- Used for debugging purposes only
instance Aeson.ToJSON SlotNumber

instance ToCBOR SlotNumber where
  toCBOR :: SlotNumber -> Encoding
toCBOR = Word64 -> Encoding
forall a. ToCBOR a => a -> Encoding
toCBOR (Word64 -> Encoding)
-> (SlotNumber -> Word64) -> SlotNumber -> Encoding
forall k (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. SlotNumber -> Word64
unSlotNumber
  encodedSizeExpr :: (forall t. ToCBOR t => Proxy t -> Size) -> Proxy SlotNumber -> Size
encodedSizeExpr forall t. ToCBOR t => Proxy t -> Size
size = (forall t. ToCBOR t => Proxy t -> Size) -> Proxy Word64 -> Size
forall a.
ToCBOR a =>
(forall t. ToCBOR t => Proxy t -> Size) -> Proxy a -> Size
encodedSizeExpr forall t. ToCBOR t => Proxy t -> Size
size (Proxy Word64 -> Size)
-> (Proxy SlotNumber -> Proxy Word64) -> Proxy SlotNumber -> Size
forall k (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. (SlotNumber -> Word64) -> Proxy SlotNumber -> Proxy Word64
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap SlotNumber -> Word64
unSlotNumber

instance FromCBOR SlotNumber where
  fromCBOR :: Decoder s SlotNumber
fromCBOR = Word64 -> SlotNumber
SlotNumber (Word64 -> SlotNumber) -> Decoder s Word64 -> Decoder s SlotNumber
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Decoder s Word64
forall a s. FromCBOR a => Decoder s a
fromCBOR

instance Monad m => ToJSON m SlotNumber where
  toJSON :: SlotNumber -> m JSValue
toJSON = Word64 -> m JSValue
forall (m :: * -> *) a. ToJSON m a => a -> m JSValue
toJSON (Word64 -> m JSValue)
-> (SlotNumber -> Word64) -> SlotNumber -> m JSValue
forall k (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. SlotNumber -> Word64
unSlotNumber

instance MonadError SchemaError m => FromJSON m SlotNumber where
  fromJSON :: JSValue -> m SlotNumber
fromJSON JSValue
val = do
    Word64
number <- JSValue -> m Word64
forall (m :: * -> *) a. FromJSON m a => JSValue -> m a
fromJSON JSValue
val
    SlotNumber -> m SlotNumber
forall (f :: * -> *) a. Applicative f => a -> f a
pure (SlotNumber -> m SlotNumber) -> SlotNumber -> m SlotNumber
forall a b. (a -> b) -> a -> b
$ Word64 -> SlotNumber
SlotNumber Word64
number

instance B.Buildable SlotNumber where
  build :: SlotNumber -> Builder
build SlotNumber
s = Format Builder (Word64 -> Builder) -> Word64 -> Builder
forall a. Format Builder a -> a
bprint Format Builder (Word64 -> Builder)
forall a r. Integral a => Format r (a -> r)
int (SlotNumber -> Word64
unSlotNumber SlotNumber
s)

-- | Increase a 'SlotNumber' by 'SlotCount'
addSlotCount :: SlotCount -> SlotNumber -> SlotNumber
addSlotCount :: SlotCount -> SlotNumber -> SlotNumber
addSlotCount (SlotCount Word64
a) (SlotNumber Word64
b)
  | Word64
a Word64 -> Word64 -> Bool
forall a. Ord a => a -> a -> Bool
<= Word64
forall a. Bounded a => a
maxBound Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
- Word64
b = Word64 -> SlotNumber
SlotNumber (Word64 -> SlotNumber) -> Word64 -> SlotNumber
forall a b. (a -> b) -> a -> b
$ Word64
a Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
+ Word64
b
  | Bool
otherwise = Word64 -> SlotNumber
SlotNumber Word64
forall a. Bounded a => a
maxBound

-- | Decrease a 'SlotNumber' by 'SlotCount', going no lower than 0
{-# DEPRECATED subSlotCount "this function is dangerous and can usually be replaced by addSlotCount" #-}
subSlotCount :: SlotCount -> SlotNumber -> SlotNumber
subSlotCount :: SlotCount -> SlotNumber -> SlotNumber
subSlotCount (SlotCount Word64
a) (SlotNumber Word64
b) =
  if Word64
a Word64 -> Word64 -> Bool
forall a. Ord a => a -> a -> Bool
> Word64
b then Word64 -> SlotNumber
SlotNumber Word64
0 else Word64 -> SlotNumber
SlotNumber (Word64
b Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
- Word64
a)