toysolver-0.0.2: src/Data/Polynomial/FactorZ.hs
{-# LANGUAGE BangPatterns #-}
-- http://en.wikipedia.org/wiki/Polynomial_factorization
-- Kronecker's method
module Data.Polynomial.FactorZ
( factor
) where
import Data.List
import Data.Numbers.Primes (primes)
import Data.Polynomial
import qualified Data.Polynomial.Lagrange as Lagrange
import Util (isInteger)
factor :: UPolynomial Integer -> [UPolynomial Integer]
factor p = normalize $ factor' p
normalize :: [UPolynomial Integer] -> [UPolynomial Integer]
normalize ps = [constant c | let c = product $ map fst xs, c /= 1] ++ sort (map snd xs)
where
xs = map f ps
f :: UPolynomial Integer -> (Integer, UPolynomial Integer)
f q = case [c | (c,_) <- terms q] of
[] -> (1,q)
(c:cs) ->
let d :: Integer
d = foldl' gcd c cs
q2 = mapCoeff (`div` d) q
in if fst (leadingTerm grlex q2) < 0
then (-d,q2)
else (d,q2)
factor' :: UPolynomial Integer -> [UPolynomial Integer]
factor' 0 = [0]
factor' 1 = []
factor' p | deg p == 0 = [p]
factor' p =
case factor2 p of
Nothing -> [p]
Just qs -> concatMap factor' qs
factor2 :: UPolynomial Integer -> Maybe [UPolynomial Integer]
factor2 p | p == var () = Nothing
factor2 p =
case find (\(_,yi) -> yi==0) vs of
Just (xi,_) ->
let q1 = x - constant xi
q2 = p' `polyDiv` mapCoeff fromInteger q1
in Just [q1, toZ q2]
Nothing ->
let qs = map Lagrange.interpolation $
sequence [[(fromInteger xi, fromInteger z) | z <- factors yi] | (xi,yi) <- vs]
zs = [ (q1,q2)
| q1 <- qs, deg q1 > 0, isUPolyZ q1
, let (q2,r) = p' `polyDivMod` q1
, r == 0, deg q2 > 0, isUPolyZ q2
]
in case zs of
[] -> Nothing
(q1,q2):_ -> Just [toZ q1, toZ q2]
where
n = (deg p `div` 2)
xs = take (fromIntegral n + 1) xvalues
vs = [(x, eval (\() -> x) p) | x <- xs]
x = var ()
p' :: UPolynomial Rational
p' = mapCoeff fromInteger p
isUPolyZ :: UPolynomial Rational -> Bool
isUPolyZ p = and [isInteger c | (c,_) <- terms p]
-- [0, 1, -1, 2, -2, 3, -3 ..]
xvalues :: [Integer]
xvalues = 0 : interleave [1,2..] [-1,-2..]
interleave :: [a] -> [a] -> [a]
interleave xs [] = xs
interleave [] ys = ys
interleave (x:xs) ys = x : interleave ys xs
factors :: Integer -> [Integer]
factors 0 = []
factors x = xs ++ map negate xs
where
ps = primeFactors (abs x)
xs = map product $ sequence [[p^i | i <- [0..n]] | (p,n) <- ps]
primeFactors :: Integer -> [(Integer,Integer)]
primeFactors 0 = []
primeFactors n = f primes n
where
f :: [Integer] -> Integer -> [(Integer,Integer)]
f !_ 1 = []
f (p:ps) n
| p*p > n = [(n,1)]
| otherwise =
case g p n of
(m,n') -> [(p,m) | m /= 0] ++ f ps n'
g :: Integer -> Integer -> (Integer, Integer)
g p = go 0
where
go !m !n
| n `mod` p == 0 = go (m+1) (n `div` p)
| otherwise = (m, n)