-- | Conversions from 'Word32'.
module Unwitch.Convert.Word32
  ( -- * Conversions
    toWord8
  , toWord16
  , toWord64
  , toWord
  , toNatural
  , toInt8
  , toInt16
  , toInt32
  , toInt64
  , toInt
  , toInteger
  , toFloat
  , toDouble
#ifdef __GLASGOW_HASKELL__
  , toCInt
#endif
#ifdef __GLASGOW_HASKELL__
  -- * Unboxed conversions
  -- $unboxed
  , toWord8#
  , toWord16#
  , toWord#
  , toInt8#
  , toInt16#
  , toInt32#
  , toInt#
  , toFloat#
#endif
  )
where

import           Unwitch.Errors
import           Unwitch.Constant
import qualified Data.Bits as Bits
import           Data.Word
import           Data.Int
import           Numeric.Natural (Natural)
import           Prelude hiding (toInteger)
#ifdef __GLASGOW_HASKELL__
import           Foreign.C.Types (CInt(CInt))
import           GHC.Exts (Int(..), Word(..), Float(..),
                           word32ToWord#, word2Int#,
                           wordToWord8#, word8ToWord#,
                           wordToWord16#, word16ToWord#,
                           intToInt8#, int8ToInt#,
                           intToInt16#, int16ToInt#,
                           intToInt32#, int32ToInt#,
                           int2Float#,
                           eqWord#, leWord#, (==#), (>=#))
import           GHC.Int (Int8(..), Int16(..), Int32(..))
import           GHC.Word (Word8(..), Word16(..), Word32(..))
#endif

#ifdef __GLASGOW_HASKELL__
-- $unboxed
-- These use GHC unboxed types and unboxed sums for zero-allocation
-- failure handling. Requires the @MagicHash@, @UnboxedSums@ and
-- @UnboxedTuples@ language extensions.
-- See the <https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/primitives.html GHC manual on unboxed types>.
#endif

toWord8 :: Word32 -> Maybe Word8
toWord8 :: Word32 -> Maybe Word8
toWord8 = Word32 -> Maybe Word8
forall a b.
(Integral a, Integral b, Bits a, Bits b) =>
a -> Maybe b
Bits.toIntegralSized

toWord16 :: Word32 -> Maybe Word16
toWord16 :: Word32 -> Maybe Word16
toWord16 = Word32 -> Maybe Word16
forall a b.
(Integral a, Integral b, Bits a, Bits b) =>
a -> Maybe b
Bits.toIntegralSized

toWord64 :: Word32 -> Word64
toWord64 :: Word32 -> Word64
toWord64 = Word32 -> Word64
forall a b. (Integral a, Num b) => a -> b
fromIntegral

toWord :: Word32 -> Maybe Word
toWord :: Word32 -> Maybe Word
toWord = Word32 -> Maybe Word
forall a b.
(Integral a, Integral b, Bits a, Bits b) =>
a -> Maybe b
Bits.toIntegralSized

toNatural :: Word32 -> Natural
toNatural :: Word32 -> Natural
toNatural = Word32 -> Natural
forall a b. (Integral a, Num b) => a -> b
fromIntegral

toInt8 :: Word32 -> Maybe Int8
toInt8 :: Word32 -> Maybe Int8
toInt8 = Word32 -> Maybe Int8
forall a b.
(Integral a, Integral b, Bits a, Bits b) =>
a -> Maybe b
Bits.toIntegralSized

toInt16 :: Word32 -> Maybe Int16
toInt16 :: Word32 -> Maybe Int16
toInt16 = Word32 -> Maybe Int16
forall a b.
(Integral a, Integral b, Bits a, Bits b) =>
a -> Maybe b
Bits.toIntegralSized

toInt32 :: Word32 -> Maybe Int32
toInt32 :: Word32 -> Maybe Int32
toInt32 = Word32 -> Maybe Int32
forall a b.
(Integral a, Integral b, Bits a, Bits b) =>
a -> Maybe b
Bits.toIntegralSized

toInt64 :: Word32 -> Int64
toInt64 :: Word32 -> Int64
toInt64 = Word32 -> Int64
forall a b. (Integral a, Num b) => a -> b
fromIntegral

toInt :: Word32 -> Maybe Int
toInt :: Word32 -> Maybe Int
toInt = Word32 -> Maybe Int
forall a b.
(Integral a, Integral b, Bits a, Bits b) =>
a -> Maybe b
Bits.toIntegralSized

toInteger :: Word32 -> Integer
toInteger :: Word32 -> Integer
toInteger = Word32 -> Integer
forall a b. (Integral a, Num b) => a -> b
fromIntegral

-- | Checked conversion, fails with 'Overflow' if outside exact float integer range.
toFloat :: Word32 -> Either Overflows Float
toFloat :: Word32 -> Either Overflows Float
toFloat Word32
x = if
  | Word32
x Word32 -> Word32 -> Bool
forall a. Ord a => a -> a -> Bool
> Word32
forall a. Num a => a
maxIntegralRepFloat -> Overflows -> Either Overflows Float
forall a b. a -> Either a b
Left Overflows
Overflow
  | Bool
otherwise               -> Float -> Either Overflows Float
forall a b. b -> Either a b
Right (Float -> Either Overflows Float)
-> Float -> Either Overflows Float
forall a b. (a -> b) -> a -> b
$ Word32 -> Float
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word32
x

toDouble :: Word32 -> Double
toDouble :: Word32 -> Double
toDouble = Word32 -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral

#ifdef __GLASGOW_HASKELL__
-- | Narrowing conversion via Int32, fails if outside Int32 range.
toCInt :: Word32 -> Maybe CInt
toCInt :: Word32 -> Maybe CInt
toCInt Word32
x = Int32 -> CInt
CInt (Int32 -> CInt) -> Maybe Int32 -> Maybe CInt
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Word32 -> Maybe Int32
toInt32 Word32
x
#endif

#ifdef __GLASGOW_HASKELL__
-- | Unsigned narrowing, roundtrip at Word#
toWord8# :: Word32 -> (# Word8 | (# #) #)
toWord8# :: Word32 -> (# Word8 | (# #) #)
toWord8# (W32# Word32#
w32#) =
  let w# :: Word#
w# = Word32# -> Word#
word32ToWord# Word32#
w32#
      n# :: Word8#
n# = Word# -> Word8#
wordToWord8# Word#
w#
  in case Word8# -> Word#
word8ToWord# Word8#
n# Word# -> Word# -> Int#
`eqWord#` Word#
w# of
    Int#
1# -> (# Word8# -> Word8
W8# Word8#
n# | #)
    Int#
_  -> (# | (# #) #)

-- | Unsigned narrowing, roundtrip at Word#
toWord16# :: Word32 -> (# Word16 | (# #) #)
toWord16# :: Word32 -> (# Word16 | (# #) #)
toWord16# (W32# Word32#
w32#) =
  let w# :: Word#
w# = Word32# -> Word#
word32ToWord# Word32#
w32#
      n# :: Word16#
n# = Word# -> Word16#
wordToWord16# Word#
w#
  in case Word16# -> Word#
word16ToWord# Word16#
n# Word# -> Word# -> Int#
`eqWord#` Word#
w# of
    Int#
1# -> (# Word16# -> Word16
W16# Word16#
n# | #)
    Int#
_  -> (# | (# #) #)

-- | Word32 always fits in Word (Word is at least 32 bits)
toWord# :: Word32 -> (# Word | (# #) #)
toWord# :: Word32 -> (# Word | (# #) #)
toWord# (W32# Word32#
w32#) = (# Word# -> Word
W# (Word32# -> Word#
word32ToWord# Word32#
w32#) | #)

-- | Unsigned->signed, source fits in Int#, roundtrip at Int#
toInt8# :: Word32 -> (# Int8 | (# #) #)
toInt8# :: Word32 -> (# Int8 | (# #) #)
toInt8# (W32# Word32#
w32#) =
  let i# :: Int#
i# = Word# -> Int#
word2Int# (Word32# -> Word#
word32ToWord# Word32#
w32#)
      n# :: Int8#
n# = Int# -> Int8#
intToInt8# Int#
i#
  in case Int8# -> Int#
int8ToInt# Int8#
n# Int# -> Int# -> Int#
==# Int#
i# of
    Int#
1# -> (# Int8# -> Int8
I8# Int8#
n# | #)
    Int#
_  -> (# | (# #) #)

-- | Unsigned->signed, source fits in Int#, roundtrip at Int#
toInt16# :: Word32 -> (# Int16 | (# #) #)
toInt16# :: Word32 -> (# Int16 | (# #) #)
toInt16# (W32# Word32#
w32#) =
  let i# :: Int#
i# = Word# -> Int#
word2Int# (Word32# -> Word#
word32ToWord# Word32#
w32#)
      n# :: Int16#
n# = Int# -> Int16#
intToInt16# Int#
i#
  in case Int16# -> Int#
int16ToInt# Int16#
n# Int# -> Int# -> Int#
==# Int#
i# of
    Int#
1# -> (# Int16# -> Int16
I16# Int16#
n# | #)
    Int#
_  -> (# | (# #) #)

-- | Unsigned->signed, roundtrip at Int#
toInt32# :: Word32 -> (# Int32 | (# #) #)
toInt32# :: Word32 -> (# Int32 | (# #) #)
toInt32# (W32# Word32#
w32#) =
  let i# :: Int#
i# = Word# -> Int#
word2Int# (Word32# -> Word#
word32ToWord# Word32#
w32#)
      n# :: Int32#
n# = Int# -> Int32#
intToInt32# Int#
i#
  in case Int32# -> Int#
int32ToInt# Int32#
n# Int# -> Int# -> Int#
==# Int#
i# of
    Int#
1# -> (# Int32# -> Int32
I32# Int32#
n# | #)
    Int#
_  -> (# | (# #) #)

-- | Word32 fits in non-negative Int on all platforms, check via sign bit
toInt# :: Word32 -> (# Int | (# #) #)
toInt# :: Word32 -> (# Int | (# #) #)
toInt# (W32# Word32#
w32#) =
  let i# :: Int#
i# = Word# -> Int#
word2Int# (Word32# -> Word#
word32ToWord# Word32#
w32#)
  in case Int#
i# Int# -> Int# -> Int#
>=# Int#
0# of
    Int#
1# -> (# Int# -> Int
I# Int#
i# | #)
    Int#
_  -> (# | (# #) #)

-- | Bounds-checked float conversion
toFloat# :: Word32 -> (# Overflows | Float #)
toFloat# :: Word32 -> (# Overflows | Float #)
toFloat# (W32# Word32#
w32#) = case Word# -> Word# -> Int#
leWord# (Word32# -> Word#
word32ToWord# Word32#
w32#) Word#
16777215## of
  Int#
1# -> (# | Float# -> Float
F# (Int# -> Float#
int2Float# (Word# -> Int#
word2Int# (Word32# -> Word#
word32ToWord# Word32#
w32#))) #)
  Int#
_  -> (# Overflows
Overflow | #)
#endif