-- | Tweak manipulation for Unique Block Iteration mode.
module Crypto.Threefish.UBI where
import Data.Word
import Data.Bits
import Crypto.Threefish.Common

data BlockType
  = Key
  | Config
  | Personalization
  | PublicKey
  | KeyIdentifier
  | Nonce
  | Message
  | Output

type2w64 :: BlockType -> Word64
type2w64 Key             = 0
type2w64 Config          = 4
type2w64 Personalization = 8
type2w64 PublicKey       = 12
type2w64 KeyIdentifier   = 16
type2w64 Nonce           = 20
type2w64 Message         = 48
type2w64 Output          = 63

type2int :: BlockType -> Int
type2int Key             = 0
type2int Config          = 4
type2int Personalization = 8
type2int PublicKey       = 12
type2int KeyIdentifier   = 16
type2int Nonce           = 20
type2int Message         = 48
type2int Output          = 63

{-# INLINE newTweak #-}
newTweak :: BlockType -> Tweak
newTweak t = setType t $ setFirst True $ Tweak 0 0

{-# INLINE setType #-}
setType :: BlockType -> Tweak -> Tweak
setType t (Tweak lo hi) =
    Tweak lo ((type2w64 t `shiftL` 56) .|. (hi .&. zeroType))
  where
    zeroType = complement (63 `shiftL` 56)

{-# INLINE setFirst #-}
setFirst :: Bool -> Tweak -> Tweak
setFirst set (Tweak lo hi) = Tweak lo ((if set then setBit else clearBit) hi 62)

{-# INLINE setLast #-}
setLast :: Bool -> Tweak -> Tweak
setLast set (Tweak lo hi) = Tweak lo ((if set then setBit else clearBit) hi 63)

{-# INLINE addBytes #-}
addBytes :: Word64 -> Tweak -> Tweak
addBytes bs (Tweak lo hi) = Tweak (lo + bs) hi

{-# INLINE configTweak #-}
configTweak :: Tweak
configTweak = setFirst True $ setLast True $ newTweak Config