{- | module: Main description: Testing the natural number arithmetic library license: MIT maintainer: Joe Leslie-Hurd stability: provisional portability: portable -} module Main ( main ) where import qualified Test.QuickCheck as QuickCheck import OpenTheory.Primitive.Natural import OpenTheory.Natural import OpenTheory.Natural.Divides import qualified OpenTheory.Natural.Bits as Bits import qualified OpenTheory.Natural.Prime as Prime import qualified OpenTheory.Primitive.Random as Random import qualified OpenTheory.Natural.Uniform as Uniform import Arithmetic.Random import Arithmetic.Prime import qualified Arithmetic.ContinuedFraction as ContinuedFraction import qualified Arithmetic.Prime.Factor as Factor import qualified Arithmetic.Modular as Modular import qualified Arithmetic.Montgomery as Montgomery import qualified Arithmetic.Quadratic as Quadratic import qualified Arithmetic.Williams as Williams propPrimes :: Natural -> Bool propPrimes k = primes !! (fromIntegral k) == Prime.primes !! (fromIntegral k) propRandomPrime :: Natural -> Random.Random -> Bool propRandomPrime wp rnd = Bits.width p == w && isPrime p r2 where w = wp + 2 p = randomPrime w r1 (r1,r2) = Random.split rnd propRandomRSA :: Natural -> Random.Random -> Bool propRandomRSA wp rnd = Bits.width n == w && not (isPrime n r2) where w = wp + 5 n = Factor.toNatural (Factor.randomRSA w r1) (r1,r2) = Random.split rnd propTrialDivision :: Natural -> Natural -> Bool propTrialDivision k np = Factor.toNatural f * m == n && all (\p -> not (divides p m)) ps where n = np + 1 ps = take (fromIntegral k) primes (f,m) = Factor.trialDivision ps n propModularNegate :: Natural -> Random.Random -> Bool propModularNegate np rnd = Modular.add n a b == 0 && b < n where n = np + 1 a = Uniform.random n rnd b = Modular.negate n a propModularInvert :: Natural -> Natural -> Bool propModularInvert np a = case Modular.invert n a of Nothing -> gcd n a /= 1 Just b -> Modular.multiply n a b == Modular.normalize n 1 && b < n where n = np + 1 propFermat :: Natural -> Random.Random -> Bool propFermat pp rnd = Modular.exp p a p == a where p = nextPrime (pp + 3) r1 a = Uniform.random p r2 (r1,r2) = Random.split rnd propMontgomeryInvariant :: Natural -> Natural -> Bool propMontgomeryInvariant np b = naturalOdd n && 1 < n && n < w2 && s * w2 == k * n + 1 && s < n && k < w2 && r == w2 `mod` n && r2 == (r * r) `mod` n && z `mod` n == 0 && w2 <= z && z < w2 + n where Montgomery.Parameters {Montgomery.nParameters = n, Montgomery.wParameters = w, Montgomery.sParameters = s, Montgomery.kParameters = k, Montgomery.rParameters = r, Montgomery.r2Parameters = r2, Montgomery.zParameters = z} = Montgomery.customParameters (2 * np + 3) (Bits.width n + b) w2 = shiftLeft 1 w propMontgomeryNormalize :: Natural -> Natural -> Bool propMontgomeryNormalize np a = b `mod` n == a `mod` n && b < w2 where n = 2 * np + 3 p = Montgomery.standardParameters n b = Montgomery.nMontgomery (Montgomery.normalize p a) w = Montgomery.wParameters p w2 = shiftLeft 1 w propMontgomeryReduce :: Natural -> Natural -> Bool propMontgomeryReduce np a = b `mod` n == (a * s) `mod` n && b < w2 + n where n = 2 * np + 3 p = Montgomery.standardParameters n b = Montgomery.reduce p a w = Montgomery.wParameters p s = Montgomery.sParameters p w2 = shiftLeft 1 w propMontgomeryReduceSmall :: Natural -> Natural -> Bool propMontgomeryReduceSmall np ap = b `mod` n == (a * s) `mod` n && b <= n where n = 2 * np + 3 p = Montgomery.standardParameters n a = ap `mod` w2 b = Montgomery.reduce p a w = Montgomery.wParameters p s = Montgomery.sParameters p w2 = shiftLeft 1 w propMontgomeryToNatural :: Natural -> Natural -> Bool propMontgomeryToNatural np a = b == (a * s) `mod` n where n = 2 * np + 3 p = Montgomery.standardParameters n b = Montgomery.toNatural (Montgomery.normalize p a) s = Montgomery.sParameters p propMontgomeryFromNatural :: Natural -> Natural -> Bool propMontgomeryFromNatural np a = b == a `mod` n where n = 2 * np + 3 p = Montgomery.standardParameters n b = Montgomery.toNatural (Montgomery.fromNatural p a) propMontgomeryZero :: Natural -> Bool propMontgomeryZero np = Montgomery.toNatural (Montgomery.zero p) == 0 where n = 2 * np + 3 p = Montgomery.standardParameters n propMontgomeryOne :: Natural -> Bool propMontgomeryOne np = Montgomery.toNatural (Montgomery.one p) == 1 where n = 2 * np + 3 p = Montgomery.standardParameters n propMontgomeryTwo :: Natural -> Bool propMontgomeryTwo np = Montgomery.toNatural (Montgomery.two p) == 2 where n = 2 * np + 3 p = Montgomery.standardParameters n propMontgomeryAdd :: Natural -> Natural -> Natural -> Bool propMontgomeryAdd np ap bp = Montgomery.toNatural c == Modular.add n (Montgomery.toNatural a) (Montgomery.toNatural b) && Montgomery.nMontgomery c < w2 where n = 2 * np + 3 p = Montgomery.standardParameters n a = Montgomery.normalize p ap b = Montgomery.normalize p bp c = Montgomery.add a b w = Montgomery.wParameters p w2 = shiftLeft 1 w propMontgomeryNegate :: Natural -> Natural -> Bool propMontgomeryNegate np ap = Montgomery.toNatural b == Modular.negate n (Montgomery.toNatural a) && Montgomery.nMontgomery b < w2 where n = 2 * np + 3 p = Montgomery.standardParameters n a = Montgomery.normalize p ap b = Montgomery.negate a w = Montgomery.wParameters p w2 = shiftLeft 1 w propMontgomeryMultiply :: Natural -> Natural -> Natural -> Bool propMontgomeryMultiply np ap bp = Montgomery.toNatural c == Modular.multiply n (Montgomery.toNatural a) (Montgomery.toNatural b) && Montgomery.nMontgomery c < w2 where n = 2 * np + 3 p = Montgomery.standardParameters n a = Montgomery.normalize p ap b = Montgomery.normalize p bp c = Montgomery.multiply a b w = Montgomery.wParameters p w2 = shiftLeft 1 w propMontgomeryModexp :: Natural -> Natural -> Natural -> Bool propMontgomeryModexp np x k = Montgomery.modexp n x k == Modular.exp n x k where n = 2 * np + 3 propMontgomeryModexp2 :: Natural -> Natural -> Natural -> Bool propMontgomeryModexp2 np x k = Montgomery.modexp2 n x k == Modular.exp2 n x k where n = 2 * np + 3 propRootFloor :: Natural -> Bool propRootFloor n = sq s <= n && n < sq (s + 1) where s = Quadratic.rootFloor n sq i = i * i propRootCeiling :: Natural -> Bool propRootCeiling n = (s == 0 || sq (s - 1) < n) && n <= sq s where s = Quadratic.rootCeiling n sq i = i * i propRootContinuedFraction :: Natural -> Bool propRootContinuedFraction n = cf == spec where cf = ContinuedFraction.toDouble (Quadratic.rootContinuedFraction n) spec = sqrt (fromIntegral n) propJacobiSymbol :: Natural -> Natural -> Random.Random -> Bool propJacobiSymbol np m rnd = case Quadratic.jacobiSymbol n m of Quadratic.Zero -> not coprime Quadratic.Residue -> coprime && (mr || not (isPrime n rnd)) Quadratic.NonResidue -> coprime && not mr where coprime = gcd m n == 1 n = 2 * np + 1 mn = Modular.normalize n m mr = any (\k -> Modular.square n k == mn) [1..np] propRootModuloPrime3Mod4 :: Natural -> Random.Random -> Bool propRootModuloPrime3Mod4 pp rnd = Modular.square p r == a where p = nextPrime3Mod4 pp r1 a = randomFilter (Quadratic.isResidue p) (Uniform.random p) r2 r = Quadratic.rootModuloPrime3Mod4 p a (r1,r2) = Random.split rnd propRootModuloPrime5Mod8 :: Natural -> Random.Random -> Bool propRootModuloPrime5Mod8 pp rnd = Modular.square p r == a where p = nextPrime5Mod8 pp r1 a = randomFilter (Quadratic.isResidue p) (Uniform.random p) r2 r = Quadratic.rootModuloPrime5Mod8 p a (r1,r2) = Random.split rnd propRootModuloPrime :: Natural -> Random.Random -> Bool propRootModuloPrime pp rnd = Modular.square p r == a where p = nextPrime pp r1 a = randomFilter (Quadratic.isResidue p) (Uniform.random p) r2 r = Quadratic.rootModuloPrime p a (r1,r2) = Random.split rnd propWilliamsNth :: Natural -> Natural -> Natural -> Bool propWilliamsNth np p k = Williams.sequence one two sub mult p !! (fromIntegral k) == Williams.nth two sub mult p k where n = np + 1 one = 1 two = 2 sub = Modular.subtract n mult = Modular.multiply n propWilliamsNthProduct :: Natural -> Natural -> Natural -> Natural -> Bool propWilliamsNthProduct np pp i j = Williams.nth two sub mult p (i * j) == Williams.nth two sub mult (Williams.nth two sub mult p i) j where n = np + 1 p = pp + 1 two = Modular.normalize n 2 sub = Modular.subtract n mult = Modular.multiply n propWilliamsNthExp :: Natural -> Natural -> Natural -> Natural -> Bool propWilliamsNthExp np p m k = Williams.nthExp two sub mult p m k == Williams.nth two sub mult p (m ^ k) where n = np + 1 two = Modular.normalize n 2 sub = Modular.subtract n mult = Modular.multiply n propWilliamsNthEqTwo :: Natural -> Natural -> Natural -> Random.Random -> Bool propWilliamsNthEqTwo pp a mp rnd = Williams.nth two sub mult a m == two where p = nextPrime (pp + 3) rnd d = sub (mult a a) 4 t = case Quadratic.jacobiSymbol p d of Quadratic.Zero -> 2 Quadratic.Residue -> p - 1 Quadratic.NonResidue -> p + 1 m = mp * t two = Modular.normalize p 2 sub = Modular.subtract p mult = Modular.multiply p propWilliamsFactor :: Natural -> Natural -> Natural -> Random.Random -> Bool propWilliamsFactor np x k rnd = case Williams.factor x (Just k) n rnd of Nothing -> True Just p -> 1 < p && p < n && divides p n where n = 2 * np + 5 check :: QuickCheck.Testable prop => String -> prop -> IO () check desc prop = do putStr (desc ++ "\n ") res <- QuickCheck.quickCheckWithResult args prop case res of QuickCheck.Failure {} -> error "Proposition failed" _ -> return () where args = QuickCheck.stdArgs {QuickCheck.maxSuccess = 1000} main :: IO () main = do check "Sieve of Eratosphenes" propPrimes check "Generating random primes" propRandomPrime check "Generating random RSA moduli" propRandomRSA check "Trial division" propTrialDivision check "Modular negate" propModularNegate check "Modular invert" propModularInvert check "Fermat's little theorem" propFermat check "Montgomery invariant" propMontgomeryInvariant check "Montgomery normalize" propMontgomeryNormalize check "Montgomery reduce" propMontgomeryReduce check "Montgomery reduce small" propMontgomeryReduceSmall check "Montgomery toNatural" propMontgomeryToNatural check "Montgomery fromNatural" propMontgomeryFromNatural check "Montgomery zero" propMontgomeryZero check "Montgomery one" propMontgomeryOne check "Montgomery two" propMontgomeryTwo check "Montgomery add" propMontgomeryAdd check "Montgomery negate" propMontgomeryNegate check "Montgomery multiply" propMontgomeryMultiply check "Montgomery modexp" propMontgomeryModexp check "Montgomery modexp2" propMontgomeryModexp2 check "Floor square root" propRootFloor check "Ceiling square root" propRootCeiling check "Continued fraction square root" propRootContinuedFraction check "Jacobi symbol" propJacobiSymbol check "Square root modulo prime congruent to 3 mod 4" propRootModuloPrime3Mod4 check "Square root modulo prime congruent to 5 mod 8" propRootModuloPrime5Mod8 check "Square root modulo prime" propRootModuloPrime check "Williams sequence" propWilliamsNth check "Williams sequence product" propWilliamsNthProduct check "Williams sequence exponential" propWilliamsNthExp check "Williams sequence equals two" propWilliamsNthEqTwo check "Williams factorization works" propWilliamsFactor return ()