packages feed

bv-little-1.3.0: test/Data/BitVector/Visual.hs

{-|

Copyright   : © 2020 Alex Washburn
License     : BSD-3-Clause
Maintainer  : github@recursion.ninja
Stability   : Stable

-}

{-# Language DeriveDataTypeable #-}
{-# Language DeriveGeneric #-}
{-# Language DerivingStrategies #-}
{-# Language FlexibleInstances #-}
{-# Language GeneralizedNewtypeDeriving #-}
{-# Language MultiParamTypeClasses #-}

module Data.BitVector.Visual
    ( HasBitVector (..)
    , VisualBitVector ()
    , VisualBitVectorSmall ()
    ) where

import Control.DeepSeq
import Data.BitVector.LittleEndian
import Data.Bits
import Data.Data
import Data.Foldable (fold)
import Data.Monoid ()
import GHC.Generics
import GHC.Natural
import Test.QuickCheck hiding (generate)
import Test.SmallCheck.Series


{-|
New-typed 'BitVector' with special instances of:

  * 'Bounded' constraining the *length ≤ 8.*
  * 'Show' providing a more detailed rendering suitable for use with testing frameworks which display counterexamples.
-}
newtype VisualBitVector
    = VBV BitVector
    deriving newtype (Eq, NFData, Ord)
    deriving stock (Data, Generic)


{-|
New-typed 'BitVector' with special instances of:

  * 'Bounded' constraining the *length ≤ 3.*
  * 'Show' providing a more detailed rendering suitable for use with testing frameworks which display counterexamples.
-}
newtype VisualBitVectorSmall
    = VBVS BitVector
    deriving newtype (Eq, NFData, Ord)
    deriving stock (Data, Generic)


{-|
Type-class for accessing 'BitVector' from structures.
Intended to facilitate generic testing code operating on either 'BitVector', 'VisualBitVector', or 'VisualBitVectorSmall' types.
-}
class HasBitVector a where

    getBitVector :: a -> BitVector


instance HasBitVector BitVector where

    getBitVector = id


instance HasBitVector VisualBitVector where

    getBitVector (VBV bv) = bv


instance HasBitVector VisualBitVectorSmall where

    getBitVector (VBVS bv) = bv


instance CoArbitrary VisualBitVector where

    coarbitrary = coarbitraryEnum


instance CoArbitrary VisualBitVectorSmall where

    coarbitrary = coarbitraryEnum


{-
instance Monad m => CoSerial m VisualBitVector where

    coseries x = genericCoseries x
-}


instance Bounded VisualBitVector where

    minBound = VBV $ fromNumber 0 0

    maxBound = VBV $ fromNumber 8 (bit 8 - 1 :: Natural)


instance Bounded VisualBitVectorSmall where

    minBound = VBVS $ fromNumber 0 0

    maxBound = VBVS $ fromNumber 3 (bit 3 - 1 :: Natural)


instance Enum VisualBitVector where

    toEnum n = go $ n `mod` (bit 9 - 1)
        where
            go :: (Integral v, FiniteBits v) => v -> VisualBitVector
            go i = VBV $ fromNumber (toEnum dim) num where (num, _, dim) = getEnumContext i

    fromEnum (VBV bv) = case dim of
        0 -> 0
        n -> num + 2 ^ n - 1
        where
            num = toUnsignedNumber bv
            dim = dimension bv


instance Enum VisualBitVectorSmall where

    toEnum n = go $ n `mod` (bit 4 - 1)
        where
            go :: (Integral v, FiniteBits v) => v -> VisualBitVectorSmall
            go i = VBVS $ fromNumber (toEnum dim) num where (num, _, dim) = getEnumContext i

    fromEnum (VBVS bv) = case dim of
        0 -> 0
        n -> num + 2 ^ n - 1
        where
            num = toUnsignedNumber bv
            dim = dimension bv


instance Monad m => Serial m VisualBitVector where

    series = generate $ const allVBVs
        where allVBVs = toEnum <$> [0 .. fromEnum (maxBound :: VisualBitVector)]


instance Monad m => Serial m VisualBitVectorSmall where

    series = generate $ const allVBVs
        where allVBVs = toEnum <$> [0 .. fromEnum (maxBound :: VisualBitVectorSmall)]


instance Show VisualBitVector where

    show (VBV bv) =
        fold ["[", show $ dimension bv, "]", "<", foldMap (\b -> if b then "1" else "0") $ toBits bv, ">"]


instance Show VisualBitVectorSmall where

    show (VBVS bv) =
        fold ["[", show $ dimension bv, "]", "<", foldMap (\b -> if b then "1" else "0") $ toBits bv, ">"]


getEnumContext :: (FiniteBits b, Num b) => b -> (b, b, Int)
getEnumContext i = (num, off, dim)
    where
        num = i - off
        off = bit dim - 1
        dim = logBase2 $ i + 1
        logBase2 :: FiniteBits b => b -> Int
        logBase2 x = finiteBitSize x - 1 - countLeadingZeros x