/// <summary>
        /// Computes the inverse mod 3.
        /// <para>Returns <c>null</c> if the polynomial is not invertible.
        /// The algorithm is described in <a href="http://www.securityinnovation.com/uploads/Crypto/NTRUTech014.pdf">
        /// Almost Inverses and Fast NTRU Key Generation</a>.</para>
        /// </summary>
        /// 
        /// <returns>A new polynomial, or <c>null</c> if no inverse exists</returns>
        public IntegerPolynomial InvertF3()
        {
            int N = Coeffs.Length;
            int k = 0;
            IntegerPolynomial b = new IntegerPolynomial(N + 1);
            b.Coeffs[0] = 1;

            IntegerPolynomial c = new IntegerPolynomial(N + 1);
            IntegerPolynomial f = new IntegerPolynomial(N + 1);
            f.Coeffs = Coeffs.CopyOf(N + 1);
            f.ModPositive(3);

            // set g(x) = x^N − 1
            IntegerPolynomial g = new IntegerPolynomial(N + 1);
            g.Coeffs[0] = -1;
            g.Coeffs[N] = 1;

            while (true)
            {
                while (f.Coeffs[0] == 0)
                {
                    for (int i = 1; i <= N; i++)
                    {
                        f.Coeffs[i - 1] = f.Coeffs[i];   // f(x) = f(x) / x
                        c.Coeffs[N + 1 - i] = c.Coeffs[N - i];   // c(x) = c(x) * x
                    }

                    f.Coeffs[N] = 0;
                    c.Coeffs[0] = 0;
                    k++;

                    if (f.EqualsZero())
                        return null;   // not invertible
                }

                if (f.EqualsAbsOne())
                    break;

                if (f.Degree() < g.Degree())
                {
                    // exchange f and g
                    IntegerPolynomial temp = f;
                    f = g;
                    g = temp;
                    // exchange b and c
                    temp = b;
                    b = c;
                    c = temp;
                }

                if (f.Coeffs[0] == g.Coeffs[0])
                {
                    f.Subtract(g, 3);
                    b.Subtract(c, 3);
                }
                else
                {
                    f.Add(g, 3);
                    b.Add(c, 3);
                }
            }

            if (b.Coeffs[N] != 0)
                return null;

            // Fp(x) = [+-] x^(N-k) * b(x)
            IntegerPolynomial Fp = new IntegerPolynomial(N);
            int j = 0;
            k %= N;

            for (int i = N - 1; i >= 0; i--)
            {
                j = i - k;
                if (j < 0)
                    j += N;
                Fp.Coeffs[j] = f.Coeffs[0] * b.Coeffs[i];
            }

            Fp.EnsurePositive(3);

            return Fp;
        }
        /// <summary>
        /// Resultant of this polynomial with <c>x^n-1 mod p</c>.
        /// </summary>
        /// 
        /// <param name="P">P value</param>
        /// 
        /// <returns>Returns <c>(rho, res)</c> satisfying <c>res = rho*this + t*(x^n-1) mod p</c> for some integer <c>t</c>.</returns>
        public ModularResultant Resultant(int P)
        {
            // Add a coefficient as the following operations involve polynomials of degree deg(f)+1
            int[] fcoeffs = Coeffs.CopyOf(Coeffs.Length + 1);
            IntegerPolynomial f = new IntegerPolynomial(fcoeffs);
            int N = fcoeffs.Length;

            IntegerPolynomial a = new IntegerPolynomial(N);
            a.Coeffs[0] = -1;
            a.Coeffs[N - 1] = 1;
            IntegerPolynomial b = new IntegerPolynomial(f.Coeffs);
            IntegerPolynomial v1 = new IntegerPolynomial(N);
            IntegerPolynomial v2 = new IntegerPolynomial(N);
            v2.Coeffs[0] = 1;
            int da = N - 1;
            int db = b.Degree();
            int ta = da;
            int c = 0;
            int r = 1;

            while (db > 0)
            {
                c = Invert(b.Coeffs[db], P);
                c = (c * a.Coeffs[da]) % P;
                a.MultShiftSub(b, c, da - db, P);
                v1.MultShiftSub(v2, c, da - db, P);
                da = a.Degree();

                if (da < db)
                {
                    r *= Pow(b.Coeffs[db], ta - da, P);
                    r %= P;

                    if (ta % 2 == 1 && db % 2 == 1)
                        r = (-r) % P;

                    IntegerPolynomial temp = a;
                    a = b;
                    b = temp;
                    int tempdeg = da;
                    da = db;
                    temp = v1;
                    v1 = v2;
                    v2 = temp;
                    ta = db;
                    db = tempdeg;
                }
            }

            r *= Pow(b.Coeffs[0], da, P);
            r %= P;
            c = Invert(b.Coeffs[0], P);
            v2.Multiply(c);
            v2.Mod(P);
            v2.Multiply(r);
            v2.Mod(P);

            // drop the highest coefficient so #coeffs matches the original input
            v2.Coeffs = v2.Coeffs.CopyOf(v2.Coeffs.Length - 1);
            return new ModularResultant(new BigIntPolynomial(v2), BigInteger.ValueOf(r), BigInteger.ValueOf(P));
        }
        /// <summary>
        /// Computes the inverse mod 2. 
        /// <para>The algorithm is described in <a href="http://www.securityinnovation.com/uploads/Crypto/NTRUTech014.pdf">
        /// Almost Inverses and Fast NTRU Key Generation</a>.</para>
        /// </summary>
        /// 
        /// <returns>Returns <c>null</c> if the polynomial is not invertible.</returns>
        private IntegerPolynomial InvertF2()
        {
            int N = Coeffs.Length;
            int k = 0;
            IntegerPolynomial b = new IntegerPolynomial(N + 1);
            b.Coeffs[0] = 1;

            IntegerPolynomial c = new IntegerPolynomial(N + 1);
            IntegerPolynomial f = new IntegerPolynomial(Coeffs.CopyOf(N + 1));
            f.Mod2();

            // set g(x) = x^N − 1
            IntegerPolynomial g = new IntegerPolynomial(N + 1);
            g.Coeffs[0] = 1;
            g.Coeffs[N] = 1;

            while (true)
            {
                while (f.Coeffs[0] == 0)
                {
                    for (int i = 1; i <= N; i++)
                    {
                        f.Coeffs[i - 1] = f.Coeffs[i];          // f(x) = f(x) / x
                        c.Coeffs[N + 1 - i] = c.Coeffs[N - i];  // c(x) = c(x) * x
                    }

                    f.Coeffs[N] = 0;
                    c.Coeffs[0] = 0;
                    k++;

                    if (f.EqualsZero())
                        return null;    // not invertible
                }

                if (f.EqualsOne())
                    break;

                if (f.Degree() < g.Degree())
                {
                    // exchange f and g
                    IntegerPolynomial temp = f;
                    f = g;
                    g = temp;
                    // exchange b and c
                    temp = b;
                    b = c;
                    c = temp;
                }

                f.Add(g);
                f.Mod2();
                b.Add(c);
                b.Mod2();
            }

            if (b.Coeffs[N] != 0)
                return null;

            // Fq(x) = x^(N-k) * b(x)
            IntegerPolynomial Fq = new IntegerPolynomial(N);
            int j = 0;
            k %= N;

            for (int i = N - 1; i >= 0; i--)
            {
                j = i - k;
                if (j < 0)
                    j += N;
                Fq.Coeffs[j] = b.Coeffs[i];
            }

            return Fq;
        }