prime factors decomposition
This commit is contained in:
1 parent
bac5cb38bc
commit
9d1b785282
3 files changed
+97
-2
No files matched your search
+19
-2
@@ -2,6 +2,7 @@ module FactorizationUI (run) where
|
|||||||
|
|
||||||
import System.Exit (exitSuccess)
|
import System.Exit (exitSuccess)
|
||||||
import Utils (askChoice, askNumber)
|
import Utils (askChoice, askNumber)
|
||||||
|
import PrimeFactors (primeFactors, formatFactors)
|
||||||
import Factorization
|
import Factorization
|
||||||
( fermatFactorization, pollardPminus1, dixon, dixonDefaultParams
|
( fermatFactorization, pollardPminus1, dixon, dixonDefaultParams
|
||||||
, gnfsWith, GNFSParams(..), defaultParams
|
, gnfsWith, GNFSParams(..), defaultParams
|
||||||
@@ -14,14 +15,16 @@ run = do
|
|||||||
putStrLn "2) Pollard p-1"
|
putStrLn "2) Pollard p-1"
|
||||||
putStrLn "3) Dixon"
|
putStrLn "3) Dixon"
|
||||||
putStrLn "4) General Number Field Sieve (GNFS)"
|
putStrLn "4) General Number Field Sieve (GNFS)"
|
||||||
|
putStrLn "5) Prime factors (GNFS)"
|
||||||
|
|
||||||
choice <- askChoice 4
|
choice <- askChoice 5
|
||||||
|
|
||||||
case choice of
|
case choice of
|
||||||
1 -> fermatUI
|
1 -> fermatUI
|
||||||
2 -> pollardUI
|
2 -> pollardUI
|
||||||
3 -> dixonUI
|
3 -> dixonUI
|
||||||
4 -> gnfsUI
|
4 -> gnfsUI
|
||||||
|
5 -> primeFactorsUI
|
||||||
_ -> error "Impossible"
|
_ -> error "Impossible"
|
||||||
|
|
||||||
fermatUI :: IO ()
|
fermatUI :: IO ()
|
||||||
@@ -47,7 +50,7 @@ dixonUI :: IO ()
|
|||||||
dixonUI = do
|
dixonUI = do
|
||||||
putStrLn "Dixon"
|
putStrLn "Dixon"
|
||||||
putStrLn "Factors a composite n (not a prime, a perfect square or a prime power)."
|
putStrLn "Factors a composite n (not a prime, a perfect square or a prime power)."
|
||||||
putStrLn "Practical for n up to roughly 22 digits; 24 digits already takes about a minute."
|
putStrLn "Practical for n up to roughly 22 digits; 24 digits already takes a minute or two."
|
||||||
n <- askNumber "Enter an integer n > 1:"
|
n <- askNumber "Enter an integer n > 1:"
|
||||||
|
|
||||||
putStrLn "Parameters:"
|
putStrLn "Parameters:"
|
||||||
@@ -118,3 +121,17 @@ gnfsUI = do
|
|||||||
putStrLn ("n = " ++ show n)
|
putStrLn ("n = " ++ show n)
|
||||||
putStrLn ("p = " ++ show p)
|
putStrLn ("p = " ++ show p)
|
||||||
putStrLn ("q = " ++ show q)
|
putStrLn ("q = " ++ show q)
|
||||||
|
|
||||||
|
primeFactorsUI :: IO ()
|
||||||
|
primeFactorsUI = do
|
||||||
|
putStrLn "Prime factors (GNFS)"
|
||||||
|
putStrLn "Decomposes n into its prime factors."
|
||||||
|
putStrLn "Large composites are split with the General Number Field Sieve, so numbers with more than about 30 digits may take very long."
|
||||||
|
n <- askNumber "Enter an integer n > 1:"
|
||||||
|
|
||||||
|
putStrLn "Running (this may take a while)..."
|
||||||
|
case primeFactors n of
|
||||||
|
Left err ->
|
||||||
|
putStrLn ("Error: " ++ err)
|
||||||
|
Right factors ->
|
||||||
|
putStrLn (show n ++ " = " ++ formatFactors factors)
|
||||||
@@ -24,6 +24,7 @@ library
|
|||||||
ModularSquareRoot
|
ModularSquareRoot
|
||||||
ModularArithmeticUtils
|
ModularArithmeticUtils
|
||||||
LinearAlgebra.GF2
|
LinearAlgebra.GF2
|
||||||
|
PrimeFactors
|
||||||
other-modules:
|
other-modules:
|
||||||
Primes.FermatPrimeTest
|
Primes.FermatPrimeTest
|
||||||
Primes.MillerRabin
|
Primes.MillerRabin
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
module PrimeFactors
|
||||||
|
( primeFactors
|
||||||
|
, formatFactors
|
||||||
|
, expandFactors
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Data.List (intercalate)
|
||||||
|
import qualified Data.Map.Strict as M
|
||||||
|
|
||||||
|
import Factorization.GNFS (gnfs)
|
||||||
|
import GNFS.Util (isPrimeDet, perfectPower)
|
||||||
|
import ModularArithmeticUtils (countDiv)
|
||||||
|
import Primes.ErathosteneSieve (erathosteneSieve)
|
||||||
|
|
||||||
|
-- | A factorisation as a list of (prime, exponent), sorted by increasing prime.
|
||||||
|
type Factors = [(Integer, Int)]
|
||||||
|
|
||||||
|
-- | Decompose n >= 1 into its prime factors, e.g. 360 -> [(2,3),(3,2),(5,1)].
|
||||||
|
-- 1 gives the empty factorisation.
|
||||||
|
--
|
||||||
|
-- Small primes are removed by trial division; what is left is split recursively with GNFS ('gnfs'): primes are recognised with a strong probable-prime test (deterministic below about 3.3e24), perfect powers are reduced to their root, and every other composite is cut in two by GNFS and both halves are factored further.
|
||||||
|
--
|
||||||
|
-- Returns Left if GNFS fails on one of the composite cofactors.
|
||||||
|
primeFactors :: Integer -> Either String Factors
|
||||||
|
primeFactors n
|
||||||
|
| n < 1 = Left "n must be a positive integer"
|
||||||
|
| otherwise =
|
||||||
|
let (small, m) = stripSmallPrimes n
|
||||||
|
in if m == 1
|
||||||
|
then Right small
|
||||||
|
else merge small <$> factorLarge m
|
||||||
|
|
||||||
|
-- | Primes below this bound are removed by trial division before GNFS is used.
|
||||||
|
trialBound :: Integer
|
||||||
|
trialBound = 10000
|
||||||
|
|
||||||
|
stripSmallPrimes :: Integer -> (Factors, Integer)
|
||||||
|
stripSmallPrimes n = go (erathosteneSieve trialBound) n []
|
||||||
|
where
|
||||||
|
go _ 1 acc = (reverse acc, 1)
|
||||||
|
go [] m acc = (reverse acc, m)
|
||||||
|
go (p : ps) m acc
|
||||||
|
| p * p > m = if m > 1 then (reverse ((m, 1) : acc), 1) else (reverse acc, 1)
|
||||||
|
| otherwise =
|
||||||
|
case countDiv p m of
|
||||||
|
(0, _) -> go ps m acc
|
||||||
|
(e, m') -> go ps m' ((p, fromInteger e) : acc)
|
||||||
|
|
||||||
|
-- | Factor m > 1 that has no prime factor below 'trialBound'.
|
||||||
|
factorLarge :: Integer -> Either String Factors
|
||||||
|
factorLarge m
|
||||||
|
| isPrimeDet m = Right [(m, 1)]
|
||||||
|
| Just (r, k) <- perfectPower m =
|
||||||
|
map (\(p, e) -> (p, e * k)) <$> factorLarge r
|
||||||
|
| otherwise =
|
||||||
|
case gnfs m of
|
||||||
|
Left err -> Left ("could not factor " ++ show m ++ ": " ++ err)
|
||||||
|
Right (p, q)
|
||||||
|
| p > 1 && q > 1 && p * q == m -> merge <$> factorLarge p <*> factorLarge q
|
||||||
|
| otherwise -> Left ("GNFS returned an invalid split of " ++ show m)
|
||||||
|
|
||||||
|
-- | Combine two factorisations, adding the exponents of equal primes.
|
||||||
|
merge :: Factors -> Factors -> Factors
|
||||||
|
merge a b = M.toAscList (M.fromListWith (+) (a ++ b))
|
||||||
|
|
||||||
|
-- | Render a factorisation as "2^2*3*5": exponents of 1 are omitted, and 1 (the empty
|
||||||
|
-- factorisation) is written "1".
|
||||||
|
formatFactors :: Factors -> String
|
||||||
|
formatFactors [] = "1"
|
||||||
|
formatFactors fs = intercalate "*" (map one fs)
|
||||||
|
where
|
||||||
|
one (p, 1) = show p
|
||||||
|
one (p, e) = show p ++ "^" ++ show e
|
||||||
|
|
||||||
|
-- | Multiply a factorisation back out.
|
||||||
|
expandFactors :: Factors -> Integer
|
||||||
|
expandFactors = product . map (\(p, e) -> p ^ e)
|
||||||
Reference in new issue
Block a user