{-# LANGUAGE NoImplicitPrelude #-}{-# LANGUAGE MultiParamTypeClasses #-}{-# LANGUAGE FlexibleInstances #-}{- |
Two-variate power series.
-}moduleMathObj.PowerSeries2whereimportqualifiedMathObj.PowerSeriesasPSimportqualifiedMathObj.PolynomialasPolyimportqualifiedAlgebra.DifferentialasDifferentialimportqualifiedAlgebra.VectorasVectorimportqualifiedAlgebra.AlgebraicasAlgebraicimportqualifiedAlgebra.FieldasFieldimportqualifiedAlgebra.RingasRingimportqualifiedAlgebra.AdditiveasAdditiveimportqualifiedAlgebra.ZeroTestableasZeroTestableimportqualifiedNumericPreludeasNPimportqualifiedPreludeBaseasPimportData.List(isPrefixOf,)importqualifiedData.List.MatchasMatchimportPreludeBasehiding(const)importNumericPreludehiding(negate,stdUnit,sqrt,exp,log,sin,cos,tan,asin,acos,atan){- |
In order to handle both variables equivalently
we maintain a list of coefficients for terms of the same total degree.
That is
> eval [[a], [b,c], [d,e,f]] (x,y) ==
> a + b*x+c*y + d*x^2+e*x*y+f*y^2
Although the sub-lists are always finite and thus are more like polynomials than power series,
division and square root computation are easier to implement for power series.
-}newtypeTa=Cons{coeffs::Corea}deriving(Ord)typeCorea=[[a]]isValid::[[a]]->BoolisValid=flipisPrefixOf[1..].maplengthcheck::[[a]]->[[a]]checkxs=zipWith(\nx->ifMatch.compareLengthnx==EQthenxelseerror"PowerSeries2.check: invalid length of sub-list")(iterate(():)[()])xsfromCoeffs::[[a]]->TafromCoeffs=Cons.checkfromPowerSeries0::Ring.Ca=>PS.Ta->TafromPowerSeries0x=fromCoeffs$zipWith(:)(PS.coeffsx)$iterate(0:)[]fromPowerSeries1::Ring.Ca=>PS.Ta->TafromPowerSeries1x=fromCoeffs$zipWith(++)(iterate(0:)[])$map(:[])(PS.coeffsx)lift0::Corea->Talift0=Conslift1::(Corea->Corea)->(Ta->Ta)lift1f(Consx0)=Cons(fx0)lift2::(Corea->Corea->Corea)->(Ta->Ta->Ta)lift2f(Consx0)(Consx1)=Cons(fx0x1)lift0fromPowerSeries::[PS.Ta]->Corealift0fromPowerSeries=mapPS.coeffslift1fromPowerSeries::([PS.Ta]->[PS.Ta])->(Corea->Corea)lift1fromPowerSeriesfx0=mapPS.coeffs(f(mapPS.fromCoeffsx0))lift2fromPowerSeries::([PS.Ta]->[PS.Ta]->[PS.Ta])->(Corea->Corea->Corea)lift2fromPowerSeriesfx0x1=mapPS.coeffs(f(mapPS.fromCoeffsx0)(mapPS.fromCoeffsx1))const::a->Taconstx=lift0[[x]]instanceFunctorTwherefmapf(Consxs)=Cons(map(mapf)xs)appPrec::IntappPrec=10instance(Showa)=>Show(Ta)whereshowsPrecp(Consxs)=showParen(p>=appPrec)(showString"PowerSeries2.fromCoeffs ".showsxs){- * Series arithmetic -}add,sub::(Additive.Ca)=>Corea->Corea->Coreaadd=PS.addsub=PS.subnegate::(Additive.Ca)=>Corea->Coreanegate=PS.negateinstance(Eqa,ZeroTestable.Ca)=>Eq(Ta)where(Consx)==(Consy)=Poly.equalxyinstance(Additive.Ca)=>Additive.C(Ta)wherenegate=lift1PS.negate(+)=lift2PS.add(-)=lift2PS.subzero=lift0[]scale::Ring.Ca=>a->Corea->Coreascale=map.(Vector.*>)mul::Ring.Ca=>Corea->Corea->Coreamul=lift2fromPowerSeriesPS.mulinstance(Ring.Ca)=>Ring.C(Ta)whereone=constonefromIntegern=const(fromIntegern)(*)=lift2mulinstanceVector.CTwherezero=zero(<+>)=(+)(*>)=Vector.functorScaledivide::(Field.Ca)=>Corea->Corea->Coreadivide=lift2fromPowerSeriesPS.divideinstance(Field.Ca)=>Field.C(Ta)where(/)=lift2dividesqrt::(Field.Ca)=>(a->a)->Corea->CoreasqrtfSqRt=lift1fromPowerSeries$PS.sqrt(PS.const.(\[x]->fSqRtx).PS.coeffs)instance(Algebraic.Ca)=>Algebraic.C(Ta)wheresqrt=lift1(sqrtAlgebraic.sqrt)-- x ^/ y = lift1 (pow (Algebraic.^/ y)-- (fromRational' y)) xswapVariables::Corea->CoreaswapVariables=mapreversedifferentiate0::(Ring.Ca)=>Corea->Coreadifferentiate0=swapVariables.differentiate1.swapVariablesdifferentiate1::(Ring.Ca)=>Corea->Coreadifferentiate1=lift1fromPowerSeries$mapDifferential.differentiateintegrate0::(Field.Ca)=>[a]->Corea->Coreaintegrate0cs=swapVariables.integrate1cs.swapVariablesintegrate1::(Field.Ca)=>[a]->Corea->Coreaintegrate1=zipWithPS.integrate{- |
Since the inner series must start with a zero,
the first term is omitted in y.
-}comp::(Ring.Ca)=>[a]->Corea->Coreacomp=lift1fromPowerSeries.PS.comp.mapPS.const