diff --git a/src/FftSharp.Tests/ExperimentalTests.cs b/src/FftSharp.Tests/ExperimentalTests.cs
index 124ab2a..244cfcd 100644
--- a/src/FftSharp.Tests/ExperimentalTests.cs
+++ b/src/FftSharp.Tests/ExperimentalTests.cs
@@ -25,4 +25,24 @@ public void Test_Experimental_DFT()
Assert.AreEqual(expectedImag[i], result[i].Imaginary, 1e-5);
}
}
+
+ [Test]
+ [System.Obsolete]
+ public void Test_Experimental_Bluestein()
+ {
+ double[] prime = { 1, 2, 3, 4, 5, 6, 7 };
+ System.Numerics.Complex[] result = FftSharp.Experimental.Bluestein(prime);
+
+ // tested with python
+ double[] expectedReal = { 28.00000, -3.50000, -3.50000, -3.50000, -3.50000, -3.50000, -3.50000 };
+ double[] expectedImag = { -0.00000, 7.26782, 2.79116, 0.79885, -0.79885, -2.79116, -7.26782 };
+
+ Assert.AreEqual(expectedReal.Length, result.Length);
+
+ for (int i = 0; i < result.Length; i++)
+ {
+ Assert.AreEqual(expectedReal[i], result[i].Real, 1e-5);
+ Assert.AreEqual(expectedImag[i], result[i].Imaginary, 1e-5);
+ }
+ }
}
diff --git a/src/FftSharp/BluesteinOperations.cs b/src/FftSharp/BluesteinOperations.cs
new file mode 100644
index 0000000..a3b5b88
--- /dev/null
+++ b/src/FftSharp/BluesteinOperations.cs
@@ -0,0 +1,145 @@
+using System;
+
+namespace FftSharp;
+
+internal static class BluesteinOperations
+{
+ /*
+ * Computes the discrete Fourier transform (DFT) or inverse transform of the given complex vector, storing the result back into the vector.
+ * The vector can have any length. This is a wrapper function. The inverse transform does not perform scaling, so it is not a true inverse.
+ */
+ public static void Transform(System.Numerics.Complex[] vec, bool inverse)
+ {
+ int n = vec.Length;
+ if (n == 0)
+ return;
+ else if ((n & (n - 1)) == 0) // Is power of 2
+ TransformRadix2(vec, inverse);
+ else // More complicated algorithm for arbitrary sizes
+ TransformBluestein(vec, inverse);
+ }
+
+
+ /*
+ * Computes the discrete Fourier transform (DFT) of the given complex vector, storing the result back into the vector.
+ * The vector's length must be a power of 2. Uses the Cooley-Tukey decimation-in-time radix-2 algorithm.
+ */
+ public static void TransformRadix2(System.Numerics.Complex[] vec, bool inverse)
+ {
+ // Length variables
+ int n = vec.Length;
+ int levels = 0; // compute levels = floor(log2(n))
+ for (int temp = n; temp > 1; temp >>= 1)
+ levels++;
+ if (1 << levels != n)
+ throw new ArgumentException("Length is not a power of 2");
+
+ // Trigonometric table
+ System.Numerics.Complex[] expTable = new System.Numerics.Complex[n / 2];
+ double coef = 2 * Math.PI / n * (inverse ? 1 : -1);
+ for (int i = 0; i < n / 2; i++)
+ expTable[i] = System.Numerics.Complex.FromPolarCoordinates(1, i * coef);
+
+ // Bit-reversed addressing permutation
+ for (int i = 0; i < n; i++)
+ {
+ int j = ReverseBits(i, levels);
+ if (j > i)
+ {
+ System.Numerics.Complex temp = vec[i];
+ vec[i] = vec[j];
+ vec[j] = temp;
+ }
+ }
+
+ // Cooley-Tukey decimation-in-time radix-2 FFT
+ for (int size = 2; size <= n; size *= 2)
+ {
+ int halfsize = size / 2;
+ int tablestep = n / size;
+ for (int i = 0; i < n; i += size)
+ {
+ for (int j = i, k = 0; j < i + halfsize; j++, k += tablestep)
+ {
+ System.Numerics.Complex temp = vec[j + halfsize] * expTable[k];
+ vec[j + halfsize] = vec[j] - temp;
+ vec[j] += temp;
+ }
+ }
+ if (size == n) // Prevent overflow in 'size *= 2'
+ break;
+ }
+ }
+
+
+ /*
+ * Computes the discrete Fourier transform (DFT) of the given complex vector, storing the result back into the vector.
+ * The vector can have any length. This requires the convolution function, which in turn requires the radix-2 FFT function.
+ * Uses Bluestein's chirp z-transform algorithm.
+ */
+ public static void TransformBluestein(System.Numerics.Complex[] vec, bool inverse)
+ {
+ // Find a power-of-2 convolution length m such that m >= n * 2 + 1
+ int n = vec.Length;
+ if (n >= 0x20000000)
+ throw new ArgumentException("Array too large");
+ int m = 1;
+ while (m < n * 2 + 1)
+ m *= 2;
+
+ // Trigonometric table
+ System.Numerics.Complex[] expTable = new System.Numerics.Complex[n];
+ double coef = Math.PI / n * (inverse ? 1 : -1);
+ for (int i = 0; i < n; i++)
+ {
+ int j = (int)((long)i * i % (n * 2)); // This is more accurate than j = i * i
+ expTable[i] = System.Numerics.Complex.Exp(new System.Numerics.Complex(0, j * coef));
+ }
+
+ // Temporary vectors and preprocessing
+ System.Numerics.Complex[] avec = new System.Numerics.Complex[m];
+ for (int i = 0; i < n; i++)
+ avec[i] = vec[i] * expTable[i];
+ System.Numerics.Complex[] bvec = new System.Numerics.Complex[m];
+ bvec[0] = expTable[0];
+ for (int i = 1; i < n; i++)
+ bvec[i] = bvec[m - i] = System.Numerics.Complex.Conjugate(expTable[i]);
+
+ // Convolution
+ System.Numerics.Complex[] cvec = new System.Numerics.Complex[m];
+ Convolve(avec, bvec, cvec);
+
+ // Postprocessing
+ for (int i = 0; i < n; i++)
+ vec[i] = cvec[i] * expTable[i];
+ }
+
+
+ /*
+ * Computes the circular convolution of the given complex vectors. Each vector's length must be the same.
+ */
+ public static void Convolve(System.Numerics.Complex[] xvec, System.Numerics.Complex[] yvec, System.Numerics.Complex[] outvec)
+ {
+ int n = xvec.Length;
+ if (n != yvec.Length || n != outvec.Length)
+ throw new ArgumentException("Mismatched lengths");
+ xvec = (System.Numerics.Complex[])xvec.Clone();
+ yvec = (System.Numerics.Complex[])yvec.Clone();
+ Transform(xvec, false);
+ Transform(yvec, false);
+ for (int i = 0; i < n; i++)
+ xvec[i] *= yvec[i];
+ Transform(xvec, true);
+ for (int i = 0; i < n; i++) // Scaling (because this FFT implementation omits it)
+ outvec[i] = xvec[i] / n;
+ }
+
+
+ private static int ReverseBits(int val, int width)
+ {
+ int result = 0;
+ for (int i = 0; i < width; i++, val >>= 1)
+ result = (result << 1) | (val & 1);
+ return result;
+ }
+}
diff --git a/src/FftSharp/Experimental.cs b/src/FftSharp/Experimental.cs
index 68c050c..4dcc110 100644
--- a/src/FftSharp/Experimental.cs
+++ b/src/FftSharp/Experimental.cs
@@ -70,5 +70,34 @@ public static System.Numerics.Complex[] DFT(double[] input, bool inverse = false
return DFT(inputComplex, inverse);
}
+
+ ///
+ /// Computes the discrete Fourier transform (DFT) of the given array.
+ /// The array may have any length.
+ /// Uses Bluestein's chirp z-transform algorithm.
+ ///
+ public static System.Numerics.Complex[] Bluestein(double[] real, bool inverse = false)
+ {
+ System.Numerics.Complex[] buffer = new System.Numerics.Complex[real.Length];
+
+ for (int i = 0; i < real.Length; i++)
+ {
+ buffer[i] = new System.Numerics.Complex(real[i], 0);
+ }
+
+ Bluestein(buffer, inverse);
+
+ return buffer;
+ }
+
+ ///
+ /// Computes the discrete Fourier transform (DFT) of the given complex vector in place.
+ /// The vector can have any length.
+ /// Uses Bluestein's chirp z-transform algorithm.
+ ///
+ public static void Bluestein(System.Numerics.Complex[] buffer, bool inverse = false)
+ {
+ BluesteinOperations.TransformBluestein(buffer, inverse);
+ }
}
}