#pragma once
#include<cassert>
#include<vector>usingnamespacestd;#include"formal-power-series.hpp"template<typenamemint>voidfft2d(vector<FormalPowerSeries<mint>>&a){intH=a.size(),W=a[0].size();assert((H&(H-1))==0);assert((W&(W-1))==0);for(inti=0;i<H;i++){boolok=false;for(auto&x:a[i]){if(x!=mint()){ok=true;break;}}if(ok)a[i].ntt();}FormalPowerSeries<mint>buf(H);for(inti=0;i<W;i++){for(intj=0;j<H;j++)buf[j]=a[j][i];buf.ntt();for(intj=0;j<H;j++)a[j][i]=buf[j];}}template<typenamemint>voidifft2d(vector<FormalPowerSeries<mint>>&a){intH=a.size(),W=a[0].size();assert((H&(H-1))==0);assert((W&(W-1))==0);FormalPowerSeries<mint>buf(H);for(inti=0;i<W;i++){for(intj=0;j<H;j++)buf[j]=a[j][i];buf.intt();for(intj=0;j<H;j++)a[j][i]=buf[j];}for(inti=0;i<H;i++){boolok=false;for(auto&x:a[i]){if(x!=mint()){ok=true;break;}}if(ok)a[i].intt();}}template<typenamemint>vector<FormalPowerSeries<mint>>transpose(vector<FormalPowerSeries<mint>>f){intH=f.size(),W=f[0].size();for(auto&v:f)assert((int)v.size()==W);vector<FormalPowerSeries<mint>>g(W,FormalPowerSeries<mint>(H));for(inti=0;i<H;i++){for(intj=0;j<W;j++)g[j][i]=f[i][j];}returng;};template<typenamemint>vector<FormalPowerSeries<mint>>multiply2d_naive(vector<FormalPowerSeries<mint>>a,vector<FormalPowerSeries<mint>>b){usingfps=FormalPowerSeries<mint>;usingfps2d=vector<fps>;if(a.empty()orb.empty())return{};if(a[0].empty()orb[0].empty())return{};intHa=a.size(),Wa=a[0].size();intHb=b.size(),Wb=b[0].size();for(auto&v:a)assert((int)v.size()==Wa);for(auto&v:b)assert((int)v.size()==Wb);fps2dc(Ha+Hb-1,fps(Wa+Wb-1));for(intia=0;ia<Ha;ia++){for(intja=0;ja<Wa;ja++){for(intib=0;ib<Hb;ib++){for(intjb=0;jb<Wb;jb++){c[ia+ib][ja+jb]+=a[ia][ja]*b[ib][jb];}}}}returnc;}template<typenamemint>vector<FormalPowerSeries<mint>>multiply2d_partially_naive(vector<FormalPowerSeries<mint>>a,vector<FormalPowerSeries<mint>>b){usingfps=FormalPowerSeries<mint>;usingfps2d=vector<fps>;if(a.empty()orb.empty())return{};if(a[0].empty()orb[0].empty())return{};intHa=a.size(),Wa=a[0].size();intHb=b.size(),Wb=b[0].size();for(auto&v:a)assert((int)v.size()==Wa);for(auto&v:b)assert((int)v.size()==Wb);if(min(Ha,Hb)*min(Wa,Wb)<=40){returnmultiply2d_naive(a,b);}intW=1;while(W<Wa+Wb-1)W*=2;if(W>=64andWa+Wb-1<=W/2+20){if(Wa<=20)swap(a,b),swap(Ha,Hb),swap(Wa,Wb);intd=Wa+Wb-1-W/2;fps2da1(Ha),a2(Ha);for(inti=0;i<Ha;i++){a1[i]=fps{begin(a[i]),end(a[i])-d};a2[i]=fps{end(a[i])-d,end(a[i])};}fps2dc1=multiply2d_partially_naive(a1,b);fps2dc2=multiply2d_partially_naive(a2,b);for(inti=0;i<Ha+Hb-1;i++){c1[i]+=c2[i]<<(Wa-d);c1[i].resize(Wa+Wb-1);}returnc1;}for(auto&v:a)v.resize(W),v.ntt();for(auto&v:b)v.resize(W),v.ntt();fps2dcT;for(intj=0;j<W;j++){fpsbufa(Ha),bufb(Hb);for(inti=0;i<Ha;i++)bufa[i]=a[i][j];for(inti=0;i<Hb;i++)bufb[i]=b[i][j];cT.push_back(bufa*bufb);}fps2dc=transpose(cT);for(auto&v:c)v.intt(),v.resize(Wa+Wb-1);returnc;}template<typenamemint>vector<FormalPowerSeries<mint>>multiply2d(vector<FormalPowerSeries<mint>>a,vector<FormalPowerSeries<mint>>b){usingfps=FormalPowerSeries<mint>;usingfps2d=vector<fps>;if(a.empty()orb.empty())return{};if(a[0].empty()orb[0].empty())return{};intHa=a.size(),Wa=a[0].size();intHb=b.size(),Wb=b[0].size();for(auto&v:a)assert((int)v.size()==Wa);for(auto&v:b)assert((int)v.size()==Wb);if(min(Ha,Hb)*min(Wa,Wb)<=40){returnmultiply2d_naive(a,b);}if(min(Ha,Hb)<=40){returnmultiply2d_partially_naive(a,b);}if(min(Wa,Wb)<=40){autoaT=transpose(a),bT=transpose(b);autocT=multiply2d_partially_naive(aT,bT);returntranspose(cT);}intH=1,W=1;while(H<Ha+Hb-1)H*=2;while(W<Wa+Wb-1)W*=2;if(Wa+Wb-1<W/2+20){intd=Wa+Wb-1-W/2;fps2da1(Ha),a2(Ha);for(inti=0;i<Ha;i++){a1[i]=fps{begin(a[i]),end(a[i])-d};a2[i]=fps{end(a[i])-d,end(a[i])};}fps2dc1=multiply2d(a1,b);fps2dc2=multiply2d(a2,b);for(inti=0;i<Ha+Hb-1;i++){c1[i]+=c2[i]<<(Wa-d);c1[i].resize(Wa+Wb-1);}returnc1;}if(Ha+Hb-1<H/2+20){autoaT=transpose(a),bT=transpose(b);autocT=multiply2d(aT,bT);returntranspose(cT);}a.resize(H),b.resize(H);for(auto&v:a)v.resize(W);for(auto&v:b)v.resize(W);fft2d(a),fft2d(b);for(inti=0;i<H;i++){for(intj=0;j<W;j++)a[i][j]*=b[i][j];}ifft2d(a);a.resize(Ha+Hb-1);for(auto&v:a)v.resize(Wa+Wb-1);returna;}