/**
   * Approximate real root.
   *
   * @param iv real root isolating interval with f(left) * f(right) < 0.
   * @param f univariate polynomial, non-zero.
   * @param eps requested interval length.
   * @return a decimal approximation d such that |d-v| < eps, for f(v) = 0, v real.
   */
  public BigDecimal approximateRoot(Interval<C> iv, GenPolynomial<C> f, C eps)
      throws NoConvergenceException {
    if (iv == null) {
      throw new IllegalArgumentException("null interval not allowed");
    }
    BigDecimal d = iv.toDecimal();
    if (f == null || f.isZERO() || f.isConstant() || eps == null) {
      return d;
    }
    if (iv.length().compareTo(eps) < 0) {
      return d;
    }
    BigDecimal left = new BigDecimal(iv.left.getRational());
    BigDecimal right = new BigDecimal(iv.right.getRational());
    BigDecimal e = new BigDecimal(eps.getRational());
    BigDecimal q = new BigDecimal("0.25");
    // System.out.println("left  = " + left);
    // System.out.println("right = " + right);
    e = e.multiply(d); // relative error
    // System.out.println("e     = " + e);
    BigDecimal dc = BigDecimal.ONE;
    // polynomials with decimal coefficients
    GenPolynomialRing<BigDecimal> dfac = new GenPolynomialRing<BigDecimal>(dc, f.ring);
    GenPolynomial<BigDecimal> df = PolyUtil.<C>decimalFromRational(dfac, f);
    GenPolynomial<C> fp = PolyUtil.<C>baseDeriviative(f);
    GenPolynomial<BigDecimal> dfp = PolyUtil.<C>decimalFromRational(dfac, fp);

    // Newton Raphson iteration: x_{n+1} = x_n - f(x_n)/f'(x_n)
    int i = 0;
    final int MITER = 50;
    int dir = 0;
    while (i++ < MITER) {
      BigDecimal fx = PolyUtil.<BigDecimal>evaluateMain(dc, df, d); // f(d)
      if (fx.isZERO()) {
        return d;
      }
      BigDecimal fpx = PolyUtil.<BigDecimal>evaluateMain(dc, dfp, d); // f'(d)
      if (fpx.isZERO()) {
        throw new NoConvergenceException("zero deriviative should not happen");
      }
      BigDecimal x = fx.divide(fpx);
      BigDecimal dx = d.subtract(x);
      // System.out.println("dx = " + dx);
      if (d.subtract(dx).abs().compareTo(e) <= 0) {
        return dx;
      }
      while (dx.compareTo(left) < 0 || dx.compareTo(right) > 0) { // dx < left: dx - left < 0
        // dx > right: dx - right > 0
        // System.out.println("trying to leave interval");
        if (i++ > MITER) { // dx > right: dx - right > 0
          throw new NoConvergenceException("no convergence after " + i + " steps");
        }
        if (i > MITER / 2 && dir == 0) {
          BigDecimal sd = new BigDecimal(iv.randomPoint().getRational());
          d = sd;
          x = sd.getZERO();
          logger.info("trying new random starting point " + d);
          i = 0;
          dir = 1;
        }
        if (i > MITER / 2 && dir == 1) {
          BigDecimal sd = new BigDecimal(iv.randomPoint().getRational());
          d = sd;
          x = sd.getZERO();
          logger.info("trying new random starting point " + d);
          // i = 0;
          dir = 2; // end
        }
        x = x.multiply(q); // x * 1/4
        dx = d.subtract(x);
        // System.out.println(" x = " + x);
        // System.out.println("dx = " + dx);
      }
      d = dx;
    }
    throw new NoConvergenceException("no convergence after " + i + " steps");
  }