/// <summary>
        /// Generates a new encryption key pair
        /// </summary>
        ///
        /// <param name="RngF">The random number generator to use for generating the secret polynomial f</param>
        /// <param name="RngG">The random number generator to use for generating the secret polynomial g</param>
        ///
        /// <returns>A key pair</returns>
        private IAsymmetricKeyPair GenerateKeyPair(IRandom RngF, IRandom RngG)
        {
            int  N      = _encParams.N;
            int  q      = _encParams.Q;
            bool fastFp = _encParams.FastFp;
            bool sparse = _encParams.Sparse;
            TernaryPolynomialType polyType = _encParams.PolyType;
            IPolynomial           t        = null;
            IntegerPolynomial     fq       = null;
            IntegerPolynomial     fp       = null;
            IntegerPolynomial     g        = null;

            if (ParallelUtils.IsParallel && _isParallel)
            {
                Action[] gA = new Action[] {
                    new Action(() => g = GenerateG(RngG)),
                    new Action(() => GenerateFQ(RngF, out t, out fq, out fp))
                };
                Parallel.Invoke(gA);
            }
            else
            {
                // Choose a random g that is invertible mod q.
                g = GenerateG(RngG);
                // choose a random f that is invertible mod 3 and q
                GenerateFQ(RngF, out t, out fq, out fp);
            }

            // if fastFp=true, fp=1
            if (fastFp)
            {
                fp           = new IntegerPolynomial(N);
                fp.Coeffs[0] = 1;
            }

            IntegerPolynomial h = g.Multiply(fq, q);

            h.Mult3(q);
            h.EnsurePositive(q);

            NTRUPrivateKey priv = new NTRUPrivateKey(t, fp, N, q, sparse, fastFp, polyType);
            NTRUPublicKey  pub  = new NTRUPublicKey(h, N, q);

            return(new NTRUKeyPair(pub, priv));
        }