{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE Rank2Types #-} {-# LANGUAGE ScopedTypeVariables #-} 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)