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

import           Unwitch.Errors
import           Unwitch.Constant
import qualified Data.Bits as Bits
import Data.Word
import Data.Int
import Numeric.Natural (Natural)
#ifdef __GLASGOW_HASKELL__
import Foreign.C.Types (CInt(CInt))
import           GHC.Exts (Int(..), Word(..), Float(..), Double(..),
                           intToInt8#, int8ToInt#,
                           intToInt16#, int16ToInt#,
                           intToInt32#, int32ToInt#,
                           intToInt64#,
                           int2Word#, word2Int#,
                           wordToWord8#, word8ToWord#,
                           wordToWord16#, word16ToWord#,
                           wordToWord32#, word32ToWord#,
                           wordToWord64#,
                           int2Float#, int2Double#,
                           (==#), (>=#), (<#), (>#))
import           GHC.Int (Int8(..), Int16(..), Int32(..), Int64(..))
import           GHC.Word (Word8(..), Word16(..), Word32(..), Word64(..))
import           GHC.Num.Integer (Integer(..), integerToWord#,
                                  integerFromWord#, integerEq#)
import           GHC.Num.Natural (Natural(NS, NB))
#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

-- | Checked conversion, fails if outside exact double integer range (±9007199254740991).
toDouble :: Integer -> Either Overflows Double
toDouble :: Integer -> Either Overflows Double
toDouble Integer
integer = if
    | Integer
integer Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
< -Integer
forall a. Num a => a
maxIntegralRepDouble -> Overflows -> Either Overflows Double
forall a b. a -> Either a b
Left Overflows
Underflow
    | Integer
integer Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
> Integer
forall a. Num a => a
maxIntegralRepDouble -> Overflows -> Either Overflows Double
forall a b. a -> Either a b
Left Overflows
Overflow
    | Bool
otherwise -> Double -> Either Overflows Double
forall a b. b -> Either a b
Right (Double -> Either Overflows Double)
-> Double -> Either Overflows Double
forall a b. (a -> b) -> a -> b
$ Integer -> Double
forall a b. (Integral a, Num b) => a -> b
Prelude.fromIntegral Integer
integer

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

-- | Returns 'Left' 'Underflow' for negative values.
toNatural :: Integer -> Either Overflows Natural
toNatural :: Integer -> Either Overflows Natural
toNatural Integer
integer = if
    | Integer
integer Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
< Integer
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
$ Integer -> Natural
forall a b. (Integral a, Num b) => a -> b
Prelude.fromIntegral Integer
integer

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

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

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

toInt64 :: Integer -> Maybe Int64
toInt64 :: Integer -> Maybe Int64
toInt64 = Integer -> Maybe Int64
forall a b.
(Integral a, Integral b, Bits a, Bits b) =>
a -> Maybe b
Bits.toIntegralSized

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

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

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

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

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

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

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

#ifdef __GLASGOW_HASKELL__
-- | Bounds-checked double conversion via IS/IP/IN
toDouble# :: Integer -> (# Overflows | Double #)
toDouble# :: Integer -> (# Overflows | Double #)
toDouble# Integer
x = case Integer
x of
  IS Int#
i# -> case Int#
i# Int# -> Int# -> Int#
<# Int#
-9007199254740991# of
    Int#
1# -> (# Overflows
Underflow | #)
    Int#
_  -> case Int#
i# Int# -> Int# -> Int#
># Int#
9007199254740991# of
      Int#
1# -> (# Overflows
Overflow | #)
      Int#
_  -> (# | Double# -> Double
D# (Int# -> Double#
int2Double# Int#
i#) #)
  IP ByteArray#
_ -> (# Overflows
Overflow | #)
  IN ByteArray#
_ -> (# Overflows
Underflow | #)

-- | Bounds-checked float conversion via IS/IP/IN
toFloat# :: Integer -> (# Overflows | Float #)
toFloat# :: Integer -> (# Overflows | Float #)
toFloat# Integer
x = case Integer
x of
  IS Int#
i# -> 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#) #)
  IP ByteArray#
_ -> (# Overflows
Overflow | #)
  IN ByteArray#
_ -> (# Overflows
Underflow | #)

-- | Integer->Natural via IS/IP/IN
toNatural# :: Integer -> (# Overflows | Natural #)
toNatural# :: Integer -> (# Overflows | Natural #)
toNatural# Integer
x = case Integer
x of
  IS Int#
i# -> case Int#
i# Int# -> Int# -> Int#
>=# Int#
0# of
    Int#
1# -> (# | Word# -> Natural
NS (Int# -> Word#
int2Word# Int#
i#) #)
    Int#
_  -> (# Overflows
Underflow | #)
  IP ByteArray#
ba# -> (# | ByteArray# -> Natural
NB ByteArray#
ba# #)
  IN ByteArray#
_ -> (# Overflows
Underflow | #)

-- | Integer->Int8 via IS/IP/IN, narrow and roundtrip at Int#
toInt8# :: Integer -> (# Int8 | (# #) #)
toInt8# :: Integer -> (# Int8 | (# #) #)
toInt8# Integer
x = case Integer
x of
  IS Int#
i# ->
    let 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#
_  -> (# | (# #) #)
  IP ByteArray#
_ -> (# | (# #) #)
  IN ByteArray#
_ -> (# | (# #) #)

-- | Integer->Int16 via IS/IP/IN
toInt16# :: Integer -> (# Int16 | (# #) #)
toInt16# :: Integer -> (# Int16 | (# #) #)
toInt16# Integer
x = case Integer
x of
  IS Int#
i# ->
    let 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#
_  -> (# | (# #) #)
  IP ByteArray#
_ -> (# | (# #) #)
  IN ByteArray#
_ -> (# | (# #) #)

-- | Integer->Int32 via IS/IP/IN
toInt32# :: Integer -> (# Int32 | (# #) #)
toInt32# :: Integer -> (# Int32 | (# #) #)
toInt32# Integer
x = case Integer
x of
  IS Int#
i# ->
    let 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#
_  -> (# | (# #) #)
  IP ByteArray#
_ -> (# | (# #) #)
  IN ByteArray#
_ -> (# | (# #) #)

-- | Integer->Int64 via IS/IP/IN
toInt64# :: Integer -> (# Int64 | (# #) #)
toInt64# :: Integer -> (# Int64 | (# #) #)
toInt64# Integer
x = case Integer
x of
  IS Int#
i# -> (# Int64# -> Int64
I64# (Int# -> Int64#
intToInt64# Int#
i#) | #)
  IP ByteArray#
_ -> (# | (# #) #)
  IN ByteArray#
_ -> (# | (# #) #)

-- | Integer->Int via IS/IP/IN
toInt# :: Integer -> (# Int | (# #) #)
toInt# :: Integer -> (# Int | (# #) #)
toInt# Integer
x = case Integer
x of
  IS Int#
i# -> (# Int# -> Int
I# Int#
i# | #)
  IP ByteArray#
_ -> (# | (# #) #)
  IN ByteArray#
_ -> (# | (# #) #)

-- | Integer->Word8, IS case uses signed->unsigned narrow
toWord8# :: Integer -> (# Word8 | (# #) #)
toWord8# :: Integer -> (# Word8 | (# #) #)
toWord8# Integer
x = case Integer
x of
  IS Int#
i# ->
    let 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#
_  -> (# | (# #) #)
  IP ByteArray#
_ -> (# | (# #) #)
  IN ByteArray#
_ -> (# | (# #) #)

-- | Integer->Word16
toWord16# :: Integer -> (# Word16 | (# #) #)
toWord16# :: Integer -> (# Word16 | (# #) #)
toWord16# Integer
x = case Integer
x of
  IS Int#
i# ->
    let 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#
_  -> (# | (# #) #)
  IP ByteArray#
_ -> (# | (# #) #)
  IN ByteArray#
_ -> (# | (# #) #)

-- | Integer->Word32
toWord32# :: Integer -> (# Word32 | (# #) #)
toWord32# :: Integer -> (# Word32 | (# #) #)
toWord32# Integer
x = case Integer
x of
  IS Int#
i# ->
    let n# :: Word32#
n# = Word# -> Word32#
wordToWord32# (Int# -> Word#
int2Word# Int#
i#)
    in case Word# -> Int#
word2Int# (Word32# -> Word#
word32ToWord# Word32#
n#) Int# -> Int# -> Int#
==# Int#
i# of
      Int#
1# -> (# Word32# -> Word32
W32# Word32#
n# | #)
      Int#
_  -> (# | (# #) #)
  IP ByteArray#
_ -> (# | (# #) #)
  IN ByteArray#
_ -> (# | (# #) #)

-- | Integer->Word64: IS checks non-negative; IP uses integerToWord# roundtrip
toWord64# :: Integer -> (# Word64 | (# #) #)
toWord64# :: Integer -> (# Word64 | (# #) #)
toWord64# Integer
x = case Integer
x of
  IS Int#
i# -> case Int#
i# Int# -> Int# -> Int#
>=# Int#
0# of
    Int#
1# -> (# Word64# -> Word64
W64# (Word# -> Word64#
wordToWord64# (Int# -> Word#
int2Word# Int#
i#)) | #)
    Int#
_  -> (# | (# #) #)
  IP ByteArray#
_ ->
    let w# :: Word#
w# = Integer -> Word#
integerToWord# Integer
x
    in case Integer -> Integer -> Int#
integerEq# (Word# -> Integer
integerFromWord# Word#
w#) Integer
x of
      Int#
1# -> (# Word64# -> Word64
W64# (Word# -> Word64#
wordToWord64# Word#
w#) | #)
      Int#
_  -> (# | (# #) #)
  IN ByteArray#
_ -> (# | (# #) #)

-- | Integer->Word: IS checks non-negative; IP uses integerToWord# roundtrip
toWord# :: Integer -> (# Word | (# #) #)
toWord# :: Integer -> (# Word | (# #) #)
toWord# Integer
x = case Integer
x of
  IS Int#
i# -> case Int#
i# Int# -> Int# -> Int#
>=# Int#
0# of
    Int#
1# -> (# Word# -> Word
W# (Int# -> Word#
int2Word# Int#
i#) | #)
    Int#
_  -> (# | (# #) #)
  IP ByteArray#
_ ->
    let w# :: Word#
w# = Integer -> Word#
integerToWord# Integer
x
    in case Integer -> Integer -> Int#
integerEq# (Word# -> Integer
integerFromWord# Word#
w#) Integer
x of
      Int#
1# -> (# Word# -> Word
W# Word#
w# | #)
      Int#
_  -> (# | (# #) #)
  IN ByteArray#
_ -> (# | (# #) #)
#endif