module Cardano.Wallet.Primitive.Types.Address.Gen
    (
      -- * Generators and shrinkers
      genAddress
    , shrinkAddress

      -- * Indicator functions on addresses
    , addressParity
    , Parity (..)
    )
    where

import Prelude

import Cardano.Wallet.Primitive.Types.Address
    ( Address (..) )
import Test.QuickCheck
    ( Gen, elements, sized )

import qualified Data.Bits as Bits
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as B8

--------------------------------------------------------------------------------
-- Addresses generated according to the size parameter
--------------------------------------------------------------------------------

genAddress :: Gen (Address)
genAddress :: Gen Address
genAddress = (Int -> Gen Address) -> Gen Address
forall a. (Int -> Gen a) -> Gen a
sized ((Int -> Gen Address) -> Gen Address)
-> (Int -> Gen Address) -> Gen Address
forall a b. (a -> b) -> a -> b
$ \Int
size -> [Address] -> Gen Address
forall a. [a] -> Gen a
elements ([Address] -> Gen Address) -> [Address] -> Gen Address
forall a b. (a -> b) -> a -> b
$ Int -> [Address] -> [Address]
forall a. Int -> [a] -> [a]
take (Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
1 Int
size) [Address]
addresses

shrinkAddress :: Address -> [Address]
shrinkAddress :: Address -> [Address]
shrinkAddress Address
a
    | Address
a Address -> Address -> Bool
forall a. Eq a => a -> a -> Bool
== Address
simplest = []
    | Bool
otherwise = [Address
simplest]
  where
    simplest :: Address
simplest = [Address] -> Address
forall a. [a] -> a
head [Address]
addresses

addresses :: [Address]
addresses :: [Address]
addresses = Char -> Address
mkAddress (Char -> Address) -> [Char] -> [Address]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [Char
'0' ..]

--------------------------------------------------------------------------------
-- Indicator functions on addresses
--------------------------------------------------------------------------------

-- | Computes the parity of an address.
--
-- Parity is defined in the following way:
--
--    - even-parity address:
--      an address with a pop count (Hamming weight) that is even.
--
--    - odd-parity address:
--      an address with a pop count (Hamming weight) that is odd.
--
-- Examples of even-parity and odd-parity addresses:
--
--    - 0b00000000 : even (Hamming weight = 0)
--    - 0b00000001 : odd  (Hamming weight = 1)
--    - 0b00000010 : odd  (Hamming weight = 1)
--    - 0b00000011 : even (Hamming weight = 2)
--    - 0b00000100 : odd  (Hamming weight = 1)
--    - ...
--    - 0b11111110 : odd  (Hamming weight = 7)
--    - 0b11111111 : even (Hamming weight = 8)
--
addressParity :: Address -> Parity
addressParity :: Address -> Parity
addressParity = Int -> Parity
forall a. Integral a => a -> Parity
parity (Int -> Parity) -> (Address -> Int) -> Address -> Parity
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Address -> Int
addressPopCount
  where
    addressPopCount :: Address -> Int
    addressPopCount :: Address -> Int
addressPopCount = (Int -> Word8 -> Int) -> Int -> ByteString -> Int
forall a. (a -> Word8 -> a) -> a -> ByteString -> a
BS.foldl' (\Int
acc -> (Int
acc Int -> Int -> Int
forall a. Num a => a -> a -> a
+) (Int -> Int) -> (Word8 -> Int) -> Word8 -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word8 -> Int
forall a. Bits a => a -> Int
Bits.popCount) Int
0 (ByteString -> Int) -> (Address -> ByteString) -> Address -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Address -> ByteString
unAddress

    parity :: Integral a => a -> Parity
    parity :: a -> Parity
parity a
a
        | a -> Bool
forall a. Integral a => a -> Bool
even a
a    = Parity
Even
        | Bool
otherwise = Parity
Odd

-- | Represents the parity of a value (whether the value is even or odd).
--
data Parity = Even | Odd
    deriving (Parity -> Parity -> Bool
(Parity -> Parity -> Bool)
-> (Parity -> Parity -> Bool) -> Eq Parity
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Parity -> Parity -> Bool
$c/= :: Parity -> Parity -> Bool
== :: Parity -> Parity -> Bool
$c== :: Parity -> Parity -> Bool
Eq, Int -> Parity -> ShowS
[Parity] -> ShowS
Parity -> [Char]
(Int -> Parity -> ShowS)
-> (Parity -> [Char]) -> ([Parity] -> ShowS) -> Show Parity
forall a.
(Int -> a -> ShowS) -> (a -> [Char]) -> ([a] -> ShowS) -> Show a
showList :: [Parity] -> ShowS
$cshowList :: [Parity] -> ShowS
show :: Parity -> [Char]
$cshow :: Parity -> [Char]
showsPrec :: Int -> Parity -> ShowS
$cshowsPrec :: Int -> Parity -> ShowS
Show)

--------------------------------------------------------------------------------
-- Internal utilities
--------------------------------------------------------------------------------

mkAddress :: Char -> Address
mkAddress :: Char -> Address
mkAddress Char
c = ByteString -> Address
Address (ByteString -> Address) -> ByteString -> Address
forall a b. (a -> b) -> a -> b
$ ByteString
"ADDR" ByteString -> Char -> ByteString
`B8.snoc` Char
c