-- | Generators for the 'Ledger.Core' values.
module Byron.Spec.Ledger.Core.Generators
  ( vkGen
  , vkgenesisGen
  , addrGen
  , slotGen
  , epochGen
  , blockCountGen
  , k
  , kForNumberOfEpochs
  )
where

import           Data.Word (Word64)
import           Hedgehog (Gen)
import qualified Hedgehog.Gen as Gen
import qualified Hedgehog.Range as Range

import           Byron.Spec.Ledger.Core (Addr (Addr), BlockCount (BlockCount), Epoch (Epoch), Owner (Owner),
                     Slot (Slot), VKey (VKey), VKeyGenesis (VKeyGenesis))
import           Byron.Spec.Ledger.GlobalParams (slotsPerEpochToK)


vkGen :: Gen VKey
vkGen :: Gen VKey
vkGen = Owner -> VKey
VKey (Owner -> VKey) -> (Natural -> Owner) -> Natural -> VKey
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Natural -> Owner
Owner (Natural -> VKey) -> GenT Identity Natural -> Gen VKey
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Range Natural -> GenT Identity Natural
forall (m :: * -> *) a. (MonadGen m, Integral a) => Range a -> m a
Gen.integral (Natural -> Natural -> Range Natural
forall a. Integral a => a -> a -> Range a
Range.linear Natural
0 Natural
100)

vkgenesisGen :: Gen VKeyGenesis
vkgenesisGen :: Gen VKeyGenesis
vkgenesisGen = VKey -> VKeyGenesis
VKeyGenesis (VKey -> VKeyGenesis) -> Gen VKey -> Gen VKeyGenesis
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Gen VKey
vkGen

addrGen :: Gen Addr
addrGen :: Gen Addr
addrGen = VKey -> Addr
Addr (VKey -> Addr) -> Gen VKey -> Gen Addr
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Gen VKey
vkGen

-- | Generates a slot within the given bound
slotGen :: Word64 -> Word64 -> Gen Slot
slotGen :: Word64 -> Word64 -> Gen Slot
slotGen Word64
lower Word64
upper =
  Word64 -> Slot
Slot (Word64 -> Slot) -> GenT Identity Word64 -> Gen Slot
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Range Word64 -> GenT Identity Word64
forall (m :: * -> *). MonadGen m => Range Word64 -> m Word64
Gen.word64 (Word64 -> Word64 -> Range Word64
forall a. Integral a => a -> a -> Range a
Range.linear Word64
lower Word64
upper)

-- | Generates an epoch within the given bound
epochGen :: Word64 -> Word64 -> Gen Epoch
epochGen :: Word64 -> Word64 -> Gen Epoch
epochGen Word64
lower Word64
upper =
  Word64 -> Epoch
Epoch (Word64 -> Epoch) -> GenT Identity Word64 -> Gen Epoch
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Range Word64 -> GenT Identity Word64
forall (m :: * -> *). MonadGen m => Range Word64 -> m Word64
Gen.word64 (Word64 -> Word64 -> Range Word64
forall a. Integral a => a -> a -> Range a
Range.linear Word64
lower Word64
upper)

-- | Generates a block count within the given bound
blockCountGen :: Word64 -> Word64 -> Gen BlockCount
blockCountGen :: Word64 -> Word64 -> Gen BlockCount
blockCountGen Word64
lower Word64
upper =
  Word64 -> BlockCount
BlockCount (Word64 -> BlockCount) -> GenT Identity Word64 -> Gen BlockCount
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Range Word64 -> GenT Identity Word64
forall (m :: * -> *). MonadGen m => Range Word64 -> m Word64
Gen.word64 (Word64 -> Word64 -> Range Word64
forall a. Integral a => a -> a -> Range a
Range.linear Word64
lower Word64
upper)

-- | Generate a chain stability parameter value (@k@) using the given chain length and desired
-- number of epochs.
--
k
  :: Word64
  -- ^ Chain length
  -> Word64
  -- ^ Maximum number of epochs
  -> Gen BlockCount
k :: Word64 -> Word64 -> Gen BlockCount
k Word64
chainLength Word64
maxNumberOfEpochs =
  Word64 -> Word64 -> BlockCount
kForNumberOfEpochs Word64
chainLength (Word64 -> BlockCount) -> GenT Identity Word64 -> Gen BlockCount
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> GenT Identity Word64
numberOfEpochsGen
    where
      numberOfEpochsGen :: Gen Word64
      numberOfEpochsGen :: GenT Identity Word64
numberOfEpochsGen =
         [(Int, GenT Identity Word64)] -> GenT Identity Word64
forall (m :: * -> *) a. MonadGen m => [(Int, m a)] -> m a
Gen.frequency [ (Int
9, Range Word64 -> GenT Identity Word64
forall (m :: * -> *) a. (MonadGen m, Integral a) => Range a -> m a
Gen.integral (Range Word64 -> GenT Identity Word64)
-> Range Word64 -> GenT Identity Word64
forall a b. (a -> b) -> a -> b
$ Word64 -> Word64 -> Range Word64
forall a. Integral a => a -> a -> Range a
Range.linear Word64
1 (Word64
maxNumberOfEpochs Word64 -> Word64 -> Word64
forall a. Ord a => a -> a -> a
`max` Word64
1))
                       , (Int
1, Word64 -> GenT Identity Word64
forall (f :: * -> *) a. Applicative f => a -> f a
pure Word64
1)
                       ]

-- | Given a chain length, determine the @k@ value that will split the chain length into the desired
-- number of epochs.
--
-- We have that:
--
-- > chainLength = slotsPerEpoch k * numberOfEpochs
-- > = { algebra }
-- > chainLength / numberOfEpochs = slotsPerEpoch k
-- > = { 'slotsPerEpochtoK' is the inverse of 'slotsPerEpoch'; algebra }
-- > slotsPerEpochToK (chainLength / numberOfEpochs) = k
--
-- So the resulting @k@ value will be directly proportional to the @chainLength@ and inversely
-- proportional to the chosen @numberOfEpochs@.
--
-- The minimum value for @k@ will be 1. In particular, this will be the value
-- returned when the number of epochs is greater or equal than @chainLength@.
kForNumberOfEpochs
  :: Word64
  -- ^ Chain length
  -> Word64
  -- ^ Desired number of epochs
  -> BlockCount
kForNumberOfEpochs :: Word64 -> Word64 -> BlockCount
kForNumberOfEpochs Word64
chainLength Word64
numberOfEpochs =
  BlockCount -> BlockCount -> BlockCount
forall a. Ord a => a -> a -> a
max BlockCount
1 (Word64 -> BlockCount
forall n. Integral n => n -> BlockCount
slotsPerEpochToK Word64
slotsPerEpoch)
  where
    slotsPerEpoch :: Word64
    slotsPerEpoch :: Word64
slotsPerEpoch = Double -> Word64
forall a b. (RealFrac a, Integral b) => a -> b
round (Double -> Word64) -> Double -> Word64
forall a b. (a -> b) -> a -> b
$ Word64 -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word64
chainLength Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ (Word64 -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word64
numberOfEpochs :: Double)