summaryrefslogtreecommitdiff
path: root/testsuite/tests/numeric/should_run/CarryOverflow.hs
diff options
context:
space:
mode:
authorReid Barton <rwbarton@gmail.com>2014-08-09 19:20:53 -0400
committerReid Barton <rwbarton@gmail.com>2014-08-09 19:20:53 -0400
commitb7b7633dfc350322756b8735a4d7c9a5c42d1721 (patch)
tree29b68d6d1e19a1989884f33c47e6767350770e5d /testsuite/tests/numeric/should_run/CarryOverflow.hs
parent8e01ca6872c1db9b1b2f7c0ba05d01b1c3436307 (diff)
downloadhaskell-b7b7633dfc350322756b8735a4d7c9a5c42d1721.tar.gz
Add a test for plusWord2#, addIntC#, subIntC#
Diffstat (limited to 'testsuite/tests/numeric/should_run/CarryOverflow.hs')
-rw-r--r--testsuite/tests/numeric/should_run/CarryOverflow.hs89
1 files changed, 89 insertions, 0 deletions
diff --git a/testsuite/tests/numeric/should_run/CarryOverflow.hs b/testsuite/tests/numeric/should_run/CarryOverflow.hs
new file mode 100644
index 0000000000..f83c1cf15c
--- /dev/null
+++ b/testsuite/tests/numeric/should_run/CarryOverflow.hs
@@ -0,0 +1,89 @@
+{-# LANGUAGE MagicHash, UnboxedTuples #-}
+
+import GHC.Prim
+import GHC.Word
+import GHC.Exts
+
+import Control.Monad
+import Data.Bits
+import Data.List
+import System.Exit
+
+allEqual :: Eq a => [a] -> Bool
+allEqual [] = error "allEqual: nothing to compare"
+allEqual (x:xs) = all (== x) xs
+
+testWords :: [Word]
+testWords = map head . group . sort $
+ concatMap (\w -> [w - 1, w, w + 1]) $
+ concatMap (\w -> [w, maxBound - w]) $
+ trailingOnes ++ randoms
+ where trailingOnes = takeWhile (/= 0) $ iterate (`div` 2) $ maxBound
+ -- What would a Haskell program be without some Fibonacci numbers?
+ randoms = take 40 $ drop 100 fibs
+ fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
+
+
+wordSizeInBits :: Int
+wordSizeInBits = length $ takeWhile (/= 0) $ iterate (`div` 2) (maxBound :: Word)
+
+
+-- plusWord2# (Word# carry)
+
+ways_plusWord2# :: [Word -> Word -> Bool]
+ways_plusWord2# = [ltTest, integerTest, primopTest]
+ where ltTest x y =
+ let r = x + y in r < x
+ integerTest x y =
+ let r = fromIntegral x + fromIntegral y :: Integer
+ in r > fromIntegral (maxBound :: Word)
+ primopTest (W# x) (W# y) = case plusWord2# x y of
+ (# 0##, _ #) -> False
+ (# 1##, _ #) -> True
+ _ -> error "unexpected result from plusWord2#"
+
+-- addIntC# (Int# addition overflow)
+
+ways_addIntC# :: [Int -> Int -> Bool]
+ways_addIntC# = [ltTest, integerTest, highBitTest, primopTest]
+ where ltTest x y =
+ let r = x + y in (y > 0 && r < x) || (y < 0 && r > x)
+ integerTest x y =
+ let r = fromIntegral x + fromIntegral y :: Integer
+ in r < fromIntegral (minBound :: Int) || r > fromIntegral (maxBound :: Int)
+ highBitTest x y =
+ let r = x + y in testBit ((x `xor` r) .&. (y `xor` r)) (wordSizeInBits - 1)
+ primopTest (I# x) (I# y) = case addIntC# x y of
+ (# _, 0# #) -> False
+ _ -> True
+
+-- subIntC# (Int# subtraction overflow)
+
+ways_subIntC# :: [Int -> Int -> Bool]
+ways_subIntC# = [ltTest, integerTest, highBitTest, primopTest]
+ where ltTest x y =
+ let r = x - y in (y > 0 && r > x) || (y < 0 && r < x)
+ integerTest x y =
+ let r = fromIntegral x - fromIntegral y :: Integer
+ in r < fromIntegral (minBound :: Int) || r > fromIntegral (maxBound :: Int)
+ highBitTest x y =
+ let r = x - y in testBit ((x `xor` r) .&. complement (y `xor` r)) (wordSizeInBits - 1)
+ primopTest (I# x) (I# y) = case subIntC# x y of
+ (# _, 0# #) -> False
+ _ -> True
+
+runTest :: Show a => String -> [a -> a -> Bool] -> a -> a -> IO ()
+runTest label ways x y = do
+ let results = map (\f -> f x y) ways
+ unless (allEqual results) $ do
+ putStrLn $ "Failed (" ++ label ++ "): " ++ show (x,y) ++ " " ++ show results
+ exitWith (ExitFailure 1)
+
+main :: IO ()
+main = do
+ forM_ testWords $ \x ->
+ forM_ testWords $ \y -> do
+ runTest "ways_plusWord2#" ways_plusWord2# x y
+ runTest "ways_addIntC#" ways_addIntC# (fromIntegral x) (fromIntegral y)
+ runTest "ways_subIntC#" ways_subIntC# (fromIntegral x) (fromIntegral y)
+ putStrLn "Passed"