/// <summary>
        /// Computes the definite integral within the borders a and b.
        /// </summary>
        /// <param name="a">Left integration border.</param>
        /// <param name="b">Right integration border.</param>
        /// <returns></returns>
        public double Integrate(double a, double b)
        {
            double[] buf = new double[Degree + 2];
            buf[0] = 0; // this value can be arbitrary, in fact

            for (int i = 1; i < buf.Length; i++)
                buf[i] = Coefficients[i - 1] / i;

            Polynomial p = new Polynomial(buf);

            return (p.Evaluate(b) - p.Evaluate(a));
        }
        public static Polynomial operator *(Polynomial p, Polynomial q)
        {
            int degree = p.Degree + q.Degree;

            Polynomial r = new Polynomial();

            for (int i = 0; i <= p.Degree; i++)
                for (int j = 0; j <= q.Degree; j++)
                    r += (p.Coefficients[i] * q.Coefficients[j]) * Monomial(i + j);

            return r;
        }
        /// <summary>
        /// Computes the roots of polynomial p via Weierstrass iteration.
        /// </summary>
        /// <param name="p">Polynomial to compute the roots of.</param>
        /// <param name="tolerance">Computation precision; e.g. 1e-12 denotes 12 exact digits.</param>
        /// <param name="max_iterations">Maximum number of iterations; this value is used to bound
        /// the computation effort if desired pecision is hard to achieve.</param>
        /// <returns></returns>
        public static double[] Roots(Polynomial p, double tolerance, int max_iterations)
        {
            Polynomial q = Normalize(p);

            double[] z = new double[q.Degree]; // approx. for roots
            double[] w = new double[q.Degree]; // Weierstraß corrections

            // init z
            for (int k = 0; k < q.Degree; k++)
                //z[k] = (new Complex(.4, .9)) ^ k;
                z[k] = Math.Exp(2 * Math.PI * k / q.Degree);

            for (int iter = 0; iter < max_iterations
                && MaxValue(q, z) > tolerance; iter++)
                for (int i = 0; i < 10; i++)
                {
                    for (int k = 0; k < q.Degree; k++)
                        w[k] = q.Evaluate(z[k]) / WeierNull(z, k);

                    for (int k = 0; k < q.Degree; k++)
                        z[k] -= w[k];
                }

            // clean...
            for (int k = 0; k < q.Degree; k++)
            {
                z[k] = Math.Round(z[k], 12);
            }

            return z;
        }
        /// <summary>
        /// Factorizes polynomial to its linear factors.
        /// </summary>
        /// <returns></returns>
        public FactorizedPolynomial Factorize()
        {
            // this is to be returned
            FactorizedPolynomial p = new FactorizedPolynomial();

            // cannot factorize polynomial of degree 0 or 1
            if (this.Degree <= 1)
            {
                p.Factor = new Polynomial[] { this };
                p.Power = new int[] { 1 };

                return p;
            }

            double[] roots = Roots(this);

            Polynomial[] factor = new Polynomial[roots.Length];
            int[] power = new int[roots.Length];

            power[0] = 1;
            factor[0] = new Polynomial(new double[] { -Coefficients[Degree] * (double)roots[0], Coefficients[Degree] });

            for (int i = 1; i < roots.Length; i++)
            {
                power[i] = 1;
                factor[i] = new Polynomial(new double[] { -(double)roots[i], 1 });
            }

            p.Factor = factor;
            p.Power = power;

            return p;
        }
        /// <summary>
        /// Computes the greatest value |p(z_k)|.
        /// </summary>
        /// <param name="p"></param>
        /// <param name="z"></param>
        /// <returns></returns>
        public static double MaxValue(Polynomial p, double[] z)
        {
            double buf = 0;
            for (int i = 0; i < z.Length; i++)
            {
                if (Math.Abs(p.Evaluate(z[i])) > buf)
                    buf = Math.Abs(p.Evaluate(z[i]));
            }

            return buf;
        }
        /// <summary>
        /// Normalizes the polynomial, e.i. divides each coefficient by the
        /// coefficient of a_n the greatest term if a_n != 1.
        /// </summary>
        public static Polynomial Normalize(Polynomial p)
        {
            Polynomial q = Clean(p);

            if (q.Coefficients[q.Degree] != 1)
                for (int k = 0; k <= q.Degree; k++)
                    q.Coefficients[k] /= q.Coefficients[q.Degree];

            return q;
        }
        /// <summary>
        /// Integrates given polynomial p.
        /// </summary>
        /// <param name="p"></param>
        /// <returns></returns>
        public static Polynomial Integral(Polynomial p)
        {
            double[] buf = new double[p.Degree + 2];
            buf[0] = 0; // this value can be arbitrary, in fact

            for (int i = 1; i < buf.Length; i++)
                buf[i] = p.Coefficients[i - 1] / i;

            return new Polynomial(buf);
        }
        public static Polynomial[] GetStandardBase(int dim)
        {
            if (dim < 1)
                throw new ArgumentException("Dimension expected to be greater than zero.");

            Polynomial[] buf = new Polynomial[dim];

            for (int i = 0; i < dim; i++)
                buf[i] = Monomial(i);

            return buf;
        }
        /// <summary>
        /// Expands factorized polynomial p_1(x)^(k_1)*...*p_r(x)^(k_r) to its normal form a_0 + a_1 x + ... + a_n x^n.
        /// </summary>
        /// <param name="p"></param>
        /// <returns></returns>
        public static Polynomial Expand(FactorizedPolynomial p)
        {
            Polynomial q = new Polynomial(1);

            for (int i = 0; i < p.Factor.Length; i++)
            {
                for (int j = 0; j < p.Power[i]; j++)
                    q *= p.Factor[i];

                q.Clean();
            }

            return q;
        }
        /// <summary>
        /// Differentiates given polynomial p.
        /// </summary>
        /// <param name="p"></param>
        /// <returns></returns>
        public static Polynomial Derivative(Polynomial p)
        {
            double[] buf = new double[p.Degree];

            for (int i = 0; i < buf.Length; i++)
                buf[i] = (i + 1) * p.Coefficients[i + 1];

            return new Polynomial(buf);
        }
        /// <summary>
        /// Removes unncessary leading zeros.
        /// </summary>
        /// <param name="p"></param>
        /// <returns></returns>
        public static Polynomial Clean(Polynomial p)
        {
            int i;

            for (i = p.Degree; i >= 0 && p.Coefficients[i] == 0; i--) ;

            double[] coeffs = new double[i + 1];

            for (int k = 0; k <= i; k++)
                coeffs[k] = p.Coefficients[k];

            return new Polynomial(coeffs);
        }