wide-word-0.1.8.1: src/Data/WideWord/Int128.hs
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE StrictData #-}
{-# LANGUAGE UnboxedTuples #-}
{-# OPTIONS_GHC -funbox-strict-fields #-}
-----------------------------------------------------------------------------
---- |
---- Module : Data.WideWord.Int128
----
---- Maintainer : erikd@mega-nerd.com
---- Stability : experimental
---- Portability : non-portable (GHC extensions and primops)
----
---- This module provides an opaque signed 128 bit value with the usual set
---- of typeclass instances one would expect for a fixed width unsigned integer
---- type.
---- Operations like addition, subtraction and multiplication etc provide a
---- "modulo 2^128" result as one would expect from a fixed width unsigned word.
-------------------------------------------------------------------------------
module Data.WideWord.Int128
( Int128 (..)
, byteSwapInt128
, showHexInt128
, zeroInt128
) where
import Control.DeepSeq (NFData (..))
import Data.Bits (Bits (..), FiniteBits (..), shiftL)
import Data.Data (Data, Typeable)
import Data.Ix (Ix)
#if ! MIN_VERSION_base(4,11,0)
import Data.Semigroup ((<>))
#endif
import Data.WideWord.Word128
import Numeric
import Foreign.Ptr (Ptr, castPtr)
import Foreign.Storable (Storable (..))
import GHC.Base (Int (..))
import GHC.Enum (predError, succError)
import GHC.Exts ((+#), (*#), State#, Int#, Addr#, ByteArray#, MutableByteArray#)
import GHC.Generics
import GHC.Real ((%))
import GHC.Word (Word32, Word64, byteSwap64)
import Data.Primitive.Types (Prim (..), defaultSetByteArray#, defaultSetOffAddr#)
import Data.Hashable (Hashable, hashWithSalt)
import Data.Binary (Binary (get, put))
import Data.WideWord.Word64
#if MIN_VERSION_base(4,17,0)
#define ONE (wordToWord64# 1##)
#else
#define ONE (1##)
#endif
data Int128 = Int128
{ int128Hi64 :: !Word64
, int128Lo64 :: !Word64
}
deriving (Eq, Data, Generic, Ix)
instance Hashable Int128 where
hashWithSalt s (Int128 a1 a2) = s `hashWithSalt` a1 `hashWithSalt` a2
-- | @since 0.1.5.0
instance Binary Int128 where
put (Int128 a1 a2) = put a1 >> put a2
get = Int128 <$> get <*> get
byteSwapInt128 :: Int128 -> Int128
byteSwapInt128 (Int128 a1 a0) = Int128 (byteSwap64 a0) (byteSwap64 a1)
showHexInt128 :: Int128 -> String
showHexInt128 (Int128 a1 a0)
| a1 == 0 = showHex a0 ""
| otherwise = showHex a1 zeros ++ showHex a0 ""
where
h0 = showHex a0 ""
zeros = replicate (16 - length h0) '0'
instance Show Int128 where
show = show . toInteger
instance Read Int128 where
readsPrec p s = [(fromInteger128 (x :: Integer), r) | (x, r) <- readsPrec p s]
instance Ord Int128 where
compare = compare128
instance Bounded Int128 where
minBound = Int128 0x8000000000000000 0
maxBound = Int128 0x7fffffffffffffff maxBound
instance Enum Int128 where
succ = succ128
pred = pred128
toEnum = toEnum128
fromEnum = fromEnum128
instance Num Int128 where
(+) = plus128
(-) = minus128
(*) = times128
negate = negate128
abs = abs128
signum = signum128
fromInteger = fromInteger128
instance Bits Int128 where
(.&.) = and128
(.|.) = or128
xor = xor128
complement = complement128
shiftL = shiftL128
unsafeShiftL = shiftL128
shiftR = shiftR128
unsafeShiftR = shiftR128
rotateL = rotateL128
rotateR = rotateR128
bitSize _ = 128
bitSizeMaybe _ = Just 128
isSigned _ = True
testBit = testBit128
bit = bit128
popCount = popCount128
instance FiniteBits Int128 where
finiteBitSize _ = 128
countLeadingZeros = countLeadingZeros128
countTrailingZeros = countTrailingZeros128
instance Real Int128 where
toRational x = toInteger128 x % 1
instance Integral Int128 where
quot n d = fst (quotRem128 n d)
rem n d = snd (quotRem128 n d)
div n d = fst (divMod128 n d)
mod n d = snd (divMod128 n d)
quotRem = quotRem128
divMod = divMod128
toInteger = toInteger128
instance Storable Int128 where
sizeOf i = I# (sizeOf128# i)
alignment i = I# (alignment128# i)
peek = peek128
peekElemOff = peekElemOff128
poke = poke128
pokeElemOff = pokeElemOff128
instance NFData Int128 where
-- The fields are already strict and unpacked, so do nothing.
rnf !_ = ()
instance Prim Int128 where
sizeOf# = sizeOf128#
alignment# = alignment128#
indexByteArray# = indexByteArray128#
readByteArray# = readByteArray128#
writeByteArray# = writeByteArray128#
setByteArray# = setByteArray128#
indexOffAddr# = indexOffAddr128#
readOffAddr# = readOffAddr128#
writeOffAddr# = writeOffAddr128#
setOffAddr# = setOffAddr128#
{-# INLINE sizeOf# #-}
{-# INLINE alignment# #-}
{-# INLINE indexByteArray# #-}
{-# INLINE readByteArray# #-}
{-# INLINE writeByteArray# #-}
{-# INLINE setByteArray# #-}
{-# INLINE indexOffAddr# #-}
{-# INLINE readOffAddr# #-}
{-# INLINE writeOffAddr# #-}
{-# INLINE setOffAddr# #-}
-- -----------------------------------------------------------------------------
-- Rewrite rules.
{-# RULES
"fromIntegral :: Int -> Int128" fromIntegral = fromInt
"fromIntegral :: Word -> Int128" fromIntegral = fromWord
"fromIntegral :: Word32 -> Int128" fromIntegral = fromWord32
"fromIntegral :: Word64 -> Int128" fromIntegral = Int128 0
"fromIntegral :: Int128 -> Int" fromIntegral = toInt
"fromIntegral :: Int128 -> Word" fromIntegral = toWord
"fromIntegral :: Int128 -> Word32" fromIntegral = toWord32
"fromIntegral :: Int128 -> Word64" fromIntegral = \(Int128 _ w) -> w
#-}
{-# INLINE fromInt #-}
fromInt :: Int -> Int128
fromInt = Int128 0 . fromIntegral
{-# INLINE fromWord #-}
fromWord :: Word -> Int128
fromWord = Int128 0 . fromIntegral
{-# INLINE fromWord32 #-}
fromWord32 :: Word32 -> Int128
fromWord32 = Int128 0 . fromIntegral
{-# INLINE toInt #-}
toInt :: Int128 -> Int
toInt (Int128 _ w) = fromIntegral w
{-# INLINE toWord #-}
toWord :: Int128 -> Word
toWord (Int128 _ w) = fromIntegral w
{-# INLINE toWord32 #-}
toWord32 :: Int128 -> Word32
toWord32 (Int128 _ w) = fromIntegral w
-- -----------------------------------------------------------------------------
-- Functions for `Ord` instance.
compare128 :: Int128 -> Int128 -> Ordering
compare128 (Int128 a1 a0) (Int128 b1 b0)
| aIsNeg == bIsNeg = compare a1 b1 <> compare a0 b0
| bIsNeg = GT
| otherwise = LT
where
aIsNeg = isNeg a1
bIsNeg = isNeg b1
isNeg = (>= 0x8000000000000000)
-- -----------------------------------------------------------------------------
-- Functions for `Enum` instance.
succ128 :: Int128 -> Int128
succ128 (Int128 a1 a0)
| a0 == maxBound =
if a1 == 0x7fffffffffffffff
then succError "Int128"
else Int128 (a1 + 1) 0
| otherwise = Int128 a1 (a0 + 1)
pred128 :: Int128 -> Int128
pred128 (Int128 a1 a0)
| a0 == 0 =
if a1 == 0x8000000000000000
then predError "Int128"
else Int128 (a1 - 1) maxBound
| otherwise = Int128 a1 (a0 - 1)
{-# INLINABLE toEnum128 #-}
toEnum128 :: Int -> Int128
toEnum128 i = Int128 0 (toEnum i)
{-# INLINABLE fromEnum128 #-}
fromEnum128 :: Int128 -> Int
fromEnum128 (Int128 _ a0) = fromEnum a0
-- -----------------------------------------------------------------------------
-- Functions for `Num` instance.
{-# INLINABLE plus128 #-}
plus128 :: Int128 -> Int128 -> Int128
plus128 (Int128 a1 a0) (Int128 b1 b0) =
Int128 s1 s0
where
!(c1, s0) = plusCarrySum a0 b0
s1a = a1 + b1
s1 = c1 + s1a
{-# INLINABLE minus128 #-}
minus128 :: Int128 -> Int128 -> Int128
minus128 (Int128 a1 a0) (Int128 b1 b0) =
Int128 d1 d0
where
!(c1, d0) = subCarryDiff a0 b0
a1c = a1 - c1
d1 = a1c - b1
times128 :: Int128 -> Int128 -> Int128
times128 (Int128 a1 a0) (Int128 b1 b0) =
Int128 p1 p0
where
!(c1, p0) = timesCarryProd a0 b0
p1a = a1 * b0
p1b = a0 * b1
p1c = p1a + p1b
p1 = p1c + c1
{-# INLINABLE negate128 #-}
negate128 :: Int128 -> Int128
negate128 (Int128 a1 a0) =
case plusCarrySum (complement a0) 1 of
(c, s) -> Int128 (complement a1 + c) s
{-# INLINABLE abs128 #-}
abs128 :: Int128 -> Int128
abs128 i@(Int128 a1 _)
| testBit a1 63 = negate128 i
| otherwise = i
{-# INLINABLE signum128 #-}
signum128 :: Int128 -> Int128
signum128 (Int128 a1 a0)
| a1 == 0 && a0 == 0 = zeroInt128
| testBit a1 63 = minusOneInt128
| otherwise = oneInt128
{-# INLINABLE complement128 #-}
complement128 :: Int128 -> Int128
complement128 (Int128 a1 a0) = Int128 (complement a1) (complement a0)
fromInteger128 :: Integer -> Int128
fromInteger128 i =
Int128 (fromIntegral $ i `shiftR` 64) (fromIntegral i)
-- -----------------------------------------------------------------------------
-- Functions for `Bits` instance.
{-# INLINABLE and128 #-}
and128 :: Int128 -> Int128 -> Int128
and128 (Int128 a1 a0) (Int128 b1 b0) =
Int128 (a1 .&. b1) (a0 .&. b0)
{-# INLINABLE or128 #-}
or128 :: Int128 -> Int128 -> Int128
or128 (Int128 a1 a0) (Int128 b1 b0) =
Int128 (a1 .|. b1) (a0 .|. b0)
{-# INLINABLE xor128 #-}
xor128 :: Int128 -> Int128 -> Int128
xor128 (Int128 a1 a0) (Int128 b1 b0) =
Int128 (xor a1 b1) (xor a0 b0)
-- Some of the following functions have quite complicated guard clauses, but we make them
-- inlineable anyway so that if the things like the shift amount is a compile time constant
-- most of the function can be dropped leaving only the needed bits inlined.
{-# INLINABLE shiftL128 #-}
shiftL128 :: Int128 -> Int -> Int128
shiftL128 w@(Int128 a1 a0) s
| s == 0 = w
| s == minBound = zeroInt128
| s < 0 = shiftR128 w (negate s)
| s >= 128 = zeroInt128
| s == 64 = Int128 a0 0
| s > 64 = Int128 (a0 `shiftL` (s - 64)) 0
| otherwise =
Int128 (a1 `shiftL` s + a0 `shiftR` (64 - s)) (a0 `shiftL` s)
{-# INLINABLE shiftR128 #-}
shiftR128 :: Int128 -> Int -> Int128
shiftR128 i@(Int128 a1 a0) s
| s == 0 = i
| s == minBound = zeroInt128
| s < 0 = shiftL128 i (negate s)
| topBitSetWord64 a1 = complement128 (shiftR128 (complement128 i) s)
| s >= 128 = zeroInt128
| s == 64 = Int128 0 a1
| s > 64 = Int128 0 (a1 `shiftR` (s - 64))
| otherwise = Int128 (a1 `shiftR` s) (a0 `shiftR` s + a1 `shiftL` (64 - s))
{-# INLINABLE rotateL128 #-}
rotateL128 :: Int128 -> Int -> Int128
rotateL128 w@(Int128 a1 a0) r
| r < 0 = rotateL128 w (128 - (abs r `mod` 128))
| r == 0 = w
| r >= 128 = rotateL128 w (r `mod` 128)
| r == 64 = Int128 a0 a1
| r > 64 = rotateL128 (Int128 a0 a1) (r `mod` 64)
| otherwise =
Int128 (a1 `shiftL` r + a0 `shiftR` (64 - r)) (a0 `shiftL` r + a1 `shiftR` (64 - r))
{-# INLINABLE rotateR128 #-}
rotateR128 :: Int128 -> Int -> Int128
rotateR128 w@(Int128 a1 a0) r
| r < 0 = rotateR128 w (128 - (abs r `mod` 128))
| r == 0 = w
| r >= 128 = rotateR128 w (r `mod` 128)
| r == 64 = Int128 a0 a1
| r > 64 = rotateR128 (Int128 a0 a1) (r `mod` 64)
| otherwise =
Int128 (a1 `shiftR` r + a0 `shiftL` (64 - r)) (a0 `shiftR` r + a1 `shiftL` (64 - r))
{-# INLINABLE testBit128 #-}
testBit128 :: Int128 -> Int -> Bool
testBit128 (Int128 a1 a0) i
| i < 0 = False
| i >= 128 = False
| i >= 64 = testBit a1 (i - 64)
| otherwise = testBit a0 i
{-# INLINABLE bit128 #-}
bit128 :: Int -> Int128
bit128 indx
| indx < 0 = zeroInt128
| indx >= 128 = zeroInt128
| otherwise = shiftL128 oneInt128 indx
{-# INLINABLE popCount128 #-}
popCount128 :: Int128 -> Int
popCount128 (Int128 a1 a0) = popCount a1 + popCount a0
-- -----------------------------------------------------------------------------
-- Functions for `FiniteBits` instance.
countLeadingZeros128 :: Int128 -> Int
countLeadingZeros128 (Int128 a1 a0) =
case countLeadingZeros a1 of
64 -> 64 + countLeadingZeros a0
res -> res
countTrailingZeros128 :: Int128 -> Int
countTrailingZeros128 (Int128 a1 a0) =
case countTrailingZeros a0 of
64 -> 64 + countTrailingZeros a1
res -> res
-- -----------------------------------------------------------------------------
-- Functions for `Integral` instance.
quotRem128 :: Int128 -> Int128 -> (Int128, Int128)
quotRem128 numer denom
| isNeg numer && isNeg denom = (word128ToInt128 wq, word128ToInt128 (negate wr))
| isNeg numer = (word128ToInt128 (negate wq), word128ToInt128 (negate wr))
| isNeg denom = (word128ToInt128 (negate wq), word128ToInt128 wr)
| otherwise = (word128ToInt128 wq, word128ToInt128 wr)
where
(wq, wr) = quotRem absNumerW absDenomW
absNumerW = int128ToWord128 $ abs128 numer
absDenomW = int128ToWord128 $ abs128 denom
isNeg = topBitSetWord64 . int128Hi64
divMod128 :: Int128 -> Int128 -> (Int128, Int128)
divMod128 numer denom
| isNeg numer && isNeg denom = (word128ToInt128 wq, word128ToInt128 (negate wr))
| isNeg numer && wr == 0 = (word128ToInt128 (negate wq), 0)
| isNeg numer = (word128ToInt128 (negate $ wq + 1), word128ToInt128 (absDenomW - wr))
| isNeg denom && wr == 0 = (word128ToInt128 (negate wq), 0)
| isNeg denom = (word128ToInt128 (negate $ wq + 1), word128ToInt128 (negate $ absDenomW - wr))
| otherwise = (word128ToInt128 wq, word128ToInt128 wr)
where
(wq, wr) = quotRem absNumerW absDenomW
isNeg = topBitSetWord64 . int128Hi64
absNumerW = int128ToWord128 $ abs128 numer
absDenomW = int128ToWord128 $ abs128 denom
toInteger128 :: Int128 -> Integer
toInteger128 i@(Int128 a1 a0)
| popCount a1 == 64 && popCount a0 == 64 = -1
| not (testBit a1 63) = fromIntegral a1 `shiftL` 64 + fromIntegral a0
| otherwise =
case negate128 i of
Int128 n1 n0 -> negate (fromIntegral n1 `shiftL` 64 + fromIntegral n0)
-- -----------------------------------------------------------------------------
-- Functions for `Storable` instance.
peek128 :: Ptr Int128 -> IO Int128
peek128 ptr =
Int128 <$> peekElemOff (castPtr ptr) index1 <*> peekElemOff (castPtr ptr) index0
peekElemOff128 :: Ptr Int128 -> Int -> IO Int128
peekElemOff128 ptr idx =
Int128 <$> peekElemOff (castPtr ptr) (idx2 + index1)
<*> peekElemOff (castPtr ptr) (idx2 + index0)
where idx2 = 2 * idx
poke128 :: Ptr Int128 -> Int128 -> IO ()
poke128 ptr (Int128 a1 a0) =
pokeElemOff (castPtr ptr) index1 a1 >> pokeElemOff (castPtr ptr) index0 a0
pokeElemOff128 :: Ptr Int128 -> Int -> Int128 -> IO ()
pokeElemOff128 ptr idx (Int128 a1 a0) = do
let idx2 = 2 * idx
pokeElemOff (castPtr ptr) (idx2 + index0) a0
pokeElemOff (castPtr ptr) (idx2 + index1) a1
-- -----------------------------------------------------------------------------
-- Helpers.
{-# INLINE int128ToWord128 #-}
int128ToWord128 :: Int128 -> Word128
int128ToWord128 (Int128 a1 a0) = Word128 a1 a0
{-# INLINE topBitSetWord64 #-}
topBitSetWord64 :: Word64 -> Bool
topBitSetWord64 w = testBit w 63
{-# INLINE word128ToInt128 #-}
word128ToInt128 :: Word128 -> Int128
word128ToInt128 (Word128 a1 a0) = Int128 a1 a0
-- -----------------------------------------------------------------------------
-- Functions for `Prim` instance.
{-# INLINE sizeOf128# #-}
sizeOf128# :: Int128 -> Int#
sizeOf128# _ = 2# *# sizeOf# (undefined :: Word64)
{-# INLINE alignment128# #-}
alignment128# :: Int128 -> Int#
alignment128# _ = 2# *# alignment# (undefined :: Word64)
{-# INLINE indexByteArray128# #-}
indexByteArray128# :: ByteArray# -> Int# -> Int128
indexByteArray128# arr# i# =
let i2# = 2# *# i#
x = indexByteArray# arr# (i2# +# unInt index1)
y = indexByteArray# arr# (i2# +# unInt index0)
in Int128 x y
{-# INLINE readByteArray128# #-}
readByteArray128# :: MutableByteArray# s -> Int# -> State# s -> (# State# s, Int128 #)
readByteArray128# arr# i# =
\s0 -> case readByteArray# arr# (i2# +# unInt index1) s0 of
(# s1, x #) -> case readByteArray# arr# (i2# +# unInt index0) s1 of
(# s2, y #) -> (# s2, Int128 x y #)
where i2# = 2# *# i#
{-# INLINE writeByteArray128# #-}
writeByteArray128# :: MutableByteArray# s -> Int# -> Int128 -> State# s -> State# s
writeByteArray128# arr# i# (Int128 a b) =
\s0 -> case writeByteArray# arr# (i2# +# unInt index1) a s0 of
s1 -> case writeByteArray# arr# (i2# +# unInt index0) b s1 of
s2 -> s2
where i2# = 2# *# i#
{-# INLINE setByteArray128# #-}
setByteArray128# :: MutableByteArray# s -> Int# -> Int# -> Int128 -> State# s -> State# s
setByteArray128# = defaultSetByteArray#
{-# INLINE indexOffAddr128# #-}
indexOffAddr128# :: Addr# -> Int# -> Int128
indexOffAddr128# addr# i# =
let i2# = 2# *# i#
x = indexOffAddr# addr# (i2# +# unInt index1)
y = indexOffAddr# addr# (i2# +# unInt index0)
in Int128 x y
{-# INLINE readOffAddr128# #-}
readOffAddr128# :: Addr# -> Int# -> State# s -> (# State# s, Int128 #)
readOffAddr128# addr# i# =
\s0 -> case readOffAddr# addr# (i2# +# unInt index1) s0 of
(# s1, x #) -> case readOffAddr# addr# (i2# +# unInt index0) s1 of
(# s2, y #) -> (# s2, Int128 x y #)
where i2# = 2# *# i#
{-# INLINE writeOffAddr128# #-}
writeOffAddr128# :: Addr# -> Int# -> Int128 -> State# s -> State# s
writeOffAddr128# addr# i# (Int128 a b) =
\s0 -> case writeOffAddr# addr# (i2# +# unInt index1) a s0 of
s1 -> case writeOffAddr# addr# (i2# +# unInt index0) b s1 of
s2 -> s2
where i2# = 2# *# i#
{-# INLINE setOffAddr128# #-}
setOffAddr128# :: Addr# -> Int# -> Int# -> Int128 -> State# s -> State# s
setOffAddr128# = defaultSetOffAddr#
-- -----------------------------------------------------------------------------
-- Constants.
zeroInt128 :: Int128
zeroInt128 = Int128 0 0
oneInt128 :: Int128
oneInt128 = Int128 0 1
minusOneInt128 :: Int128
minusOneInt128 = Int128 maxBound maxBound
unInt :: Int -> Int#
unInt (I# i#) = i#
index0, index1 :: Int
#if WORDS_BIGENDIAN
index0 = 1
index1 = 0
#else
index0 = 0
index1 = 1
#endif