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]