{-# LANGUAGE ScopedTypeVariables, FlexibleInstances, MultiParamTypeClasses, FunctionalDependencies, TypeFamilies, BangPatterns, DeriveDataTypeable #-} {-# OPTIONS_GHC -Wall #-} ----------------------------------------------------------------------------- -- | -- Module : Data.Polynomial.Base -- Copyright : (c) Masahiro Sakai 2012-2013 -- License : BSD-style -- -- Maintainer : masahiro.sakai@gmail.com -- Stability : provisional -- Portability : non-portable (ScopedTypeVariables, FlexibleInstances, MultiParamTypeClasses, FunctionalDependencies, TypeFamilies, BangPatterns, DeriveDataTypeable) -- -- Polynomials -- -- References: -- -- * Monomial order -- -- * Polynomial class for Ruby -- -- * constructive-algebra package -- ----------------------------------------------------------------------------- module Data.Polynomial.Base ( -- * Polynomial type Polynomial -- * Conversion , Var (..) , constant , terms , fromTerms , coeffMap , fromCoeffMap , fromTerm -- * Query , Degree (..) , Vars (..) , lt , lc , lm , coeff , lookupCoeff , isPrimitive , isRootOf -- * Operations , Factor (..) , SQFree (..) , ContPP (..) , deriv , integral , eval , subst , mapCoeff , toMonic , toUPolynomialOf , divModMP , reduce -- * Univariate polynomials , UPolynomial , X (..) , UTerm , UMonomial , div , mod , divMod , divides , gcd , lcm , exgcd , pdivMod , pdiv , pmod , gcd' , isSquareFree -- * Term , Term , tdeg , tmult , tdivides , tdiv , tderiv , tintegral -- * Monic monomial , Monomial , mone , mfromIndices , mfromIndicesMap , mindices , mindicesMap , mmult , mpow , mdivides , mdiv , mderiv , mintegral , mlcm , mgcd , mcoprime -- * Monomial order , MonomialOrder , lex , revlex , grlex , grevlex -- * Pretty Printing , PrintOptions (..) , defaultPrintOptions , prettyPrint , PrettyCoeff (..) , PrettyVar (..) ) where import Prelude hiding (lex, div, mod, divMod, gcd, lcm) import qualified Prelude import Control.DeepSeq import Control.Exception (assert) import Control.Monad import Data.Data import qualified Data.FiniteField as FF import Data.Function import Data.List import Data.Monoid import Data.Ratio import Data.Map (Map) import qualified Data.Map as Map import Data.Set (Set) import qualified Data.Set as Set import Data.IntMap (IntMap) import qualified Data.IntMap as IntMap import Data.Typeable import Data.VectorSpace import qualified Text.PrettyPrint.HughesPJClass as PP import Text.PrettyPrint.HughesPJClass (Doc, PrettyLevel, Pretty (..), prettyParen) infixl 7 `div`, `mod` {-------------------------------------------------------------------- Classes --------------------------------------------------------------------} class Vars a v => Var a v | a -> v where var :: v -> a class Ord v => Vars a v | a -> v where vars :: a -> Set v -- | total degree of a given polynomial class Degree t where deg :: t -> Integer {-------------------------------------------------------------------- Polynomial type --------------------------------------------------------------------} -- | Polynomial over commutative ring r newtype Polynomial r v = Polynomial{ coeffMap :: Map (Monomial v) r } deriving (Eq, Ord, Typeable) instance (Eq k, Num k, Ord v) => Num (Polynomial k v) where (+) = plus (*) = mult negate = neg abs x = x -- OK? signum _ = 1 -- OK? fromInteger x = constant (fromInteger x) instance (Eq k, Num k, Ord v) => AdditiveGroup (Polynomial k v) where (^+^) = plus zeroV = zero negateV = neg instance (Eq k, Num k, Ord v) => VectorSpace (Polynomial k v) where type Scalar (Polynomial k v) = k k *^ p = scale k p instance (Show v, Ord v, Show k) => Show (Polynomial k v) where showsPrec d p = showParen (d > 10) $ showString "fromTerms " . shows (terms p) instance (NFData k, NFData v) => NFData (Polynomial k v) where rnf (Polynomial m) = rnf m instance (Eq k, Num k, Ord v) => Var (Polynomial k v) v where var x = fromTerm (1, var x) instance (Eq k, Num k, Ord v) => Vars (Polynomial k v) v where vars p = Set.unions $ [vars mm | (_, mm) <- terms p] instance Degree (Polynomial k v) where deg p | isZero p = -1 | otherwise = maximum [deg mm | (_,mm) <- terms p] normalize :: (Eq k, Num k, Ord v) => Polynomial k v -> Polynomial k v normalize (Polynomial m) = Polynomial (Map.filter (0/=) m) asConstant :: Num k => Polynomial k v -> Maybe k asConstant p = case terms p of [] -> Just 0 [(c,xs)] | Map.null (mindicesMap xs) -> Just c _ -> Nothing scale :: (Eq k, Num k, Ord v) => k -> Polynomial k v -> Polynomial k v scale 0 _ = zero scale 1 p = p scale a (Polynomial m) = normalize $ Polynomial (Map.map (a*) m) zero :: (Eq k, Num k, Ord v) => Polynomial k v zero = Polynomial $ Map.empty plus :: (Eq k, Num k, Ord v) => Polynomial k v -> Polynomial k v -> Polynomial k v plus (Polynomial m1) (Polynomial m2) = normalize $ Polynomial $ Map.unionWith (+) m1 m2 neg :: (Eq k, Num k, Ord v) => Polynomial k v -> Polynomial k v neg (Polynomial m) = Polynomial $ Map.map negate m mult :: (Eq k, Num k, Ord v) => Polynomial k v -> Polynomial k v -> Polynomial k v mult a b | Just c <- asConstant a = scale c b | Just c <- asConstant b = scale c a mult (Polynomial m1) (Polynomial m2) = normalize $ Polynomial $ Map.fromListWith (+) [ (xs1 `mmult` xs2, c1*c2) | (xs1,c1) <- Map.toList m1, (xs2,c2) <- Map.toList m2 ] isZero :: Polynomial k v -> Bool isZero (Polynomial m) = Map.null m -- | construct a polynomial from a constant constant :: (Eq k, Num k, Ord v) => k -> Polynomial k v constant c = fromTerm (c, mone) -- | construct a polynomial from a list of monomials fromTerms :: (Eq k, Num k, Ord v) => [Term k v] -> Polynomial k v fromTerms = normalize . Polynomial . Map.fromListWith (+) . map (\(c,xs) -> (xs,c)) fromCoeffMap :: (Eq k, Num k, Ord v) => Map (Monomial v) k -> Polynomial k v fromCoeffMap m = normalize $ Polynomial m -- | construct a polynomial from a monomial fromTerm :: (Eq k, Num k, Ord v) => Term k v -> Polynomial k v fromTerm (c,xs) = normalize $ Polynomial $ Map.singleton xs c -- | list of monomials terms :: Polynomial k v -> [Term k v] terms (Polynomial m) = [(c,xs) | (xs,c) <- Map.toList m] -- | leading term with respect to a given monomial order lt :: (Eq k, Num k, Ord v) => MonomialOrder v -> Polynomial k v -> Term k v lt cmp p = case terms p of [] -> (0, mone) -- should be error? ms -> maximumBy (cmp `on` snd) ms -- | leading coefficient with respect to a given monomial order lc :: (Eq k, Num k, Ord v) => MonomialOrder v -> Polynomial k v -> k lc cmp = fst . lt cmp -- | leading monomial with respect to a given monomial order lm :: (Eq k, Num k, Ord v) => MonomialOrder v -> Polynomial k v -> Monomial v lm cmp = snd . lt cmp coeff :: (Num k, Ord v) => Monomial v -> Polynomial k v -> k coeff xs (Polynomial m) = Map.findWithDefault 0 xs m lookupCoeff :: Ord v => Monomial v -> Polynomial k v -> Maybe k lookupCoeff xs (Polynomial m) = Map.lookup xs m contI :: (Integral r, Ord v) => Polynomial r v -> r contI 0 = 1 contI p = foldl1' Prelude.gcd [abs c | (c,_) <- terms p] ppI :: (Integral r, Ord v) => Polynomial r v -> Polynomial r v ppI p = mapCoeff f p where c = contI p f x = assert (x `Prelude.mod` c == 0) $ x `Prelude.div` c class ContPP k where -- | Content of a polynomial cont :: (Ord v) => Polynomial k v -> k -- constructive-algebra-0.3.0 では cont 0 は error になる -- | Primitive part of a polynomial pp :: (Ord v) => Polynomial k v -> Polynomial k v instance ContPP Integer where cont = contI pp = ppI instance Integral r => ContPP (Ratio r) where {-# SPECIALIZE instance ContPP (Ratio Integer) #-} cont 0 = 1 cont p = foldl1' Prelude.gcd ns % foldl' Prelude.lcm 1 ds where ns = [abs (numerator c) | (c,_) <- terms p] ds = [denominator c | (c,_) <- terms p] pp p = mapCoeff (/ c) p where c = cont p isPrimitive :: (Eq k, Num k, ContPP k, Ord v) => Polynomial k v -> Bool isPrimitive p = isZero p || cont p == 1 -- | Formal derivative of polynomials deriv :: (Eq k, Num k, Ord v) => Polynomial k v -> v -> Polynomial k v deriv p x = sumV [fromTerm (tderiv m x) | m <- terms p] -- | Formal integral of polynomials integral :: (Eq k, Fractional k, Ord v) => Polynomial k v -> v -> Polynomial k v integral p x = sumV [fromTerm (tintegral m x) | m <- terms p] -- | Evaluation eval :: (Num k, Ord v) => (v -> k) -> Polynomial k v -> k eval env p = sum [c * product [(env x) ^ e | (x,e) <- mindices xs] | (c,xs) <- terms p] -- | Substitution or bind subst :: (Eq k, Num k, Ord v1, Ord v2) => Polynomial k v1 -> (v1 -> Polynomial k v2) -> Polynomial k v2 subst p s = sumV [constant c * product [(s x)^e | (x,e) <- mindices xs] | (c, xs) <- terms p] isRootOf :: (Eq k, Num k) => k -> UPolynomial k -> Bool isRootOf x p = eval (\_ -> x) p == 0 isSquareFree :: (Eq k, Fractional k) => UPolynomial k -> Bool isSquareFree p = gcd p (deriv p X) == 1 mapCoeff :: (Eq k1, Num k1, Ord v) => (k -> k1) -> Polynomial k v -> Polynomial k1 v mapCoeff f (Polynomial m) = Polynomial $ Map.mapMaybe g m where g x = if y == 0 then Nothing else Just y where y = f x toMonic :: (Eq r, Fractional r, Ord v) => MonomialOrder v -> Polynomial r v -> Polynomial r v toMonic cmp p | c == 0 || c == 1 = p | otherwise = mapCoeff (/c) p where c = lc cmp p toUPolynomialOf :: (Ord k, Num k, Ord v) => Polynomial k v -> v -> UPolynomial (Polynomial k v) toUPolynomialOf p v = fromTerms $ do (c,mm) <- terms p let m = mindicesMap mm return ( fromTerms [(c, mfromIndicesMap (Map.delete v m))] , var X `mpow` Map.findWithDefault 0 v m ) -- | Multivariate division algorithm divModMP :: forall k v. (Eq k, Fractional k, Ord v) => MonomialOrder v -> Polynomial k v -> [Polynomial k v] -> ([Polynomial k v], Polynomial k v) divModMP cmp p fs = go IntMap.empty p where ls = [(lt cmp f, f) | f <- fs] go :: IntMap (Polynomial k v) -> Polynomial k v -> ([Polynomial k v], Polynomial k v) go qs g = case xs of [] -> ([IntMap.findWithDefault 0 i qs | i <- [0 .. length fs - 1]], g) (i, b, g') : _ -> go (IntMap.insertWith (+) i b qs) g' where ms = sortBy (flip cmp `on` snd) (terms g) xs = do (i,(a,f)) <- zip [0..] ls h <- ms guard $ a `tdivides` h let b = fromTerm $ tdiv h a return (i, b, g - b * f) -- | Multivariate division algorithm reduce :: (Eq k, Fractional k, Ord v) => MonomialOrder v -> Polynomial k v -> [Polynomial k v] -> Polynomial k v reduce cmp p fs = go p where ls = [(lt cmp f, f) | f <- fs] go g = if null xs then g else go (head xs) where ms = sortBy (flip cmp `on` snd) (terms g) xs = do (a,f) <- ls h <- ms guard $ a `tdivides` h return (g - fromTerm (tdiv h a) * f) -- | Factorization of polynomials class Factor a where -- | factor a polynomial @p@ into @p1 ^ n1 + p2 ^ n2 + ..@ and -- return a list @[(p1,n1), (p2,n2), ..]@. factor :: a -> [(a, Integer)] -- | Square-free factorization of polynomials class SQFree a where -- | factor a polynomial @p@ into @p1 ^ n1 + p2 ^ n2 + ..@ and -- return a list @[(p1,n1), (p2,n2), ..]@. sqfree :: a -> [(a, Integer)] {-------------------------------------------------------------------- Pretty printing --------------------------------------------------------------------} data PrintOptions k v = PrintOptions { pOptPrintVar :: PrettyLevel -> Rational -> v -> Doc , pOptPrintCoeff :: PrettyLevel -> Rational -> k -> Doc , pOptIsNegativeCoeff :: k -> Bool , pOptMonomialOrder :: MonomialOrder v } defaultPrintOptions :: (PrettyCoeff k, PrettyVar v, Ord v) => PrintOptions k v defaultPrintOptions = PrintOptions { pOptPrintVar = pPrintVar , pOptPrintCoeff = pPrintCoeff , pOptIsNegativeCoeff = isNegativeCoeff , pOptMonomialOrder = grlex } instance (Ord k, Num k, Ord v, PrettyCoeff k, PrettyVar v) => Pretty (Polynomial k v) where pPrintPrec = prettyPrint defaultPrintOptions prettyPrint :: (Ord k, Num k, Ord v) => PrintOptions k v -> PrettyLevel -> Rational -> Polynomial k v -> Doc prettyPrint opt lv prec p = case sortBy (flip (pOptMonomialOrder opt) `on` snd) $ terms p of [] -> PP.int 0 [t] -> pLeadingTerm prec t t:ts -> prettyParen (prec > addPrec) $ PP.hcat (pLeadingTerm addPrec t : map pTrailingTerm ts) where pLeadingTerm prec (c,xs) = if pOptIsNegativeCoeff opt c then prettyParen (prec > addPrec) $ PP.char '-' <> prettyPrintTerm opt lv (addPrec+1) (-c,xs) else prettyPrintTerm opt lv prec (c,xs) pTrailingTerm (c,xs) = if pOptIsNegativeCoeff opt c then PP.space <> PP.char '-' <> PP.space <> prettyPrintTerm opt lv (addPrec+1) (-c,xs) else PP.space <> PP.char '+' <> PP.space <> prettyPrintTerm opt lv (addPrec+1) (c,xs) prettyPrintTerm :: (Ord k, Num k, Ord v) => PrintOptions k v -> PrettyLevel -> Rational -> Term k v -> Doc prettyPrintTerm opt lv prec (c,xs) | len == 0 = pOptPrintCoeff opt lv (appPrec+1) c -- intentionally specify (appPrec+1) to parenthesize any composite expression | len == 1 && c == 1 = pPow prec $ head (mindices xs) | otherwise = prettyParen (prec > mulPrec) $ PP.hcat $ intersperse (PP.char '*') fs where len = Map.size $ mindicesMap xs fs = [pOptPrintCoeff opt lv (appPrec+1) c | c /= 1] ++ [pPow (mulPrec+1) p | p <- mindices xs] -- intentionally specify (appPrec+1) to parenthesize any composite expression pPow prec (x,1) = pOptPrintVar opt lv prec x pPow prec (x,n) = prettyParen (prec > expPrec) $ pOptPrintVar opt lv (expPrec+1) x <> PP.char '^' <> PP.integer n class PrettyCoeff a where pPrintCoeff :: PrettyLevel -> Rational -> a -> Doc isNegativeCoeff :: a -> Bool isNegativeCoeff _ = False instance PrettyCoeff Integer where pPrintCoeff = pPrintPrec isNegativeCoeff = (0>) instance (PrettyCoeff a, Integral a) => PrettyCoeff (Ratio a) where pPrintCoeff lv p r | denominator r == 1 = pPrintCoeff lv p (numerator r) | otherwise = prettyParen (p > ratPrec) $ pPrintCoeff lv (ratPrec+1) (numerator r) <> PP.char '/' <> pPrintCoeff lv (ratPrec+1) (denominator r) isNegativeCoeff x = isNegativeCoeff (numerator x) instance PrettyCoeff (FF.PrimeField a) where pPrintCoeff lv p a = pPrintCoeff lv p (FF.toInteger a) isNegativeCoeff _ = False instance (Num c, Ord c, PrettyCoeff c, Ord v, PrettyVar v) => PrettyCoeff (Polynomial c v) where pPrintCoeff = pPrintPrec class PrettyVar a where pPrintVar :: PrettyLevel -> Rational -> a -> Doc instance PrettyVar Int where pPrintVar _ _ n = PP.char 'x' <> PP.int n instance PrettyVar X where pPrintVar _ _ X = PP.char 'x' addPrec, mulPrec, ratPrec, expPrec, appPrec :: Rational addPrec = 6 -- Precedence of '+' mulPrec = 7 -- Precedence of '*' ratPrec = 7 -- Precedence of '/' expPrec = 8 -- Precedence of '^' appPrec = 10 -- Precedence of function application {-------------------------------------------------------------------- Univariate polynomials --------------------------------------------------------------------} -- | Univariate polynomials over commutative ring r type UPolynomial r = Polynomial r X data X = X deriving (Eq, Ord, Bounded, Enum, Show, Read, Typeable, Data) instance NFData X ucmp :: MonomialOrder X ucmp = grlex -- | division of univariate polynomials div :: (Eq k, Fractional k) => UPolynomial k -> UPolynomial k -> UPolynomial k div f1 f2 = fst (divMod f1 f2) -- | division of univariate polynomials mod :: (Eq k, Fractional k) => UPolynomial k -> UPolynomial k -> UPolynomial k mod f1 f2 = snd (divMod f1 f2) -- | division of univariate polynomials divMod :: (Eq k, Fractional k) => UPolynomial k -> UPolynomial k -> (UPolynomial k, UPolynomial k) divMod f g | isZero g = error "divMod: division by zero" | otherwise = go 0 f where lt_g = lt ucmp g go !q !r | deg r < deg g = (q,r) | otherwise = go (q + t) (r - t * g) where lt_r = lt ucmp r t = fromTerm $ lt_r `tdiv` lt_g divides :: (Eq k, Fractional k) => UPolynomial k -> UPolynomial k -> Bool divides f1 f2 = f2 `mod` f1 == 0 -- | GCD of univariate polynomials gcd :: (Eq k, Fractional k) => UPolynomial k -> UPolynomial k -> UPolynomial k gcd f1 0 = toMonic ucmp f1 gcd f1 f2 = gcd f2 (f1 `mod` f2) -- | LCM of univariate polynomials lcm :: (Eq k, Fractional k) => UPolynomial k -> UPolynomial k -> UPolynomial k lcm _ 0 = 0 lcm 0 _ = 0 lcm f1 f2 = toMonic ucmp $ (f1 `mod` (gcd f1 f2)) * f2 -- | Extended GCD algorithm exgcd :: (Eq k, Fractional k) => UPolynomial k -> UPolynomial k -> (UPolynomial k, UPolynomial k, UPolynomial k) exgcd f1 f2 = f $ go f1 f2 1 0 0 1 where go !r0 !r1 !s0 !s1 !t0 !t1 | r1 == 0 = (r0, s0, t0) | otherwise = go r1 r2 s1 s2 t1 t2 where (q, r2) = r0 `divMod` r1 s2 = s0 - q*s1 t2 = t0 - q*t1 f (g,u,v) | lc_g == 0 = (g, u, v) | otherwise = (mapCoeff (/lc_g) g, mapCoeff (/lc_g) u, mapCoeff (/lc_g) v) where lc_g = lc ucmp g -- | pseudo division pdivMod :: (Eq r, Num r) => UPolynomial r -> UPolynomial r -> (r, UPolynomial r, UPolynomial r) pdivMod _ 0 = error "pdivMod: division by 0" pdivMod f g | deg f < deg g = (1, 0, f) | otherwise = go (deg f - deg g + 1) f 0 where (lc_g, lm_g) = lt ucmp g b = lc_g ^ (deg f - deg_g + 1) deg_g = deg g go !n !f1 !q | deg_g > deg f1 = (b, q, scale (lc_g ^ n) f1) | otherwise = go (n - 1) (scale lc_g f1 - s * g) (q + scale (lc_g ^ (n-1)) s) where (lc_f1, lm_f1) = lt ucmp f1 s = fromTerm (lc_f1, lm_f1 `mdiv` lm_g) -- | pseudo quotient pdiv :: (Eq r, Num r) => UPolynomial r -> UPolynomial r -> UPolynomial r pdiv f g = case f `pdivMod` g of (_, q, _) -> q -- | pseudo reminder pmod :: (Eq r, Num r) => UPolynomial r -> UPolynomial r -> UPolynomial r pmod _ 0 = error "pmod: division by 0" pmod f g | deg f < deg g = f | otherwise = go (deg f - deg g + 1) f where (lc_g, lm_g) = lt ucmp g deg_g = deg g go !n !f1 | deg_g > deg f1 = scale (lc_g ^ n) f1 | otherwise = go (n - 1) (scale lc_g f1 - s * g) where (lc_f1, lm_f1) = lt ucmp f1 s = fromTerm (lc_f1, lm_f1 `mdiv` lm_g) -- | GCD of univariate polynomials gcd' :: (Eq r, Integral r) => UPolynomial r -> UPolynomial r -> UPolynomial r gcd' f1 0 = ppI f1 gcd' f1 f2 = gcd' f2 (f1 `pmod` f2) {-------------------------------------------------------------------- Term --------------------------------------------------------------------} type Term k v = (k, Monomial v) type UTerm k = Term k X tdeg :: Term k v -> Integer tdeg (_,xs) = deg xs tmult :: (Num k, Ord v) => Term k v -> Term k v -> Term k v tmult (c1,xs1) (c2,xs2) = (c1*c2, xs1 `mmult` xs2) tdivides :: (Fractional k, Ord v) => Term k v -> Term k v -> Bool tdivides (_,xs1) (_,xs2) = xs1 `mdivides` xs2 tdiv :: (Fractional k, Ord v) => Term k v -> Term k v -> Term k v tdiv (c1,xs1) (c2,xs2) = (c1 / c2, xs1 `mdiv` xs2) tderiv :: (Eq k, Num k, Ord v) => Term k v -> v -> Term k v tderiv (c,xs) x = case mderiv xs x of (s,ys) -> (c * fromIntegral s, ys) tintegral :: (Eq k, Fractional k, Ord v) => Term k v -> v -> Term k v tintegral (c,xs) x = case mintegral xs x of (s,ys) -> (c * fromRational s, ys) {-------------------------------------------------------------------- Monic Monomial --------------------------------------------------------------------} -- 本当は変数の型に応じて type family で表現を変えたい -- | Monic monomials newtype Monomial v = Monomial{ mindicesMap :: Map v Integer } deriving (Eq, Ord, Typeable) type UMonomial = Monomial X instance (Ord v, Show v) => Show (Monomial v) where showsPrec d m = showParen (d > 10) $ showString "mfromIndices " . shows (mindices m) instance (NFData v) => NFData (Monomial v) where rnf (Monomial m) = rnf m instance Degree (Monomial v) where deg (Monomial m) = sum $ Map.elems m instance Ord v => Var (Monomial v) v where var x = Monomial $ Map.singleton x 1 instance Ord v => Vars (Monomial v) v where vars mm = Map.keysSet (mindicesMap mm) mone :: Monomial v mone = Monomial $ Map.empty mfromIndices :: Ord v => [(v, Integer)] -> Monomial v mfromIndices xs | any (\(_,e) -> 0>e) xs = error "mfromIndices: negative exponent" | otherwise = Monomial $ Map.fromListWith (+) [(x,e) | (x,e) <- xs, e > 0] mfromIndicesMap :: Ord v => Map v Integer -> Monomial v mfromIndicesMap m | any (\(_,e) -> 0>e) (Map.toList m) = error "mfromIndicesMap: negative exponent" | otherwise = mfromIndicesMap' m mfromIndicesMap' :: Ord v => Map v Integer -> Monomial v mfromIndicesMap' m = Monomial $ Map.filter (>0) m mindices :: Ord v => Monomial v -> [(v, Integer)] mindices = Map.toAscList . mindicesMap mmult :: Ord v => Monomial v -> Monomial v -> Monomial v mmult (Monomial xs1) (Monomial xs2) = mfromIndicesMap' $ Map.unionWith (+) xs1 xs2 mpow :: Ord v => Monomial v -> Integer -> Monomial v mpow _ 0 = mone mpow m 1 = m mpow (Monomial xs) e | 0 > e = error "mpow: negative exponent" | otherwise = Monomial $ Map.map (e*) xs mdivides :: Ord v => Monomial v -> Monomial v -> Bool mdivides (Monomial xs1) (Monomial xs2) = Map.isSubmapOfBy (<=) xs1 xs2 mdiv :: Ord v => Monomial v -> Monomial v -> Monomial v mdiv (Monomial xs1) (Monomial xs2) = Monomial $ Map.differenceWith f xs1 xs2 where f m n | m <= n = Nothing | otherwise = Just (m - n) mderiv :: Ord v => Monomial v -> v -> (Integer, Monomial v) mderiv (Monomial xs) x | n==0 = (0, mone) | otherwise = (n, Monomial $ Map.update f x xs) where n = Map.findWithDefault 0 x xs f m | m <= 1 = Nothing | otherwise = Just $! m - 1 mintegral :: Ord v => Monomial v -> v -> (Rational, Monomial v) mintegral (Monomial xs) x = (1 % fromIntegral (n + 1), Monomial $ Map.insert x (n+1) xs) where n = Map.findWithDefault 0 x xs mlcm :: Ord v => Monomial v -> Monomial v -> Monomial v mlcm (Monomial m1) (Monomial m2) = Monomial $ Map.unionWith max m1 m2 mgcd :: Ord v => Monomial v -> Monomial v -> Monomial v mgcd (Monomial m1) (Monomial m2) = Monomial $ Map.intersectionWith min m1 m2 mcoprime :: Ord v => Monomial v -> Monomial v -> Bool mcoprime m1 m2 = mgcd m1 m2 == mone {-------------------------------------------------------------------- Monomial Order --------------------------------------------------------------------} type MonomialOrder v = Monomial v -> Monomial v -> Ordering -- | Lexicographic order lex :: Ord v => MonomialOrder v lex xs1 xs2 = go (mindices xs1) (mindices xs2) where go [] [] = EQ go [] _ = LT -- = compare 0 n2 go _ [] = GT -- = compare n1 0 go ((x1,n1):xs1) ((x2,n2):xs2) = case compare x1 x2 of LT -> GT -- = compare n1 0 GT -> LT -- = compare 0 n2 EQ -> compare n1 n2 `mappend` go xs1 xs2 -- | Reverse lexicographic order. -- -- Note that revlex is NOT a monomial order. revlex :: Ord v => Monomial v -> Monomial v -> Ordering revlex xs1 xs2 = go (Map.toDescList (mindicesMap xs1)) (Map.toDescList (mindicesMap xs2)) where go [] [] = EQ go [] _ = GT -- = cmp 0 n2 go _ [] = LT -- = cmp n1 0 go ((x1,n1):xs1) ((x2,n2):xs2) = case compare x1 x2 of LT -> GT -- = cmp 0 n2 GT -> LT -- = cmp n1 0 EQ -> cmp n1 n2 `mappend` go xs1 xs2 cmp n1 n2 = compare n2 n1 -- | Graded lexicographic order grlex :: Ord v => MonomialOrder v grlex = (compare `on` deg) `mappend` lex -- | Graded reverse lexicographic order grevlex :: Ord v => MonomialOrder v grevlex = (compare `on` deg) `mappend` revlex