-- | 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 forall b c a. (b -> c) -> (a -> b) -> a -> c
. Natural -> Owner
Owner forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (m :: * -> *) a. (MonadGen m, Integral a) => Range a -> m a
Gen.integral (forall a. Integral a => a -> a -> Range a
Range.linear Natural
0 Natural
100)

vkgenesisGen :: Gen VKeyGenesis
vkgenesisGen :: Gen VKeyGenesis
vkgenesisGen = VKey -> VKeyGenesis
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 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 forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (m :: * -> *). MonadGen m => Range Word64 -> m Word64
Gen.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 forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (m :: * -> *). MonadGen m => Range Word64 -> m Word64
Gen.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 forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (m :: * -> *). MonadGen m => Range Word64 -> m Word64
Gen.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 forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Gen Word64
numberOfEpochsGen
  where
    numberOfEpochsGen :: Gen Word64
    numberOfEpochsGen :: Gen Word64
numberOfEpochsGen =
      forall (m :: * -> *) a. MonadGen m => [(Int, m a)] -> m a
Gen.frequency
        [ (Int
9, forall (m :: * -> *) a. (MonadGen m, Integral a) => Range a -> m a
Gen.integral forall a b. (a -> b) -> a -> b
$ forall a. Integral a => a -> a -> Range a
Range.linear Word64
1 (Word64
maxNumberOfEpochs forall a. Ord a => a -> a -> a
`max` Word64
1))
        , (Int
1, 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 =
  forall a. Ord a => a -> a -> a
max BlockCount
1 (forall n. Integral n => n -> BlockCount
slotsPerEpochToK Word64
slotsPerEpoch)
  where
    slotsPerEpoch :: Word64
    slotsPerEpoch :: Word64
slotsPerEpoch = forall a b. (RealFrac a, Integral b) => a -> b
round forall a b. (a -> b) -> a -> b
$ forall a b. (Integral a, Num b) => a -> b
fromIntegral Word64
chainLength forall a. Fractional a => a -> a -> a
/ (forall a b. (Integral a, Num b) => a -> b
fromIntegral Word64
numberOfEpochs :: Double)