/// <summary>
        /// 注:compensationが指定しないとき、結果をNで割りません。
        /// </summary>
        /// <param name="aFrom">時間ドメイン値x[n] 0≦n<N</param>
        /// <param name="compensation">倍率。指定しないとき1.0f倍となる。</param>
        /// <returns>周波数ドメイン値X^m(q)</returns>
        public WWComplexF[] ForwardFft(WWComplexF[] aFrom, float?compensation = null)
        {
            if (aFrom == null || aFrom.Length != mNumPoints)
            {
                throw new ArgumentOutOfRangeException("aFrom");
            }
            var aTo = new WWComplexF[aFrom.Length];

            var aTmp0 = new WWComplexF[mNumPoints];

            for (int i = 0; i < aTmp0.Length; ++i)
            {
                aTmp0[i] = aFrom[mBitReversalTable[i]];
            }
            var aTmp1 = new WWComplexF[mNumPoints];

            for (int i = 0; i < aTmp1.Length; ++i)
            {
                aTmp1[i] = WWComplexF.Zero();
            }

            var aTmps = new WWComplexF[2][];

            aTmps[0] = aTmp0;
            aTmps[1] = aTmp1;

            for (int i = 0; i < mNumStage - 1; ++i)
            {
                FftStageN(i, aTmps[((i & 1) == 1) ? 1 : 0], aTmps[((i & 1) == 0) ? 1 : 0]);
            }
            FftStageN(mNumStage - 1, aTmps[(((mNumStage - 1) & 1) == 1) ? 1 : 0], aTo);

            float c = 1.0f;

            if (compensation != null)
            {
                c = (float)compensation;
            }

            if (c != 1.0f)
            {
                var aToC = new WWComplexF[aTo.Length];
                for (int i = 0; i < aTo.Length; ++i)
                {
                    aToC[i] = new WWComplexF(aTo[i].real * c, aTo[i].imaginary * c);
                }
                return(aToC);
            }
            else
            {
                return(aTo);
            }
        }
        private void FftStageN(int stageNr, WWComplexF[] x, WWComplexF[] y)
        {
            /*
             * stage0: 2つの入力データにバタフライ演算 (length=8の時) 4回 (nRepeat=4, nSubRepeat=2)
             * y[0] = x[0] + w_n^(0*4) * x[1]
             * y[1] = x[0] + w_n^(1*4) * x[1]
             *
             * y[2] = x[2] + w_n^(0*4) * x[3]
             * y[3] = x[2] + w_n^(1*4) * x[3]
             *
             * y[4] = x[4] + w_n^(0*4) * x[5]
             * y[5] = x[4] + w_n^(1*4) * x[5]
             *
             * y[6] = x[6] + w_n^(0*4) * x[7]
             * y[7] = x[6] + w_n^(1*4) * x[7]
             */

            /*
             * stage1: 4つの入力データにバタフライ演算 (length=8の時) 2回 (nRepeat=2, nSubRepeat=4)
             * y[0] = x[0] + w_n^(0*2) * x[2]
             * y[1] = x[1] + w_n^(1*2) * x[3]
             * y[2] = x[0] + w_n^(2*2) * x[2]
             * y[3] = x[1] + w_n^(3*2) * x[3]
             *
             * y[4] = x[4] + w_n^(0*2) * x[6]
             * y[5] = x[5] + w_n^(1*2) * x[7]
             * y[6] = x[4] + w_n^(2*2) * x[6]
             * y[7] = x[5] + w_n^(3*2) * x[7]
             */

            /*
             * stage2: 8つの入力データにバタフライ演算 (length=8の時) 1回 (nRepeat=1, nSubRepeat=8)
             * y[0] = x[0] + w_n^(0*1) * x[4]
             * y[1] = x[1] + w_n^(1*1) * x[5]
             * y[2] = x[2] + w_n^(2*1) * x[6]
             * y[3] = x[3] + w_n^(3*1) * x[7]
             * y[4] = x[0] + w_n^(4*1) * x[4]
             * y[5] = x[1] + w_n^(5*1) * x[5]
             * y[6] = x[2] + w_n^(6*1) * x[6]
             * y[7] = x[3] + w_n^(7*1) * x[7]
             */

            /*
             * stageN:
             */

            int nRepeat    = Pow2(mNumStage - stageNr - 1);
            int nSubRepeat = mNumPoints / nRepeat;
            var t          = WWComplexF.Zero();

            for (int i = 0; i < nRepeat; ++i)
            {
                int offsBase = i * nSubRepeat;

                bool allZero = true;
                for (int j = 0; j < nSubRepeat / 2; ++j)
                {
                    int offs = offsBase + (j % (nSubRepeat / 2));
                    if (Double.Epsilon < x[offs].Magnitude())
                    {
                        allZero = false;
                        break;
                    }
                    if (Double.Epsilon < x[offs + nSubRepeat / 2].Magnitude())
                    {
                        allZero = false;
                        break;
                    }
                }

                if (allZero)
                {
                    for (int j = 0; j < nSubRepeat; ++j)
                    {
                        y[j + offsBase] = WWComplexF.Zero();
                    }
                }
                else
                {
                    for (int j = 0; j < nSubRepeat; ++j)
                    {
                        int offs = offsBase + (j % (nSubRepeat / 2));

                        var v1 = x[offs];
                        var v2 = WWComplexF.Mul(mWn[j * nRepeat], x[offs + nSubRepeat / 2]);
                        y[j + offsBase] = WWComplexF.Add(v1, v2);
                    }
                }
            }
        }