/// <summary>
        /// This function computes the the multiplication of the transpose of the trajectory matrix H by an arbitrary vector v, i.e. H' * v.
        /// Since the trajectory matrix is a Hankel matrix, using the Discrete Fourier Transform,
        /// the multiplication is carried out in O(N.log(N)) instead of O(N^2), wheere N is the series length.
        /// For details, refer to Algorithm 3 in http://arxiv.org/pdf/0911.4498.pdf.
        /// </summary>
        /// <param name="vector">The input vector</param>
        /// <param name="result">The output vector allocated by the caller</param>
        /// <param name="add">Whether the multiplication result should be added to the current value in result</param>
        /// <param name="srcIndex">The starting index for the vector argument</param>
        /// <param name="dstIndex">The starting index for the result</param>
        private void FftMultiplyTranspose(Single[] vector, Single[] result, bool add = false, int srcIndex = 0, int dstIndex = 0)
        {
            _ectx.Assert(srcIndex >= 0);
            _ectx.Assert(dstIndex >= 0);
            _ectx.Assert(Utils.Size(vector) >= _windowSize + srcIndex);
            _ectx.Assert(Utils.Size(result) >= _k + dstIndex);

            int i;

            // Computing the FFT of the trajectory matrix
            if (!_isSeriesFftCached)
            {
                CacheInputSeriesFft();
            }

            // Computing the FFT of the input vector
            for (i = 0; i < _k - 1; ++i)
            {
                _inputRe[i] = 0;
            }

            for (i = _k - 1; i < _seriesLength; ++i)
            {
                _inputRe[i] = vector[_seriesLength - i - 1 + srcIndex];
            }

            FftUtils.ComputeForwardFft(_inputRe, _allZerosIm, _outputRe, _outputIm, _inputRe.Length);

            // Computing the element-by-element product in the Fourier space
            double re;
            double im;

            for (i = 0; i < _seriesLength; ++i)
            {
                re = _outputRe[i];
                im = _outputIm[i];

                _outputRe[i] = _cachedSeriesFftRe[i] * re - _cachedSeriesFftIm[i] * im;
                _outputIm[i] = _cachedSeriesFftRe[i] * im + _cachedSeriesFftIm[i] * re;
            }

            // Computing the inverse FFT of the result
            FftUtils.ComputeBackwardFft(_outputRe, _outputIm, _outputRe, _outputIm, _inputRe.Length);

            // Generating the output
            if (add)
            {
                for (i = 0; i < _k; ++i)
                {
                    result[i + dstIndex] += RoundUpToReal(_outputRe[_windowSize - 1 + i], _outputIm[_windowSize - 1 + i]);
                }
            }
            else
            {
                for (i = 0; i < _k; ++i)
                {
                    result[i + dstIndex] = RoundUpToReal(_outputRe[_windowSize - 1 + i], _outputIm[_windowSize - 1 + i]);
                }
            }
        }
        /// <summary>
        /// Sets the value of the underlying series to new values.
        /// </summary>
        /// <param name="data">The new series</param>
        public void SetSeries(Single[] data)
        {
            _ectx.Check(Utils.Size(data) >= _seriesLength, "The length of the input series cannot be less than that of the original series.");

            _data = data;
            if (_isSeriesFftCached)
            {
                int i;

                for (i = _k - 1; i < _seriesLength; ++i)
                {
                    _inputRe[i - _k + 1] = _data[i];
                }

                for (i = 0; i < _k - 1; ++i)
                {
                    _inputRe[_windowSize + i] = _data[i];
                }

                FftUtils.ComputeForwardFft(_inputRe, _allZerosIm, _cachedSeriesFftRe, _cachedSeriesFftIm, _inputRe.Length);
            }
        }
        private void CacheInputSeriesFft()
        {
            int i;

            _cachedSeriesFftRe = new Double[_seriesLength];
            _cachedSeriesFftIm = new Double[_seriesLength];
            _allZerosIm        = new Double[_seriesLength];
            _inputRe           = new Double[_seriesLength];
            _outputIm          = new Double[_seriesLength];
            _outputRe          = new Double[_seriesLength];

            for (i = _k - 1; i < _seriesLength; ++i)
            {
                _inputRe[i - _k + 1] = _data[i];
            }

            for (i = 0; i < _k - 1; ++i)
            {
                _inputRe[_windowSize + i] = _data[i];
            }

            FftUtils.ComputeForwardFft(_inputRe, _allZerosIm, _cachedSeriesFftRe, _cachedSeriesFftIm, _inputRe.Length);
            _isSeriesFftCached = true;
        }
        /// <summary>
        /// This function computes the efficient Hankelization of the matrix sigma * u * v' using Fast Fourier Transform in in O((L + K) * log(L + K)).
        /// For details, refer to Algorithm 4 in http://arxiv.org/pdf/0911.4498.pdf.
        /// </summary>
        /// <param name="u">The u vector</param>
        /// <param name="v">The v vector</param>
        /// <param name="sigma">The scalar coefficient</param>
        /// <param name="result">The output series</param>
        /// <param name="add">Whether the hankelization result should be added to the current value in result</param>
        /// <param name="uIndex">The starting index for the u vector argument</param>
        /// <param name="vIndex">The starting index for the v vector argument</param>
        /// <param name="dstIndex">The starting index for the result</param>
        /// <param name="start">The staring index of the series to be reconstructed (by default zero)</param>
        /// <param name="end">The ending index of the series to be reconstructed (by default series length)</param>
        private void FftRankOneHankelization(Single[] u, Single[] v, Single sigma, Single[] result, bool add = false,
                                             int uIndex = 0, int vIndex = 0, int dstIndex = 0, int?start = null, int?end = null)
        {
            int s;
            int e;
            int us;
            int ue;
            int vs;
            int ve;
            int i;

            s = start ?? 0;
            e = end ?? _seriesLength - 1;

            ComputeBoundryIndices(s, e, out us, out ue, out vs, out ve);
            _ectx.Assert(0 <= ue && ue < _windowSize);
            _ectx.Assert(0 <= us && us <= ue);
            _ectx.Assert(0 <= ve && ve < _k);
            _ectx.Assert(0 <= vs && vs <= ve);

            var len = e - s + 1;

            _ectx.Assert(uIndex >= 0);
            _ectx.Assert(vIndex >= 0);
            _ectx.Assert(dstIndex >= 0);
            _ectx.Assert(Utils.Size(u) >= _windowSize + uIndex);
            _ectx.Assert(Utils.Size(v) >= _k + vIndex);
            _ectx.Assert(Utils.Size(result) >= len + dstIndex);
            _ectx.Assert(!Single.IsNaN(sigma));
            _ectx.Assert(!Single.IsInfinity(sigma));

            if (!_isSeriesFftCached)
            {
                CacheInputSeriesFft();
            }

            // Computing the FFT of u
            for (i = us; i <= ue; ++i)
            {
                _inputRe[i - us] = u[i + uIndex];
            }

            for (i = ue + 1; i < len + us; ++i)
            {
                _inputRe[i - us] = 0;
            }

            FftUtils.ComputeForwardFft(_inputRe, _allZerosIm, _outputRe, _outputIm, len);

            // Computing the FFT of v
            for (i = vs; i <= ve; ++i)
            {
                _inputRe[i - vs] = v[i + vIndex];
            }

            for (i = ve + 1; i < len + vs; ++i)
            {
                _inputRe[i - vs] = 0;
            }

            FftUtils.ComputeForwardFft(_inputRe, _allZerosIm, _inputRe, _allZerosIm, len);

            // Computing the element-by-element product in the Fourier space
            double re;
            double im;

            for (i = 0; i < len; ++i)
            {
                re = _outputRe[i];
                im = _outputIm[i];

                _outputRe[i] = _inputRe[i] * re - _allZerosIm[i] * im;
                _outputIm[i] = _inputRe[i] * im + _allZerosIm[i] * re;
            }

            // Setting _allZerosIm to 0's again
            for (i = 0; i < _seriesLength; ++i)
            {
                _allZerosIm[i] = 0;
            }

            // Computing the inverse FFT of the result
            FftUtils.ComputeBackwardFft(_outputRe, _outputIm, _outputRe, _outputIm, len);

            // Generating the output
            int a = Math.Min(ue - us + 1, ve - vs + 1);

            if (add)
            {
                for (i = 0; i < a; ++i)
                {
                    result[i + dstIndex] += RoundUpToReal(_outputRe[i], _outputIm[i], sigma / (i + 1));
                }

                for (i = a; i < len - a + 1; ++i)
                {
                    result[i + dstIndex] += RoundUpToReal(_outputRe[i], _outputIm[i], sigma / a);
                }

                for (i = len - a + 1; i < len; ++i)
                {
                    result[i + dstIndex] += RoundUpToReal(_outputRe[i], _outputIm[i], sigma / (len - i));
                }
            }
            else
            {
                for (i = 0; i < a; ++i)
                {
                    result[i + dstIndex] = RoundUpToReal(_outputRe[i], _outputIm[i], sigma / (i + 1));
                }

                for (i = a; i < len - a + 1; ++i)
                {
                    result[i + dstIndex] = RoundUpToReal(_outputRe[i], _outputIm[i], sigma / a);
                }

                for (i = len - a + 1; i < len; ++i)
                {
                    result[i + dstIndex] = RoundUpToReal(_outputRe[i], _outputIm[i], sigma / (len - i));
                }
            }
        }