{-# 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.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           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           System.Random (Random)

import qualified Tests.Reference.Implementation as Ref
import           Tests.Reference.Implementation (UInt(..))
import           Tests.Reference.Generators
import           Tests.Term
                   ( fromRefTerm, toRefTerm, eqTerm, canonicaliseTerm )
import           Tests.Util

#if !MIN_VERSION_base(4,8,0)
import           Control.Applicative
#endif


-- | 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 :: *

  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


--------------------------------------------------------------------------------
-- 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))
    ]

  , 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))
    ]

  , 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))
    ]

  , 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))
    ]

  , 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))
    ]

  , 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))
    ]

  , 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))
    ]

  , 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))
    ]

  , 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))
    ]
  ]
