packages feed

fast-arithmetic-0.2.1.0: test/Spec.hs

import           Data.List             (zipWith4)
import           Numeric.Combinatorics
import           Numeric.Integer
import           Numeric.NumberTheory
import           Numeric.Pure
import           Test.Hspec
import           Test.Hspec.QuickCheck
import           Test.QuickCheck       hiding (choose)

tooBig :: Int -> Int -> Bool
tooBig x y = go x y >= 2 ^ (16 :: Integer)
    where
        go :: Int -> Int -> Integer
        go m n = fromIntegral m ^ (fromIntegral n :: Integer)

agree :: (Eq a, Show b, Integral b, Arbitrary b) => String -> (b -> a) -> (b -> a) -> SpecWith ()
agree s f g = describe s $
    prop "should agree with the pure Haskell function" $
        \n -> n < 1 || f n == g n

check :: (Eq a, Show a) => String -> (Int -> IO a) -> (Integer -> a) -> Int -> SpecWith ()
check s f g n = describe s $
    it ("should work for n=" ++ show n) $
        f n >>= (`shouldBe` g (fromIntegral n))

{- mkIoProp :: (Int -> IO Integer) -> (Int -> Integer) -> Property -}
{- mkIoProp f g = again $ ioProperty $ do -}
    {- m <- randomIO -}
    {- actual <- f m -}
    {- let expected = g m -}
    {- pure $ m < 1 || actual == expected -}

main :: IO ()
main = hspec $ parallel $ do

    sequence_ $ zipWith3 agree
        ["totient", "tau", "littleOmega", "sumDivisors"]
        [totient, tau, littleOmega, sumDivisors]
        [hsTotient, hsTau, hsLittleOmega, hsSumDivisors]

    sequence_ $ zipWith3 agree
        ["isPrime", "isPerfect"]
        [isPrime, isPerfect]
        [hsIsPrime, hsIsPerfect]

    describe "totient" $
        prop "should be equal to m-1 for m prime" $
            \m -> m < 1 || not (isPrime m) || totient m == m - 1
    describe "derangement" $
        prop "should be equal to [n!/e]" $
            \n -> n < 1 || n > 18 || (derangement n :: Integer) == floor ((fromIntegral (factorial n :: Integer) :: Double) / exp 1 + 0.5)
    describe "totient" $
        prop "should satisfy Fermat's little theorem" $
            \a m -> a < 1 || m < 2 || gcd a m /= 1 || tooBig a m || (a ^ totient m) `mod` m == 1

    -- TODO property test w/ recurrence relations?
    sequence_ $ zipWith4 check
        ["choose 101", "doubleFactorial"]
        [choose 101, doubleFactorial]
        [hsChoose 101 . fromIntegral, hsDoubleFactorial]
        [16, 79]