-- | Conversions from 'Int32'.
module Unwitch.Convert.Int32
  ( -- * Conversions
    toInt8
  , toInt16
  , toInt64
  , toInt
  , toInteger
  , toWord8
  , toWord16
  , toWord32
  , toWord64
  , toWord
  , toNatural
  , toFloat
  , toDouble
#ifdef __GLASGOW_HASKELL__
  , toCInt
#endif
#ifdef __GLASGOW_HASKELL__
  -- * Unboxed conversions
  -- $unboxed
  , toInt8#
  , toInt16#
  , toInt#
  , toWord8#
  , toWord16#
  , toWord32#
  , toWord64#
  , toWord#
  , toNatural#
  , 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(..),
                           int32ToInt#, intToInt8#, int8ToInt#,
                           intToInt16#, int16ToInt#,
                           int2Word#, word2Int#,
                           wordToWord8#, word8ToWord#,
                           wordToWord16#, word16ToWord#,
                           wordToWord32#, wordToWord64#,
                           int2Float#,
                           (==#), (>=#), (<#), (>#))
import           GHC.Int (Int8(..), Int16(..), Int32(..))
import           GHC.Word (Word8(..), Word16(..), Word32(..), Word64(..))
import           GHC.Num.Natural (Natural(NS))
#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

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

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

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

#ifdef __GLASGOW_HASKELL__
-- | Total conversion — GHC implements 'Int' using the primitive type
-- @Int#@, whose size equals the @MachDeps.h@ constant
-- @WORD_SIZE_IN_BITS@ (32 on 32-bit platforms, 64 on 64-bit).
-- Since @Int#@ is always at least 32 bits wide, every 'Int32' value
-- fits without loss.
--
-- See: <https://hackage.haskell.org/package/ghc-prim-0.9.0/docs/src/GHC.Prim.html GHC.Prim>
-- ("Int32 always fits in Int (Int is at least 32 bits)") and
-- <https://hackage.haskell.org/package/base/docs/Data-Int.html Data.Int>
-- ("A fixed-precision integer type with at least the range [-2^29 .. 2^29-1]").
toInt :: Int32 -> Int
toInt :: Int32 -> Int
toInt (I32# Int32#
x#) = Int# -> Int
I# (Int32# -> Int#
int32ToInt# Int32#
x#)
#else
-- | Fallible conversion — the Haskell Report only guarantees 'Int' has
-- at least the range @[-2^29 .. 2^29-1]@ (30 bits), so 'Int32' values
-- outside that range may not fit on non-GHC compilers.
toInt :: Int32 -> Maybe Int
toInt = Bits.toIntegralSized
#endif

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

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

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

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

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

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

-- | Signed-to-unsigned conversion, returns 'Left' 'Underflow' for negative values.
toNatural :: Int32 -> Either Overflows Natural
toNatural :: Int32 -> Either Overflows Natural
toNatural Int32
x = if
  | Int32
x Int32 -> Int32 -> Bool
forall a. Ord a => a -> a -> Bool
< Int32
0     -> Overflows -> Either Overflows Natural
forall a b. a -> Either a b
Left Overflows
Underflow
  | Bool
otherwise  -> Natural -> Either Overflows Natural
forall a b. b -> Either a b
Right (Natural -> Either Overflows Natural)
-> Natural -> Either Overflows Natural
forall a b. (a -> b) -> a -> b
$ Int32 -> Natural
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int32
x

-- | Checked conversion, fails if outside exact float integer range (+-16777215).
toFloat :: Int32 -> Either Overflows Float
toFloat :: Int32 -> Either Overflows Float
toFloat Int32
x = if
  | Int32
x Int32 -> Int32 -> Bool
forall a. Ord a => a -> a -> Bool
< -Int32
forall a. Num a => a
maxIntegralRepFloat -> Overflows -> Either Overflows Float
forall a b. a -> Either a b
Left Overflows
Underflow
  | Int32
x Int32 -> Int32 -> Bool
forall a. Ord a => a -> a -> Bool
> Int32
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
$ Int32 -> Float
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int32
x

#ifdef __GLASGOW_HASKELL__
-- | Direct wrapping, CInt is a newtype over Int32.
toCInt :: Int32 -> CInt
toCInt :: Int32 -> CInt
toCInt = Int32 -> CInt
CInt
#endif

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

#ifdef __GLASGOW_HASKELL__
-- | Signed narrowing, roundtrip at Int#
toInt8# :: Int32 -> (# Int8 | (# #) #)
toInt8# :: Int32 -> (# Int8 | (# #) #)
toInt8# (I32# Int32#
x32#) =
  let i# :: Int#
i# = Int32# -> Int#
int32ToInt# Int32#
x32#
      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#
_  -> (# | (# #) #)

-- | Signed narrowing, roundtrip at Int#
toInt16# :: Int32 -> (# Int16 | (# #) #)
toInt16# :: Int32 -> (# Int16 | (# #) #)
toInt16# (I32# Int32#
x32#) =
  let i# :: Int#
i# = Int32# -> Int#
int32ToInt# Int32#
x32#
      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#
_  -> (# | (# #) #)

-- | Int32 always fits in Int (Int is at least 32 bits)
toInt# :: Int32 -> (# Int | (# #) #)
toInt# :: Int32 -> (# Int | (# #) #)
toInt# (I32# Int32#
x32#) = (# Int# -> Int
I# (Int32# -> Int#
int32ToInt# Int32#
x32#) | #)

-- | Signed->unsigned narrow, roundtrip via Word# back to Int#
toWord8# :: Int32 -> (# Word8 | (# #) #)
toWord8# :: Int32 -> (# Word8 | (# #) #)
toWord8# (I32# Int32#
x32#) =
  let i# :: Int#
i# = Int32# -> Int#
int32ToInt# Int32#
x32#
      n# :: Word8#
n# = Word# -> Word8#
wordToWord8# (Int# -> Word#
int2Word# Int#
i#)
  in case Word# -> Int#
word2Int# (Word8# -> Word#
word8ToWord# Word8#
n#) Int# -> Int# -> Int#
==# Int#
i# of
    Int#
1# -> (# Word8# -> Word8
W8# Word8#
n# | #)
    Int#
_  -> (# | (# #) #)

-- | Signed->unsigned narrow, roundtrip via Word# back to Int#
toWord16# :: Int32 -> (# Word16 | (# #) #)
toWord16# :: Int32 -> (# Word16 | (# #) #)
toWord16# (I32# Int32#
x32#) =
  let i# :: Int#
i# = Int32# -> Int#
int32ToInt# Int32#
x32#
      n# :: Word16#
n# = Word# -> Word16#
wordToWord16# (Int# -> Word#
int2Word# Int#
i#)
  in case Word# -> Int#
word2Int# (Word16# -> Word#
word16ToWord# Word16#
n#) Int# -> Int# -> Int#
==# Int#
i# of
    Int#
1# -> (# Word16# -> Word16
W16# Word16#
n# | #)
    Int#
_  -> (# | (# #) #)

-- | Signed->unsigned, check non-negative
toWord32# :: Int32 -> (# Word32 | (# #) #)
toWord32# :: Int32 -> (# Word32 | (# #) #)
toWord32# (I32# Int32#
x32#) = case Int32# -> Int#
int32ToInt# Int32#
x32# Int# -> Int# -> Int#
>=# Int#
0# of
  Int#
1# -> (# Word32# -> Word32
W32# (Word# -> Word32#
wordToWord32# (Int# -> Word#
int2Word# (Int32# -> Int#
int32ToInt# Int32#
x32#))) | #)
  Int#
_  -> (# | (# #) #)

-- | Signed->unsigned, check non-negative
toWord64# :: Int32 -> (# Word64 | (# #) #)
toWord64# :: Int32 -> (# Word64 | (# #) #)
toWord64# (I32# Int32#
x32#) = case Int32# -> Int#
int32ToInt# Int32#
x32# Int# -> Int# -> Int#
>=# Int#
0# of
  Int#
1# -> (# Word64# -> Word64
W64# (Word# -> Word64#
wordToWord64# (Int# -> Word#
int2Word# (Int32# -> Int#
int32ToInt# Int32#
x32#))) | #)
  Int#
_  -> (# | (# #) #)

-- | Signed->unsigned, check non-negative
toWord# :: Int32 -> (# Word | (# #) #)
toWord# :: Int32 -> (# Word | (# #) #)
toWord# (I32# Int32#
x32#) = case Int32# -> Int#
int32ToInt# Int32#
x32# Int# -> Int# -> Int#
>=# Int#
0# of
  Int#
1# -> (# Word# -> Word
W# (Int# -> Word#
int2Word# (Int32# -> Int#
int32ToInt# Int32#
x32#)) | #)
  Int#
_  -> (# | (# #) #)

-- | Check non-negative, construct NS directly
toNatural# :: Int32 -> (# Overflows | Natural #)
toNatural# :: Int32 -> (# Overflows | Natural #)
toNatural# (I32# Int32#
x32#) = case Int32# -> Int#
int32ToInt# Int32#
x32# Int# -> Int# -> Int#
>=# Int#
0# of
  Int#
1# -> (# | Word# -> Natural
NS (Int# -> Word#
int2Word# (Int32# -> Int#
int32ToInt# Int32#
x32#)) #)
  Int#
_  -> (# Overflows
Underflow | #)

-- | Bounds-checked float conversion
toFloat# :: Int32 -> (# Overflows | Float #)
toFloat# :: Int32 -> (# Overflows | Float #)
toFloat# (I32# Int32#
x32#) =
  let i# :: Int#
i# = Int32# -> Int#
int32ToInt# Int32#
x32#
  in case Int#
i# Int# -> Int# -> Int#
<# Int#
-16777215# of
    Int#
1# -> (# Overflows
Underflow | #)
    Int#
_  -> case Int#
i# Int# -> Int# -> Int#
># Int#
16777215# of
      Int#
1# -> (# Overflows
Overflow | #)
      Int#
_  -> (# | Float# -> Float
F# (Int# -> Float#
int2Float# Int#
i#) #)
#endif