{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE TypeApplications    #-}

module Evaluation.Builtins.BLS12_381.Utils
where

import Data.Bits (complement, xor, (.&.), (.|.))
import Data.ByteString as BS (ByteString, cons, uncons)
import Data.Word (Word8)

{- | ByteString utilities.  These are used in tests to check that the format of
   compressed points conforms to the specification at
   https://github.com/supranational/blst#serialization-format . -}

-- The most signiificant bit of a serialised curve point is set if the
-- serialised point is in compressed form (x-coordinate only)
compressionBit :: Word8
compressionBit :: Word8
compressionBit = Word8
0x80

-- The second most significant bit is set if and only if the point is the point
-- at infinity (the zero of the group); if it is set, all other bits should be zero.
infinityBit :: Word8
infinityBit :: Word8
infinityBit = Word8
0x40

-- The third most significant bit of a compressed point denotes the "sign" of
-- the y-coordinate of the associated point: it is set if and only if the point
-- is not the point at infinity and the y-coordinate is the lexicographically
-- larger one with the given x coordinate.
signBit :: Word8
signBit :: Word8
signBit = Word8
0x20

unsafeUnconsBS :: ByteString -> (Word8, ByteString)
unsafeUnconsBS :: ByteString -> (Word8, ByteString)
unsafeUnconsBS ByteString
b =
    case ByteString -> Maybe (Word8, ByteString)
BS.uncons ByteString
b of
      Maybe (Word8, ByteString)
Nothing -> [Char] -> (Word8, ByteString)
forall a. HasCallStack => [Char] -> a
error [Char]
"Tried to uncons empty bytestring"
      Just (Word8, ByteString)
p  -> (Word8, ByteString)
p

-- | Apply some function to the most significant byte of a bytestring
modifyMSB :: (Word8 -> Word8) -> ByteString -> ByteString
modifyMSB :: (Word8 -> Word8) -> ByteString -> ByteString
modifyMSB Word8 -> Word8
f ByteString
s =
    let (Word8
w,ByteString
rest) = ByteString -> (Word8, ByteString)
unsafeUnconsBS ByteString
s
    in Word8 -> ByteString -> ByteString
BS.cons (Word8 -> Word8
f Word8
w) ByteString
rest

-- | Flip a specified set of bits in the most significant byte of a bytestring.
flipBits :: Word8 -> ByteString -> ByteString
flipBits :: Word8 -> ByteString -> ByteString
flipBits Word8
mask = (Word8 -> Word8) -> ByteString -> ByteString
modifyMSB (Word8
mask Word8 -> Word8 -> Word8
forall a. Bits a => a -> a -> a
`xor`)

-- | Clear a specified set of bits in the most significant byte of a bytestring.
clearBits :: Word8 -> ByteString -> ByteString
clearBits :: Word8 -> ByteString -> ByteString
clearBits Word8
mask = (Word8 -> Word8) -> ByteString -> ByteString
modifyMSB ((Word8 -> Word8
forall a. Bits a => a -> a
complement Word8
mask) Word8 -> Word8 -> Word8
forall a. Bits a => a -> a -> a
.&.)

-- | Set a specified set of bits in the most significant byte of a bytestring.
setBits :: Word8 -> ByteString -> ByteString
setBits :: Word8 -> ByteString -> ByteString
setBits Word8
mask = (Word8 -> Word8) -> ByteString -> ByteString
modifyMSB (Word8
mask Word8 -> Word8 -> Word8
forall a. Bits a => a -> a -> a
.|.)

-- | Check that a specified set of bits is set in the most significant byte of a
-- bytestring.
isSet :: Word8 -> ByteString -> Bool
isSet :: Word8 -> ByteString -> Bool
isSet Word8
mask ByteString
s =
    let (Word8
w,ByteString
_) = ByteString -> (Word8, ByteString)
unsafeUnconsBS ByteString
s
    in Word8
w Word8 -> Word8 -> Word8
forall a. Bits a => a -> a -> a
.&. Word8
mask Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
mask