GAP 4.8.9 installation with standard packages -- copy to your CoCalc project to get it
#ifdef NMZ_COCOA1/*2* nmzIntegrate3* Copyright (C) 2012-2014 Winfried Bruns, Christof Soeger4* This program is free software: you can redistribute it and/or modify5* it under the terms of the GNU General Public License as published by6* the Free Software Foundation, either version 3 of the License, or7* (at your option) any later version.8*9* This program is distributed in the hope that it will be useful,10* but WITHOUT ANY WARRANTY; without even the implied warranty of11* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12* GNU General Public License for more details.13*14* You should have received a copy of the GNU General Public License15* along with this program. If not, see <http://www.gnu.org/licenses/>.16*17* As an exception, when this program is distributed through (i) the App Store18* by Apple Inc.; (ii) the Mac App Store by Apple Inc.; or (iii) Google Play19* by Google Inc., then that store may impose any digital rights management,20* device limits and/or redistribution restrictions that are required by its21* terms of service.22*/2324#include <fstream>25#include <sstream>26#include<string>2728#include "nmz_integrate.h"2930using namespace CoCoA;3132#include <boost/dynamic_bitset.hpp>3334#include "../libnormaliz/my_omp.h"3536using namespace std;3738namespace libnormaliz {3940ourFactorization::ourFactorization(const vector<RingElem>& myFactors,41const vector<long>& myMultiplicities, const RingElem& myRemainingFactor){4243this->myFactors=myFactors;44this->myMultiplicities=myMultiplicities;45this->myRemainingFactor=myRemainingFactor;46}4748ourFactorization::ourFactorization(const factorization<RingElem>& FF){4950ourFactorization(FF.myFactors(),FF.myMultiplicities(),FF.myRemainingFactor());5152}5354RingElem binomial(const RingElem& f, long k)55// computes binomial coefficient (f choose k)56{57const SparsePolyRing& P=owner(f);58RingElem g(P);59g=1;60for(int i=0;i<k;i++)61g*=(f-i)/(i+1);62return(g);63}6465RingElem ascFact(const RingElem& f, long k)66// computes (f+1)*...*(f+k)67{68const SparsePolyRing& P=owner(f);69RingElem g(P);70g=1;71for(int i=0;i<k;i++)72g*=(f+i+1);73return(g);74}7576RingElem descFact(const RingElem& f, long k)77// computes f*(f-1)*...*(f-k+1)78{79const SparsePolyRing& P=owner(f);80RingElem g(P);81g=1;82for(int i=0;i<k;i++)83g*=(f-i);84return(g);85}8687bool compareLength(const RingElem& p, const RingElem& q){88return(NumTerms(p)>NumTerms(q));89}9091vector<RingElem> ourCoeffs(const RingElem& F, const long j){92// our version of expanding a poly nomial wrt to indeterminate j93// The return value is the vector of coefficients of x[j]^i94vector<RingElem> c;95const SparsePolyRing& P=owner(F);96RingElem x=indets(P)[j];97if(F==0){98c.push_back(zero(P));99return(c);100}101102vector<long> v(NumIndets(P));103long k,cs;104105SparsePolyIter i=BeginIter(F);106for (; !IsEnded(i); ++i){107exponents(v,PP(i));108k=v[j];109cs=c.size();110if(k>cs-1)111c.resize(k+1,zero(P));112v[j]=0;113// c[k]+=monomial(P,coeff(i),v);114PushBack(c[k],coeff(i),v);115}116return(c);117}118119RingElem mySubstitution(const RingElem& F, const vector<RingElem>& w){120121const SparsePolyRing& R=owner(F);122RingElem G(zero(R));123RingElem H(one(R));124vector<long> v(NumIndets(R));125vector<long> Z(NumIndets(R));126127SparsePolyIter i=BeginIter(F);128for (; !IsEnded(i); ++i){129exponents(v,PP(i));130H=zero(R);131PushBack(H,coeff(i),Z);132for(size_t j=0;j<v.size();++j)133H*=power(w[j],v[j]);134G+=H;135}136return G;137}138139vector<long> MxV(const vector<vector<long> >& M, vector<long> V){140// matrix*vector141vector<long> P(M.size());142for(size_t i=0;i< M.size();++i){143long s=0;144for(size_t j=0;j<V.size();++j)145s+=M[i][j]*V[j];146P[i]=s;147}148return(P);149}150151vector<RingElem> VxM(const vector<RingElem>& V, const vector<vector<long> >& M){152// vector*matrix153const SparsePolyRing& R=owner(V[0]);154RingElem s(zero(R));155vector<RingElem> P(M[0].size(),zero(R));156for(size_t j=0;j<M[0].size();++j){157s=0;158for(size_t i=0;i<M.size();++i)159s+=V[i]*M[i][j];160P[j]=s;161}162return(P);163}164165166/*167RingElem affineLinearSubstitution(const RingElem& F,const vector<vector<long> >& A,168const vector<long>& b, const long& denom){169// NOT IN USE170size_t i;171const SparsePolyRing& R=owner(F);172size_t m=A.size();173// long n=A[0].size();174vector<RingElem> v(m,zero(R));175RingElem g(zero(R));176177for(i=0;i<m;i++)178{179g=b[i];180g=g/denom;181v[i]=g+indets(R)[i+1];182}183vector<RingElem> w=VxM(v,A);184vector<RingElem> w1(w.size()+1,zero(R));185w1[0]=indets(R)[0];186for(i=1;i<w1.size();++i)187w1[i]=w[i-1];188189RingHom phi=PolyAlgebraHom(R,R,w1);190RingElem G=phi(F);191return(G);192}193*/194195bool DDD=false;196197vector<long> shiftVars(const vector<long>& v, const vector<long>& key){198// selects components of v and reorders them according to key199vector<long> w(v.size(),0);200for(size_t i=0;i<key.size();++i){201w[i]=v[key[i]];202}203return(w);204}205206void makeLocalDegreesAndKey(const boost::dynamic_bitset<>& indicator,const vector<long>& degrees, vector<long>& localDeg,vector<long>& key){207208localDeg.clear();209key.clear();210key.push_back(0);211for(size_t i=0;i<indicator.size();++i)212if(indicator.test(i))213key.push_back(i+1);214for(size_t i=0;i<key.size()-1;++i)215localDeg.push_back(degrees[key[i+1]-1]);216}217218void makeStartEnd(const vector<long>& localDeg, vector<long>& St, vector<long>& End){219220vector<long> denom=degrees2denom(localDeg); // first we must find the blocks of equal degree221if(denom.size()==0)222return;223St.push_back(1);224for(size_t i=0;i<denom.size();++i)225if(denom[i]!=0){226End.push_back(St[St.size()-1]+denom[i]-1);227if(i<denom.size()-1)228St.push_back(End[End.size()-1]+1);229}230/* if(St.size()!=End.size()){231for (size_t i=0;i<denom.size(); ++i){232verboseOutput() << denom.size() << endl;233verboseOutput() << denom[i] << " ";234verboseOutput() << endl;235}236}*/237}238239vector<long> orderExposInner(vector<long>& vin, const vector<long>& St, vector<long>& End){240241vector<long> v=vin;242long p,s,pend,pst;243bool ordered;244if(St.size()!=End.size()){245verboseOutput() << St.size() << " " << End.size() << " " << vin.size() << endl;246verboseOutput() << St[0] << endl;247for (size_t i=0;i<vin.size(); ++i){248verboseOutput() << vin[i] << " ";249}250verboseOutput() << endl;251assert(false);252}253for(size_t j=0;j<St.size();++j){ // now we go over the blocks254pst=St[j];255pend=End[j];256while(1){257ordered=true;258for(p=pst;p<pend;++p){259if(v[p]<v[p+1]){260ordered=false;261s=v[p];262v[p]=v[p+1];263v[p+1]=s;264}265}266if(ordered)267break;268pend--;269}270}271return(v);272}273274RingElem orderExpos(const RingElem& F, const vector<long>& degrees, const boost::dynamic_bitset<>& indicator, bool compactify){275// orders the exponent vectors v of the terms of F276// the exponents v[i] and v[j], i < j, are swapped if277// (1) degrees[i]==degrees[j] and (2) v[i] < v[j]278// so that the exponents are descending in each degree block279// the ordered exponent vectors are inserted into a map280// and their coefficients are added281// at the end the polynomial is rebuilt from the map282// If compactify==true, the exponents will be shifted to the left in order to keep the correspondence283// of variables to degrees284// compactification not used at present (occurs only in restrictToFaces)285286const SparsePolyRing& P=owner(F);287vector<long> v(NumIndets(P));288vector<long> key,localDeg;289key.reserve(v.size()+1);290localDeg.reserve(degrees.size()+1);291292if(compactify){293makeLocalDegreesAndKey(indicator,degrees,localDeg,key);294}295else{296localDeg=degrees;297}298299vector<long> St,End;300makeStartEnd(localDeg,St,End);301302// now the main job303304map<vector<long>,RingElem> orderedMons; // will take the ordered exponent vectors305map<vector<long>,RingElem>::iterator ord_mon;306307SparsePolyIter mon=BeginIter(F); // go over the given polynomial308for (; !IsEnded(mon); ++mon){309exponents(v,PP(mon)); // this function gives the exponent vector back as v310if(compactify)311v=shiftVars(v,key);312v=orderExposInner(v,St,End);313ord_mon=orderedMons.find(v); // insert into map or add coefficient314if(ord_mon!=orderedMons.end()){315ord_mon->second+=coeff(mon);316}317else{318orderedMons.insert(pair<vector<long>,RingElem>(v,coeff(mon)));319}320}321322// now we must reconstruct the polynomial323// we use that the natural order of vectors in C++ STL is inverse324// to lex. Therefore push_front325326RingElem r(zero(P));327//JAA verboseOutput() << "Loop start " << orderedMons.size() << endl;328//JAA size_t counter=0;329for(ord_mon=orderedMons.begin();ord_mon!=orderedMons.end();++ord_mon){330//JAA verboseOutput() << counter++ << ord_mon->first << endl;331//JAA try {332PushFront(r,ord_mon->second,ord_mon->first);333//JAA }334//JAA catch(const std::exception& exc){verboseOutput() << "Caught exception: " << exc.what() << endl;}335}336//JAA verboseOutput() << "Loop end" << endl;337return(r);338}339340341void restrictToFaces(const RingElem& G,RingElem& GOrder, vector<RingElem>& GRest,const vector<long> degrees, const vector<SIMPLINEXDATA_INT>& inExSimplData){342// Computesd the restrictions of G to the faces in inclusion-exclusion.343// All terms are simultaneously compactified and exponentwise ordered344// Polynomials returned in GRest345// Ordering is also applied to G itself, returned in GOrder346// Note: degrees are given for the full simplex. Therefore "local" degreees must be made347// (depend only on face and not on offset, but generation here is cheap)348349const SparsePolyRing& P=owner(G);350351vector<long> v(NumIndets(P));352vector<long> w(NumIndets(P));353vector<long> localDeg;354localDeg.reserve(v.size());355size_t dim=NumIndets(P)-1;356357// first we make the facewise data that are needed for the compactification and otrdering358// of exponent vectors359vector<vector<long> > St(inExSimplData.size()),End(inExSimplData.size()),key(inExSimplData.size());360vector<long> active;361for(size_t i=0;i<inExSimplData.size();++i)362if(!inExSimplData[i].done){363active.push_back(i);364makeLocalDegreesAndKey(inExSimplData[i].GenInFace ,degrees,localDeg,key[i]);365makeStartEnd(localDeg,St[i],End[i]);366}367368// now the same for the full simplex (localDeg=degrees)369boost::dynamic_bitset<> fullSimpl(dim);370fullSimpl.set();371vector<long> StSimpl,EndSimpl;372makeStartEnd(degrees,StSimpl,EndSimpl);373374vector<map<vector<long>,RingElem> > orderedMons(inExSimplData.size()); // will take the ordered exponent vectors375map<vector<long>,RingElem> orderedMonsSimpl;376map<vector<long>,RingElem>::iterator ord_mon;377378379boost::dynamic_bitset<> indicator(dim);380381// now we go over the terms of G382SparsePolyIter term=BeginIter(G);383//PPMonoid TT = PPM(owner(G));384for (; !IsEnded(term); ++term){385//PPMonoidElem mon(PP(term));386exponents(v,PP(term));387w=v;388indicator.reset();389for(size_t j=0;j<dim;++j)390if(v[j+1]!=0) // we must add 1 since the 0-th indeterminate is irrelevant here391indicator.set(j);392for(size_t i=0;i<active.size();++i){393int j=active[i];394if(indicator.is_subset_of(inExSimplData[j].GenInFace)){395w=shiftVars(v,key[j]);396w=orderExposInner(w,St[j],End[j]);397// w=shiftVars(v,key[j]);398ord_mon=orderedMons[j].find(w); // insert into map or add coefficient399if(ord_mon!=orderedMons[j].end()){400ord_mon->second+=coeff(term);401}402else{403orderedMons[j].insert(pair<vector<long>,RingElem>(w,coeff(term)));404}405}406} // for i407408v=orderExposInner(v,StSimpl,EndSimpl);409ord_mon=orderedMonsSimpl.find(v); // insert into map or add coefficient410if(ord_mon!=orderedMonsSimpl.end()){411ord_mon->second+=coeff(term);412}413else{414orderedMonsSimpl.insert(pair<vector<long>,RingElem>(v,coeff(term)));415}416} // loop over term417418// now we must make the resulting polynomials from the maps419420for(size_t i=0;i<active.size();++i){421int j=active[i];422for(ord_mon=orderedMons[j].begin();ord_mon!=orderedMons[j].end();++ord_mon){423PushFront(GRest[j],ord_mon->second,ord_mon->first);424}425// verboseOutput() << "GRest[j] " << j << " " << NumTerms(GRest[j]) << endl;426}427428for(ord_mon=orderedMonsSimpl.begin();ord_mon!=orderedMonsSimpl.end();++ord_mon){429PushFront(GOrder,ord_mon->second,ord_mon->first);430}431432}433434long nrActiveFaces=0;435long nrActiveFacesOld=0;436437void all_contained_faces(const RingElem& G, RingElem& GOrder,const vector<long>& degrees,438boost::dynamic_bitset<>& indicator, long Deg,vector<SIMPLINEXDATA_INT>& inExSimplData,439deque<pair<vector<long>,RingElem> >& facePolysThread){440441const SparsePolyRing& R=owner(G);442vector<RingElem> GRest;443// size_t dim=indicator.size();444for(size_t i=0;i<inExSimplData.size();++i){445GRest.push_back(zero(R));446447if(!indicator.is_subset_of(inExSimplData[i].GenInFace))448inExSimplData[i].done=true; // done if face cannot contribute to result for this offset449else450inExSimplData[i].done=false; // not done otherwise451}452restrictToFaces(G,GOrder,GRest,degrees,inExSimplData);453454for(size_t j=0;j<inExSimplData.size();++j){455if(inExSimplData[j].done)456continue;457#pragma omp atomic458nrActiveFaces++;459// verboseOutput() << "Push back " << NumTerms(GRest[j]);460GRest[j]=power(indets(R)[0],Deg)*inExSimplData[j].mult*GRest[j]; // shift by degree of offset amd multiply by mult of face461facePolysThread.push_back(pair<vector<long>,RingElem>(inExSimplData[j].degrees,GRest[j]));462// verboseOutput() << " Now " << facePolysThread.size() << endl;463}464}465466RingElem affineLinearSubstitutionFL(const ourFactorization& FF,const vector<vector<long> >& A,467const vector<long>& b, const long& denom, const SparsePolyRing& R, const vector<long>& degrees, const BigInt& lcmDets,468vector<SIMPLINEXDATA_INT>& inExSimplData,deque<pair<vector<long>,RingElem> >& facePolysThread){469// applies linar substitution y --> lcmDets*A(y+b/denom) to all factors in FF470// and returns the product of the modified factorsafter ordering the exponent vectors471472size_t i;473size_t m=A.size();474size_t dim=m; // TO DO: eliminate this duplication475vector<RingElem> v(m,zero(R));476RingElem g(zero(R));477478for(i=0;i<m;i++)479{480g=b[i]*(lcmDets/denom);481v[i]=g+lcmDets*indets(R)[i+1];482}483vector<RingElem> w=VxM(v,A);484vector<RingElem> w1(w.size()+1,zero(R));485w1[0]=RingElem(R,lcmDets);486for(i=1;i<w1.size();++i)487w1[i]=w[i-1];488489// RingHom phi=PolyAlgebraHom(R,R,w1);490491RingElem G1(zero(R));492list<RingElem> sortedFactors;493for(i=0;i<FF.myFactors.size();++i){494// G1=phi(FF.myFactors[i]);495G1=mySubstitution(FF.myFactors[i],w1);496for(int nn=0;nn<FF.myMultiplicities[i];++nn)497sortedFactors.push_back(G1);498}499500list<RingElem>::iterator sf;501sortedFactors.sort(compareLength);502503RingElem G(one(R));504505for(sf=sortedFactors.begin();sf!=sortedFactors.end();++sf)506G*=*sf;507508if(inExSimplData.size()==0){ // not really necesary, but a slight shortcut509boost::dynamic_bitset<> dummyInd;510return(orderExpos(G,degrees,dummyInd,false));511}512513// if(inExSimplData.size()!=0){514long Deg=0;515boost::dynamic_bitset<> indicator(dim); // indicates the non-zero components of b516indicator.reset();517for(size_t i=0;i<dim;++i)518if(b[i]!=0){519indicator.set(i);520Deg+=degrees[i]*b[i];521}522Deg/=denom;523RingElem Gorder(zero(R));524all_contained_faces(G,Gorder,degrees,indicator, Deg, inExSimplData,facePolysThread);525return(Gorder);526// }527}528529530vector<RingElem> homogComps(const RingElem& F){531// returns the vector of homogeneous components of F532// w.r.t. standard grading533534const SparsePolyRing& P=owner(F);535long dim=NumIndets(P);536vector<long> v(dim);537vector<RingElem> c(deg(F)+1,zero(P));538long j,k;539540//TODO there is a leading_term() function coming in cocoalib541//TODO maybe there will be even a "splice_leading_term"542SparsePolyIter i=BeginIter(F);543for (; !IsEnded(i); ++i){544exponents(v,PP(i));545k=0;546for(j=0;j<dim;j++)547k+=v[j];548PushBack(c[k],coeff(i),v);549}550return(c);551}552553RingElem homogenize(const RingElem& F){554// homogenizes F wrt the zeroth variable and returns the555// homogenized polynomial556557SparsePolyRing P=owner(F);558int d=deg(F);559vector<RingElem> c(d+1,zero(P));560c=homogComps(F);561RingElem h(zero(P));562for(int i=0;i<=d;++i)563h+=c[i]*power(indets(P)[0],d-i);564return(h);565}566567RingElem makeZZCoeff(const RingElem& F, const SparsePolyRing& RZZ){568// F is a polynomial over RingQQ with integral coefficients569// This function converts it into a polynomial over RingZZ570571SparsePolyIter mon=BeginIter(F); // go over the given polynomial572RingElem G(zero(RZZ));573for (; !IsEnded(mon); ++mon){574PushBack(G,num(coeff(mon)),PP(mon));575}576return(G);577}578579580RingElem makeQQCoeff(const RingElem& F, const SparsePolyRing& R){581// F is a polynomial over RingZZ582// This function converts it into a polynomial over RingQQ583SparsePolyIter mon=BeginIter(F); // go over the given polynomial584RingElem G(zero(R));585for (; !IsEnded(mon); ++mon){586PushBack(G,RingElem(RingQQ(),coeff(mon)),PP(mon));587}588return(G);589}590591RingElem processInputPolynomial(const string& poly_as_string, const SparsePolyRing& R, const SparsePolyRing& RZZ,592vector<RingElem>& resPrimeFactors, vector<RingElem>& resPrimeFactorsNonhom, vector<long>& resMultiplicities,593RingElem& remainingFactor, bool& homogeneous,const bool& do_leadCoeff){594// "res" stands for "result"595// resPrimeFactors are homogenized, the "nonhom" come from the original polynomial596597long i,j;598string dummy=poly_as_string;599RingElem the_only_dactor= ReadExpr(R, dummy); // there is only one600vector<RingElem> factorsRead; // this is from the very first version of NmzIntegrate601factorsRead.push_back(the_only_dactor);602vector<long> multiplicities;603604vector<RingElem> primeFactors; // for use in this routine605vector<RingElem> primeFactorsNonhom; // return results will go into the "res" parameters for output606607if(verbose_INT)608verboseOutput() << "Polynomial read" << endl;609610homogeneous=true; // we factor the polynomials read611for(i=0;i< (long) factorsRead.size();++i){ // and make them integral this way612// they must further be homogenized613// and converted to polynomials with ZZ614// coefficients (instead of inegral QQ)615// The homogenization is necessary to allow616// substitutions over ZZ617RingElem G(factorsRead[i]);618if(deg(G)==0){619remainingFactor*=G; // constants go into remainingFactor620continue; // this extra treatment would not be necessary621}622// homogeneous=(G==LF(G));623vector<RingElem> compsG= homogComps(G);624// we test for homogeneity. In case do_leadCoeff==true, polynomial625// is replaced by highest homogeneous component626if(G!=compsG[compsG.size()-1]){627homogeneous=false;628// if(!homogeneous){629if(verbose_INT && do_leadCoeff)630verboseOutput() << "Polynomial is inhomogeneous. Replacing it by highest hom. comp." << endl;631if(do_leadCoeff){632G=compsG[compsG.size()-1];633// G=LF(G);634factorsRead[i]=G; // though it may no longer be the factor read from input635}636}637638factorization<RingElem> FF=factor(G); // now the factorization and transfer to integer coefficients639for(j=0;j< (long) FF.myFactors().size();++j){640primeFactorsNonhom.push_back(FF.myFactors()[j]); // these are the factors of the polynomial to be integrated641primeFactors.push_back(makeZZCoeff(homogenize(FF.myFactors()[j]),RZZ)); // the homogenized factors with ZZ coeff642multiplicities.push_back(FF.myMultiplicities()[j]); // homogenized for substitution !643}644remainingFactor*=FF.myRemainingFactor();645}646647648// it remains to collect multiple factors that come from different input factors649650for(i=0;i< (long) primeFactors.size();++i)651if(primeFactors[i]!=0)652for(j=i+1;j< (long) primeFactors.size();++j)653if(primeFactors[j]!=0 && primeFactors[i]==primeFactors[j]){654primeFactors[j]=0;655multiplicities[i]++;656}657658for(i=0;i< (long) primeFactors.size();++i) // now everything is transferred to the return parameters659if(primeFactors[i]!=0){660resPrimeFactorsNonhom.push_back(primeFactorsNonhom[i]);661resPrimeFactors.push_back(primeFactors[i]);662resMultiplicities.push_back(multiplicities[i]);663}664665RingElem F(one(R)); //th polynomial to be integrated666for(i=0;i< (long) factorsRead.size();++i) // with QQ coefficients667F*=factorsRead[i];668669return(F);670}671672CyclRatFunct genFunct(const vector<vector<CyclRatFunct> >& GFP, const RingElem& F, const vector<long>& degrees)673// writes \sum_{x\in\ZZ_+^n} f(x,t) T^x674// under the specialization T_i --> t^g_i675// as a rational function in t676{677const SparsePolyRing& P=owner(F);678RingElem t=indets(P)[0];679680CyclRatFunct s(F); // F/1681682CyclRatFunct g(zero(P)),h(zero(P));683684long nd=degrees.size();685long i,k,mg;686vector<RingElem> c;687688for(k=1; k<=nd;k++)689{690c=ourCoeffs(s.num,k); // we split the numerator according691// to powers of var k692mg=c.size(); // max degree+1 in var k693694h.set2(zero(P));695for(i=0;i<mg;i++) // now we replace the powers of var k696{ // by the corrseponding rational function,697// multiply, and sum the products698699h.num=(1-power(t,degrees[k-1]))*h.num+GFP[degrees[k-1]][i].num*c[i];700h.denom=GFP[degrees[k-1]][i].denom;701}702s.num=h.num;703s.denom=prodDenom(s.denom,h.denom);704}705return(s);706}707708vector<RingElem> power2ascFact(const SparsePolyRing& P, const long& k)709// computes the representation of the power x^n as the linear combination710// of (x+1)_n,...,(x+1)_0711// return value is the vector of coefficients (they belong to ZZ)712{713RingElem t=indets(P)[0];714const vector<long> ONE(NumIndets(P));715RingElem f(P),g(P), h(P);716f=power(t,k);717long m;718vector<RingElem> c(k+1,zero(P));719while(f!=0)720{721m=deg(f);722h=monomial(P,LC(f),ONE);723c[m]=h;724f-=h*ascFact(t,m);725}726return(c);727}728729730CyclRatFunct genFunctPower1(const SparsePolyRing& P, long k,long n)731// computes the generating function for732// \sum_j j^n (t^k)^j733{734vector<RingElem> a=power2ascFact(P,n);735RingElem b(P);736vector<long> u;737CyclRatFunct g(zero(P)), h(zero(P));738long i,s=a.size();739for(i=0;i<s;++i)740{741u=makeDenom(k,i+1);742b=a[i]*factorial(i);743g.set2(b,u);744h.addCRF(g);745}746return(h);747}748749void CyclRatFunct::extendDenom(const vector<long>& target)750// extends the denominator to target751// by multiplying the numrerator with the remaining factor752{753RingElem t=indets(owner(num))[0];754long i,ns=target.size(),nf=denom.size();755for(i=1;i<ns;++i){756if(i>nf-1)757num*=power(1-power(t,i),(target[i]));758else759if(target[i]>denom[i])760num*=power(1-power(t,i),(target[i]-denom[i]));761}762denom=target;763}764765vector<long> lcmDenom(const vector<long>& df, const vector<long>& dg){766// computes the lcm of ztwo denominators as used in CyclRatFunct767// (1-t^i and 1-t^j, i != j, are considered as coprime)768size_t nf=df.size(),ng=dg.size(),i;769size_t n=max(nf,ng);770vector<long> dh=df;771dh.resize(n);772for(i=1;i<n;++i)773if(i<ng && dh[i]<dg[i])774dh[i]=dg[i];775return(dh);776}777778779vector<long> prodDenom(const vector<long>& df, const vector<long>& dg){780// as above, but computes the profduct781size_t nf=df.size(),ng=dg.size(),i;782size_t n=max(nf,ng);783vector<long> dh=df;784dh.resize(n);785for(i=1;i<n;++i)786if(i<ng)787dh[i]+=dg[i];788return(dh);789}790791vector<long> degrees2denom(const vector<long>& d){792// converts a vector of degrees to a "denominator"793// listing at position i the multiplicity of i in d794long m=0;795size_t i;796if(d.size()==0)797return vector<long> (0);798for(i=0;i<d.size();++i)799m=max(m,d[i]);800vector<long> e(m+1);801for(i=0;i<d.size();++i)802e[d[i]]++;803return(e);804}805806vector<long> denom2degrees(const vector<long>& d){807// the converse operation808vector<long> denomDeg;809for(size_t i=0;i<d.size();++i)810for(long j=0;j<d[i];++j)811denomDeg.push_back(i);812return(denomDeg);813}814815RingElem denom2poly(const SparsePolyRing& P, const vector<long>& d){816// converts a denominator into a real polynomial817// the variable for the denominator is x[0]818RingElem t=indets(P)[0];819RingElem f(one(P));820for(size_t i=1;i<d.size();++i)821f*=power(1-power(t,i),d[i]);822return(f);823}824825vector<long> makeDenom(long k,long n)826// makes the denominator (1-t^k)^n827{828vector<long> d(k+1);829d[k]=n;830return(d);831}832833void CyclRatFunct::addCRF(const CyclRatFunct& r){834// adds r to *this, r is preserved in its given form835CyclRatFunct s(zero(owner(num)));836const vector<long> lcmden(lcmDenom(denom,r.denom));837s=r;838s.extendDenom(lcmden);839extendDenom(lcmden);840num+=s.num;841}842843void CyclRatFunct::multCRF(const CyclRatFunct& r){844// nmultiplies *this by r845num*=r.num;846denom=prodDenom(denom,r.denom);847}848849void CyclRatFunct::showCRF(){850if(!verbose_INT)851return;852853verboseOutput() << num << endl;854for(size_t i=1;i<denom.size();++i)855verboseOutput() << denom[i] << " ";856verboseOutput() << endl;857}858859void CyclRatFunct::showCoprimeCRF(){860// shows *this also with coprime numerator and denominator861// makes only sense if only x[0] appears in the numerator (not checked)862863if(!verbose_INT)864return;865866verboseOutput() << "--------------------------------------------" << endl << endl;867verboseOutput() << "Given form" << endl << endl;868showCRF();869verboseOutput() << endl;870const SparsePolyRing& R=owner(num);871SparsePolyRing P=NewPolyRing_DMPI(RingQQ(),symbols("t"));872vector<RingElem> Im(NumIndets(R),zero(P));873Im[0]=indets(P)[0];874RingHom phi=PolyAlgebraHom(R,P,Im);875RingElem f(phi(num));876RingElem g(denom2poly(P,denom));877RingElem h=CoCoA::gcd(f,g);878f/=h;879g/=h;880verboseOutput() << "Coprime numerator (for denom with remaining factor 1)" << endl <<endl;881factorization<RingElem> gf=factor(g);882verboseOutput() << f/gf.myRemainingFactor() << endl << endl << "Factorization of denominator" << endl << endl;883size_t nf=gf.myFactors().size();884for(size_t i=0;i<nf;++i)885verboseOutput() << gf.myFactors()[i] << " mult " << gf.myMultiplicities()[i] << endl;886verboseOutput() << "--------------------------------------------" << endl;887888}889890void CyclRatFunct::simplifyCRF(){891// cancels factors 1-t^i from the denominator that appear there explicitly892// (and not just as factors of 1-t^j for some j)893894const SparsePolyRing& R=owner(num);895long nd=denom.size();896for(long i=1;i<nd;i++)897{898while(denom[i]>0)899{900if(!IsDivisible(num,1-power(indets(R)[0],i)))901break;902num/=1-power(indets(R)[0],i);903denom[i]--;904}905}906}907908void CyclRatFunct::set2(const RingElem& f, const vector<long>& d)909{910num=f;911denom=d;912}913914void CyclRatFunct::set2(const RingElem& f)915{916num=f;917denom.resize(1,0);918}919920CyclRatFunct::CyclRatFunct(const RingElem& c):num(c)921// constructor starting from a RingElem922// initialization necessary because RingElem has no default923// constructor924{925denom.resize(1,0);926}927928CyclRatFunct::CyclRatFunct(const RingElem& c,const vector<long>& d):num(c),denom(d){929}930931932933} // namespace934#endif //NMZ_COCOA935936