packages feed

data-debruijn-0.1.0.0: src-arbitrary/Data/DeBruijn/Thinning/Arbitrary.hs

{-# LANGUAGE CPP #-}
{-# LANGUAGE DerivingStrategies #-}

module Data.DeBruijn.Thinning.Arbitrary (
  SomeThRep (..),
  SomeThBoundRep (..),
  getSomeThBoundRep,
) where

import Control.Exception (assert)
import Data.Bits (Bits (..))
import Data.DeBruijn.Thinning.Fast (ThRep, bitsToThRep, thRepToBits)
import Data.Type.Nat.Singleton.Fast (SNatRep, intToSNatRep)
import Test.QuickCheck.Arbitrary (Arbitrary (..))
import Test.QuickCheck.Extra (chooseSizedBoundedIntegral, chooseSizesBoundedPositiveIntegral)
import Test.QuickCheck.Gen (Gen, chooseInteger)
import Test.QuickCheck.Modifiers (NonNegative (..), Positive (..))
import Text.Printf (printf)

data SomeThRep = SomeThRep !SNatRep !ThRep
  deriving stock (Eq)

instance Show SomeThRep where
  showsPrec :: Int -> SomeThRep -> ShowS
  showsPrec p (SomeThRep nRep nmRep) =
    let mRep = nRep + intToSNatRep (popCount nmRep)
    in  showParen (p > 10) . showString $
          printf ("SomeThRep %d 0b%0" <> show mRep <> "b") nRep (thRepToBits @Integer nmRep)

instance Arbitrary SomeThRep where
  arbitrary :: Gen SomeThRep
  arbitrary = do
    mRep <- maybe (getPositive <$> arbitrary) chooseSizesBoundedPositiveIntegral (bitSizeMaybe (undefined :: ThRep))
    nmRep <- bitsToThRep <$> chooseInteger (0, 2 ^ mRep - 1)
    let nRep = mRep - intToSNatRep (popCount nmRep)
    assert (nRep >= 0) $ pure ()
    pure $ SomeThRep nRep nmRep

--------------------------------------------------------------------------------
-- QuickCheck instances for SomeThBoundRep

newtype SomeThBoundRep = SomeThBoundRep SNatRep
  deriving stock (Eq, Show)

getSomeThBoundRep :: SomeThBoundRep -> SNatRep
getSomeThBoundRep (SomeThBoundRep nRep) = nRep

instance Arbitrary SomeThBoundRep where
  arbitrary :: Gen SomeThBoundRep
  arbitrary = case bitSizeMaybe (undefined :: ThRep) of
    Nothing -> SomeThBoundRep . getNonNegative <$> arbitrary
    Just thSize -> SomeThBoundRep <$> chooseSizedBoundedIntegral (0, thSize)

  shrink :: SomeThBoundRep -> [SomeThBoundRep]
  shrink (SomeThBoundRep nRep) = SomeThBoundRep <$> shrink nRep