Esempio n. 1
0
        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);
            }
        }