module Data.SizedInt
( SizedInt ) where
import Data.Bits
(Bits, shiftL, shiftR, rotateL, rotateR, bit, testBit, popCount,
complement, xor, bitSize, isSigned, (.&.), (.|.), )
import qualified Type.Data.Num as Num
import Type.Base.Proxy (Proxy(Proxy))
newtype SizedInt nT = SizedInt Integer
_sizeT :: SizedInt nT -> Proxy nT
_sizeT _ = Proxy
mask :: forall nT . Num.Natural nT => Proxy nT -> Integer
mask n = bit (Num.fromInteger n) 1
signBit :: forall nT . Num.Natural nT => Proxy nT -> Int
signBit n = Num.fromInteger n 1
isNegative :: forall nT . Num.Natural nT
=> SizedInt nT
-> Bool
isNegative (SizedInt x) =
testBit x $ signBit (Proxy :: Proxy nT)
instance Num.Natural nT => Eq (SizedInt nT) where
(SizedInt x) == (SizedInt y) = x == y
(SizedInt x) /= (SizedInt y) = x /= y
instance Num.Natural nT => Show (SizedInt nT) where
showsPrec prec n =
showsPrec prec $ toInteger n
instance Num.Natural nT => Read (SizedInt nT) where
readsPrec prec str0 =
[ (fromInteger n, str)
| (n, str) <- readsPrec prec str0 ]
instance Num.Natural nT => Ord (SizedInt nT) where
a `compare` b = toInteger a `compare` toInteger b
instance Num.Natural nT => Bounded (SizedInt nT) where
minBound = SizedInt $ negate $ 1 `shiftL` (Num.fromInteger (Proxy :: Proxy nT) 1)
maxBound = SizedInt $ (1 `shiftL` (Num.fromInteger (Proxy :: Proxy nT) 1)) 1
instance Num.Natural nT => Enum (SizedInt nT) where
succ x
| x == maxBound = error $ "Enum.succ{" ++ showSizedIntType x ++ "}: tried to take `succ' of maxBound"
| otherwise = x + 1
pred x
| x == minBound = error $ "Enum.succ{" ++ showSizedIntType x ++ "}: tried to take `pred' of minBound"
| otherwise = x 1
fromEnum s@(SizedInt x)
| x > toInteger (maxBound :: Int) =
error $ "Enum.fromEnum{" ++ showSizedIntType s ++ "}: tried to take `fromEnum' on SizedInt greater than maxBound :: Int"
| x < toInteger (minBound :: Int) =
error $ "Enum.fromEnum{" ++ showSizedIntType s ++ "}: tried to take `fromEnum' on SizedInt smaller than minBound :: Int"
| otherwise =
fromInteger x
toEnum x
| x' > toInteger (maxBound :: SizedInt nT) =
error $ "Enum.fromEnum{" ++ showSizedIntTypeProxy n ++ "}: tried to take `fromEnum' on SizedInt greater than maxBound :: " ++ showSizedIntTypeProxy n
| x' < toInteger (minBound :: SizedInt nT) =
error $ "Enum.fromEnum{" ++ showSizedIntTypeProxy n ++ "}: tried to take `fromEnum' on SizedInt smaller than minBound :: " ++ showSizedIntTypeProxy n
| otherwise =
fromInteger x'
where x' = toInteger x
n = Proxy :: Proxy nT
instance Num.Natural nT => Num (SizedInt nT) where
(SizedInt a) + (SizedInt b) =
fromInteger $ a + b
(SizedInt a) * (SizedInt b) =
fromInteger $ a * b
negate (SizedInt n) =
fromInteger $ (n `xor` mask (Proxy :: Proxy nT)) + 1
a b =
a + (negate b)
fromInteger n =
let fromCardinal m = SizedInt $ m .&. mask (Proxy :: Proxy nT)
in if n>=0
then fromCardinal n
else negate $ fromCardinal $ negate n
abs s
| isNegative s =
negate s
| otherwise =
s
signum s
| isNegative s =
1
| s == 0 =
0
| otherwise =
1
instance Num.Natural nT => Real (SizedInt nT) where
toRational n = toRational $ toInteger n
instance Num.Natural nT => Integral (SizedInt nT) where
a `quot` b =
fromInteger $ toInteger a `quot` toInteger b
a `rem` b =
fromInteger $ toInteger a `rem` toInteger b
a `div` b =
fromInteger $ toInteger a `div` toInteger b
a `mod` b =
fromInteger $ toInteger a `mod` toInteger b
a `quotRem` b =
let (quot_, rem_) = toInteger a `quotRem` toInteger b
in (fromInteger quot_, fromInteger rem_)
a `divMod` b =
let (div_, mod_) = toInteger a `divMod` toInteger b
in (fromInteger div_, fromInteger mod_)
toInteger s@(SizedInt x) =
if isNegative s
then let SizedInt x' = negate s in negate x'
else x
instance Num.Natural nT => Bits (SizedInt nT) where
(SizedInt a) .&. (SizedInt b) = SizedInt $ a .&. b
(SizedInt a) .|. (SizedInt b) = SizedInt $ a .|. b
(SizedInt a) `xor` SizedInt b = SizedInt $ a `xor` b
complement (SizedInt x) = SizedInt $ x `xor` mask (Proxy :: Proxy nT)
bit b =
case SizedInt $ bit b of
s | b < 0 -> error $ "Bits.bit{" ++ showSizedIntType s ++ "}: tried to set negative position"
| b >= bitSize s -> error $ "Bits.bit{" ++ showSizedIntType s ++ "}: tried to set too large position"
| otherwise -> s
s@(SizedInt x) `testBit` b
| b < 0 = error $ "Bits.testBit{" ++ showSizedIntType s ++ "}: tried to test negative position"
| b >= bitSize s = error $ "Bits.testBit{" ++ showSizedIntType s ++ "}: tried to test too large position"
| otherwise =
testBit x b
s@(SizedInt x) `shiftL` b
| b < 0 = error $ "Bits.shiftL{" ++ showSizedIntType s ++ "}: tried to shift by negative amount"
| otherwise =
SizedInt $ mask (Proxy :: Proxy nT) .&. (x `shiftL` b)
s@(SizedInt x) `shiftR` b
| b < 0 = error $ "Bits.shiftR{" ++ showSizedIntType s ++ "}: tried to shift by negative amount"
| isNegative s =
SizedInt $ mask (Proxy :: Proxy nT) .&.
((x `shiftR` b) .|. (mask (Proxy :: Proxy nT) `shiftL` (Num.fromInteger (Proxy :: Proxy nT) b)))
| otherwise =
SizedInt $ (mask (Proxy :: Proxy nT)) .&. (x `shiftR` b)
s@(SizedInt a) `rotateL` b
| b < 0 =
error $ "Bits.rotateL{" ++ showSizedIntType s ++ "}: tried to rotate by negative amount"
| otherwise =
SizedInt $ mask (Proxy :: Proxy nT) .&.
((a `shiftL` b) .|. (a `shiftR` (Num.fromInteger (Proxy :: Proxy nT) b)))
s@(SizedInt a) `rotateR` b
| b < 0 =
error $ "Bits.rotateR{" ++ showSizedIntType s ++ "}: tried to rotate by negative amount"
| otherwise =
SizedInt $ mask (Proxy :: Proxy nT) .&.
((a `shiftR` b) .|. (a `shiftL` (Num.fromInteger (Proxy :: Proxy nT) b)))
popCount (SizedInt x) = popCount x
bitSize _ = Num.fromInteger (Proxy :: Proxy nT)
isSigned _ = True
showSizedIntTypeProxy :: forall nT. Num.Natural nT => Proxy nT -> String
showSizedIntTypeProxy n =
"SizedInt " ++ show (Num.fromInteger n :: Integer)
showSizedIntType :: forall nT. Num.Natural nT => SizedInt nT -> String
showSizedIntType _ = showSizedIntTypeProxy (Proxy :: Proxy nT)