Created
August 25, 2012 20:24
-
-
Save jamak/3470614 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| -- numbertheoryfundamentals.hs | |
| module NumberTheoryFundamentals where | |
| -- Ch 1 of Cohen, A Course in Computational Algebraic Number Theory | |
| import Bits (shiftL, shiftR) | |
| import RandomGenerator (randomInteger) -- for sqrts mod p | |
| -- BASICS | |
| d `divides` n = n `mod` d == 0 | |
| -- splitWith :: Integer -> Integer -> (Int, Integer) | |
| -- n `splitWith` p = (s,t), where n = p^s * t | |
| n `splitWith` p = doSplitWith 0 n | |
| where doSplitWith s t | |
| | p `divides` t = doSplitWith (s+1) (t `div` p) | |
| | otherwise = (s, t) | |
| -- calculate x^n in a (semi)group | |
| power (idG,multG) x n = doPower idG x n | |
| where | |
| doPower y _ 0 = y | |
| doPower y x n = | |
| let y' = if odd n then (y `multG` x) else y | |
| x' = x `multG` x | |
| n' = n `div` 2 | |
| in doPower y' x' n' | |
| -- EXTENDED EUCLIDEAN ALGORITHM | |
| -- extendedEuclid a b returns (u,v,d) such that u*a + v*b = d | |
| extendedEuclid a b | a >= 0 && b >= 0 = extendedEuclid' a b | |
| extendedEuclid' a b = doExtendedEuclid a b [] | |
| where | |
| doExtendedEuclid d 0 qs = let (u,v) = unwind 1 0 qs in (u,v,d) | |
| doExtendedEuclid a b qs = let (q,r) = quotRem a b in doExtendedEuclid b r (q:qs) | |
| unwind u v [] = (u,v) | |
| unwind u v (q:qs) = unwind v (u-v*q) qs | |
| -- from Cohen, p16 | |
| -- appears to be slightly slower than the above | |
| extendedEuclidCohen d 0 | d > 0 = (1,0,d) | |
| extendedEuclidCohen a b | a >= 0 && b >= 0 = doExtendedEuclid a b a 1 0 b | |
| where | |
| doExtendedEuclid a b d u _ 0 = (u, (d-a*u) `div` b, d) | |
| doExtendedEuclid a b d u v1 v3 = | |
| let | |
| (q,t3) = quotRem d v3 | |
| t1 = u - q * v1 | |
| in doExtendedEuclid a b v3 v1 t1 t3 | |
| -- INTEGER SQUARE ROOT | |
| intSqrt :: Integer -> Integer | |
| intSqrt 0 = 0 | |
| intSqrt n = newtonianIteration n (findx0 n 1) | |
| where | |
| -- find x0 == 2^(a+1), such that 4^a <= n < 4^(a+1). | |
| findx0 a b = if a == 0 then b else findx0 (a `shiftR` 2) (b `shiftL` 1) | |
| newtonianIteration n x = | |
| let x' = (x + n `div` x) `div` 2 | |
| in if x' < x then newtonianIteration n x' else x | |
| minus1to n = if even n then 1 else -1 | |
| -- LEGENDRE SYMBOL | |
| -- from Koblitz, A Course in Number Theory and Cryptography 47-8 | |
| -- see also Cohen p29,30 | |
| -- Legendre symbol, via Jacobi symbol | |
| -- returns 0 if p divides a, 1 if a is a square mod p, -1 if a is not a square mod p | |
| legendreSymbol a p | p > 0 && odd p = legendreSymbol' a p | |
| legendreSymbol a 2 = a `mod` 2 | |
| -- strictly speaking, legendreSymbol is only defined for odd p | |
| -- we define it for p == 2 as a convenience | |
| -- !! note that in some applications you should use the Kronecker symbol instead, which gives a *different* answer for p == 2 | |
| legendreSymbol' a p = if a' == 0 then 0 else twoSymbol * oddSymbol | |
| where | |
| a' = a `mod` p -- hence a' >= 0 | |
| (s,q) = a' `splitWith` 2 -- a' == 2^s * q, hence (a'/p) == (2/p)^s * (q/p) | |
| twoSymbol = if (p `mod` 8) `elem` [1,7] then 1 else minus1to s -- (2/p) == (-1)^((p^2-1)/8), p odd | |
| oddSymbol = if q == 1 then 1 else qrMultiplier * legendreSymbol' p q | |
| qrMultiplier = if p `mod` 4 == 3 && q `mod` 4 == 3 then -1 else 1 -- == (-1)^((p-1)*(q-1)/4), p, q odd | |
| -- a slight optimisation would be to use .&. 7 an .&. 3 instead of `mod` 8 and `mod` 4 | |
| legendreSymbolTest a p = | |
| if p `divides` a | |
| then 0 | |
| else if or [(x*x-a) `mod` p == 0 | x <- [1..p `div` 2]] then 1 else -1 | |
| -- !! brute force method - for testing purposes only | |
| -- KRONECKER SYMBOL | |
| -- Cohen p28 | |
| kroneckerSymbol a 0 = if a `elem` [-1,1] then 1 else 0 | |
| kroneckerSymbol a b = kroneckerSign * kroneckerTwo * kroneckerOdd | |
| where | |
| (b2s,bOdd) = (abs b) `splitWith` 2 | |
| kroneckerSign = if b < 0 && a < 0 then -1 else 1 | |
| kroneckerTwo | |
| | even a = if b2s == 0 then 1 else 0 -- (a/2) == 0, a even | |
| | odd a = if (a `mod` 8) `elem` [1,7] then 1 else minus1to b2s -- (a/2) == (-1)^((a^2-1)/8), a odd | |
| kroneckerOdd | |
| | bOdd == 1 = 1 | |
| | otherwise = legendreSymbol' a bOdd | |
| -- Cohen p29 has an algorithm which may be faster | |
| -- SQRTS MOD P | |
| -- based on Cohen p32-3 | |
| -- note that this implementation is poor when p-1 is divisible by a large power of 2 | |
| -- Koblitz, A Course in Number Theory and Cryptography, 48-9, explains how to fix this (so does Cohen, but rather incomprehensibly) | |
| sqrtsmodp a p = sqrtsmodp' (a `mod` p) p | |
| sqrtsmodp' 0 _ = [0] | |
| sqrtsmodp' 1 2 = [1] | |
| sqrtsmodp' a p = | |
| let | |
| (e,q) = (p-1) `splitWith` 2 | |
| z = findSylow2Generator q | |
| zpowers = take (2^(e-1)) (iterate (\(z_k,z_2k)->(z * z_k `mod` p ,z * z * z_2k `mod` p)) (1,1)) | |
| a_q = power (1, \x y -> x*y `mod` p) a q -- powerMod p a q | |
| in case (filter (\(_,z_2k) -> a_q * z_2k `mod` p == 1) zpowers) of | |
| [] -> [] | |
| (z_k,_):_ -> let x = power (1, \x y -> x*y `mod` p) a ((q+1) `div` 2) * z_k `mod` p in ascendingPair [x,p-x] | |
| -- (z_k,_):_ -> let x = powerMod p a ((q+1) `div` 2) * z_k `mod` p in ascendingPair [x,p-x] | |
| where | |
| findSylow2Generator q = | |
| let nonresidue = head [n | (n,_) <- iterate (\(_,seed) -> randomInteger p seed) (0,342349871), legendreSymbol n p == -1] | |
| in power (1, \x y -> x*y `mod` p) nonresidue q -- powerMod p nonresidue q | |
| ascendingPair [x,y] = if x < y then [x,y] else [y,x] | |
| sqrtsmodptest a p = [x | x <- [0..p-1], (x*x-a) `mod` p == 0] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment