In today’s Programming Praxis exercise we need to write an improved version of a factorization algorithm. I was on vacation when the original exercise was posted, so let’s see what we can do with it.
As usual, some imports:
import Data.Bits import Data.List
We need the same expm function we have used in several previous exercises. Alternatively we could use the expmod function from Codec.Encryption.RSA.NumberTheory, but it’s a lot slower than this version.
expm :: Integer -> Integer -> Integer -> Integer expm b e m = foldl' (\r (b', _) -> mod (r * b') m) 1 . filter (flip testBit 0 . snd) . zip (iterate (flip mod m . (^ 2)) b) . takeWhile (> 0) $ iterate (`shiftR` 1) e
The scheme solution has some duplication in it: the start of the pollard1 and pollard2 functions is nearly identical. Since programmers hate repeating themselves, let’s factor that out into a separate function.
pollard :: (Integer -> t) -> (Integer -> t) -> Integer -> Integer -> t pollard found notFound n b1 = f 2 2 where f a i | i < b1 = f (expm a i n) (i + 1) | 1 < d && d < n = found d | otherwise = notFound a where d = gcd (a - 1) n
pollard1 then becomes very simple: if we don’t find anything we stop, otherwise we return the result.
pollard1 :: Integer -> Integer -> Maybe Integer pollard1 = pollard Just (const Nothing)
pollard2 is a bit more involved, because we now have an extra step if we don’t find anything. The structure of this is very similar to the pollard function, but there are enough differences that it’s not worth the bother of abstracting it.
pollard2 :: Integer -> Integer -> Integer -> Maybe (String, Integer) pollard2 n b1 b2 = pollard (Just . (,) "stage1") (f b1) n b1 where f j a | j == b2 = Nothing | 1 < d && d < n = Just ("stage2", d) | otherwise = f (j + 1) a where d = gcd (expm a j n - 1) n
And of course the test to see if everything’s working correctly:
main :: IO () main = do print $ pollard1 15770708441 150 print $ pollard1 15770708441 180 print $ pollard2 15770708441 150 180