{-# LANGUAGE BangPatterns #-} module Poly where import Prelude hiding (gcd, div, mod, divMod, const) import Control.Monad import qualified Data.Vector.Generic as G import qualified Data.Vector.Generic.Mutable as GM import qualified Data.Vector.Unboxing -- for writing SPECIALIZE instance pragma import qualified Data.Vector -- for writing SPECIALIZE instance pragma infixl 7 `div`, `mod` -- univariate polynomial, coefficients in ascending order newtype Poly vect a = Poly (vect a) deriving (Eq,Show) zero :: (G.Vector vect a) => Poly vect a zero = Poly G.empty {-# INLINE zero #-} isZero :: (G.Vector vect a) => Poly vect a -> Bool isZero (Poly xs) = G.null xs {-# INLINE isZero #-} const :: (Eq a, Num a, G.Vector vect a) => a -> Poly vect a const 0 = zero const a = Poly (G.singleton a) {-# INLINE const #-} x :: (Num a, Eq a, G.Vector vect a) => Poly vect a x = Poly (G.fromList [0, 1]) {-# INLINE x #-} fromCoeffAsc :: (Eq a, Num a, G.Vector vect a) => vect a -> Poly vect a fromCoeffAsc xs | G.null xs = Poly G.empty | G.last xs == 0 = fromCoeffAsc (G.init xs) | otherwise = Poly xs {-# INLINE fromCoeffAsc #-} coeffAsc :: Poly vect a -> vect a coeffAsc (Poly xs) = xs {-# INLINE coeffAsc #-} addPoly :: (Eq a, Num a, G.Vector vect a) => Poly vect a -> Poly vect a -> Poly vect a addPoly (Poly xs) (Poly ys) | n < m = Poly $ G.create $ do v <- GM.new m forM_ [0..n-1] $ \i -> GM.write v i ((xs G.! i) + (ys G.! i)) forM_ [n..m-1] $ \i -> GM.write v i (ys G.! i) return v | m < n = Poly $ G.create $ do v <- GM.new n forM_ [0..m-1] $ \i -> GM.write v i ((xs G.! i) + (ys G.! i)) forM_ [m..n-1] $ \i -> GM.write v i (xs G.! i) return v | otherwise = fromCoeffAsc $ G.zipWith (+) xs ys where n = G.length xs m = G.length ys {-# INLINABLE addPoly #-} subPoly :: (Eq a, Num a, G.Vector vect a) => Poly vect a -> Poly vect a -> Poly vect a subPoly (Poly xs) (Poly ys) | n < m = Poly $ G.create $ do v <- GM.new m forM_ [0..n-1] $ \i -> GM.write v i ((xs G.! i) - (ys G.! i)) forM_ [n..m-1] $ \i -> GM.write v i (negate (ys G.! i)) return v | m < n = Poly $ G.create $ do v <- GM.new n forM_ [0..m-1] $ \i -> GM.write v i ((xs G.! i) - (ys G.! i)) forM_ [m..n-1] $ \i -> GM.write v i (xs G.! i) return v | otherwise = fromCoeffAsc $ G.zipWith (-) xs ys where n = G.length xs m = G.length ys {-# INLINABLE subPoly #-} -- multiplication: naive method mulPoly :: (Eq a, Num a, G.Vector vect a) => Poly vect a -> Poly vect a -> Poly vect a mulPoly (Poly xs) (Poly ys) | n == 0 || m == 0 = zero | otherwise = Poly $ G.generate (n + m - 1) (\i -> sum [(xs G.! j) * (ys G.! (i - j)) | j <- [max 0 (i - m + 1)..min i (n-1)]]) where n = G.length xs m = G.length ys {-# INLINABLE mulPoly #-} instance (Eq a, Num a, G.Vector vect a) => Num (Poly vect a) where negate (Poly xs) = Poly $ G.map negate xs {-# INLINE negate #-} (+) = addPoly {-# INLINE (+) #-} (-) = subPoly {-# INLINE (-) #-} (*) = mulPoly {-# INLINE (*) #-} fromInteger n = const $ fromInteger n {-# INLINE fromInteger #-} abs = undefined signum = undefined {-# SPECIALIZE instance (Eq a, Num a) => Num (Poly Data.Vector.Vector a) #-} {-# SPECIALIZE instance (Eq a, Num a, Data.Vector.Unboxing.Unboxable a) => Num (Poly Data.Vector.Unboxing.Vector a) #-} degree :: (G.Vector vect a) => Poly vect a -> Maybe Int degree (Poly xs) = case G.length xs - 1 of -1 -> Nothing n -> Just n {-# INLINE degree #-} degree' :: (G.Vector vect a) => Poly vect a -> Int degree' (Poly xs) = case G.length xs of 0 -> error "degree': zero polynomial" n -> n - 1 {-# INLINE degree' #-} leadingCoefficient :: (Num a, G.Vector vect a) => Poly vect a -> a leadingCoefficient (Poly xs) | G.null xs = 0 | otherwise = G.last xs {-# INLINE leadingCoefficient #-} toMonic :: (Fractional a, G.Vector vect a) => Poly vect a -> Poly vect a toMonic f@(Poly xs) | G.null xs = zero | otherwise = Poly $ G.map (* recip (leadingCoefficient f)) xs {-# INLINE toMonic #-} scale :: (Eq a, Num a, G.Vector vect a) => a -> Poly vect a -> Poly vect a scale a (Poly xs) | a == 0 = zero | otherwise = Poly $ G.map (* a) xs {-# INLINE scale #-} divMod :: (Eq a, Fractional a, G.Vector vect a) => Poly vect a -> Poly vect a -> (Poly vect a, Poly vect a) divMod f g | isZero g = error "divMod: divide by zero" | degree f < degree g = (zero, f) | otherwise = loop zero (scale (recip b) f) where g' = toMonic g b = leadingCoefficient g -- invariant: f == q * g + scale b p loop q !p | degree p < degree g = (q, scale b p) | otherwise = let !q' = Poly (G.drop (degree' g) (coeffAsc p)) in loop (q + q') (p - q' * g') {-# INLINABLE divMod #-} div, mod :: (Eq a, Fractional a, G.Vector vect a) => Poly vect a -> Poly vect a -> Poly vect a div f g = fst (divMod f g) mod f g = snd (divMod f g) {-# INLINE div #-} {-# INLINE mod #-} -- | GCD with naive Euclidean algorithm gcd :: (Eq a, Fractional a, G.Vector vect a) => Poly vect a -> Poly vect a -> Poly vect a gcd f g | isZero g = f | otherwise = gcd g (f `mod` g) {-# INLINE gcd #-} powMod :: (Eq a, Fractional a, G.Vector vect a) => Poly vect a -> Int -> Poly vect a -> Poly vect a powMod _a 0 _modulo = 1 powMod a n modulo = loop (n-1) a a where loop 0 !_ !acc = acc loop 1 !m !acc = m * acc `mod` modulo loop i !m !acc | even i = loop (i `quot` 2) (m * m `mod` modulo) acc | otherwise = loop (i `quot` 2) (m * m `mod` modulo) (m * acc `mod` modulo) {-# INLINABLE powMod #-}