#ifndef P_RECURSIVE_HPP
#define P_RECURSIVE_HPP
#include<algorithm>
#include<cassert>
#include<cstdio>
#include<utility>
#include<vector>// https://min-25.hatenablog.com/entry/2018/05/10/212805template<typenameZ>std::vector<Z>extended(intn,conststd::vector<std::vector<Z>>&coeffs,conststd::vector<Z>&terms){std::vector<Z>ret(std::max(n+1,int(terms.size())));std::copy(terms.begin(),terms.end(),ret.begin());autoorder=int(coeffs.size())-1;autodeg=int(coeffs[0].size())-1;assert(order<=int(terms.size()));for(autom=int(terms.size());m<=n;++m){Zs=0;for(autoi=1;i<=order;++i){autok=m-i;autot=ret[k];for(autod=0;d<=deg;++d){s+=t*coeffs[i][d];t*=k;}}Zdenom=0,mpow=1;for(autod=0;d<=deg;++d){denom+=mpow*coeffs[0][d];mpow*=m;}ret[m]=-s/denom;}returnret;}template<typenameZ>std::vector<std::vector<Z>>find_recurrence_relation(conststd::vector<Z>&terms,intdeg,boolverify=true,boolverbose=true){auton=int(terms.size());autoB=(n+2)/(deg+2);// number of blocksautoC=B*(deg+1);// number of columnsautoR=n-(B-1);// number of rowsassert(2<=B);assert(C-1<=R);autoerror=[](intorder,intd){std::fprintf(stderr,"Error: Could not find a recurrence relation ""of order <= %d and degree <= %d.\n\n",order,d);assert(0);};std::vector<std::vector<Z>>mat(R,std::vector<Z>(C));for(autoy=0;y<R;++y){for(autob=0;b<B;++b){autov=terms[y+b];for(autod=0;d<=deg;++d){mat[y][b*(deg+1)+d]=v;v*=y+b;}}}autorank=0;for(autox=0;x<C;++x){autopivot=-1;for(autoy=rank;y<R;++y){if(mat[y][x]!=0){pivot=y;break;}}if(pivot<0){break;}if(pivot!=rank){std::swap(mat[rank],mat[pivot]);}autoinv=mat[rank][x].inv();for(autox2=x;x2<C;++x2){mat[rank][x2]*=inv;}for(autoy=rank+1;y<R;++y){if(mat[y][x]!=0){autoc=-mat[y][x];for(autox2=x;x2<C;++x2){mat[y][x2]+=c*mat[rank][x2];}}}++rank;}if(rank==C){error(B-1,deg);}for(autoy=rank-1;0<=y;--y){if(mat[y][rank]!=0){assert(mat[y][y]==1);autoc=-mat[y][rank];for(inty2=0;y2<y;++y2){mat[y2][rank]+=c*mat[y2][y];}}}autoorder=rank/(deg+1);std::vector<std::vector<Z>>ret(order+1,std::vector<Z>(deg+1));ret[0][rank%(deg+1)]=1;for(autoy=rank-1;0<=y;--y){autok=order-y/(deg+1);autod=y%(deg+1);ret[k][d]=-mat[y][rank];}if(verify){autoextended_terms=extended(n-1,ret,std::vector<Z>(terms.begin(),terms.begin()+order));for(autoi=0;i<int(terms.size());++i){if(terms[i]!=extended_terms[i]){error(B-1,deg);}}}if(verbose){automod=Z::mod();autolast=verify?n-1:order+R-1;std::fprintf(stderr,"[ Found a recurrence relation ]\n""- order %d\n""- degree %d\n""- verified up to a(%d) (number of non-trivial terms: %d)\n",order,deg,last,(last+1)-((deg+2)*(order+1)-2));std::fprintf(stderr,"{\n");for(autok=0;k<=order;++k){std::fprintf(stderr," {");for(autod=0;d<=deg;++d){if(d){std::fprintf(stderr,", ");}autov=ret[k][d].val();std::fprintf(stderr,"%d",v<=mod/2?v:v-mod);}std::fprintf(stderr,"}%s\n",k==order?"":",");}std::fprintf(stderr,"}\n\n");}returnret;}#endif // P_RECURSIVE_HPP
#line 1 "math/p_recursive.hpp"
#include<algorithm>
#include<cassert>
#include<cstdio>
#include<utility>
#include<vector>// https://min-25.hatenablog.com/entry/2018/05/10/212805template<typenameZ>std::vector<Z>extended(intn,conststd::vector<std::vector<Z>>&coeffs,conststd::vector<Z>&terms){std::vector<Z>ret(std::max(n+1,int(terms.size())));std::copy(terms.begin(),terms.end(),ret.begin());autoorder=int(coeffs.size())-1;autodeg=int(coeffs[0].size())-1;assert(order<=int(terms.size()));for(autom=int(terms.size());m<=n;++m){Zs=0;for(autoi=1;i<=order;++i){autok=m-i;autot=ret[k];for(autod=0;d<=deg;++d){s+=t*coeffs[i][d];t*=k;}}Zdenom=0,mpow=1;for(autod=0;d<=deg;++d){denom+=mpow*coeffs[0][d];mpow*=m;}ret[m]=-s/denom;}returnret;}template<typenameZ>std::vector<std::vector<Z>>find_recurrence_relation(conststd::vector<Z>&terms,intdeg,boolverify=true,boolverbose=true){auton=int(terms.size());autoB=(n+2)/(deg+2);// number of blocksautoC=B*(deg+1);// number of columnsautoR=n-(B-1);// number of rowsassert(2<=B);assert(C-1<=R);autoerror=[](intorder,intd){std::fprintf(stderr,"Error: Could not find a recurrence relation ""of order <= %d and degree <= %d.\n\n",order,d);assert(0);};std::vector<std::vector<Z>>mat(R,std::vector<Z>(C));for(autoy=0;y<R;++y){for(autob=0;b<B;++b){autov=terms[y+b];for(autod=0;d<=deg;++d){mat[y][b*(deg+1)+d]=v;v*=y+b;}}}autorank=0;for(autox=0;x<C;++x){autopivot=-1;for(autoy=rank;y<R;++y){if(mat[y][x]!=0){pivot=y;break;}}if(pivot<0){break;}if(pivot!=rank){std::swap(mat[rank],mat[pivot]);}autoinv=mat[rank][x].inv();for(autox2=x;x2<C;++x2){mat[rank][x2]*=inv;}for(autoy=rank+1;y<R;++y){if(mat[y][x]!=0){autoc=-mat[y][x];for(autox2=x;x2<C;++x2){mat[y][x2]+=c*mat[rank][x2];}}}++rank;}if(rank==C){error(B-1,deg);}for(autoy=rank-1;0<=y;--y){if(mat[y][rank]!=0){assert(mat[y][y]==1);autoc=-mat[y][rank];for(inty2=0;y2<y;++y2){mat[y2][rank]+=c*mat[y2][y];}}}autoorder=rank/(deg+1);std::vector<std::vector<Z>>ret(order+1,std::vector<Z>(deg+1));ret[0][rank%(deg+1)]=1;for(autoy=rank-1;0<=y;--y){autok=order-y/(deg+1);autod=y%(deg+1);ret[k][d]=-mat[y][rank];}if(verify){autoextended_terms=extended(n-1,ret,std::vector<Z>(terms.begin(),terms.begin()+order));for(autoi=0;i<int(terms.size());++i){if(terms[i]!=extended_terms[i]){error(B-1,deg);}}}if(verbose){automod=Z::mod();autolast=verify?n-1:order+R-1;std::fprintf(stderr,"[ Found a recurrence relation ]\n""- order %d\n""- degree %d\n""- verified up to a(%d) (number of non-trivial terms: %d)\n",order,deg,last,(last+1)-((deg+2)*(order+1)-2));std::fprintf(stderr,"{\n");for(autok=0;k<=order;++k){std::fprintf(stderr," {");for(autod=0;d<=deg;++d){if(d){std::fprintf(stderr,", ");}autov=ret[k][d].val();std::fprintf(stderr,"%d",v<=mod/2?v:v-mod);}std::fprintf(stderr,"}%s\n",k==order?"":",");}std::fprintf(stderr,"}\n\n");}returnret;}