content: {-# LANGUAGE CPP                  #-}
  {-# LANGUAGE ScopedTypeVariables  #-}
  {-# LANGUAGE TypeFamilies         #-}
  {-# LANGUAGE FlexibleContexts     #-}
  {-# LANGUAGE DefaultSignatures    #-}
  {-# OPTIONS_GHC -fno-warn-orphans #-}
  
  module Tests.Properties (
      testTree
  
      -- * Token type class and derived functions
    , Token(..)
    , serialiseRef
    , serialiseImp
    , deserialiseRef
    , deserialiseImp
  
      -- * Various test token types
    , TokInt
    , TokInt8
    , TokInt16
    , TokInt32
    , TokInt64
    , TokInteger
    , TokWord
    , TokWord8
    , TokWord16
    , TokWord32
    , TokWord64
    , TokHalf
    , TokFloat
    , TokDouble
    , TokTag
    , TokTag64
    , Ref.Simple
    , Ref.Term
    ) where
  
  import           Prelude hiding (decodeFloat, encodeFloat)
  
  import qualified Data.ByteString as BS
  import qualified Data.ByteString.Lazy as LBS
  import           Data.Word
  import           Data.Int
  import           Data.Bits (complement)
  import qualified Numeric.Half as Half
  import           Data.Function (on)
  import           Data.Proxy
  import           Data.Kind (Type)
  
  import GHC.Exts
  import           Codec.CBOR.ByteArray
  import qualified Codec.CBOR.ByteArray.Sliced as Sliced
  import           Codec.CBOR.Term
  import           Codec.CBOR.Read
  import           Codec.CBOR.Write
  import           Codec.CBOR.Decoding
  import           Codec.CBOR.Encoding
  
  import           Test.Tasty (TestTree, testGroup, localOption)
  import           Test.Tasty.QuickCheck (testProperty, QuickCheckMaxSize(..))
  import           Test.QuickCheck
  import           Test.QuickCheck.Gen (Gen (MkGen))
  
  import qualified Tests.Reference.Implementation as Ref
  import           Tests.Reference.Implementation (UInt(..), lengthUInt)
  import           Tests.Reference.Generators
  import           Tests.Term
                     ( fromRefTerm, toRefTerm, eqTerm, canonicaliseTerm )
  import           Tests.Util
  
  
  #if MIN_VERSION_bytestring(0,11,1)
  import qualified Data.ByteString.Short as SBS
  #else
  import qualified Data.ByteString.Short.Internal as SBS
  #endif
  
  import qualified Data.Primitive.ByteArray as Prim (ByteArray (..))
  import System.Random.Stateful hiding (genByteString, genShortByteString)
  
  -- | The CBOR implementation and its reference implementation satisfy all the
  -- properties implied in the following commuting diagram.
  --
  -- The properties in this module exercise various paths throguh this diagram,
  -- and do so for various different types.
  --
  -- >        canon          id
  -- >  Ref──────────▶Ref───────────▶Ref
  -- >    │            ▲ ╲  (ref)  ╱ │
  -- >    │            │  ╲enc dec╱  │
  -- >    │            │   ╲     ╱   │
  -- >    │from      to│    ▶Enc▶    │from
  -- >    │            │   ╱     ╲   │
  -- >    │            │  ╱enc dec╲  │
  -- >    ▼            │ ╱  (imp)  ╲ ▼
  -- >  Imp──────────▶Imp───────────▶Imp
  -- >         id           canon
  --
  -- Key
  --
  --  * Imp:  Implementation token type
  --  * Ref:  Reference token type
  --  * Enc:  Encoding (ie bytes)
  --  * canon: canonicaliseRef or canonicaliseImp
  --  * enc:   encodeRef       or encodeImp
  --  * dec:   decodeRef       or decodeImp
  --
  -- We capture these types and arrows with a type class and an associated type.
  --
  class (Eq t, Show t) => Token t where
    type Imp t :: Type
  
    encodeImp :: Proxy t -> Imp t -> Encoding
    encodeRef :: Ref.Encoder t
  
    decodeImp :: forall s. Proxy t -> Decoder s (Imp t)
    decodeRef :: Proxy t -> Ref.Decoder t
  
    canonicaliseImp :: Proxy t -> Imp t -> Imp t
    canonicaliseRef ::                t ->     t
  
    eqImp   :: Proxy t -> Imp t -> Imp t -> Bool
  
    toRef   :: Proxy t -> Imp t -> t
    fromRef ::            t -> Imp t
  
    -- defaults
    canonicaliseImp _ = id
    canonicaliseRef   = toRef t . fromRef
      where
        t  = Proxy :: Proxy t
  
    default eqImp :: Eq (Imp t) => Proxy t -> Imp t -> Imp t -> Bool
    eqImp _ = (==)
  
  
  -- A few derived utils
  
  serialiseRef :: forall t. Token t => t -> LBS.ByteString
  serialiseRef = LBS.pack . encodeRef
  
  serialiseImp :: forall t. Token t => Proxy t -> Imp t -> LBS.ByteString
  serialiseImp _ = toLazyByteString . encodeImp t
    where
      t  = Proxy :: Proxy t
  
  deserialiseRef :: forall t. Token t => Proxy t -> LBS.ByteString -> t
  deserialiseRef _ bytes =
    case Ref.runDecoder (decodeRef t) (LBS.unpack bytes) of
      Just (x, trailing)
        | null trailing -> x
        | otherwise     -> error "deserialiseRef: trailing bytes"
      Nothing           -> error "deserialiseRef: decode failure"
    where
      t  = Proxy :: Proxy t
  
  deserialiseImp :: forall t. Token t => Proxy t -> LBS.ByteString -> Imp t
  deserialiseImp _ bytes =
      case deserialiseFromBytes (decodeImp t) bytes of
        Right (trailing, x)
          | LBS.null trailing -> x
          | otherwise         -> error "deserialiseImp: trailing data"
        Left _failure         -> error "deserialiseImp: decode failure"
    where
      t  = Proxy :: Proxy t
  
  
  --------------------------------------------------------------------------------
  -- Properties
  --
  
  
  -- | The property corresponding to the following part of the commuting diagram.
  --
  -- >        canon
  -- >  Ref──────────▶Ref . . . . . ▷.
  -- >    │            ▲ .         . .
  -- >    │            │  .       .  .
  -- >    │            │   .     .   .
  -- >    │from      to│    ▷   ▷    .
  -- >    │            │   .     .   .
  -- >    │            │  .       .  .
  -- >    ▼            │ .         . ▽
  -- >  Imp──────────▶Imp . . . . . ▷.
  -- >         id
  --
  -- > to . id . from = canon_ref
  --
  prop_fromRefToRef :: Token t => Proxy t -> t -> Bool
  prop_fromRefToRef _ x =
  
      (toRef t . fromRef) x == canonicaliseRef x
  
    where
      t  = Proxy :: Proxy t
  
  
  -- | The property corresponding to the following part of the commuting diagram.
  --
  -- >                       id
  -- >    . . . . . .▷Ref───────────▶Ref
  -- >    .            ▲ .         . │
  -- >    .            │  .       .  │
  -- >    .            │   .     .   │
  -- >    .          to│    ▷   ▷    │from
  -- >    .            │   .     .   │
  -- >    .            │  .       .  │
  -- >    ▽            │ .         . ▼
  -- >    . . . . . .▶Imp───────────▶Imp
  -- >                      canon
  --
  -- > from . id . to = canon_imp
  --
  prop_toRefFromRef :: forall t. Token t => Proxy t -> Imp t -> Bool
  prop_toRefFromRef _ x =
  
      (fromRef . toRef t) x  `eq`  canonicaliseImp t x
  
    where
      eq = eqImp t
      t  = Proxy :: Proxy t
  
  
  -- | The property corresponding to the following part of the commuting diagram.
  --
  -- This is a round trip property, with the reference implementation of the
  -- encoder and decoder.
  --
  -- >                       id
  -- >    . . . . . .▷Ref───────────▶Ref
  -- >    .            △ ╲         ╱ .
  -- >    .            .  ╲enc dec╱  .
  -- >    .            .   ╲     ╱   .
  -- >    .            .    ▶Enc▶    .
  -- >    .            .   .     .   .
  -- >    .            .  .       .  .
  -- >    ▽            . .         . ▽
  -- >    . . . . . . ▷.. . . . . . ▷.
  --
  -- > dec_ref . enc_ref = id
  --
  prop_encodeRefdecodeRef :: forall t. Token t => Proxy t -> t -> Bool
  prop_encodeRefdecodeRef _ x =
  
      (deserialiseRef t . serialiseRef) x  ==  x
  
    where
      t  = Proxy :: Proxy t
  
  
  -- | The property corresponding to the following part of the commuting diagram.
  --
  -- This is a round trip property, with the production implementation of the
  -- encoder and decoder.
  --
  -- >    . . . . . . ▷. . . . . . .▷.
  -- >    .            △ .         . .
  -- >    .            .  .       .  .
  -- >    .            .   .     .   .
  -- >    .            .    ▶Enc▶    .
  -- >    .            .   ╱     ╲   .
  -- >    .            .  ╱enc dec╲  .
  -- >    ▽            . ╱         ╲ ▽
  -- >    . . . . . .▷Imp───────────▶Imp
  -- >                      canon
  --
  -- > dec_imp . enc_imp = canon_imp
  --
  prop_encodeImpdecodeImp :: forall t. Token t => Proxy t -> Imp t -> Bool
  prop_encodeImpdecodeImp _ x =
  
      (deserialiseImp t . serialiseImp t) x  `eq`  canonicaliseImp t x
  
    where
      eq = eqImp t
      t  = Proxy :: Proxy t
  
  
  -- | This is the same property as 'prop_encodeImpdecodeImp' but the encoded
  -- data is split into two chunks provided as input into the decoder. All
  -- possible 2-chunk splits are tried. This checks that the decoder gives the
  -- same result irrespective of the chunk boundaries.
  --
  prop_encodeImpdecodeImp_splits2 :: forall t. Token t => Proxy t -> Imp t -> Bool
  prop_encodeImpdecodeImp_splits2 _ x =
      and [ deserialiseImp t enc'  `eq`  x'
          | let enc = serialiseImp    t x
                x'  = canonicaliseImp t x
          , enc' <- splits2 enc ]
    where
      eq = eqImp t
      t  = Proxy :: Proxy t
  
  
  -- | This is the same idea as 'prop_encodeImpdecodeImp_splits2' but with all
  -- possible 3-chunk splits of the input data. This test is of course more
  -- expensive and so the size of the input must be limited.
  --
  prop_encodeImpdecodeImp_splits3 :: forall t. Token t => Proxy t -> Imp t -> Bool
  prop_encodeImpdecodeImp_splits3 _ x =
      and [ deserialiseImp t enc'  `eq`  x'
          | let enc = serialiseImp    t x
                x'  = canonicaliseImp t x
          , enc' <- splits3 enc ]
    where
      eq = eqImp t
      t  = Proxy :: Proxy t
  
  
  -- | The property corresponding to the following part of the commuting diagram.
  --
  -- This checks that the reference and real implementation produce the same
  -- encoded bytes. It starts from a value in the reference implementation.
  --
  -- >        canon
  -- >  Ref──────────▶Ref . . . . . ▷.
  -- >    │            △ ╲         . .
  -- >    │            .  ╲enc    .  .
  -- >    │            .   ╲     .   .
  -- >    │from        .    ▶Enc▷    .
  -- >    │            .   ╱     .   .
  -- >    │            .  ╱enc    .  .
  -- >    ▼            . ╱         . ▽
  -- >  Imp──────────▶Imp . . . . . ▷.
  -- >         id
  --
  -- > enc_imp . id . from = enc_ref . canon_ref
  --
  prop_encodeRefencodeImp1 :: forall t. Token t => Proxy t -> t -> Bool
  prop_encodeRefencodeImp1 _ x =
  
      (serialiseImp t . fromRef) x  ==  (serialiseRef . canonicaliseRef) x
  
    where
      t  = Proxy :: Proxy t
  
  
  -- | The property corresponding to the following part of the commuting diagram.
  --
  -- This checks that the reference and real implementation produce the same
  -- encoded bytes.  It starts from a value in the real implementation.
  --
  -- >    . . . . . .▷Ref . . . . . ▷.
  -- >    .            ▲ ╲         . .
  -- >    .            │  ╲enc    .  .
  -- >    .            │   ╲     .   .
  -- >    .          to│    ▶Enc▷    .
  -- >    .            │   ╱     .   .
  -- >    .            │  ╱enc    .  .
  -- >    ▽            │ ╱         . ▽
  -- >    . . . . . .▷Imp . . . . . ▷.
  --
  -- > enc_ref . id . to = enc_imp
  --
  prop_encodeRefencodeImp2 :: forall t. Token t => Proxy t -> Imp t -> Bool
  prop_encodeRefencodeImp2 _ x =
  
      (serialiseRef . toRef t) x == serialiseImp t x
  
    where
      t  = Proxy :: Proxy t
  
  
  -- | The property corresponding to the following part of the commuting diagram.
  --
  -- This checks that starting from the same encoding, the reference and real
  -- implementation deserialise to equivalent values.
  --
  -- >    . . . . . .▷Ref . . . . . ▶Ref
  -- >    .            △ ╲         ╱ │
  -- >    .            .  ╲enc dec╱  │
  -- >    .            .   ╲     ╱   │
  -- >    .            .    ▶Enc▶    │from
  -- >    .            .   .     ╲   │
  -- >    .            .  .    dec╲  │
  -- >    ▽            . .         ╲ ▼
  -- >    . . . . . . ▷.. . . . . . ▶Imp
  --
  -- > dec_imp . enc_ref = from . dec_ref . enc_ref
  --
  prop_decodeRefdecodeImp :: forall t. Token t => Proxy t -> t -> Bool
  prop_decodeRefdecodeImp _ x =
  
      deserialiseImp t enc  `eq`  (fromRef . deserialiseRef t) enc
  
    where
      enc = serialiseRef x
      eq  = eqImp t
      t   = Proxy :: Proxy t
  
  
  --------------------------------------------------------------------------------
  -- Token class instances for unsigned types
  --
  
  newtype TokWord8 = TokWord8 { unTokWord8 :: UInt }
    deriving (Eq, Show)
  
  instance Token TokWord8 where
      type Imp TokWord8 = Word8
  
      fromRef = fromIntegral . Ref.fromUInt . unTokWord8
      toRef _ = TokWord8 . Ref.toUInt . fromIntegral
  
      encodeImp _ = encodeWord8
      decodeImp _ = decodeWord8
  
      encodeRef (TokWord8 n) = Ref.encodeToken (Ref.MT0_UnsignedInt n)
      decodeRef _ = do Ref.MT0_UnsignedInt n <- Ref.decodeToken
                       return (TokWord8 n)
  
  instance Arbitrary TokWord8 where
      arbitrary = TokWord8 <$> oneof arbitraryUInt_Word8
  
  arbitraryUInt_Word8 :: [Gen UInt]
  arbitraryUInt_Word8  = [ UIntSmall <$> arbitrarySmall
                         , UInt8     <$> arbitrarySmall
                         , UInt8     <$> arbitraryUInt8
                         ]
  
  
  newtype TokWord16 = TokWord16 { unTokWord16 :: UInt }
    deriving (Eq, Show)
  
  instance Token TokWord16 where
      type Imp TokWord16 = Word16
  
      fromRef = fromIntegral . Ref.fromUInt . unTokWord16
      toRef _ = TokWord16 . Ref.toUInt . fromIntegral
  
      encodeImp _ = encodeWord16
      decodeImp _ = decodeWord16
  
      encodeRef (TokWord16 n) = Ref.encodeToken (Ref.MT0_UnsignedInt n)
      decodeRef _ = do Ref.MT0_UnsignedInt n <- Ref.decodeToken
                       return (TokWord16 n)
  
  instance Arbitrary TokWord16 where
        arbitrary = TokWord16 <$> oneof arbitraryUInt_Word16
  
  arbitraryUInt_Word16 :: [Gen UInt]
  arbitraryUInt_Word16 = arbitraryUInt_Word8
                      ++ [ UInt16 <$> arbitrarySmall
                         , UInt16 <$> arbitraryUInt8
                         , UInt16 <$> arbitraryUInt16
                         ]
  
  
  newtype TokWord32 = TokWord32 { unTokWord32 :: UInt }
    deriving (Eq, Show)
  
  instance Token TokWord32 where
      type Imp TokWord32 = Word32
  
      fromRef = fromIntegral . Ref.fromUInt . unTokWord32
      toRef _ = TokWord32 . Ref.toUInt . fromIntegral
  
      encodeImp _ = encodeWord32
      decodeImp _ = decodeWord32
  
      encodeRef (TokWord32 n) = Ref.encodeToken (Ref.MT0_UnsignedInt n)
      decodeRef _ = do Ref.MT0_UnsignedInt n <- Ref.decodeToken
                       return (TokWord32 n)
  
  instance Arbitrary TokWord32 where
      arbitrary = TokWord32 <$> oneof arbitraryUInt_Word32
  
  arbitraryUInt_Word32 :: [Gen UInt]
  arbitraryUInt_Word32 = arbitraryUInt_Word16
                      ++ [ UInt32 <$> arbitrarySmall
                         , UInt32 <$> arbitraryUInt8
                         , UInt32 <$> arbitraryUInt16
                         , UInt32 <$> arbitraryUInt32
                         ]
  
  newtype TokWord64 = TokWord64 { unTokWord64 :: UInt }
    deriving (Eq, Show)
  
  instance Token TokWord64 where
      type Imp TokWord64 = Word64
  
      fromRef = fromIntegral . Ref.fromUInt . unTokWord64
      toRef _ = TokWord64 . Ref.toUInt . fromIntegral
  
      encodeImp _ = encodeWord64
      decodeImp _ = decodeWord64
  
      encodeRef (TokWord64 n) = Ref.encodeToken (Ref.MT0_UnsignedInt n)
      decodeRef _ = do Ref.MT0_UnsignedInt n <- Ref.decodeToken
                       return (TokWord64 n)
  
  instance Arbitrary TokWord64 where
      arbitrary = TokWord64 <$> oneof arbitraryUInt_Word64
  
  arbitraryUInt_Word64 :: [Gen UInt]
  arbitraryUInt_Word64 = arbitraryUInt_Word32
                      ++ [ UInt64 <$> arbitrarySmall
                         , UInt64 <$> arbitraryUInt8
                         , UInt64 <$> arbitraryUInt16
                         , UInt64 <$> arbitraryUInt32
                         , UInt64 <$> arbitraryUInt64
                         ]
  
  
  newtype TokWord = TokWord { unTokWord :: UInt }
    deriving (Eq, Show)
  
  instance Arbitrary TokWord where
      arbitrary = TokWord <$> oneof arbitraryUInt_Word
  
  arbitraryUInt_Word :: [Gen UInt]
  arbitraryUInt_Word   = arbitraryUInt_Word32
                      ++ [ UInt64 <$> arbitrarySmall
                         , UInt64 <$> arbitraryUInt8
                         , UInt64 <$> arbitraryUInt16
                         , UInt64 <$> arbitraryUInt32
  #if defined(ARCH_64bit)
                         , UInt64 <$> arbitraryUInt64
  #endif
                         ]
  
  instance Token TokWord where
      type Imp TokWord = Word
  
      fromRef = fromIntegral . Ref.fromUInt . unTokWord
      toRef _ = TokWord . Ref.toUInt . fromIntegral
  
      encodeImp _ = encodeWord
      decodeImp _ = decodeWord
  
      encodeRef (TokWord n) = Ref.encodeToken (Ref.MT0_UnsignedInt n)
      decodeRef _ = do Ref.MT0_UnsignedInt n <- Ref.decodeToken
                       return (TokWord n)
  
  
  --------------------------------------------------------------------------------
  -- Token class instances for signed types
  --
  
  
  data TokInt8 = TokInt8 Bool UInt
    deriving (Eq, Show)
  
  instance Token TokInt8 where
      type Imp TokInt8 = Int8
  
      fromRef (TokInt8 True  n) =              (fromIntegral . Ref.fromUInt) n
      fromRef (TokInt8 False n) = (complement . fromIntegral . Ref.fromUInt) n
      toRef _ n
        | n >= 0    = TokInt8 True  ((Ref.toUInt . fromIntegral) n)
        | otherwise = TokInt8 False ((Ref.toUInt . fromIntegral . complement) n)
  
      encodeImp _ = encodeInt8
      decodeImp _ = decodeInt8
  
      encodeRef (TokInt8 True  n) = Ref.encodeToken (Ref.MT0_UnsignedInt n)
      encodeRef (TokInt8 False n) = Ref.encodeToken (Ref.MT1_NegativeInt n)
  
      decodeRef _ = do
        tok <- Ref.decodeToken
        case tok of
          Ref.MT0_UnsignedInt n -> return (TokInt8 True  n)
          Ref.MT1_NegativeInt n -> return (TokInt8 False n)
          _                     -> fail "decodeRef (TokInt)"
  
  instance Arbitrary TokInt8 where
      arbitrary = TokInt8 <$> arbitrary <*> oneof arbitraryUInt_Int8
      shrink (TokInt8 sign n) = [ TokInt8 sign' n'
                                | (sign', n') <- shrink (sign, n) ]
  
  arbitraryUInt_Int8 :: [Gen UInt]
  arbitraryUInt_Int8   = [ UIntSmall <$> arbitrarySmall
                         , UInt8     <$> arbitrarySmall
                         , UInt8     <$> arbitraryUInt7
                         ]
  
  
  data TokInt16 = TokInt16 Bool UInt
    deriving (Eq, Show)
  
  instance Token TokInt16 where
      type Imp TokInt16 = Int16
  
      fromRef (TokInt16 True  n) =              (fromIntegral . Ref.fromUInt) n
      fromRef (TokInt16 False n) = (complement . fromIntegral . Ref.fromUInt) n
      toRef _ n
        | n >= 0    = TokInt16 True  ((Ref.toUInt . fromIntegral) n)
        | otherwise = TokInt16 False ((Ref.toUInt . fromIntegral . complement) n)
  
      encodeImp _ = encodeInt16
      decodeImp _ = decodeInt16
  
      encodeRef (TokInt16 True  n) = Ref.encodeToken (Ref.MT0_UnsignedInt n)
      encodeRef (TokInt16 False n) = Ref.encodeToken (Ref.MT1_NegativeInt n)
  
      decodeRef _ = do
        tok <- Ref.decodeToken
        case tok of
          Ref.MT0_UnsignedInt n -> return (TokInt16 True  n)
          Ref.MT1_NegativeInt n -> return (TokInt16 False n)
          _                     -> fail "decodeRef (TokInt16)"
  
  
  instance Arbitrary TokInt16 where
      arbitrary = TokInt16 <$> arbitrary <*> oneof arbitraryUInt_Int16
  
  arbitraryUInt_Int16 :: [Gen UInt]
  arbitraryUInt_Int16  = arbitraryUInt_Int8
                      ++ [ UInt16 <$> arbitrarySmall
                         , UInt16 <$> arbitraryUInt7
                         , UInt16 <$> arbitraryUInt15
                         ]
  
  
  data TokInt32 = TokInt32 Bool UInt
    deriving (Eq, Show)
  
  instance Token TokInt32 where
      type Imp TokInt32 = Int32
  
      fromRef (TokInt32 True  n) =              (fromIntegral . Ref.fromUInt) n
      fromRef (TokInt32 False n) = (complement . fromIntegral . Ref.fromUInt) n
      toRef _ n
        | n >= 0    = TokInt32 True  ((Ref.toUInt . fromIntegral) n)
        | otherwise = TokInt32 False ((Ref.toUInt . fromIntegral . complement) n)
  
      encodeImp _ = encodeInt32
      decodeImp _ = decodeInt32
  
      encodeRef (TokInt32 True  n) = Ref.encodeToken (Ref.MT0_UnsignedInt n)
      encodeRef (TokInt32 False n) = Ref.encodeToken (Ref.MT1_NegativeInt n)
  
      decodeRef _ = do
        tok <- Ref.decodeToken
        case tok of
          Ref.MT0_UnsignedInt n -> return (TokInt32 True  n)
          Ref.MT1_NegativeInt n -> return (TokInt32 False n)
          _                     -> fail "decodeRef (TokInt32)"
  
  instance Arbitrary TokInt32 where
      arbitrary = TokInt32 <$> arbitrary <*> oneof arbitraryUInt_Int32
  
  arbitraryUInt_Int32 :: [Gen UInt]
  arbitraryUInt_Int32  = arbitraryUInt_Int16
                      ++ [ UInt32 <$> arbitrarySmall
                         , UInt32 <$> arbitraryUInt7
                         , UInt32 <$> arbitraryUInt15
                         , UInt32 <$> arbitraryUInt31
                         ]
  
  
  data TokInt64 = TokInt64 Bool UInt
    deriving (Eq, Show)
  
  instance Token TokInt64 where
      type Imp TokInt64 = Int64
  
      fromRef (TokInt64 True  n) =              (fromIntegral . Ref.fromUInt) n
      fromRef (TokInt64 False n) = (complement . fromIntegral . Ref.fromUInt) n
      toRef _ n
        | n >= 0    = TokInt64 True  ((Ref.toUInt . fromIntegral) n)
        | otherwise = TokInt64 False ((Ref.toUInt . fromIntegral . complement) n)
  
      encodeImp _ = encodeInt64
      decodeImp _ = decodeInt64
  
      encodeRef (TokInt64 True  n) = Ref.encodeToken (Ref.MT0_UnsignedInt n)
      encodeRef (TokInt64 False n) = Ref.encodeToken (Ref.MT1_NegativeInt n)
  
      decodeRef _ = do
        tok <- Ref.decodeToken
        case tok of
          Ref.MT0_UnsignedInt n -> return (TokInt64 True  n)
          Ref.MT1_NegativeInt n -> return (TokInt64 False n)
          _                     -> fail "decodeRef (TokInt64)"
  
  instance Arbitrary TokInt64 where
      arbitrary = TokInt64 <$> arbitrary <*> oneof arbitraryUInt_Int64
  
  arbitraryUInt_Int64 :: [Gen UInt]
  arbitraryUInt_Int64  = arbitraryUInt_Int32
                      ++ [ UInt64 <$> arbitrarySmall
                         , UInt64 <$> arbitraryUInt7
                         , UInt64 <$> arbitraryUInt15
                         , UInt64 <$> arbitraryUInt31
                         , UInt64 <$> arbitraryUInt63
                         ]
  
  
  data TokInt = TokInt Bool UInt
    deriving (Eq, Show)
  
  instance Token TokInt where
      type Imp TokInt = Int
  
      fromRef (TokInt True  n) =              (fromIntegral . Ref.fromUInt) n
      fromRef (TokInt False n) = (complement . fromIntegral . Ref.fromUInt) n
      toRef _ n
        | n >= 0    = TokInt True  ((Ref.toUInt . fromIntegral) n)
        | otherwise = TokInt False ((Ref.toUInt . fromIntegral . complement) n)
  
      encodeImp _ = encodeInt
      decodeImp _ = decodeInt
  
      encodeRef (TokInt True  n) = Ref.encodeToken (Ref.MT0_UnsignedInt n)
      encodeRef (TokInt False n) = Ref.encodeToken (Ref.MT1_NegativeInt n)
  
      decodeRef _ = do
        tok <- Ref.decodeToken
        case tok of
          Ref.MT0_UnsignedInt n -> return (TokInt True  n)
          Ref.MT1_NegativeInt n -> return (TokInt False n)
          _                     -> fail "decodeRef (TokInt)"
  
  instance Arbitrary TokInt where
      arbitrary = TokInt <$> arbitrary <*> oneof arbitraryUInt_Int
  
  arbitraryUInt_Int :: [Gen UInt]
  arbitraryUInt_Int    = arbitraryUInt_Int32
                      ++ [ UInt64 <$> arbitrarySmall
                         , UInt64 <$> arbitraryUInt7
                         , UInt64 <$> arbitraryUInt15
                         , UInt64 <$> arbitraryUInt31
  #if defined(ARCH_64bit)
                         , UInt64 <$> arbitraryUInt63
  #endif
                         ]
  
  
  data TokInteger = TokIntegerUInt UInt
                  | TokIntegerNInt UInt
                  | TokIntegerBig  LargeInteger
    deriving (Eq, Show)
  
  instance Arbitrary TokInteger where
    arbitrary = oneof [ TokIntegerUInt <$> arbitrary
                      , TokIntegerNInt <$> arbitrary
                      , TokIntegerBig  <$> arbitrary
                      ]
  
  
  instance Token TokInteger where
      type Imp TokInteger = Integer
  
      fromRef (TokIntegerUInt n) =              (fromIntegral . Ref.fromUInt) n
      fromRef (TokIntegerNInt n) = (complement . fromIntegral . Ref.fromUInt) n
      fromRef (TokIntegerBig  n) = getLargeInteger n
  
      toRef _ n
        | n >= 0 && n <= fromIntegral (maxBound :: Word64)
        = TokIntegerUInt ((Ref.toUInt . fromIntegral) n)
  
        | n < 0  && complement n <= fromIntegral (maxBound :: Word64)
        = TokIntegerNInt ((Ref.toUInt . fromIntegral . complement) n)
  
        | otherwise = TokIntegerBig (LargeInteger n)
  
      encodeImp _ = encodeInteger
      decodeImp _ = decodeInteger
  
      encodeRef (TokIntegerUInt n) = Ref.encodeToken (Ref.MT0_UnsignedInt n)
      encodeRef (TokIntegerNInt n) = Ref.encodeToken (Ref.MT1_NegativeInt n)
      encodeRef (TokIntegerBig  n) = Ref.encodeTerm (Ref.TBigInt (getLargeInteger n))
  
      decodeRef _ = do
        tok <- Ref.decodeToken
        case tok of
          Ref.MT0_UnsignedInt n -> return (TokIntegerUInt  n)
          Ref.MT1_NegativeInt n -> return (TokIntegerNInt n)
          Ref.MT6_Tag       tag -> do Ref.TBigInt n <- Ref.decodeTagged tag
                                      return (TokIntegerBig (LargeInteger n))
          _                     -> fail "decodeRef (TokInteger)"
  
  
  --------------------------------------------------------------------------------
  -- Arbitrary helpers for integer types
  --
  
  arbitrarySmall,
    arbitraryUInt8, arbitraryUInt16, arbitraryUInt32, arbitraryUInt64,
    arbitraryUInt7, arbitraryUInt15, arbitraryUInt31, arbitraryUInt63
    :: (Num n, Random n) => Gen n
  
  arbitrarySmall  = chooseZeroToBound (23       :: Word)
  arbitraryUInt8  = chooseZeroToBound (maxBound :: Word8)
  arbitraryUInt16 = chooseZeroToBound (maxBound :: Word16)
  arbitraryUInt32 = chooseZeroToBound (maxBound :: Word32)
  arbitraryUInt64 = chooseZeroToBound (maxBound :: Word64)
  
  arbitraryUInt7  = chooseZeroToBound (maxBound :: Int8)
  arbitraryUInt15 = chooseZeroToBound (maxBound :: Int16)
  arbitraryUInt31 = chooseZeroToBound (maxBound :: Int32)
  arbitraryUInt63 = chooseZeroToBound (maxBound :: Int64)
  
  chooseZeroToBound :: (Num a, Random a, Integral a1) => a1 -> Gen a
  chooseZeroToBound bound =
      frequency [ (9, choose (0, bound'))
                , (1, pure bound') ]
    where
      bound' = fromIntegral bound
  
  
  --------------------------------------------------------------------------------
  -- Token class instances for floating point types
  --
  
  data TokHalf = TokHalf HalfSpecials
    deriving (Eq, Show)
  
  instance Arbitrary TokHalf where
    arbitrary = TokHalf <$> arbitrary
  
  instance Token TokHalf where
      type Imp TokHalf = Float
  
      eqImp _ = (==) `on` floatToWord
  
      fromRef (TokHalf (HalfSpecials n)) = Half.fromHalf n
      toRef _ = TokHalf
              . canonicaliseNaN
              . HalfSpecials
              . Half.toHalf
  
      canonicaliseImp _ = Half.fromHalf
                        . canonicaliseNaN
                        . Half.toHalf
      canonicaliseRef (TokHalf n) = TokHalf (canonicaliseNaN n)
  
      encodeImp _ = encodeFloat16
      decodeImp _ = decodeFloat
  
      encodeRef (TokHalf n) = Ref.encodeToken (Ref.MT7_Float16 n)
      decodeRef _ = do Ref.MT7_Float16 n <- Ref.decodeToken
                       return (TokHalf n)
  
  data TokFloat = TokFloat FloatSpecials
                | TokFloatNan
    deriving (Eq, Show)
  
  instance Arbitrary TokFloat where
    arbitrary = frequency [(19, TokFloat <$> arbitrary), (1, pure TokFloatNan)]
  
  instance Token TokFloat where
      type Imp TokFloat = Float
  
      eqImp _ = (==) `on` floatToWord
  
      fromRef (TokFloat n) = getFloatSpecials n
      fromRef TokFloatNan  = canonicalNaN
      toRef _ n
        | isNaN n   = TokFloatNan
        | otherwise = TokFloat (FloatSpecials n)
  
      canonicaliseImp _ = canonicaliseNaN
  
      canonicaliseRef TokFloatNan = TokFloatNan
      canonicaliseRef (TokFloat (FloatSpecials n))
        | isNaN n   = TokFloatNan
        | otherwise = TokFloat (FloatSpecials n)
  
      encodeImp _ = encodeFloat
      decodeImp _ = decodeFloat
  
      encodeRef (TokFloat n) = Ref.encodeToken (Ref.MT7_Float32 n)
      encodeRef TokFloatNan  = Ref.encodeToken (Ref.MT7_Float16 canonicalNaN)
  
      decodeRef _ = do
        tok <- Ref.decodeToken
        case tok of
          Ref.MT7_Float16 n  | isNaN n
                            -> return TokFloatNan
          Ref.MT7_Float32 n -> return (TokFloat n)
          _                 -> fail "decodeRef (TokFloat)"
  
  
  data TokDouble = TokDouble DoubleSpecials
                 | TokDoubleNan
    deriving (Eq, Show)
  
  instance Arbitrary TokDouble where
    arbitrary = frequency [(19, TokDouble <$> arbitrary), (1, pure TokDoubleNan)]
  
  instance Token TokDouble where
      type Imp TokDouble = Double
  
      eqImp _ = (==) `on` doubleToWord
  
      fromRef (TokDouble n) = getDoubleSpecials n
      fromRef TokDoubleNan  = canonicalNaN
      toRef _ n
        | isNaN n   = TokDoubleNan
        | otherwise = TokDouble (DoubleSpecials n)
  
      canonicaliseImp _ = canonicaliseNaN
      canonicaliseRef TokDoubleNan = TokDoubleNan
      canonicaliseRef (TokDouble (DoubleSpecials n))
        | isNaN n   = TokDoubleNan
        | otherwise = TokDouble (DoubleSpecials n)
  
      encodeImp _ = encodeDouble
      decodeImp _ = decodeDouble
  
      encodeRef (TokDouble n) = Ref.encodeToken (Ref.MT7_Float64 n)
      encodeRef TokDoubleNan  = Ref.encodeToken (Ref.MT7_Float16 canonicalNaN)
  
      decodeRef _ = do
        tok <- Ref.decodeToken
        case tok of
          Ref.MT7_Float16 n  | isNaN n
                            -> return TokDoubleNan
          Ref.MT7_Float64 n -> return (TokDouble n)
          _                 -> fail "decodeRef (TokDouble)"
  
  
  --------------------------------------------------------------------------------
  -- Miscelaneous token class instances
  --
  
  data TokTag = TokTag { unTokTag :: UInt }
    deriving (Eq, Show)
  
  instance Arbitrary TokTag where
      arbitrary = TokTag <$> oneof arbitraryUInt_Word
  
  instance Token TokTag where
      type Imp TokTag = Word
  
      fromRef = fromIntegral . Ref.fromUInt . unTokTag
      toRef _ = TokTag . Ref.toUInt . fromIntegral
  
      encodeImp _ = encodeTag
      decodeImp _ = decodeTag
  
      encodeRef (TokTag n) = Ref.encodeToken (Ref.MT6_Tag n)
      decodeRef _          = do Ref.MT6_Tag n <- Ref.decodeToken
                                return (TokTag n)
  
  
  data TokTag64 = TokTag64 { unTokTag64 :: UInt }
    deriving (Eq, Show)
  
  instance Arbitrary TokTag64 where
      arbitrary = TokTag64 <$> oneof arbitraryUInt_Word64
  
  instance Token TokTag64 where
      type Imp TokTag64 = Word64
  
      fromRef = fromIntegral . Ref.fromUInt . unTokTag64
      toRef _ = TokTag64 . Ref.toUInt . fromIntegral
  
      encodeImp _ = encodeTag64
      decodeImp _ = decodeTag64
  
      encodeRef (TokTag64 n) = Ref.encodeToken (Ref.MT6_Tag n)
      decodeRef _            = do Ref.MT6_Tag n <- Ref.decodeToken
                                  return (TokTag64 n)
  
  
  
  instance Token Ref.Simple where
      type Imp Ref.Simple = Word8
  
      fromRef = Ref.fromSimple
      toRef _ = Ref.toSimple
  
      encodeImp _ = encodeSimple
      decodeImp _ = decodeSimple
  
      encodeRef n = Ref.encodeToken (Ref.MT7_Simple n)
      decodeRef _ = do Ref.MT7_Simple n <- Ref.decodeToken
                       return n
  
  
  --------------------------------------------------------------------------------
  -- Token class instances for Term type
  --
  
  instance Token Ref.Term where
      type Imp Ref.Term = Term
  
      eqImp _ = eqTerm
  
      fromRef = fromRefTerm
      toRef _ = toRefTerm
  
      canonicaliseImp _ = canonicaliseTerm
      canonicaliseRef   = Ref.canonicaliseTerm
  
      encodeImp _ = encodeTerm
      decodeImp _ = decodeTerm
  
      encodeRef   = Ref.encodeTerm
      decodeRef _ = Ref.decodeTerm
  
  --------------------------------------------------------------------------------
  -- Token class instances for ByteArray tokens.
  --
  
  newtype TokByteArray = TokByteArray { unTokByteArray :: [Word8] }
    deriving (Eq, Show)
  
  instance Arbitrary TokByteArray where
    arbitrary = TokByteArray <$> arbitrary
  
  instance Token TokByteArray where
      type Imp TokByteArray = Sliced.SlicedByteArray
  
      eqImp _ = (==)
  
      fromRef = Sliced.fromByteString . BS.pack . unTokByteArray
      toRef _ = TokByteArray . toList
  
      canonicaliseImp _ = id
      canonicaliseRef   = id
  
      encodeImp _ = encodeByteArray
      decodeImp _ = toSliced <$> decodeByteArray
  
      encodeRef (TokByteArray bs) = Ref.encodeToken (Ref.MT2_ByteString (lengthUInt bs) bs)
      decodeRef _ = do Ref.MT2_ByteString _n bs <- Ref.decodeToken
                       -- TODO? check _n == bs
                       return (TokByteArray bs)
  
  instance Arbitrary Sliced.SlicedByteArray where
      -- Taken from cardano-ledger-binary testlib.
      arbitrary = do
        NonNegative off <- arbitrary
        Positive count <- arbitrary
        NonNegative slack <- arbitrary
        let len = off + count + slack
        ba <- genByteArray len
        pure $ Sliced.SBA ba off count
  
        where
          genShortByteString :: Int -> Gen SBS.ShortByteString
          genShortByteString n = MkGen (\r _n -> runStateGen_ r (uniformShortByteString n))
  
          genByteArray :: Int -> Gen Prim.ByteArray
          genByteArray n = do
                    bss <- genShortByteString n
                    case bss of
                      SBS.SBS ba -> pure $ Prim.ByteArray ba
  
  --------------------------------------------------------------------------------
  -- TestTree API
  
  testTree :: TestTree
  testTree =
    testGroup "properties"
    [ testGroup "to . id . from = canon_ref"
      [ testProperty "Word8"   (prop_fromRefToRef (Proxy :: Proxy TokWord8))
      , testProperty "Word16"  (prop_fromRefToRef (Proxy :: Proxy TokWord16))
      , testProperty "Word32"  (prop_fromRefToRef (Proxy :: Proxy TokWord32))
      , testProperty "Word64"  (prop_fromRefToRef (Proxy :: Proxy TokWord64))
      , testProperty "Word"    (prop_fromRefToRef (Proxy :: Proxy TokWord))
  --  , testProperty "NegWord" (prop_fromRefToRef (Proxy :: Proxy TokNegWord))
      , testProperty "Int8"    (prop_fromRefToRef (Proxy :: Proxy TokInt8))
      , testProperty "Int16"   (prop_fromRefToRef (Proxy :: Proxy TokInt16))
      , testProperty "Int32"   (prop_fromRefToRef (Proxy :: Proxy TokInt32))
      , testProperty "Int64"   (prop_fromRefToRef (Proxy :: Proxy TokInt64))
      , testProperty "Int"     (prop_fromRefToRef (Proxy :: Proxy TokInt))
      , testProperty "Integer" (prop_fromRefToRef (Proxy :: Proxy TokInteger))
      , testProperty "Half"    (prop_fromRefToRef (Proxy :: Proxy TokHalf))
      , testProperty "Float"   (prop_fromRefToRef (Proxy :: Proxy TokFloat))
      , testProperty "Double"  (prop_fromRefToRef (Proxy :: Proxy TokDouble))
      , testProperty "Tag"     (prop_fromRefToRef (Proxy :: Proxy TokTag))
      , testProperty "Tag64"   (prop_fromRefToRef (Proxy :: Proxy TokTag64))
      , testProperty "Simple"  (prop_fromRefToRef (Proxy :: Proxy Ref.Simple))
      , testProperty "Term"    (prop_fromRefToRef (Proxy :: Proxy Ref.Term))
      , testProperty "ByteArray" (prop_fromRefToRef (Proxy :: Proxy TokByteArray))
      ]
  
    , testGroup "from . id . to = canon_imp"
      [ testProperty "Word8"   (prop_toRefFromRef (Proxy :: Proxy TokWord8))
      , testProperty "Word16"  (prop_toRefFromRef (Proxy :: Proxy TokWord16))
      , testProperty "Word32"  (prop_toRefFromRef (Proxy :: Proxy TokWord32))
      , testProperty "Word64"  (prop_toRefFromRef (Proxy :: Proxy TokWord64))
      , testProperty "Word"    (prop_toRefFromRef (Proxy :: Proxy TokWord))
  --  , testProperty "NegWord" (prop_toRefFromRef (Proxy :: Proxy TokNegWord))
      , testProperty "Int8"    (prop_toRefFromRef (Proxy :: Proxy TokInt8))
      , testProperty "Int16"   (prop_toRefFromRef (Proxy :: Proxy TokInt16))
      , testProperty "Int32"   (prop_toRefFromRef (Proxy :: Proxy TokInt32))
      , testProperty "Int64"   (prop_toRefFromRef (Proxy :: Proxy TokInt64))
      , testProperty "Int"     (prop_toRefFromRef (Proxy :: Proxy TokInt))
      , testProperty "Integer" (prop_toRefFromRef (Proxy :: Proxy TokInteger))
      , testProperty "Half"    (prop_toRefFromRef (Proxy :: Proxy TokHalf))
      , testProperty "Float"   (prop_toRefFromRef (Proxy :: Proxy TokFloat))
      , testProperty "Double"  (prop_toRefFromRef (Proxy :: Proxy TokDouble))
      , testProperty "Tag"     (prop_toRefFromRef (Proxy :: Proxy TokTag))
      , testProperty "Tag64"   (prop_toRefFromRef (Proxy :: Proxy TokTag64))
      , testProperty "Simple"  (prop_toRefFromRef (Proxy :: Proxy Ref.Simple))
      , testProperty "Term"    (prop_toRefFromRef (Proxy :: Proxy Ref.Term))
      , testProperty "ByteArray" (prop_toRefFromRef (Proxy :: Proxy TokByteArray))
      ]
  
    , testGroup "dec_ref . enc_ref = id"
      [ testProperty "Word8"   (prop_encodeRefdecodeRef (Proxy :: Proxy TokWord8))
      , testProperty "Word16"  (prop_encodeRefdecodeRef (Proxy :: Proxy TokWord16))
      , testProperty "Word32"  (prop_encodeRefdecodeRef (Proxy :: Proxy TokWord32))
      , testProperty "Word64"  (prop_encodeRefdecodeRef (Proxy :: Proxy TokWord64))
      , testProperty "Word"    (prop_encodeRefdecodeRef (Proxy :: Proxy TokWord))
  --  , testProperty "NegWord" (prop_encodeRefdecodeRef (Proxy :: Proxy TokNegWord))
      , testProperty "Int8"    (prop_encodeRefdecodeRef (Proxy :: Proxy TokInt8))
      , testProperty "Int16"   (prop_encodeRefdecodeRef (Proxy :: Proxy TokInt16))
      , testProperty "Int32"   (prop_encodeRefdecodeRef (Proxy :: Proxy TokInt32))
      , testProperty "Int64"   (prop_encodeRefdecodeRef (Proxy :: Proxy TokInt64))
      , testProperty "Int"     (prop_encodeRefdecodeRef (Proxy :: Proxy TokInt))
      , testProperty "Integer" (prop_encodeRefdecodeRef (Proxy :: Proxy TokInteger))
      , testProperty "Half"    (prop_encodeRefdecodeRef (Proxy :: Proxy TokHalf))
      , testProperty "Float"   (prop_encodeRefdecodeRef (Proxy :: Proxy TokFloat))
      , testProperty "Double"  (prop_encodeRefdecodeRef (Proxy :: Proxy TokDouble))
      , testProperty "Tag"     (prop_encodeRefdecodeRef (Proxy :: Proxy TokTag))
      , testProperty "Tag64"   (prop_encodeRefdecodeRef (Proxy :: Proxy TokTag64))
      , testProperty "Simple"  (prop_encodeRefdecodeRef (Proxy :: Proxy Ref.Simple))
      , testProperty "Term"    (prop_encodeRefdecodeRef (Proxy :: Proxy Ref.Term))
      , testProperty "ByteArray" (prop_encodeRefdecodeRef (Proxy :: Proxy TokByteArray))
      ]
  
    , testGroup "dec_imp . enc_imp = canon_imp"
      [ testProperty "Word8"   (prop_encodeImpdecodeImp (Proxy :: Proxy TokWord8))
      , testProperty "Word16"  (prop_encodeImpdecodeImp (Proxy :: Proxy TokWord16))
      , testProperty "Word32"  (prop_encodeImpdecodeImp (Proxy :: Proxy TokWord32))
      , testProperty "Word64"  (prop_encodeImpdecodeImp (Proxy :: Proxy TokWord64))
      , testProperty "Word"    (prop_encodeImpdecodeImp (Proxy :: Proxy TokWord))
  --  , testProperty "NegWord" (prop_encodeImpdecodeImp (Proxy :: Proxy TokNegWord))
      , testProperty "Int8"    (prop_encodeImpdecodeImp (Proxy :: Proxy TokInt8))
      , testProperty "Int16"   (prop_encodeImpdecodeImp (Proxy :: Proxy TokInt16))
      , testProperty "Int32"   (prop_encodeImpdecodeImp (Proxy :: Proxy TokInt32))
      , testProperty "Int64"   (prop_encodeImpdecodeImp (Proxy :: Proxy TokInt64))
      , testProperty "Int"     (prop_encodeImpdecodeImp (Proxy :: Proxy TokInt))
      , testProperty "Integer" (prop_encodeImpdecodeImp (Proxy :: Proxy TokInteger))
      , testProperty "Half"    (prop_encodeImpdecodeImp (Proxy :: Proxy TokHalf))
      , testProperty "Float"   (prop_encodeImpdecodeImp (Proxy :: Proxy TokFloat))
      , testProperty "Double"  (prop_encodeImpdecodeImp (Proxy :: Proxy TokDouble))
      , testProperty "Tag"     (prop_encodeImpdecodeImp (Proxy :: Proxy TokTag))
      , testProperty "Tag64"   (prop_encodeImpdecodeImp (Proxy :: Proxy TokTag64))
      , testProperty "Simple"  (prop_encodeImpdecodeImp (Proxy :: Proxy Ref.Simple))
      , testProperty "Term"    (prop_encodeImpdecodeImp (Proxy :: Proxy Ref.Term))
      , testProperty "ByteArray" (prop_encodeImpdecodeImp (Proxy :: Proxy TokByteArray))
      ]
  
    , testGroup "dec_imp . enc_imp = canon_imp (all 2-splits)"
      [ testProperty "Word8"   (prop_encodeImpdecodeImp_splits2 (Proxy :: Proxy TokWord8))
      , testProperty "Word16"  (prop_encodeImpdecodeImp_splits2 (Proxy :: Proxy TokWord16))
      , testProperty "Word32"  (prop_encodeImpdecodeImp_splits2 (Proxy :: Proxy TokWord32))
      , testProperty "Word64"  (prop_encodeImpdecodeImp_splits2 (Proxy :: Proxy TokWord64))
      , testProperty "Word"    (prop_encodeImpdecodeImp_splits2 (Proxy :: Proxy TokWord))
  --  , testProperty "NegWord" (prop_encodeImpdecodeImp_splits2 (Proxy :: Proxy TokNegWord))
      , testProperty "Int8"    (prop_encodeImpdecodeImp_splits2 (Proxy :: Proxy TokInt8))
      , testProperty "Int16"   (prop_encodeImpdecodeImp_splits2 (Proxy :: Proxy TokInt16))
      , testProperty "Int32"   (prop_encodeImpdecodeImp_splits2 (Proxy :: Proxy TokInt32))
      , testProperty "Int64"   (prop_encodeImpdecodeImp_splits2 (Proxy :: Proxy TokInt64))
      , testProperty "Int"     (prop_encodeImpdecodeImp_splits2 (Proxy :: Proxy TokInt))
      , testProperty "Integer" (prop_encodeImpdecodeImp_splits2 (Proxy :: Proxy TokInteger))
      , testProperty "Half"    (prop_encodeImpdecodeImp_splits2 (Proxy :: Proxy TokHalf))
      , testProperty "Float"   (prop_encodeImpdecodeImp_splits2 (Proxy :: Proxy TokFloat))
      , testProperty "Double"  (prop_encodeImpdecodeImp_splits2 (Proxy :: Proxy TokDouble))
      , testProperty "Tag"     (prop_encodeImpdecodeImp_splits2 (Proxy :: Proxy TokTag))
      , testProperty "Tag64"   (prop_encodeImpdecodeImp_splits2 (Proxy :: Proxy TokTag64))
      , testProperty "Simple"  (prop_encodeImpdecodeImp_splits2 (Proxy :: Proxy Ref.Simple))
      , localOption (QuickCheckMaxSize 100) $
        testProperty "Term"    (prop_encodeImpdecodeImp_splits2 (Proxy :: Proxy Ref.Term))
      , testProperty "ByteArray" (prop_encodeImpdecodeImp_splits2 (Proxy :: Proxy TokByteArray))
      ]
  
    , testGroup "dec_imp . enc_imp = canon_imp (all 3-splits)"
      [ testProperty "Word8"   (prop_encodeImpdecodeImp_splits3 (Proxy :: Proxy TokWord8))
      , testProperty "Word16"  (prop_encodeImpdecodeImp_splits3 (Proxy :: Proxy TokWord16))
      , testProperty "Word32"  (prop_encodeImpdecodeImp_splits3 (Proxy :: Proxy TokWord32))
      , testProperty "Word64"  (prop_encodeImpdecodeImp_splits3 (Proxy :: Proxy TokWord64))
      , testProperty "Word"    (prop_encodeImpdecodeImp_splits3 (Proxy :: Proxy TokWord))
  --  , testProperty "NegWord" (prop_encodeImpdecodeImp_splits3 (Proxy :: Proxy TokNegWord))
      , testProperty "Int8"    (prop_encodeImpdecodeImp_splits3 (Proxy :: Proxy TokInt8))
      , testProperty "Int16"   (prop_encodeImpdecodeImp_splits3 (Proxy :: Proxy TokInt16))
      , testProperty "Int32"   (prop_encodeImpdecodeImp_splits3 (Proxy :: Proxy TokInt32))
      , testProperty "Int64"   (prop_encodeImpdecodeImp_splits3 (Proxy :: Proxy TokInt64))
      , testProperty "Int"     (prop_encodeImpdecodeImp_splits3 (Proxy :: Proxy TokInt))
      , testProperty "Integer" (prop_encodeImpdecodeImp_splits3 (Proxy :: Proxy TokInteger))
      , testProperty "Half"    (prop_encodeImpdecodeImp_splits3 (Proxy :: Proxy TokHalf))
      , testProperty "Float"   (prop_encodeImpdecodeImp_splits3 (Proxy :: Proxy TokFloat))
      , testProperty "Double"  (prop_encodeImpdecodeImp_splits3 (Proxy :: Proxy TokDouble))
      , testProperty "Tag"     (prop_encodeImpdecodeImp_splits3 (Proxy :: Proxy TokTag))
      , testProperty "Tag64"   (prop_encodeImpdecodeImp_splits3 (Proxy :: Proxy TokTag64))
      , testProperty "Simple"  (prop_encodeImpdecodeImp_splits3 (Proxy :: Proxy Ref.Simple))
      , localOption (QuickCheckMaxSize 25) $
        testProperty "Term"    (prop_encodeImpdecodeImp_splits3 (Proxy :: Proxy Ref.Term))
      , testProperty "ByteArray" (prop_encodeImpdecodeImp_splits3 (Proxy :: Proxy TokByteArray))
      ]
  
    , testGroup "enc_imp . from = enc_ref . canon_ref"
      [ testProperty "Word8"   (prop_encodeRefencodeImp1 (Proxy :: Proxy TokWord8))
      , testProperty "Word16"  (prop_encodeRefencodeImp1 (Proxy :: Proxy TokWord16))
      , testProperty "Word32"  (prop_encodeRefencodeImp1 (Proxy :: Proxy TokWord32))
      , testProperty "Word64"  (prop_encodeRefencodeImp1 (Proxy :: Proxy TokWord64))
      , testProperty "Word"    (prop_encodeRefencodeImp1 (Proxy :: Proxy TokWord))
  --  , testProperty "NegWord" (prop_encodeRefencodeImp1 (Proxy :: Proxy TokNegWord))
      , testProperty "Int8"    (prop_encodeRefencodeImp1 (Proxy :: Proxy TokInt8))
      , testProperty "Int16"   (prop_encodeRefencodeImp1 (Proxy :: Proxy TokInt16))
      , testProperty "Int32"   (prop_encodeRefencodeImp1 (Proxy :: Proxy TokInt32))
      , testProperty "Int64"   (prop_encodeRefencodeImp1 (Proxy :: Proxy TokInt64))
      , testProperty "Int"     (prop_encodeRefencodeImp1 (Proxy :: Proxy TokInt))
      , testProperty "Integer" (prop_encodeRefencodeImp1 (Proxy :: Proxy TokInteger))
      , testProperty "Half"    (prop_encodeRefencodeImp1 (Proxy :: Proxy TokHalf))
      , testProperty "Float"   (prop_encodeRefencodeImp1 (Proxy :: Proxy TokFloat))
      , testProperty "Double"  (prop_encodeRefencodeImp1 (Proxy :: Proxy TokDouble))
      , testProperty "Tag"     (prop_encodeRefencodeImp1 (Proxy :: Proxy TokTag))
      , testProperty "Tag64"   (prop_encodeRefencodeImp1 (Proxy :: Proxy TokTag64))
      , testProperty "Simple"  (prop_encodeRefencodeImp1 (Proxy :: Proxy Ref.Simple))
      , testProperty "Term"    (prop_encodeRefencodeImp1 (Proxy :: Proxy Ref.Term))
      , testProperty "ByteArray" (prop_encodeRefencodeImp1 (Proxy :: Proxy TokByteArray))
      ]
  
    , testGroup "enc_ref . to = enc_imp"
      [ testProperty "Word8"   (prop_encodeRefencodeImp2 (Proxy :: Proxy TokWord8))
      , testProperty "Word16"  (prop_encodeRefencodeImp2 (Proxy :: Proxy TokWord16))
      , testProperty "Word32"  (prop_encodeRefencodeImp2 (Proxy :: Proxy TokWord32))
      , testProperty "Word64"  (prop_encodeRefencodeImp2 (Proxy :: Proxy TokWord64))
      , testProperty "Word"    (prop_encodeRefencodeImp2 (Proxy :: Proxy TokWord))
  --  , testProperty "NegWord" (prop_encodeRefencodeImp2 (Proxy :: Proxy TokNegWord))
      , testProperty "Int8"    (prop_encodeRefencodeImp2 (Proxy :: Proxy TokInt8))
      , testProperty "Int16"   (prop_encodeRefencodeImp2 (Proxy :: Proxy TokInt16))
      , testProperty "Int32"   (prop_encodeRefencodeImp2 (Proxy :: Proxy TokInt32))
      , testProperty "Int64"   (prop_encodeRefencodeImp2 (Proxy :: Proxy TokInt64))
      , testProperty "Int"     (prop_encodeRefencodeImp2 (Proxy :: Proxy TokInt))
      , testProperty "Integer" (prop_encodeRefencodeImp2 (Proxy :: Proxy TokInteger))
      , testProperty "Half"    (prop_encodeRefencodeImp2 (Proxy :: Proxy TokHalf))
      , testProperty "Float"   (prop_encodeRefencodeImp2 (Proxy :: Proxy TokFloat))
      , testProperty "Double"  (prop_encodeRefencodeImp2 (Proxy :: Proxy TokDouble))
      , testProperty "Tag"     (prop_encodeRefencodeImp2 (Proxy :: Proxy TokTag))
      , testProperty "Tag64"   (prop_encodeRefencodeImp2 (Proxy :: Proxy TokTag64))
      , testProperty "Simple"  (prop_encodeRefencodeImp2 (Proxy :: Proxy Ref.Simple))
      , testProperty "Term"    (prop_encodeRefencodeImp2 (Proxy :: Proxy Ref.Term))
      , testProperty "ByteArray" (prop_encodeRefencodeImp2 (Proxy :: Proxy TokByteArray))
      ]
  
    , testGroup "dec_imp . enc_ref = from . dec_ref . enc_ref"
      [ testProperty "Word8"   (prop_decodeRefdecodeImp (Proxy :: Proxy TokWord8))
      , testProperty "Word16"  (prop_decodeRefdecodeImp (Proxy :: Proxy TokWord16))
      , testProperty "Word32"  (prop_decodeRefdecodeImp (Proxy :: Proxy TokWord32))
      , testProperty "Word64"  (prop_decodeRefdecodeImp (Proxy :: Proxy TokWord64))
      , testProperty "Word"    (prop_decodeRefdecodeImp (Proxy :: Proxy TokWord))
  --  , testProperty "NegWord" (prop_decodeRefdecodeImp (Proxy :: Proxy TokNegWord))
      , testProperty "Int8"    (prop_decodeRefdecodeImp (Proxy :: Proxy TokInt8))
      , testProperty "Int16"   (prop_decodeRefdecodeImp (Proxy :: Proxy TokInt16))
      , testProperty "Int32"   (prop_decodeRefdecodeImp (Proxy :: Proxy TokInt32))
      , testProperty "Int64"   (prop_decodeRefdecodeImp (Proxy :: Proxy TokInt64))
      , testProperty "Int"     (prop_decodeRefdecodeImp (Proxy :: Proxy TokInt))
      , testProperty "Integer" (prop_decodeRefdecodeImp (Proxy :: Proxy TokInteger))
      , testProperty "Half"    (prop_decodeRefdecodeImp (Proxy :: Proxy TokHalf))
      , testProperty "Float"   (prop_decodeRefdecodeImp (Proxy :: Proxy TokFloat))
      , testProperty "Double"  (prop_decodeRefdecodeImp (Proxy :: Proxy TokDouble))
      , testProperty "Tag"     (prop_decodeRefdecodeImp (Proxy :: Proxy TokTag))
      , testProperty "Tag64"   (prop_decodeRefdecodeImp (Proxy :: Proxy TokTag64))
      , testProperty "Simple"  (prop_decodeRefdecodeImp (Proxy :: Proxy Ref.Simple))
      , testProperty "Term"    (prop_decodeRefdecodeImp (Proxy :: Proxy Ref.Term))
      , testProperty "ByteArray" (prop_decodeRefdecodeImp (Proxy :: Proxy TokByteArray))
      ]
    ]
  