packages feed

data-debruijn-0.1.0.0: src-arbitrary/Test/QuickCheck/Extra.hs

{-# OPTIONS_GHC -Wno-incomplete-patterns #-}

module Test.QuickCheck.Extra (
  chooseSizedBoundedIntegral,
  chooseSizesBoundedPositiveIntegral,
) where

import Control.Exception (assert)
import Data.Bits (Bits (..))
import Test.QuickCheck.Gen (Gen, chooseInteger, sized)

chooseSizedBoundedIntegral :: (Integral a) => (a, a) -> Gen a
chooseSizedBoundedIntegral (mn, mx) =
  assert (mn >= 0 && mx >= mn) $
    let ilog2 1 = 0
        ilog2 n | n > 0 = 1 + ilog2 (n `div` 2)

        -- How many bits are needed to represent this type?
        -- (This number is an upper bound, not exact.)
        bits = ilog2 (toInteger mx - toInteger mn + 1)
    in  sized $ \k ->
          let
            -- Reach maximum size by k=80, or quicker for small integer types
            power = ((bits `max` 40) * k) `div` 80

            -- Bounds should be 2^power, but:
            --   * clamp the result to minBound/maxBound
            --   * clamp power to 'bits', in case k is a huge number
            lo = toInteger mn `max` (-1 `shiftL` (power `min` bits))
            hi = toInteger mx `min` (1 `shiftL` (power `min` bits))
          in
            fmap fromInteger (chooseInteger (lo, hi))
{-# INLINEABLE chooseSizedBoundedIntegral #-}
{-# ANN chooseSizedBoundedIntegral ("HLint: ignore Avoid partial function" :: String) #-}
{-# ANN chooseSizedBoundedIntegral ("HLint: ignore Parenthesize unary negation" :: String) #-}

chooseSizesBoundedPositiveIntegral :: (Integral a) => a -> Gen a
chooseSizesBoundedPositiveIntegral n = chooseSizedBoundedIntegral (1, n)