{-# LANGUAGE DataKinds #-} {-# LANGUAGE ScopedTypeVariables #-} module FromRationalSpec where import Control.Monad import Data.Proxy import Data.Ratio import Numeric.Rounded.Hardware.Internal import Test.Hspec import Test.Hspec.QuickCheck (prop) import Test.QuickCheck import Util prop_fromRational_nearest_stock :: forall a. (RealFloat a, RoundedFractional a) => Proxy a -> Rational -> Property prop_fromRational_nearest_stock _proxy x = (roundedFromRational ToNearest x :: a) `sameFloatP` (fromRational x :: a) prop_roundedFromRational_check :: forall a. (RealFloat a, RoundedFractional a) => Proxy a -> RoundingMode -> Rational -> Property prop_roundedFromRational_check _proxy r x = (roundedFromRational_default r x :: a) -- the standard implementation `sameFloatP` (roundedFromRational r x :: a) -- may be optimized prop_fromRatio_order :: forall a. RealFloat a => Proxy a -> Rational -> Property prop_fromRatio_order _proxy x = let ne = roundedFromRational_default ToNearest x :: a ze = roundedFromRational_default TowardZero x :: a inf = roundedFromRational_default TowardInf x :: a ninf = roundedFromRational_default TowardNegInf x :: a in ninf <= inf .&&. (ne == ninf || ne == inf) .&&. (if x < 0 then ze == inf else ze == ninf) prop_fromRatio_exact :: forall a. RealFloat a => Proxy a -> Rational -> Property prop_fromRatio_exact _proxy x = let inf = roundedFromRational_default TowardInf x :: a ninf = roundedFromRational_default TowardNegInf x :: a in if ninf == inf then not (isInfinite inf) .&&. toRational inf === x else if isInfinite inf then inf > 0 .&&. not (isInfinite ninf) .&&. toRational ninf =/= x else if isInfinite ninf then ninf < 0 .&&. not (isInfinite inf) .&&. toRational inf =/= x else toRational inf =/= x .&&. toRational ninf =/= x specT :: (RealFloat a, RoundedFractional a) => Proxy a -> Bool -> Spec specT proxy checkAgainstStock = do when checkAgainstStock $ do -- Although fromRational for Double/Float correctly round to nearest, other types may not. prop "fromRational (nearest) coincides with stock fromRational" $ forAllShrink variousRationals shrinkRealFrac $ prop_fromRational_nearest_stock proxy prop "roundedFromRational coincides with the standard implementation" $ \r -> forAllShrink variousRationals shrinkRealFrac $ prop_roundedFromRational_check proxy r prop "order" $ forAllShrink variousRationals shrinkRealFrac $ prop_fromRatio_order proxy prop "exactness" $ forAllShrink variousRationals shrinkRealFrac $ prop_fromRatio_exact proxy spec :: Spec spec = do describe "Double" $ specT (Proxy :: Proxy Double) True describe "Float" $ specT (Proxy :: Proxy Float) True