61 lines
1.7 KiB
Haskell
61 lines
1.7 KiB
Haskell
module ModularArithmeticUtils (modExp, modMul, legendre, factorOutTwos) where
|
|
|
|
import Data.Bits (testBit, shiftR)
|
|
|
|
-- Modular exponentiation: compute a^b mod m
|
|
modExp :: Integer -> Integer -> Integer -> Integer
|
|
modExp b 0 m = 1
|
|
modExp b e m = t * modExp ((b * b) `mod` m) (shiftR e 1) m `mod` m
|
|
where t = if testBit e 0 then b `mod` m else 1
|
|
|
|
-- Compute (a * b) mod m
|
|
modMul :: Integer -> Integer -> Integer -> Integer
|
|
modMul a b m = (a * b) `mod` m
|
|
|
|
-- Compute the Legendre symbol (a/p)
|
|
-- (a/p) = a^((p-1)/2) mod p
|
|
-- (a/p) in {-1, 0, 1}
|
|
legendre :: Integer -> Integer -> Integer
|
|
legendre a p = modExp a ((p - 1) `div` 2) p
|
|
|
|
-- Factor p-1 as q * 2^s, where q is odd
|
|
factorOutTwos :: Integer -> (Integer, Integer)
|
|
factorOutTwos n = go n 0
|
|
where
|
|
go x s
|
|
| even x = go (x `div` 2) (s + 1)
|
|
| otherwise = (x, s)
|
|
|
|
-- jacobi symbol (a/n)
|
|
-- (a/n) in {-1, 0, 1}
|
|
jacobi :: Integer -> Integer -> Integer
|
|
jacobi a n
|
|
| n <= 0 = error "jacobi: n must be positive"
|
|
| even n = error "jacobi: n must be odd"
|
|
| a `mod` n == 0 = 0
|
|
| otherwise = go (a `mod` n) n 1
|
|
where
|
|
go :: Integer -> Integer -> Integer -> Integer
|
|
go 0 _ _ = 0
|
|
go 1 _ s = s
|
|
go x m s =
|
|
let (xOdd, e) = factorOutTwos x
|
|
s' = if even e then s else s * jacobi2 m
|
|
in
|
|
if xOdd == 1
|
|
then s'
|
|
else
|
|
let s'' = if (xOdd `mod` 4 == 3) && (m `mod` 4 == 3) then -s' else s'
|
|
xNext = m `mod` xOdd
|
|
in go xNext xOdd s''
|
|
|
|
-- Jacobi(2, m), helper function
|
|
jacobi2 :: Integer -> Integer
|
|
jacobi2 m =
|
|
case m `mod` 8 of
|
|
1 -> 1
|
|
7 -> 1
|
|
3 -> -1
|
|
5 -> -1
|
|
_ -> error "jacobi2: unexpected (m mod 8) for odd m"
|