public static void FullForwardFFT(this ComplexNumber[] data, RealNumber[] temporaryArray = null) { int N = data.Length; int precisionDigits = 1; for (int i = data.Length; --i >= 0;) { precisionDigits = Math.Max(precisionDigits, data[i].GetPrecisionDigits()); } FFTRealConstants precision = new FFTRealConstants(precisionDigits); List <int> factors = FourierTransform235.GetSlowFactorization(N).OrderBy(x => x).ToList(); ComplexNumber[] roots = null; ComplexNumber[] temp = null; ComplexNumber[] simpleRoots = null; List <int> factorDigits = new List <int>(); List <int> decimalRepresentation = new List <int>(); List <ComplexNumber> incrementalProduct = new List <ComplexNumber>(); int totalProduct = 1; int N1 = N; int count = 1; foreach (int factor in factors) { int N2 = N1; N1 /= factor; totalProduct *= factor; factorDigits.Add(factor); incrementalProduct.Clear(); int partialProduct = totalProduct; for (int i = 0; i < factorDigits.Count; i++) { incrementalProduct.Add(ComplexNumber.FromPolarAngle((precision.PI << 1) / partialProduct)); partialProduct /= factorDigits[i]; } ComplexNumber[] exactProduct = new ComplexNumber[factorDigits.Count - 1]; if (factorDigits.Count > 1) { exactProduct[factorDigits.Count - 2] = incrementalProduct[factorDigits.Count - 2]; for (int i = factorDigits.Count - 2; --i >= 0;) { exactProduct[i] = exactProduct[i + 1] * (incrementalProduct[i] * incrementalProduct[i + 2].Conjugate); } } decimalRepresentation.Clear(); decimalRepresentation.AddRange(Enumerable.Repeat(0, factorDigits.Count - 1)); ComplexNumber exactPower = ComplexNumber.One; for (int i = 0; i < count; i++) { DirectFFTWithStartTwiddle(data, i * N2, N1, N1, factor, precision, ref roots, ref temp, ref simpleRoots, exactPower); getIncrement(ref exactPower, decimalRepresentation, factorDigits, exactProduct); } count = totalProduct; } if (temporaryArray == null || temporaryArray.Length < N * 2) { Array.Resize(ref temporaryArray, N * 2); } for (int i = N; --i >= 0;) { var number = data[i]; temporaryArray[i * 2] = number.Real; temporaryArray[i * 2 + 1] = number.Imaginary; } FourierTransform235.ReversedBaseIterate(factors, (i, reversed) => data[reversed] = new ComplexNumber(temporaryArray[i * 2], temporaryArray[i * 2 + 1])); }
private static void DirectFFTWithStartTwiddle(ComplexNumber[] data, int index, int multiplier, int length, int prime, FFTRealConstants precision, ref ComplexNumber[] roots, ref ComplexNumber[] temp, ref ComplexNumber[] simpleRoots, ComplexNumber requiredPower) { if (roots == null || roots.Length < prime) { Array.Resize(ref roots, prime); } roots[1] = requiredPower; for (int i = 2; i < prime; i++) { roots[i] = roots[i >> 1] * roots[(i + 1) >> 1]; } for (; --length >= 0; index++) { switch (prime) { case 2: { var v0 = data[index]; var v1 = data[index + multiplier] * roots[1]; data[index] = v0 + v1; data[index + multiplier] = v0 - v1; } break; case 3: //w^2 = -1-w { ComplexNumber z0 = data[index], z1 = data[index + multiplier] * roots[1], z2 = data[index + 2 * multiplier] * roots[2]; ComplexNumber t1 = z1 + z2, t2 = z0 - (t1 >> 1), t3 = (z1 - z2).MulI() * precision.M_SQRT_3_4; data[index] = z0 + t1; data[index + multiplier] = t2 + t3; data[index + 2 * multiplier] = t2 - t3; } break; case 5: //http://www2.itap.physik.uni-stuttgart.de/lehre/vorlesungen/SS08/simmeth/fftalgorithms.pdf { ComplexNumber z0 = data[index], z1 = data[index + multiplier] * roots[1], z2 = data[index + 2 * multiplier] * roots[2], z3 = data[index + 3 * multiplier] * roots[3], z4 = data[index + 4 * multiplier] * roots[4]; ComplexNumber t1 = z1 + z4, t2 = z2 + z3, t3 = z1 - z4, t4 = z2 - z3, t5 = t1 + t2, t6 = (t1 - t2) * precision.M_ROOT5_C2, t7 = z0 - (t5 >> 2), t8 = t7 + t6, t9 = t7 - t6, t10 = (t3 * precision.M_ROOT5_C4 + t4 * precision.M_ROOT5_C3).MulI(), t11 = (t3 * precision.M_ROOT5_C3 - t4 * precision.M_ROOT5_C4).MulI(); data[index] = z0 + t5; data[index + multiplier] = t8 + t10; data[index + 2 * multiplier] = t9 + t11; data[index + 3 * multiplier] = t9 - t11; data[index + 4 * multiplier] = t8 - t10; } break; default: if (simpleRoots == null || simpleRoots.Length != prime) { Array.Resize(ref simpleRoots, prime); simpleRoots[1] = ComplexNumber.FromPolarAngle((precision.PI << 1) / prime); for (int i = 2; i < prime; i++) { simpleRoots[i] = simpleRoots[i >> 1] * simpleRoots[(i + 1) >> 1]; } } if (temp == null || temp.Length < prime) { Array.Resize(ref temp, prime); } var sum = temp[0] = data[index]; for (int i = prime; --i > 0;) { temp[i] = data[index + i * multiplier] * roots[i]; sum += temp[i]; } data[index] = sum; for (int i = prime; --i > 0;) { var result = temp[0]; for (int j = prime, k = 0; --j > 0;) { k -= i; k = k < 0 ? k + prime : k; //k = i * j mod prime. result += temp[j] * simpleRoots[k]; } data[index + i * multiplier] = result; } break; } } }