/// <summary>
        /// Converts the given element in representation according to this field to a new element in 
        /// representation according to B1 using the change-of-basis matrix calculated by computeCOBMatrix.
        /// </summary>
        /// 
        /// <param name="Elem">The GF2nElement to convert</param>
        /// <param name="Basis">The basis to convert <c>Elem</c> to</param>
        /// 
        /// <returns>Returns <c>Elem</c> converted to a new element representation according to <c>basis</c></returns>
        public GF2nElement Convert(GF2nElement Elem, GF2nField Basis)
        {
            if (Basis == this)
                return (GF2nElement)Elem.Clone();
            if (FieldPoly.Equals(Basis.FieldPoly))
                return (GF2nElement)Elem.Clone();
            if (DegreeN != Basis.DegreeN)
                throw new Exception("GF2nField.Convert: B1 has a different degree and thus cannot be coverted to!");

            int i;
            GF2Polynomial[] COBMatrix;
            i = Fields.IndexOf(Basis);

            if (i == -1)
            {
                ComputeCOBMatrix(Basis);
                i = Fields.IndexOf(Basis);
            }
            COBMatrix = (GF2Polynomial[])Matrices[i];

            GF2nElement elemCopy = (GF2nElement)Elem.Clone();
            if (elemCopy is GF2nONBElement)
                ((GF2nONBElement)elemCopy).ReverseOrder();

            GF2Polynomial bs = new GF2Polynomial(DegreeN, elemCopy.ToFlexiBigInt());
            bs.ExpandN(DegreeN);
            GF2Polynomial result = new GF2Polynomial(DegreeN);
            for (i = 0; i < DegreeN; i++)
            {
                if (bs.VectorMult(COBMatrix[i]))
                    result.SetBit(DegreeN - 1 - i);
            }

            if (Basis is GF2nPolynomialField)
            {
                return new GF2nPolynomialElement((GF2nPolynomialField)Basis, result);
            }
            else if (Basis is GF2nONBField)
            {
                GF2nONBElement res = new GF2nONBElement((GF2nONBField)Basis, result.ToFlexiBigInt());
                res.ReverseOrder();

                return res;
            }
            else
            {
                throw new Exception("GF2nField.convert: B1 must be an instance of GF2nPolynomialField or GF2nONBField!");
            }
        }
        /// <summary>
        /// Computes the change-of-basis matrix for basis conversion according to 1363.
        /// The result is stored in the lists fields and matrices.
        /// </summary>
        /// 
        /// <param name="B1">The GF2nField to convert to</param>
        public override void ComputeCOBMatrix(GF2nField B1)
        {
            // we are in B0 here!
            if (DegreeN != B1.Degree)
                throw new ArgumentException("GF2nPolynomialField.computeCOBMatrix: B1 has a different degree and thus cannot be coverted to!");

            if (B1 is GF2nONBField)
            {
                // speedup (calculation is done in PolynomialElements instead of ONB)
                B1.ComputeCOBMatrix(this);
                return;
            }

            int i, j;
            GF2nElement[] gamma;
            GF2nElement u;
            GF2Polynomial[] COBMatrix = new GF2Polynomial[DegreeN];

            for (i = 0; i < DegreeN; i++)
                COBMatrix[i] = new GF2Polynomial(DegreeN);

            // find Random Root
            do
            {
                // u is in representation according to B1
                u = B1.RandomRoot(FieldPoly);
            }
            while (u.IsZero());

            // build gamma matrix by multiplying by u
            if (u is GF2nONBElement)
            {
                gamma = new GF2nONBElement[DegreeN];
                gamma[DegreeN - 1] = GF2nONBElement.One((GF2nONBField)B1);
            }
            else
            {
                gamma = new GF2nPolynomialElement[DegreeN];
                gamma[DegreeN - 1] = GF2nPolynomialElement.One((GF2nPolynomialField)B1);
            }
            gamma[DegreeN - 2] = u;
            for (i = DegreeN - 3; i >= 0; i--)
                gamma[i] = (GF2nElement)gamma[i + 1].Multiply(u);

            if (B1 is GF2nONBField)
            {
                // convert horizontal gamma matrix by vertical Bitstrings
                for (i = 0; i < DegreeN; i++)
                {
                    for (j = 0; j < DegreeN; j++)
                    {
                        // TODO remember: ONB treats its Bits in reverse order !!!
                        if (gamma[i].TestBit(DegreeN - j - 1))
                            COBMatrix[DegreeN - j - 1].SetBit(DegreeN - i - 1);
                    }
                }
            }
            else
            {
                // convert horizontal gamma matrix by vertical Bitstrings
                for (i = 0; i < DegreeN; i++)
                {
                    for (j = 0; j < DegreeN; j++)
                    {
                        if (gamma[i].TestBit(j))
                            COBMatrix[DegreeN - j - 1].SetBit(DegreeN - i - 1);
                    }
                }
            }

            // store field and matrix for further use
            Fields.Add(B1);
            Matrices.Add(COBMatrix);
            // store field and inverse matrix for further use in B1
            B1.Fields.Add(this);
            B1.Matrices.Add(InvertMatrix(COBMatrix));
        }
        /// <summary>
        /// Computes a random root of the given polynomial
        /// </summary>
        /// 
        /// <param name="P">A polynomial</param>
        /// 
        /// <returns>A random root of the polynomial</returns>
        public override GF2nElement RandomRoot(GF2Polynomial P)
        {
            // We are in B1!!!
            GF2nPolynomial c;
            GF2nPolynomial ut;
            GF2nElement u;
            GF2nPolynomial h;
            int hDegree;
            // 1. Set g(t) <- f(t)
            GF2nPolynomial g = new GF2nPolynomial(P, this);
            int gDegree = g.Degree;
            int i;

            // 2. while deg(g) > 1
            while (gDegree > 1)
            {
                do
                {
                    // 2.1 choose random u (element of) GF(2^m)
                    u = new GF2nONBElement(this, _secRand);
                    ut = new GF2nPolynomial(2, GF2nONBElement.Zero(this));
                    // 2.2 Set c(t) <- ut
                    ut.Set(1, u);
                    c = new GF2nPolynomial(ut);
                    // 2.3 For i from 1 to m-1 do
                    for (i = 1; i <= DegreeN - 1; i++)
                    {
                        // 2.3.1 c(t) <- (c(t)^2 + ut) mod g(t)
                        c = c.MultiplyAndReduce(c, g);
                        c = c.Add(ut);
                    }
                    // 2.4 set h(t) <- GCD(c(t), g(t))
                    h = c.Gcd(g);
                    // 2.5 if h(t) is constant or deg(g) = deg(h) then go to
                    // step 2.1
                    hDegree = h.Degree;
                    gDegree = g.Degree;
                }
                while ((hDegree == 0) || (hDegree == gDegree));

                // 2.6 If 2deg(h) > deg(g) then set g(t) <- g(t)/h(t) ...
                if ((hDegree << 1) > gDegree)
                    g = g.Quotient(h);
                else // ... else g(t) <- h(t)
                    g = new GF2nPolynomial(h);

                gDegree = g.Degree;
            }
            // 3. Output g(0)
            return g.At(0);
        }