{-# LANGUAGE PatternGuards #-}-- | Monomials in a countably infinite set of variables x1, x2, x3, ...moduleMathObj.Monomial(-- * TypeT(..)-- * Creating monomials,mkMonomial,constant,x-- * Utility functions,degree,pDegree,scaleMon)whereimportqualifiedAlgebra.AdditiveasAdditiveimportqualifiedAlgebra.RingasRingimportqualifiedAlgebra.ZeroTestableasZeroTestableimportqualifiedAlgebra.DifferentialasDifferentialimportqualifiedAlgebra.FieldasFieldimportqualifiedData.MapasMimportData.Ord(comparing)importData.List(sort,intercalate)importNumericPrelude-- | A monomial is a map from variable indices to integer powers,-- paired with a (polymorphic) coefficient. Note that negative-- integer powers are handled just fine, so monomials form a field.---- Instances are provided for Eq, Ord, ZeroTestable, Additive, Ring,-- Differential, and Field. Note that adding two monomials only-- makes sense if they have matching variables and exponents. The-- Differential instance represents partial differentiation with-- respect to x1.---- The Ord instance for monomials orders them first by permutation-- degree, then by largest variable index (largest first), then by-- exponent (largest first). This may seem a bit odd, but in fact-- reflects the use of these monomials to implement cycle index-- series, where this ordering corresponds nicely to generation-- of integer partitions. To make the library more general we could-- parameterize monomials by the desired ordering.dataTa=Cons{coeff::a,powers::M.MapIntegerInteger}mkMonomial::a->[(Integer,Integer)]->TamkMonomialap=Consa(M.fromListp)negOne::Ring.Ca=>anegOne=negateoneinstance(ZeroTestable.Ca,Ring.Ca,Eqa,Showa)=>Show(Ta)whereshow(Consapows)|isZeroa="0"|M.nullpows=showa|a==one=showVarspows|a==negOne="-"++showVarspows|otherwise=showa++" "++showVarspowsshowVars::M.MapIntegerInteger->StringshowVarsm=intercalate" "$concatMapshowVar(M.toListm)whereshowVar(_,0)=[]showVar(v,1)=["x"++showv]showVar(v,p)=["x"++showv++"^"++showp]-- | The degree of a monomial is the sum of its exponents.degree::Ta->Integerdegree(Cons_m)=M.fold(+)0m-- | The \"partition degree\" of a monomial is the sum of the products-- of each variable index with its exponent. For example, x1^3 x2^2-- x4^3 has partition degree 1*3 + 2*2 + 4*3 = 19. The terminology-- comes from the fact that, for example, we can view x1^3 x2^2 x4^3-- as corresponding to an integer partition of 19 (namely, 1 + 1 + 1-- + 2 + 2 + 4 + 4 + 4).pDegree::Ta->IntegerpDegree(Cons_m)=sum.map(uncurry(*)).M.assocs$m-- | Create a constant monomial.constant::a->Taconstanta=ConsaM.empty-- | Create the monomial xn for a given n.x::(Ring.Ca)=>Integer->Taxn=ConsRing.one(M.singletonn1)-- | Scale all the variable subscripts by a constant. Useful for-- operations like plethyistic substitution or Mobius inversion.scaleMon::Integer->Ta->TascaleMonn(Consam)=Consa(M.mapKeys(n*)m)instanceEq(Ta)where(Cons_m1)==(Cons_m2)=m1==m2instanceOrd(Ta)wherecomparem1m2|d1<d2=LT|d1>d2=GT|otherwise=comparingqm1m2whered1=pDegreem1d2=pDegreem2q=mapRev.reverse.sort.M.assocs.powersnewtypeReva=RevaderivingEqinstanceOrda=>Ord(Reva)wherecompare(Reva)(Revb)=comparebainstance(ZeroTestable.Ca)=>ZeroTestable.C(Ta)whereisZero(Consa_)=isZeroainstance(Additive.Ca,ZeroTestable.Ca)=>Additive.C(Ta)wherezero=ConszeroM.emptynegate(Consam)=Cons(negatea)m-- precondition: m1 == m2(Consa1m1)+(Consa2_m2)|isZeros=ConssM.empty|otherwise=Conssm1wheres=a1+a2instance(Ring.Ca,ZeroTestable.Ca)=>Ring.C(Ta)wherefromIntegern=Cons(fromIntegern)M.empty(Consa1m1)*(Consa2m2)=Cons(a1*a2)(M.filterWithKey(\_p->not(isZerop))$M.unionWith(+)m1m2)-- Partial differentiation with respect to x1.instance(ZeroTestable.Ca,Ring.Ca)=>Differential.C(Ta)wheredifferentiate(Consam)|Just1<-M.lookup1m=ConsaM.empty|Justp<-M.lookup1m=Cons(a*fromIntegerp)(M.adjust(subtract1)1m)|otherwise=ConszeroM.emptyinstance(ZeroTestable.Ca,Field.Ca,Eqa)=>Field.C(Ta)whererecip(Consapows)=ifisZeroathenerror"Monomial.recip: division by zero"elseCons(recipa)(M.mapnegatepows)