{-# LANGUAGE PatternSynonyms     #-}

-- | PackStream integer type covering the full @[-2^63, 2^64-1]@ range.
module Data.PackStream.Integer
    ( PSInteger
    , ToPSInteger(..)
    , FromPSInteger(..)
    , fromIntegerTry

      -- ** Internal helper
    , tryPSInteger
    ) where

import           Compat.Prelude

import           Control.Exception     (ArithException (DivideByZero, Overflow, Underflow),
                                        throw)
import           Data.Kind             (Constraint, Type)
import           TextShow
import TextShow.Generic
-- import GHC.Generics

import           Compat.Binary
import           Data.PackStream.Tags


-- | Integer type that represents the value range of integral numbers in PackStream; i.e. \( \left[ -2^{63}, 2^{64}-1 \right] \).
-- In other words, `PSInteger` provides the union of the value ranges of `Word64` and `Int64`.
--
-- This type can be unboxed (i.e. via @{-# UNPACK #-}@).
type PSInteger :: Type
data PSInteger = PSInteger {-# UNPACK #-} !Int64
  deriving stock (PSInteger -> PSInteger -> Bool
(PSInteger -> PSInteger -> Bool)
-> (PSInteger -> PSInteger -> Bool) -> Eq PSInteger
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: PSInteger -> PSInteger -> Bool
== :: PSInteger -> PSInteger -> Bool
$c/= :: PSInteger -> PSInteger -> Bool
/= :: PSInteger -> PSInteger -> Bool
Eq, Eq PSInteger
Eq PSInteger =>
(PSInteger -> PSInteger -> Ordering)
-> (PSInteger -> PSInteger -> Bool)
-> (PSInteger -> PSInteger -> Bool)
-> (PSInteger -> PSInteger -> Bool)
-> (PSInteger -> PSInteger -> Bool)
-> (PSInteger -> PSInteger -> PSInteger)
-> (PSInteger -> PSInteger -> PSInteger)
-> Ord PSInteger
PSInteger -> PSInteger -> Bool
PSInteger -> PSInteger -> Ordering
PSInteger -> PSInteger -> PSInteger
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
$ccompare :: PSInteger -> PSInteger -> Ordering
compare :: PSInteger -> PSInteger -> Ordering
$c< :: PSInteger -> PSInteger -> Bool
< :: PSInteger -> PSInteger -> Bool
$c<= :: PSInteger -> PSInteger -> Bool
<= :: PSInteger -> PSInteger -> Bool
$c> :: PSInteger -> PSInteger -> Bool
> :: PSInteger -> PSInteger -> Bool
$c>= :: PSInteger -> PSInteger -> Bool
>= :: PSInteger -> PSInteger -> Bool
$cmax :: PSInteger -> PSInteger -> PSInteger
max :: PSInteger -> PSInteger -> PSInteger
$cmin :: PSInteger -> PSInteger -> PSInteger
min :: PSInteger -> PSInteger -> PSInteger
Ord, (forall x. PSInteger -> Rep PSInteger x)
-> (forall x. Rep PSInteger x -> PSInteger) -> Generic PSInteger
forall x. Rep PSInteger x -> PSInteger
forall x. PSInteger -> Rep PSInteger x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. PSInteger -> Rep PSInteger x
from :: forall x. PSInteger -> Rep PSInteger x
$cto :: forall x. Rep PSInteger x -> PSInteger
to :: forall x. Rep PSInteger x -> PSInteger
Generic)
  deriving Int -> PSInteger -> Text
Int -> PSInteger -> Text
Int -> PSInteger -> Builder
[PSInteger] -> Text
[PSInteger] -> Text
[PSInteger] -> Builder
PSInteger -> Text
PSInteger -> Text
PSInteger -> Builder
(Int -> PSInteger -> Builder)
-> (PSInteger -> Builder)
-> ([PSInteger] -> Builder)
-> (Int -> PSInteger -> Text)
-> (PSInteger -> Text)
-> ([PSInteger] -> Text)
-> (Int -> PSInteger -> Text)
-> (PSInteger -> Text)
-> ([PSInteger] -> Text)
-> TextShow PSInteger
forall a.
(Int -> a -> Builder)
-> (a -> Builder)
-> ([a] -> Builder)
-> (Int -> a -> Text)
-> (a -> Text)
-> ([a] -> Text)
-> (Int -> a -> Text)
-> (a -> Text)
-> ([a] -> Text)
-> TextShow a
$cshowbPrec :: Int -> PSInteger -> Builder
showbPrec :: Int -> PSInteger -> Builder
$cshowb :: PSInteger -> Builder
showb :: PSInteger -> Builder
$cshowbList :: [PSInteger] -> Builder
showbList :: [PSInteger] -> Builder
$cshowtPrec :: Int -> PSInteger -> Text
showtPrec :: Int -> PSInteger -> Text
$cshowt :: PSInteger -> Text
showt :: PSInteger -> Text
$cshowtList :: [PSInteger] -> Text
showtList :: [PSInteger] -> Text
$cshowtlPrec :: Int -> PSInteger -> Text
showtlPrec :: Int -> PSInteger -> Text
$cshowtl :: PSInteger -> Text
showtl :: PSInteger -> Text
$cshowtlList :: [PSInteger] -> Text
showtlList :: [PSInteger] -> Text
TextShow via FromGeneric (PSInteger)

-- NOTE: Internal invariant of 'PSInteger'
--
-- 'isW64' MUST be true IFF the value range of `Int64` cannot represent the semantic value of 'value'
--
-- Consequently, when 'isW64' is true, 'value :: Int64' must be negative.

-- NB: only valid if isW64 is true
-- toW64 :: Int64 -> Word64
-- toW64 = intCastIso

-- | Types that can be losslessly converted to 'PSInteger'.
type ToPSInteger :: Type -> Constraint
class ToPSInteger a where
  toPSInteger :: a -> PSInteger

instance ToPSInteger Int8  where toPSInteger :: Int8 -> PSInteger
toPSInteger Int8
i = Int64 -> PSInteger
PSInteger (Int8 -> Int64
forall a b.
(Integral a, Integral b, IsIntSubType a b ~ 'True) =>
a -> b
intCast Int8
i)
instance ToPSInteger Int16 where toPSInteger :: Int16 -> PSInteger
toPSInteger Int16
i = Int64 -> PSInteger
PSInteger (Int16 -> Int64
forall a b.
(Integral a, Integral b, IsIntSubType a b ~ 'True) =>
a -> b
intCast Int16
i)
instance ToPSInteger Int32 where toPSInteger :: Int32 -> PSInteger
toPSInteger Int32
i = Int64 -> PSInteger
PSInteger (Int32 -> Int64
forall a b.
(Integral a, Integral b, IsIntSubType a b ~ 'True) =>
a -> b
intCast Int32
i)
instance ToPSInteger Int64 where toPSInteger :: Int64 -> PSInteger
toPSInteger   = Int64 -> PSInteger
PSInteger 
instance ToPSInteger Int   where toPSInteger :: Int -> PSInteger
toPSInteger Int
i = Int64 -> PSInteger
PSInteger (Int -> Int64
forall a b.
(Integral a, Integral b, IsIntSubType a b ~ 'True) =>
a -> b
intCast Int
i)

instance ToPSInteger Word8  where toPSInteger :: Word8 -> PSInteger
toPSInteger Word8
w = Int64 -> PSInteger
PSInteger (Word8 -> Int64
forall a b.
(Integral a, Integral b, IsIntSubType a b ~ 'True) =>
a -> b
intCast Word8
w)
instance ToPSInteger Word16 where toPSInteger :: Word16 -> PSInteger
toPSInteger Word16
w = Int64 -> PSInteger
PSInteger (Word16 -> Int64
forall a b.
(Integral a, Integral b, IsIntSubType a b ~ 'True) =>
a -> b
intCast Word16
w)
instance ToPSInteger Word32 where toPSInteger :: Word32 -> PSInteger
toPSInteger Word32
w = Int64 -> PSInteger
PSInteger (Word32 -> Int64
forall a b.
(Integral a, Integral b, IsIntSubType a b ~ 'True) =>
a -> b
intCast Word32
w)
-- instance ToPSInteger Word64 where toPSInteger w = PSInteger (i<0) i where i = fromIntegral w
-- instance ToPSInteger Word   where toPSInteger w = PSInteger (i<0) i where i = fromIntegral w

-- | Convert a 'PSInteger' value to something else if possible
--
-- The instances for 'FromPSInteger' are supposed to be consistent with the respective instances for 'ToPSInteger', e.g.
--
-- > fromPSInteger . toPSInteger == Just
--
type FromPSInteger :: Type -> Constraint
class FromPSInteger a where
  fromPSInteger :: PSInteger -> Maybe a

-- instance FromPSInteger Word where
--   fromPSInteger (PSInteger w)  = intCastMaybe (toW64 w)
--   fromPSInteger (PSInteger i) = intCastMaybe i

-- instance FromPSInteger Word64 where
--   fromPSInteger (PSInteger w)  = Just $! toW64 w
--   fromPSInteger (PSInteger i) = intCastMaybe i

instance FromPSInteger Word32 where
  fromPSInteger :: PSInteger -> Maybe Word32
fromPSInteger (PSInteger Int64
i) = Int64 -> Maybe Word32
forall a b.
(Integral a, Integral b, Bits a, Bits b) =>
a -> Maybe b
intCastMaybe Int64
i

instance FromPSInteger Word16 where
  fromPSInteger :: PSInteger -> Maybe Word16
fromPSInteger (PSInteger Int64
i) = Int64 -> Maybe Word16
forall a b.
(Integral a, Integral b, Bits a, Bits b) =>
a -> Maybe b
intCastMaybe Int64
i

instance FromPSInteger Word8 where
  fromPSInteger :: PSInteger -> Maybe Word8
fromPSInteger (PSInteger Int64
i) = Int64 -> Maybe Word8
forall a b.
(Integral a, Integral b, Bits a, Bits b) =>
a -> Maybe b
intCastMaybe Int64
i

-----

instance FromPSInteger Int where
  fromPSInteger :: PSInteger -> Maybe Int
fromPSInteger (PSInteger Int64
i) = Int64 -> Maybe Int
forall a b.
(Integral a, Integral b, Bits a, Bits b) =>
a -> Maybe b
intCastMaybe Int64
i

instance FromPSInteger Int64 where
  fromPSInteger :: PSInteger -> Maybe Int64
fromPSInteger (PSInteger Int64
i) = Int64 -> Maybe Int64
forall a. a -> Maybe a
Just Int64
i

instance FromPSInteger Int32 where
  fromPSInteger :: PSInteger -> Maybe Int32
fromPSInteger (PSInteger Int64
i) = Int64 -> Maybe Int32
forall a b.
(Integral a, Integral b, Bits a, Bits b) =>
a -> Maybe b
intCastMaybe Int64
i

instance FromPSInteger Int16 where
  fromPSInteger :: PSInteger -> Maybe Int16
fromPSInteger (PSInteger Int64
i) = Int64 -> Maybe Int16
forall a b.
(Integral a, Integral b, Bits a, Bits b) =>
a -> Maybe b
intCastMaybe Int64
i

instance FromPSInteger Int8 where
  fromPSInteger :: PSInteger -> Maybe Int8
fromPSInteger (PSInteger Int64
i) = Int64 -> Maybe Int8
forall a b.
(Integral a, Integral b, Bits a, Bits b) =>
a -> Maybe b
intCastMaybe Int64
i

----------------------------------------------------------------------------

instance Bounded PSInteger where
  minBound :: PSInteger
minBound = Int64 -> PSInteger
PSInteger Int64
forall a. Bounded a => a
minBound
  maxBound :: PSInteger
maxBound = Int64 -> PSInteger
PSInteger  (-Int64
1) -- this is why we can't autoderive

instance Enum PSInteger where
  toEnum :: Int -> PSInteger
toEnum Int
i = Int64 -> PSInteger
PSInteger (Int -> Int64
forall a. Enum a => Int -> a
toEnum Int
i)
  -- fromEnum (PSInteger i)  = fromEnum (toW64 i)
  fromEnum :: PSInteger -> Int
fromEnum (PSInteger Int64
i) = Int64 -> Int
forall a. Enum a => a -> Int
fromEnum Int64
i

instance Show PSInteger where
  showsPrec :: Int -> PSInteger -> ShowS
showsPrec Int
p (PSInteger Int64
v) = Int -> Int64 -> ShowS
forall a. Show a => Int -> a -> ShowS
showsPrec Int
p Int64
v
  -- showsPrec p (PSInteger  v) = showsPrec p (toW64 v)

instance Read PSInteger where
  readsPrec :: Int -> ReadS PSInteger
readsPrec Int
p String
s = [ (PSInteger
i, String
rest) | (Integer
j, String
rest) <- Int -> ReadS Integer
forall a. Read a => Int -> ReadS a
readsPrec Int
p String
s, Right PSInteger
i <- [Integer -> Either ArithException PSInteger
fromIntegerTry Integer
j] ]

instance NFData PSInteger where
  rnf :: PSInteger -> ()
rnf (PSInteger Int64
_) = ()

-- | Try to convert 'Integer' into 'PSInteger'
--
-- Will return @'Left' 'Underflow'@ or @'Left' 'Overflow'@ respectively if out of range
fromIntegerTry :: Integer -> Either ArithException PSInteger
fromIntegerTry :: Integer -> Either ArithException PSInteger
fromIntegerTry Integer
i
  | Integer
i Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
<  Int64 -> Integer
forall a. Integral a => a -> Integer
toInteger (Int64
forall a. Bounded a => a
minBound :: Int64)  = ArithException -> Either ArithException PSInteger
forall a b. a -> Either a b
Left ArithException
Underflow
  | Integer
i Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
<= Int64 -> Integer
forall a. Integral a => a -> Integer
toInteger (Int64
forall a. Bounded a => a
maxBound :: Int64)  = PSInteger -> Either ArithException PSInteger
forall a b. b -> Either a b
Right (PSInteger -> Either ArithException PSInteger)
-> PSInteger -> Either ArithException PSInteger
forall a b. (a -> b) -> a -> b
$! Int64 -> PSInteger
PSInteger (Integer -> Int64
forall a. Num a => Integer -> a
fromInteger Integer
i)
  | Integer
i Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
<= Word64 -> Integer
forall a. Integral a => a -> Integer
toInteger (Word64
forall a. Bounded a => a
maxBound :: Word64) = PSInteger -> Either ArithException PSInteger
forall a b. b -> Either a b
Right (PSInteger -> Either ArithException PSInteger)
-> PSInteger -> Either ArithException PSInteger
forall a b. (a -> b) -> a -> b
$! Int64 -> PSInteger
PSInteger (Integer -> Int64
forall a. Num a => Integer -> a
fromInteger Integer
i)
  | Bool
otherwise = ArithException -> Either ArithException PSInteger
forall a b. a -> Either a b
Left ArithException
Overflow

-- | This instance will throw the respective arithmetic 'Underflow' and 'Overflow' exception if the range of 'PSInteger' is exceeded.
instance Num PSInteger where
  fromInteger :: Integer -> PSInteger
fromInteger Integer
i = (ArithException -> PSInteger)
-> (PSInteger -> PSInteger)
-> Either ArithException PSInteger
-> PSInteger
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either ArithException -> PSInteger
forall a e. Exception e => e -> a
throw PSInteger -> PSInteger
forall a. a -> a
id (Integer -> Either ArithException PSInteger
fromIntegerTry Integer
i)

  negate :: PSInteger -> PSInteger
negate (PSInteger Int64
v)
    | Int64
v Int64 -> Int64 -> Bool
forall a. Eq a => a -> a -> Bool
== Int64
forall a. Bounded a => a
minBound = Int64 -> PSInteger
PSInteger Int64
v -- NB: for the usual twos complement integers, `negate minBound == minBound`
    | Bool
otherwise     = Int64 -> PSInteger
PSInteger (Int64 -> Int64
forall a. Num a => a -> a
negate Int64
v)
  negate (PSInteger Int64
v)
    | Int64
v Int64 -> Int64 -> Bool
forall a. Eq a => a -> a -> Bool
== Int64
forall a. Bounded a => a
minBound = Int64 -> PSInteger
PSInteger Int64
v
    | Bool
otherwise     = ArithException -> PSInteger
forall a e. Exception e => e -> a
throw ArithException
Underflow

  -- addition
  PSInteger Int64
0 + :: PSInteger -> PSInteger -> PSInteger
+ PSInteger
x = PSInteger
x
  PSInteger
x + PSInteger Int64
0 = PSInteger
x

  PSInteger Int64
_  + PSInteger  Int64
_ = ArithException -> PSInteger
forall a e. Exception e => e -> a
throw ArithException
Overflow

  x :: PSInteger
x@(PSInteger Int64
_) + y :: PSInteger
y@(PSInteger Int64
_) = PSInteger
y PSInteger -> PSInteger -> PSInteger
forall a. Num a => a -> a -> a
+ PSInteger
x
  PSInteger Int64
y + PSInteger Int64
x
    | Int64
y Int64 -> Int64 -> Bool
forall a. Ord a => a -> a -> Bool
> Int64
0     = if Int64
z Int64 -> Int64 -> Bool
forall a. Ord a => a -> a -> Bool
< Int64
0 then Int64 -> PSInteger
PSInteger Int64
z else ArithException -> PSInteger
forall a e. Exception e => e -> a
throw ArithException
Overflow
    | Bool
otherwise = Int64 -> PSInteger
PSInteger Int64
z
    where
      z :: Int64
z = Int64
x Int64 -> Int64 -> Int64
forall a. Num a => a -> a -> a
+ Int64
y

  PSInteger Int64
y + PSInteger Int64
x
    | Int64
x Int64 -> Int64 -> Bool
forall a. Ord a => a -> a -> Bool
> Int64
0, Int64
y Int64 -> Int64 -> Bool
forall a. Ord a => a -> a -> Bool
> Int64
0, Int64
z Int64 -> Int64 -> Bool
forall a. Ord a => a -> a -> Bool
< Int64
0 = Int64 -> PSInteger
PSInteger Int64
z
    | Int64
x Int64 -> Int64 -> Bool
forall a. Ord a => a -> a -> Bool
< Int64
0, Int64
y Int64 -> Int64 -> Bool
forall a. Ord a => a -> a -> Bool
< Int64
0, Int64
z Int64 -> Int64 -> Bool
forall a. Ord a => a -> a -> Bool
> Int64
0 = ArithException -> PSInteger
forall a e. Exception e => e -> a
throw ArithException
Underflow
    | Bool
otherwise           = Int64 -> PSInteger
PSInteger Int64
z
    where
      z :: Int64
z = Int64
x Int64 -> Int64 -> Int64
forall a. Num a => a -> a -> a
+ Int64
y

  signum :: PSInteger -> PSInteger
signum (PSInteger Int64
_)  = Int64 -> PSInteger
PSInteger Int64
1
  signum (PSInteger Int64
v) = Int64 -> PSInteger
PSInteger (Int64 -> Int64
forall a. Num a => a -> a
signum Int64
v)

  abs :: PSInteger -> PSInteger
abs v :: PSInteger
v@(PSInteger Int64
_) = PSInteger
v
  abs v0 :: PSInteger
v0@(PSInteger Int64
v)
    | Int64
v Int64 -> Int64 -> Bool
forall a. Ord a => a -> a -> Bool
>= Int64
0 = PSInteger
v0
    | Int64
v Int64 -> Int64 -> Bool
forall a. Eq a => a -> a -> Bool
== Int64
forall a. Bounded a => a
minBound = Int64 -> PSInteger
PSInteger  Int64
v
    | Bool
otherwise     = Int64 -> PSInteger
PSInteger (Int64 -> Int64
forall a. Num a => a -> a
negate Int64
v)


  PSInteger Int64
_  * :: PSInteger -> PSInteger -> PSInteger
* PSInteger Int64
_ = ArithException -> PSInteger
forall a e. Exception e => e -> a
throw ArithException
Overflow
  PSInteger Int64
0  * PSInteger Int64
_     = Int64 -> PSInteger
PSInteger Int64
0
  PSInteger Int64
1  * PSInteger
x                 = PSInteger
x
  PSInteger Int64
_      * PSInteger Int64
0 = Int64 -> PSInteger
PSInteger Int64
0
  PSInteger
x                  * PSInteger Int64
1 = PSInteger
x

  -- cheat
  PSInteger
x * PSInteger
y = Integer -> PSInteger
forall a. Num a => Integer -> a
fromInteger (PSInteger -> Integer
forall a. Integral a => a -> Integer
toInteger PSInteger
x Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* PSInteger -> Integer
forall a. Integral a => a -> Integer
toInteger PSInteger
y)

instance Real PSInteger where
  toRational :: PSInteger -> Rational
toRational (PSInteger Int64
i) = Int64 -> Rational
forall a. Real a => a -> Rational
toRational Int64
i
  -- toRational (PSInteger u)  = toRational (toW64 u)

instance Integral PSInteger where
  toInteger :: PSInteger -> Integer
toInteger (PSInteger Int64
i) = Int64 -> Integer
forall a. Integral a => a -> Integer
toInteger Int64
i
  -- toInteger (PSInteger u)  = toInteger (toW64 u)

  quotRem :: PSInteger -> PSInteger -> (PSInteger, PSInteger)
quotRem PSInteger
_ (PSInteger Int64
0)    = ArithException -> (PSInteger, PSInteger)
forall a e. Exception e => e -> a
throw ArithException
DivideByZero
  quotRem PSInteger
x (PSInteger Int64
1)    = (PSInteger
x, Int64 -> PSInteger
PSInteger Int64
0)
  quotRem PSInteger
x (PSInteger (-1)) = (PSInteger -> PSInteger
forall a. Num a => a -> a
negate PSInteger
x, Int64 -> PSInteger
PSInteger Int64
0)

  quotRem (PSInteger Int64
x) (PSInteger Int64
y)
    | (Int64
x',Int64
y') <- Int64 -> Int64 -> (Int64, Int64)
forall a. Integral a => a -> a -> (a, a)
quotRem Int64
x Int64
y = (Int64 -> PSInteger
PSInteger Int64
x', Int64 -> PSInteger
PSInteger Int64
y')

  -- cheat
  quotRem PSInteger
x PSInteger
y
    | (Integer
x',Integer
y') <- Integer -> Integer -> (Integer, Integer)
forall a. Integral a => a -> a -> (a, a)
quotRem (PSInteger -> Integer
forall a. Integral a => a -> Integer
toInteger PSInteger
x) (PSInteger -> Integer
forall a. Integral a => a -> Integer
toInteger PSInteger
y) = (Integer -> PSInteger
forall a. Num a => Integer -> a
fromInteger Integer
x', Integer -> PSInteger
forall a. Num a => Integer -> a
fromInteger Integer
y')

----------------------------------------------------------------------------

-- | This 'Persist' instance encodes\/decodes to\/from PackStream format
--
-- When serializing 'PSInteger's via 'get' the shortest encoding is
-- used. Moreoever, for non-negative integers the unsigned encoding is
-- always used.
--
-- Deserialization via 'get' will only fail if a non-integer PackStream tag is encountered.
--
instance Persist PSInteger where
  get :: Get PSInteger
get = Get PSInteger
getPSInteger
  put :: PSInteger -> Put ()
put = PSInteger -> Put ()
putPSInteger

-- | Serializes 'PSInteger' to PackStream
--
-- The shortest encoding is used to serialize
-- 'PSInteger's. Moreoever, for non-negative integers the unsigned
-- encoding is always used.
putPSInteger :: PSInteger -> Put
putPSInteger :: PSInteger -> Put ()
putPSInteger (PSInteger Int64
i)
  | Int64
i Int64 -> Int64 -> Bool
forall a. Ord a => a -> a -> Bool
>= -Int64
16 Bool -> Bool -> Bool
&& Int64
i Int64 -> Int64 -> Bool
forall a. Ord a => a -> a -> Bool
<= Int64
127              = Int8 -> Put ()
putInt8 (Int64 -> Int8
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int64
i)
  | Int64
i Int64 -> Int64 -> Bool
forall a. Ord a => a -> a -> Bool
>= -Int64
0x80 Bool -> Bool -> Bool
&& Int64
i Int64 -> Int64 -> Bool
forall a. Ord a => a -> a -> Bool
< Int64
0x80           = Word8 -> Put ()
putWord8 Word8
forall a. (Eq a, Num a) => a
TAG_INT_8  Put () -> Put () -> Put ()
forall a b. Put a -> Put b -> Put b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Int8 -> Put ()
putInt8     (Int64 -> Int8
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int64
i)
  | Int64
i Int64 -> Int64 -> Bool
forall a. Ord a => a -> a -> Bool
>= -Int64
0x8000 Bool -> Bool -> Bool
&& Int64
i Int64 -> Int64 -> Bool
forall a. Ord a => a -> a -> Bool
< Int64
0x8000       = Word8 -> Put ()
putWord8 Word8
forall a. (Eq a, Num a) => a
TAG_INT_16 Put () -> Put () -> Put ()
forall a b. Put a -> Put b -> Put b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Int16 -> Put ()
putInt16be  (Int64 -> Int16
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int64
i)
  | Int64
i Int64 -> Int64 -> Bool
forall a. Ord a => a -> a -> Bool
>= -Int64
0x80000000 Bool -> Bool -> Bool
&& Int64
i Int64 -> Int64 -> Bool
forall a. Ord a => a -> a -> Bool
< Int64
0x80000000 = Word8 -> Put ()
putWord8 Word8
forall a. (Eq a, Num a) => a
TAG_INT_32 Put () -> Put () -> Put ()
forall a b. Put a -> Put b -> Put b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Int32 -> Put ()
putInt32be  (Int64 -> Int32
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int64
i)
  | Bool
otherwise                          = Word8 -> Put ()
putWord8 Word8
forall a. (Eq a, Num a) => a
TAG_INT_64 Put () -> Put () -> Put ()
forall a b. Put a -> Put b -> Put b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Int64 -> Put ()
putInt64be                Int64
i

-- | Deserializes 'PSInteger' from PackStream
--
-- This operation will only fail if a non-integer PackStream tag is encountered.
getPSInteger :: Get PSInteger
getPSInteger :: Get PSInteger
getPSInteger = do { Word8
tag <- Get Word8
getWord8; Word8 -> (PSInteger -> PSInteger) -> Get PSInteger -> Get PSInteger
forall a. Word8 -> (PSInteger -> a) -> Get a -> Get a
tryPSInteger Word8
tag PSInteger -> PSInteger
forall a. a -> a
id (String -> Get PSInteger
forall a. String -> Get a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"expected PackStream int") }

-- | Try to decode a 'PSInteger' given a tag byte, applying @f@ on success or falling through to the continuation.
{-# INLINE tryPSInteger #-}
tryPSInteger :: Word8 -> (PSInteger -> a) -> Get a -> Get a
tryPSInteger :: forall a. Word8 -> (PSInteger -> a) -> Get a -> Get a
tryPSInteger Word8
tag' PSInteger -> a
f Get a
cont = case Word8
tag' of
  Word8
c | Word8 -> Bool
is_TAG_TINY_INT Word8
c -> a -> Get a
forall a. a -> Get a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (a -> Get a) -> a -> Get a
forall a b. (a -> b) -> a -> b
$! PSInteger -> a
f (PSInteger -> a) -> PSInteger -> a
forall a b. (a -> b) -> a -> b
$! Int8 -> PSInteger
forall a. ToPSInteger a => a -> PSInteger
toPSInteger (Word8 -> Int8
forall a b.
(Integral a, Integral b, IsIntTypeIso a b ~ 'True) =>
a -> b
intCastIso Word8
c :: Int8)
  Word8
TAG_INT_8           -> PSInteger -> a
f (PSInteger -> a) -> (Int8 -> PSInteger) -> Int8 -> a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int8 -> PSInteger
forall a. ToPSInteger a => a -> PSInteger
toPSInteger (Int8 -> a) -> Get Int8 -> Get a
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Get Int8
getInt8
  Word8
TAG_INT_16          -> PSInteger -> a
f (PSInteger -> a) -> (Int16 -> PSInteger) -> Int16 -> a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int16 -> PSInteger
forall a. ToPSInteger a => a -> PSInteger
toPSInteger (Int16 -> a) -> Get Int16 -> Get a
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Get Int16
getInt16be
  Word8
TAG_INT_32          -> PSInteger -> a
f (PSInteger -> a) -> (Int32 -> PSInteger) -> Int32 -> a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int32 -> PSInteger
forall a. ToPSInteger a => a -> PSInteger
toPSInteger (Int32 -> a) -> Get Int32 -> Get a
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Get Int32
getInt32be
  Word8
TAG_INT_64          -> PSInteger -> a
f (PSInteger -> a) -> (Int64 -> PSInteger) -> Int64 -> a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int64 -> PSInteger
forall a. ToPSInteger a => a -> PSInteger
toPSInteger (Int64 -> a) -> Get Int64 -> Get a
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Get Int64
getInt64be
  Word8
_                   -> Get a
cont