public static uint[] Convolution <TMod>(ReadOnlySpan <uint> a, ReadOnlySpan <uint> b) where TMod : struct, IStaticMod { var mod = default(TMod).Mod; if (default(TMod).IsPrime && a.Length + b.Length - 1 <= (1 << InternalBit.BSF(mod - 1))) { // ACL で解けるならOK return(MathLib.Convolution <TMod>(a, b)); } unchecked { var n = a.Length; var m = b.Length; var la = new long[n]; for (int i = 0; i < la.Length; i++) { la[i] = a[i] % mod; } var lb = new long[m]; for (int i = 0; i < lb.Length; i++) { lb[i] = b[i] % mod; } if (n == 0 || m == 0) { return(Array.Empty <uint>()); } const long Mod1 = 167772161; const long Mod2 = 469762049; const long Mod3 = 754974721; const long M1i2 = 104391568; const long M12i3 = 190329765; long M12i = (long)(ulong)(Mod1 * Mod2) % mod; Debug.Assert(default(FFTMod1).Mod == Mod1); Debug.Assert(default(FFTMod2).Mod == Mod2); Debug.Assert(default(FFTMod3).Mod == Mod3); Debug.Assert(M1i2 == new StaticModInt <FFTMod2>(Mod1).Inv().Value); Debug.Assert(M12i3 == new StaticModInt <FFTMod3>(Mod1 * Mod2).Inv().Value); var c1 = MathLib.Convolution <FFTMod1>(la, lb); var c2 = MathLib.Convolution <FFTMod2>(la, lb); var c3 = MathLib.Convolution <FFTMod3>(la, lb); var c = new uint[n + m - 1]; for (int i = 0; i < c.Length; i++) { var v1 = ((c2[i] - c1[i]) * M1i2) % Mod2; if (v1 < 0) { v1 += Mod2; } var v2 = (c3[i] - (c1[i] + Mod1 * v1) % Mod3) * M12i3 % Mod3; if (v2 < 0) { v2 += Mod3; } var x = (c1[i] + Mod1 * v1 + M12i * v2) % mod; if (x < 0) { x += mod; } c[i] = (uint)x; } return(c); } }