exact-real-0.8.0.0: test/RealFrac.hs
{-# LANGUAGE ScopedTypeVariables #-}
module RealFrac
( realFrac
) where
import Data.Function (on)
import Data.Ratio.Extra ()
import Test.QuickCheck.Checkers (EqProp, (=-=))
import Test.Tasty (testGroup, TestTree)
import Test.Tasty.QuickCheck (testProperty, Arbitrary)
-- TODO: Test the other functions
realFrac :: forall a. (Arbitrary a, EqProp a, Show a, RealFrac a) =>
a -> TestTree
realFrac x = testGroup "Test RealFrac instance" ts
where ts = [ properFractionLaws "properFraction laws" x
, truncateLaws "truncate laws" x
, roundLaws "round laws" x
, ceilingLaws "ceiling laws" x
, floorLaws "floor laws" x
]
-- Thses are used to cope with CReal 0 being a little weird with comparisons
-- between non-integers
infix 4 <., >.
(<.), (>.) :: (Ord a, Num a) => a -> a -> Bool
(<.) = (<) `on` (*2)
(>.) = (>) `on` (*2)
-- | This tests a slightly different law for n having the same sign as x
properFractionLaws :: forall a. (Arbitrary a, EqProp a, Show a, RealFrac a) =>
String -> a -> TestTree
properFractionLaws s _ = testGroup s ts
where ts = [ testProperty "x = n + f"
(\x -> let (n, f) = properFraction (x :: a)
in x =-= fromInteger n + f)
, testProperty "n has same sign or is zero"
(\x -> let (n, _) = properFraction (x :: a)
in n == 0 || sign x == sign (n::Integer))
, testProperty "abs f < 1"
(\x -> let (_::Int, f) = properFraction (x :: a)
in abs f <. 1)
, testProperty "f has same sign or is zero"
(\x -> let (_::Int, f) = properFraction (x :: a)
in f == 0 || sign x == sign f)
]
truncateLaws :: forall a. (Arbitrary a, EqProp a, Show a, RealFrac a) =>
String -> a -> TestTree
truncateLaws s _ = testGroup s ts
where ts = [ testProperty "abs (truncate x) <= abs x"
(\x -> let t = truncate (x :: a)
in fromInteger (abs t) <= abs x)
, testProperty "abs (truncate x) + 1 > abs x"
(\x -> let t = truncate (x :: a)
in fromInteger (abs t + 1) >. abs x)
, testProperty "truncate x has same sign or is zero"
(\x -> let t = truncate (x :: a)
in t == 0 || sign x == sign (t::Integer))
]
roundLaws :: forall a. (Arbitrary a, EqProp a, Show a, RealFrac a) =>
String -> a -> TestTree
roundLaws s _ = testGroup s ts
where ts = [ testProperty "abs (round x - x) <= 0.5"
(\x -> let r = round (x :: a)
in abs (fromInteger r - x) <= 0.5)
, testProperty "round to even if eqiudistant"
(\i -> let x = fromInteger i + 0.5 :: a
in even (round x :: Integer))
]
ceilingLaws :: forall a. (Arbitrary a, EqProp a, Show a, RealFrac a) =>
String -> a -> TestTree
ceilingLaws s _ = testGroup s ts
where ts = [ testProperty "ceiling x - 1 < x"
(\x -> let c = ceiling (x :: a)
in fromInteger c - 1 <. x)
, testProperty "ceiling x >= x"
(\x -> let c = ceiling (x :: a)
in fromInteger c >= x)
]
floorLaws :: forall a. (Arbitrary a, EqProp a, Show a, RealFrac a) =>
String -> a -> TestTree
floorLaws s _ = testGroup s ts
where ts = [ testProperty "floor x + 1 > x"
(\x -> let f = floor (x :: a)
in fromInteger f + 1 >. x)
, testProperty "floor x <= x"
(\x -> let f = floor (x :: a)
in fromInteger f <= x)
]
data Sign = Positive
| Negative
deriving (Eq, Show)
-- | Note that this returns Positive on zero rather than 0 like signum
sign :: (Ord a, Num a) => a -> Sign
sign x = if x < 0 then Negative
else Positive