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

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)
import Data.Word (Word64)
import Hedgehog (Gen)
import qualified Hedgehog.Gen as Gen
import qualified Hedgehog.Range as Range

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 ::
  -- | Chain length
  Word64 ->
  -- | Maximum number of epochs
  Word64 ->
  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 a. a -> GenT Identity a
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 ::
  -- | Chain length
  Word64 ->
  -- | Desired number of epochs
  Word64 ->
  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 b. Integral b => Double -> b
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)