public static ModInt[,] NumberTheoreticTransform2D( ModInt[,] a, bool inverses = false, bool extends = false, bool inplaces = true) { int h = a.GetLength(0); int w = a.GetLength(1); if (extends) { h = CeilPow2(h); w = CeilPow2(w); inplaces = false; } var ret = a; if (inplaces == false) { ret = new ModInt[h, w]; int hh = a.GetLength(0); int ww = a.GetLength(1); for (int i = 0; i < hh; i++) { for (int j = 0; j < ww; j++) { ret[i, j] = a[i, j]; } } } if (h == 1 && w == 1) { return(ret); } var b = new ModInt[h, w]; { int n = w; int r = inverses ? (int)(ModInt.P - 1 - (ModInt.P - 1) / n) : (int)((ModInt.P - 1) / n); ModInt s = ModInt.Pow(ModInt.ROOT, r); var kp = new ModInt[n / 2 + 1]; kp.AsSpan().Fill(1); for (int i = 0; i < n / 2; ++i) { kp[i + 1] = kp[i] * s; } for (int y = 0; y < h; ++y) { int l = n / 2; for (int i = 1; i < n; i <<= 1, l >>= 1) { r = 0; for (int j = 0; j < l; ++j, r += i) { s = kp[i * j]; for (int k = 0; k < i; ++k) { var p = ret[y, k + r]; var q = ret[y, k + r + n / 2]; b[y, k + 2 * r] = p + q; b[y, k + 2 * r + i] = (p - q) * s; } } var temp = ret; ret = b; b = temp; } if (inverses) { s = ModInt.Inverse(n); for (int i = 0; i < n; ++i) { ret[y, i] = ret[y, i] * s; } } } } for (int i = 0; i < h; ++i) { for (int j = 0; j < w; ++j) { b[i, j] = 0; } } { int n = h; int r = inverses ? (int)(ModInt.P - 1 - (ModInt.P - 1) / n) : (int)((ModInt.P - 1) / n); ModInt s = ModInt.Pow(ModInt.ROOT, r); var kp = new ModInt[n / 2 + 1]; kp.AsSpan().Fill(1); for (int i = 0; i < n / 2; ++i) { kp[i + 1] = kp[i] * s; } for (int x = 0; x < w; ++x) { int l = n / 2; for (int i = 1; i < n; i <<= 1, l >>= 1) { r = 0; for (int j = 0; j < l; ++j, r += i) { s = kp[i * j]; for (int k = 0; k < i; ++k) { var p = ret[k + r, x]; var q = ret[k + r + n / 2, x]; b[k + 2 * r, x] = p + q; b[k + 2 * r + i, x] = (p - q) * s; } } var temp = ret; ret = b; b = temp; } if (inverses) { s = ModInt.Inverse(n); for (int i = 0; i < n; ++i) { ret[i, x] = ret[i, x] * s; } } } } return(ret); }
public static Span <ModInt> NumberTheoreticTransform( Span <ModInt> a, bool inverses = false, bool extends = false, bool inplaces = true) { int n = a.Length; if (extends) { n = CeilPow2(n); inplaces = false; } var ret = a; if (inplaces == false) { ret = new ModInt[n]; a.CopyTo(ret); } if (n == 1) { return(ret); } var b = new ModInt[n].AsSpan(); int r = inverses ? (int)(ModInt.P - 1 - (ModInt.P - 1) / n) : (int)((ModInt.P - 1) / n); ModInt s = ModInt.Pow(ModInt.ROOT, r); var kp = new ModInt[n / 2 + 1]; kp.AsSpan().Fill(1); for (int i = 0; i < n / 2; ++i) { kp[i + 1] = kp[i] * s; } int l = n / 2; for (int i = 1; i < n; i <<= 1, l >>= 1) { r = 0; for (int j = 0; j < l; ++j, r += i) { s = kp[i * j]; for (int k = 0; k < i; ++k) { var p = ret[k + r]; var q = ret[k + r + n / 2]; b[k + 2 * r] = p + q; b[k + 2 * r + i] = (p - q) * s; } } var temp = ret; ret = b; b = temp; } if (inverses) { s = ModInt.Inverse(n); for (int i = 0; i < n; ++i) { ret[i] = ret[i] * s; } } return(ret); }