/// <summary>
        /// Initializes a new instance of the Fourier transformer with the given sign and normalization conventions.
        /// </summary>
        /// <param name="size">The series length of the transformer, which must be positive.</param>
        /// <param name="signConvention">The sign convention of the transformer.</param>
        /// <param name="normalizationConvention">The normalization convention of the transformer.</param>
        /// <remarks>
        /// <para>There are multiple conventions for both the sign of the exponent and the overall normalization of
        /// Fourier transforms. The default conventions for some widely used software packages are summarized in the following
        /// table.</para>
        /// <table>
        ///     <tr><th>Software</th><th>Sign</th><th>Normalization</th></tr>
        ///     <tr><td>Meta.Numerics</td><td><see cref="FourierSign.Negative"/></td><td><see cref="FourierNormalization.None"/></td></tr>
        ///     <tr><td>Matlab</td><td><see cref="FourierSign.Negative"/></td><td><see cref="FourierNormalization.None"/></td></tr>
        ///     <tr><td>Mathmatica</td><td><see cref="FourierSign.Positive"/></td><td><see cref="FourierNormalization.Unitary"/></td></tr>
        ///     <tr><td>Numerical Recipies</td><td><see cref="FourierSign.Positive"/></td><td><see cref="FourierNormalization.None"/></td></tr>
        /// </table>
        /// </remarks>
        public FourierTransformer(int size, FourierSign signConvention, FourierNormalization normalizationConvention)
        {
            if (size < 1)
            {
                throw new ArgumentOutOfRangeException("size");
            }

            this.size                    = size;
            this.signConvention          = signConvention;
            this.normalizationConvention = normalizationConvention;

            // pre-compute the Nth complex roots of unity
            this.roots = FourierAlgorithms.ComputeRoots(size, +1);

            // decompose the size into prime factors
            this.factors = AdvancedIntegerMath.Factor(size);

            // store a plan for the transform based on the prime factorization
            plan = new List <Transformlet>();
            foreach (Element factor in factors)
            {
                Transformlet t;
                switch (factor.Value)
                {
                // use a radix-specialized transformlet when available
                case 2:
                    t = new RadixTwoTransformlet(size, roots);
                    break;

                case 3:
                    t = new RadixThreeTransformlet(size, roots);
                    break;

                // eventually, we should make an optimized radix-4 transform
                case 5:
                    t = new RadixFiveTransformlet(size, roots);
                    break;

                case 7:
                    t = new RadixSevenTransformlet(size, roots);
                    break;

                case 11:
                case 13:
                    // the base transformlet is R^2, but when R is small, this can still be faster than the Bluestein algorithm
                    // timing measurements appear to indicate that this is the case for radix 11 and 13
                    // eventually, we should make optimized Winograd transformlets for these factors
                    t = new Transformlet(factor.Value, size, roots);
                    break;

                default:
                    // for large factors with no available specialized transformlet, use the Bluestein algorithm
                    t = new BluesteinTransformlet(factor.Value, size, roots);
                    break;
                }

                t.Multiplicity = factor.Multiplicity;

                /*
                 * if ((factor.Value == 2) && (factor.Multiplicity % 2 == 0)) {
                 *  t = new RadixFourTransformlet(size, roots);
                 *  t.Multiplicity = factor.Multiplicity / 2;
                 * }
                 */

                plan.Add(t);
            }
        }
        /// <summary>
        /// Initializes a new instance of the Fourier transformer with the given sign and normalization conventions.
        /// </summary>
        /// <param name="size">The series length of the transformer, which must be positive.</param>
        /// <param name="signConvention">The sign convention of the transformer.</param>
        /// <param name="normalizationConvention">The normalization convention of the transformer.</param>
        /// <remarks>
        /// <para>There are multiple conventions for both the sign of the exponent and the overall normalization of
        /// Fourier transforms. The default conventions for some widely used software packages are summarized in the following
        /// table.</para>
        /// <table>
        ///     <tr><th>Software</th><th>Sign</th><th>Normalization</th></tr>
        ///     <tr><td>Meta.Numerics</td><td><see cref="FourierSign.Negative"/></td><td><see cref="FourierNormalization.None"/></td></tr>
        ///     <tr><td>Matlab</td><td><see cref="FourierSign.Negative"/></td><td><see cref="FourierNormalization.None"/></td></tr>
        ///     <tr><td>Mathmatica</td><td><see cref="FourierSign.Positive"/></td><td><see cref="FourierNormalization.Unitary"/></td></tr>
        ///     <tr><td>Numerical Recipies</td><td><see cref="FourierSign.Positive"/></td><td><see cref="FourierNormalization.None"/></td></tr>
        /// </table>
        /// </remarks>
        public FourierTransformer(int size, FourierSign signConvention, FourierNormalization normalizationConvention)
        {
            if (size < 1) throw new ArgumentOutOfRangeException("size");

            this.size = size;
            this.signConvention = signConvention;
            this.normalizationConvention = normalizationConvention;

            // pre-compute the Nth complex roots of unity
            this.roots = FourierAlgorithms.ComputeRoots(size, +1);

            // decompose the size into prime factors
            this.factors = AdvancedIntegerMath.Factor(size);

            // store a plan for the transform based on the prime factorization
            plan = new List<Transformlet>();
            foreach (Element factor in factors) {

                Transformlet t;
                switch (factor.Value) {
                    // use a radix-specialized transformlet when available
                    case 2:
                        t = new RadixTwoTransformlet(size, roots);
                        break;
                    case 3:
                        t = new RadixThreeTransformlet(size, roots);
                        break;
                    // eventually, we should make an optimized radix-4 transform
                    case 5:
                        t = new RadixFiveTransformlet(size, roots);
                        break;
                    case 7:
                        t = new RadixSevenTransformlet(size, roots);
                        break;
                    case 11:
                    case 13:
                        // the base transformlet is R^2, but when R is small, this can still be faster than the Bluestein algorithm
                        // timing measurements appear to indicate that this is the case for radix 11 and 13
                        // eventually, we should make optimized Winograd transformlets for these factors
                        t = new Transformlet(factor.Value, size, roots);
                        break;
                    default:
                        // for large factors with no available specialized transformlet, use the Bluestein algorithm
                        t = new BluesteinTransformlet(factor.Value, size, roots);
                        break;
                }

                t.Multiplicity = factor.Multiplicity;

                /*
                if ((factor.Value == 2) && (factor.Multiplicity % 2 == 0)) {
                    t = new RadixFourTransformlet(size, roots);
                    t.Multiplicity = factor.Multiplicity / 2;
                }
                */

                plan.Add(t);

            }
        }