arithmoi-0.12.0.0: test-suite/Math/NumberTheory/ArithmeticFunctionsTests.hs
-- |
-- Module: Math.NumberTheory.ArithmeticFunctionsTests
-- Copyright: (c) 2016 Andrew Lelechenko
-- Licence: MIT
-- Maintainer: Andrew Lelechenko <andrew.lelechenko@gmail.com>
--
-- Tests for Math.NumberTheory.ArithmeticFunctions
--
{-# LANGUAGE CPP #-}
{-# OPTIONS_GHC -fno-warn-type-defaults #-}
module Math.NumberTheory.ArithmeticFunctionsTests
( testSuite
) where
import Test.Tasty
import Test.Tasty.HUnit
import Data.List (sort)
import qualified Data.Set as S
import qualified Data.IntSet as IS
import Math.NumberTheory.ArithmeticFunctions
import Math.NumberTheory.Primes (UniqueFactorisation (factorise))
import Math.NumberTheory.TestUtils
import Math.NumberTheory.Zeta (zetas)
import Numeric.Natural
oeisAssertion :: (Eq a, Show a) => String -> ArithmeticFunction Natural a -> [a] -> Assertion
oeisAssertion name f baseline = assertEqual name baseline (map (runFunction f) [1 .. fromIntegral (length baseline)])
wolframAlphaAssertion :: (Eq a, Show a) => String -> ArithmeticFunction Integer a -> [Integer] -> [a] -> Assertion
wolframAlphaAssertion name f domain baseline = assertEqual name baseline (map (runFunction f) domain)
-- | tau(n) equals to a number of divisors.
divisorsProperty1 :: NonZero Natural -> Bool
divisorsProperty1 (NonZero n) = S.size (runFunction divisorsA n) == runFunction tauA n
-- | sigma(n) equals to a number of divisors.
divisorsProperty2 :: NonZero Natural -> Bool
divisorsProperty2 (NonZero n) = sum (runFunction divisorsA n) == runFunction (sigmaA 1) n
-- | All divisors of n truly divides n.
divisorsProperty3 :: NonZero Natural -> Bool
divisorsProperty3 (NonZero n) = all (\d -> n `rem` d == 0) (runFunction divisorsA n)
-- | 'divisorsA' matches 'divisorsSmallA'
divisorsProperty4 :: NonZero Int -> Bool
divisorsProperty4 (NonZero n) = S.toAscList (runFunction divisorsA n) == IS.toAscList (runFunction divisorsSmallA n)
-- | 'divisorsA' matches 'divisorsListA'
divisorsProperty5 :: NonZero Int -> Bool
divisorsProperty5 (NonZero n) = S.toAscList (runFunction divisorsA n) == sort (runFunction divisorsListA n)
-- | 'divisorsTo' matches 'divisorsA' with a filter
divisorsProperty6 :: Positive Int -> NonNegative Int -> Bool
divisorsProperty6 (Positive a) (NonNegative b) = runFunction (divisorsToA to) n == expected
where to = a
n = to + b
expected = S.filter (<=to) (runFunction divisorsA n)
-- | tau matches baseline from OEIS.
tauOeis :: Assertion
tauOeis = oeisAssertion "A000005" tauA
[ 1, 2, 2, 3, 2, 4, 2, 4, 3, 4, 2, 6, 2, 4, 4, 5, 2, 6, 2, 6, 4, 4, 2, 8
, 3, 4, 4, 6, 2, 8, 2, 6, 4, 4, 4, 9, 2, 4, 4, 8, 2, 8, 2, 6, 6, 4, 2, 10
, 3, 6, 4, 6, 2, 8, 4, 8, 4, 4, 2, 12, 2, 4, 6, 7, 4, 8, 2, 6, 4, 8, 2
, 12, 2, 4, 6, 6, 4, 8, 2, 10, 5, 4, 2, 12, 4, 4, 4, 8, 2, 12, 4, 6, 4, 4
, 4, 12, 2, 6, 6, 9, 2, 8, 2, 8
]
-- | sigma_0 coincides with tau by definition
sigmaProperty1 :: NonZero Natural -> Bool
sigmaProperty1 (NonZero n) = runFunction tauA n == (runFunction (sigmaA 0) n :: Natural)
-- | value of totient is bigger than argument
sigmaProperty2 :: NonZero Natural -> Bool
sigmaProperty2 (NonZero n) = n <= 1 || runFunction (sigmaA 1) n > n
-- | sigma_1 matches baseline from OEIS.
sigma1Oeis :: Assertion
sigma1Oeis = oeisAssertion "A000203" (sigmaA 1)
[ 1, 3, 4, 7, 6, 12, 8, 15, 13, 18, 12, 28, 14, 24, 24, 31, 18, 39, 20
, 42, 32, 36, 24, 60, 31, 42, 40, 56, 30, 72, 32, 63, 48, 54, 48, 91, 38
, 60, 56, 90, 42, 96, 44, 84, 78, 72, 48, 124, 57, 93, 72, 98, 54, 120
, 72, 120, 80, 90, 60, 168, 62, 96, 104, 127, 84, 144, 68, 126, 96, 144 :: Natural
]
-- | sigma_2 matches baseline from OEIS.
sigma2Oeis :: Assertion
sigma2Oeis = oeisAssertion "A001157" (sigmaA 2)
[ 1, 5, 10, 21, 26, 50, 50, 85, 91, 130, 122, 210, 170, 250, 260, 341, 290
, 455, 362, 546, 500, 610, 530, 850, 651, 850, 820, 1050, 842, 1300, 962
, 1365, 1220, 1450, 1300, 1911, 1370, 1810, 1700, 2210, 1682, 2500, 1850
, 2562, 2366, 2650, 2210, 3410, 2451, 3255 :: Natural
]
-- | value of totient if even, except totient(1) and totient(2)
totientProperty1 :: NonZero Natural -> Bool
totientProperty1 (NonZero n) = n <= 2 || even (runFunction totientA n)
-- | value of totient is smaller than argument
totientProperty2 :: NonZero Natural -> Bool
totientProperty2 (NonZero n) = n <= 1 || runFunction totientA n < n
-- | totient matches baseline from OEIS.
totientOeis :: Assertion
totientOeis = oeisAssertion "A000010" totientA
[ 1, 1, 2, 2, 4, 2, 6, 4, 6, 4, 10, 4, 12, 6, 8, 8, 16, 6, 18, 8, 12, 10
, 22, 8, 20, 12, 18, 12, 28, 8, 30, 16, 20, 16, 24, 12, 36, 18, 24, 16, 40
, 12, 42, 20, 24, 22, 46, 16, 42, 20, 32, 24, 52, 18, 40, 24, 36, 28, 58
, 16, 60, 30, 36, 32, 48, 20, 66, 32, 44
]
-- | jordan_0 is zero for argument > 1
jordanProperty1 :: NonZero Natural -> Bool
jordanProperty1 (NonZero n) = n <= 1 || runFunction (jordanA 0) n == 0
-- | jordan_1 coincides with totient by definition
jordanProperty2 :: NonZero Natural -> Bool
jordanProperty2 (NonZero n) = runFunction totientA n == runFunction (jordanA 1) n
-- | jordan_2 matches baseline from OEIS.
jordan2Oeis :: Assertion
jordan2Oeis = oeisAssertion "A007434" (jordanA 2)
[ 1, 3, 8, 12, 24, 24, 48, 48, 72, 72, 120, 96, 168, 144, 192, 192, 288
, 216, 360, 288, 384, 360, 528, 384, 600, 504, 648, 576, 840, 576, 960
, 768, 960, 864, 1152, 864, 1368, 1080, 1344, 1152, 1680, 1152, 1848, 1440
, 1728, 1584, 2208, 1536
]
-- | congruences 1,2,3,4 from https://en.wikipedia.org/wiki/Ramanujan_tau_function
ramanujanCongruence1 :: NonZero Natural -> Bool
ramanujanCongruence1 (NonZero n)
| k == 1 = (ramanujan n' - sigma 11 n') `rem` (2^11) == 0
| k == 3 = (ramanujan n' - 1217 * sigma 11 n') `rem` (2^13) == 0
| k == 5 = (ramanujan n' - 1537 * sigma 11 n') `rem` (2^12) == 0
| k == 7 = (ramanujan n' - 705 * sigma 11 n') `rem` (2^14) == 0
| otherwise = True
where k = n `mod` 8
n' = fromIntegral n :: Integer
-- | congruences 8,9 from https://en.wikipedia.org/wiki/Ramanujan_tau_function
ramanujanCongruence2 :: NonZero Natural -> Bool
ramanujanCongruence2 (NonZero n)
| (n `mod` 7) `elem` [0,1,2,4] = m `rem` 7 == 0
| otherwise = m `rem` 49 == 0
where m = ramanujan n' - n' * sigma 9 n'
n' = fromIntegral n :: Integer
-- | ramanujan matches baseline from wolframAlpha: https://www.wolframalpha.com/input/?i=RamanujanTau%5BRange%5B100%5D%5D
ramanujanRange :: Assertion
ramanujanRange = wolframAlphaAssertion "A000594" ramanujanA [1..100]
[ 1, -24, 252, -1472, 4830, -6048, -16744, 84480, -113643, -115920
, 534612, -370944, -577738, 401856, 1217160, 987136, -6905934, 2727432
, 10661420, -7109760, -4219488, -12830688, 18643272, 21288960, -25499225
, 13865712, -73279080, 24647168, 128406630, -29211840, -52843168
, -196706304, 134722224, 165742416, -80873520, 167282496, -182213314
, -255874080, -145589976, 408038400, 308120442, 101267712, -17125708
, -786948864, -548895690, -447438528, 2687348496, 248758272, -1696965207
, 611981400, -1740295368, 850430336, -1596055698, 1758697920, 2582175960
, -1414533120, 2686677840, -3081759120, -5189203740, -1791659520, 6956478662
, 1268236032, 1902838392, 2699296768, -2790474540, -3233333376, -15481826884
, 10165534848, 4698104544, 1940964480, 9791485272, -9600560640, 1463791322
, 4373119536, -6425804700, -15693610240, -8951543328, 3494159424, 38116845680
, 4767866880, 1665188361, -7394890608, -29335099668, 6211086336, -33355661220
, 411016992, 32358470760, 45164021760, -24992917110, 13173496560, 9673645072
, -27442896384, -13316478336, -64496363904, 51494658600, -49569988608
, 75013568546, 40727164968, -60754911516, 37534859200
]
-- | ramanujan matches baseline from wolframAlpha: https://www.wolframalpha.com/input/?i=RamanujanTau%5B2%5ERange%5B20%5D%5D
ramanujanPowers2 :: Assertion
ramanujanPowers2 = wolframAlphaAssertion "wolframAlpha2^n" ramanujanA [2^n | n <- [1..20]]
[ -24, -1472, 84480, 987136, -196706304, 2699296768, 338071388160
, -13641873096704, -364965248630784, 36697722069188608, -133296500464680960
, -71957818786545926144, 1999978883828768833536, 99370119662955604738048
, -6480839625992253084794880, -47969854045919004468445184
, 14424036051134190424902598656, -247934604141178449046286630912
, -23589995333334539213089642905600, 1073929957281162404760946449842176
]
-- | ramanujan matches baseline from wolframAlpha: https://www.wolframalpha.com/input/?i=RamanujanTau%5B3%5ERange%5B20%5D%5D
ramanujanPowers3 :: Assertion
ramanujanPowers3 = wolframAlphaAssertion "wolframAlpha3^n" ramanujanA [3^n | n <- [1..20]]
[ 252, -113643, -73279080, 1665188361, 13400796651732, 3082017633650397
, -1597242480784468560, -948475282905952954479, 43930942451226107469612
, 179090148438649827109433637, 37348482744132405171657919560
, -22313464873940134819697044764519, -12239164820907737153507340756954108
, 868493827155123300221022518147812077, 2386991774972433985188062567645398013280
, 447670851294004737003138291024309833342241
, -310035377434952569449318870332553243856267428
, -157432463407787104647123294163886831498857358283
, 15248856227707192449163419793501327951694151780600
, 31731400364681474724113131979212395183355010696469801
]
-- | moebius does not require full factorisation
moebiusLazy :: Assertion
moebiusLazy = assertEqual "moebius" MoebiusZ (runFunction moebiusA (2^2 * (2^100000-1) :: Natural))
-- | moebius matches baseline from OEIS.
moebiusOeis :: Assertion
moebiusOeis = oeisAssertion "A008683" moebiusA
[ MoebiusP, MoebiusN, MoebiusN, MoebiusZ, MoebiusN, MoebiusP, MoebiusN, MoebiusZ, MoebiusZ, MoebiusP, MoebiusN, MoebiusZ, MoebiusN, MoebiusP, MoebiusP, MoebiusZ, MoebiusN, MoebiusZ, MoebiusN, MoebiusZ, MoebiusP, MoebiusP, MoebiusN
, MoebiusZ, MoebiusZ, MoebiusP, MoebiusZ, MoebiusZ, MoebiusN, MoebiusN, MoebiusN, MoebiusZ, MoebiusP, MoebiusP, MoebiusP, MoebiusZ, MoebiusN, MoebiusP, MoebiusP, MoebiusZ, MoebiusN, MoebiusN, MoebiusN, MoebiusZ, MoebiusZ, MoebiusP
, MoebiusN, MoebiusZ, MoebiusZ, MoebiusZ, MoebiusP, MoebiusZ, MoebiusN, MoebiusZ, MoebiusP, MoebiusZ, MoebiusP, MoebiusP, MoebiusN, MoebiusZ, MoebiusN, MoebiusP, MoebiusZ, MoebiusZ, MoebiusP, MoebiusN, MoebiusN, MoebiusZ, MoebiusP
, MoebiusN, MoebiusN, MoebiusZ, MoebiusN, MoebiusP, MoebiusZ, MoebiusZ, MoebiusP
]
-- | liouville values are [-1, 1]
liouvilleProperty1 :: NonZero Natural -> Bool
liouvilleProperty1 (NonZero n) = runFunction liouvilleA n `elem` [-1, 1]
-- | moebius is zero or equal to liouville
liouvilleProperty2 :: NonZero Natural -> Bool
liouvilleProperty2 (NonZero n) = m == MoebiusZ || l == runMoebius m
where
l = runFunction liouvilleA n
m = runFunction moebiusA n
-- | liouville matches baseline from OEIS.
liouvilleOeis :: Assertion
liouvilleOeis = oeisAssertion "A008836" liouvilleA
[ 1, -1, -1, 1, -1, 1, -1, -1, 1, 1, -1, -1, -1, 1, 1, 1, -1, -1, -1, -1, 1, 1
, -1, 1, 1, 1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, 1, 1, 1, -1, -1, -1, -1
, -1, 1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, 1, -1
, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1, 1, -1, -1, -1, 1, 1, -1, 1, 1, 1, 1, 1
, -1, 1, 1, -1, 1, 1, 1, 1, -1, -1, -1, 1, -1
]
-- | carmichaeil divides totient
carmichaelProperty1 :: NonZero Natural -> Bool
carmichaelProperty1 (NonZero n) = runFunction totientA n `rem` runFunction carmichaelA n == 0
-- | carmichael matches baseline from OEIS.
carmichaelOeis :: Assertion
carmichaelOeis = oeisAssertion "A002322" carmichaelA
[ 1, 1, 2, 2, 4, 2, 6, 2, 6, 4, 10, 2, 12, 6, 4, 4, 16, 6, 18, 4, 6, 10, 22, 2
, 20, 12, 18, 6, 28, 4, 30, 8, 10, 16, 12, 6, 36, 18, 12, 4, 40, 6, 42, 10, 12
, 22, 46, 4, 42, 20, 16, 12, 52, 18, 20, 6, 18, 28, 58, 4, 60, 30, 6, 16, 12
, 10, 66, 16, 22, 12, 70, 6, 72, 36, 20, 18, 30, 12, 78, 4, 54
]
-- | smallOmega is smaller than bigOmega
omegaProperty1 :: NonZero Natural -> Bool
omegaProperty1 (NonZero n) = runFunction smallOmegaA n <= runFunction bigOmegaA n
-- | smallOmega matches baseline from OEIS.
smallOmegaOeis :: Assertion
smallOmegaOeis = oeisAssertion "A001221" smallOmegaA
[ 0, 1, 1, 1, 1, 2, 1, 1, 1, 2, 1, 2, 1, 2, 2, 1, 1, 2, 1, 2, 2, 2, 1, 2, 1, 2
, 1, 2, 1, 3, 1, 1, 2, 2, 2, 2, 1, 2, 2, 2, 1, 3, 1, 2, 2, 2, 1, 2, 1, 2, 2, 2
, 1, 2, 2, 2, 2, 2, 1, 3, 1, 2, 2, 1, 2, 3, 1, 2, 2, 3, 1, 2, 1, 2, 2, 2, 2, 3
, 1, 2, 1, 2, 1, 3, 2, 2, 2, 2, 1, 3, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 1, 3, 1, 2
, 3, 2, 1, 2, 1, 3, 2
]
-- | bigOmega matches baseline from OEIS.
bigOmegaOeis :: Assertion
bigOmegaOeis = oeisAssertion "A001222" bigOmegaA
[ 0, 1, 1, 2, 1, 2, 1, 3, 2, 2, 1, 3, 1, 2, 2, 4, 1, 3, 1, 3, 2, 2, 1, 4, 2, 2
, 3, 3, 1, 3, 1, 5, 2, 2, 2, 4, 1, 2, 2, 4, 1, 3, 1, 3, 3, 2, 1, 5, 2, 3, 2, 3
, 1, 4, 2, 4, 2, 2, 1, 4, 1, 2, 3, 6, 2, 3, 1, 3, 2, 3, 1, 5, 1, 2, 3, 3, 2, 3
, 1, 5, 4, 2, 1, 4, 2, 2, 2, 4, 1, 4, 2, 3, 2, 2, 2, 6, 1, 3, 3, 4, 1, 3, 1, 4
, 3, 2, 1, 5, 1, 3, 2
]
-- | expMangoldt matches baseline from OEIS.
mangoldtOeis :: Assertion
mangoldtOeis = oeisAssertion "A014963" expMangoldtA
[ 1, 2, 3, 2, 5, 1, 7, 2, 3, 1, 11, 1, 13, 1, 1, 2, 17, 1, 19, 1, 1, 1, 23, 1
, 5, 1, 3, 1, 29, 1, 31, 2, 1, 1, 1, 1, 37, 1, 1, 1, 41, 1, 43, 1, 1, 1, 47, 1
, 7, 1, 1, 1, 53, 1, 1, 1, 1, 1, 59, 1, 61, 1, 1, 2, 1, 1, 67, 1, 1, 1, 71, 1
, 73, 1, 1, 1, 1, 1, 79, 1, 3, 1, 83, 1, 1, 1, 1, 1, 89, 1, 1, 1, 1, 1, 1
]
nFreedomProperty1 :: Word -> NonZero Natural -> Bool
nFreedomProperty1 n (NonZero m) =
isNFree n m == (all ((< n) . snd) . factorise) m
nFreedomProperty2 :: Power Word -> NonNegative Int -> Bool
nFreedomProperty2 (Power n) (NonNegative m) =
let n' | n == maxBound = n
| otherwise = n + 1
in take m (filter (isNFree n') [1 ..]) == take m (nFrees n' :: [Integer])
nFreedomProperty3 :: Power Word -> Positive Int -> Bool
nFreedomProperty3 (Power n) (Positive m) =
let n' | n == maxBound = n
| otherwise = n + 1
zet = 1 / zetas 1e-14 !! fromIntegral n' :: Double
m' = 100 * m
nfree = fromIntegral m' /
fromIntegral (head (drop (m' - 1) $ nFrees n' :: [Integer]))
in 1 / fromIntegral m >= abs (zet - nfree)
-- |
-- * Using a bounded integer type like @Int@ instead of @Integer@ here means
-- even a relatively low value of @n@, e.g. 20 may cause out-of-bounds memory
-- accesses in @nFreesBlock@.
-- * Using @Integer@ prevents this, so that is the numeric type used here.
nFreesBlockProperty1 :: Power Word -> Positive Integer -> Word -> Bool
nFreesBlockProperty1 (Power n) (Positive lo) w =
let block = nFreesBlock n lo w
len = length block
blk = take len . dropWhile (< lo) . nFrees $ n
in block == blk
nFreedomAssertion1 :: Assertion
nFreedomAssertion1 =
assertEqual "1 is the sole 0-free number" (nFrees 0) ([1] :: [Int])
nFreedomAssertion2 :: Assertion
nFreedomAssertion2 =
assertEqual "1 is the sole 1-free number" (nFrees 1) ([1] :: [Int])
testSuite :: TestTree
testSuite = testGroup "ArithmeticFunctions"
[ testGroup "Divisors"
[ testSmallAndQuick "length . divisors = tau" divisorsProperty1
, testSmallAndQuick "sum . divisors = sigma_1" divisorsProperty2
, testSmallAndQuick "matches definition" divisorsProperty3
, testSmallAndQuick "divisors = divisorsSmall" divisorsProperty4
, testSmallAndQuick "divisors = divisorsList" divisorsProperty5
, testSmallAndQuick "divisors = divisorsTo" divisorsProperty6
]
, testGroup "Tau"
[ testCase "OEIS" tauOeis
]
, testGroup "Sigma"
[ testSmallAndQuick "sigma_0 = tau" sigmaProperty1
, testSmallAndQuick "sigma_1 n > n" sigmaProperty2
, testCase "OEIS sigma_1" sigma1Oeis
, testCase "OEIS sigma_2" sigma2Oeis
]
, testGroup "Totient"
[ testSmallAndQuick "totient is even" totientProperty1
, testSmallAndQuick "totient n < n" totientProperty2
, testCase "OEIS" totientOeis
]
, testGroup "Jordan"
[ testSmallAndQuick "jordan_0 = [== 1]" jordanProperty1
, testSmallAndQuick "jordan_1 = totient" jordanProperty2
, testCase "OEIS jordan_2" jordan2Oeis
]
, testGroup "Ramanujan"
[ testSmallAndQuick "ramanujan mod 8 congruences" ramanujanCongruence1
, testSmallAndQuick "ramanujan mod 7 congruences" ramanujanCongruence2
, testCase "baseline ramanujan range" ramanujanRange
, testCase "baseline ramanujan powers2" ramanujanPowers2
, testCase "baseline ramanujan powers3" ramanujanPowers3
]
, testGroup "Moebius"
[ testCase "OEIS" moebiusOeis
, testCase "Lazy" moebiusLazy
]
, testGroup "Liouville"
[ testSmallAndQuick "liouville values" liouvilleProperty1
, testSmallAndQuick "liouville matches moebius" liouvilleProperty2
, testCase "OEIS" liouvilleOeis
]
, testGroup "Carmichael"
[ testSmallAndQuick "carmichael divides totient" carmichaelProperty1
, testCase "OEIS" carmichaelOeis
]
, testGroup "Omegas"
[ testSmallAndQuick "smallOmega <= bigOmega" omegaProperty1
, testCase "OEIS smallOmega" smallOmegaOeis
, testCase "OEIS bigOmega" bigOmegaOeis
]
, testGroup "Mangoldt"
[ testCase "OEIS" mangoldtOeis
]
, testGroup "N-freedom"
[ testSmallAndQuick "`isNFree` matches the definition" nFreedomProperty1
, testSmallAndQuick "numbers produces by `nFrees`s are `n`-free" nFreedomProperty2
, testSmallAndQuick "distribution of n-free numbers matches expected" nFreedomProperty3
, testSmallAndQuick "nFreesBlock matches nFrees" nFreesBlockProperty1
, testCase "`1` is the only 0-free number" nFreedomAssertion1
, testCase "`1` is the only 1-free number" nFreedomAssertion2
]
]