// calc tangent at x=a
  @Override
  public final void compute() {
    double a = n.getDouble();
    if (!f.isDefined() || !deriv.isDefined() || Double.isInfinite(a) || Double.isNaN(a)) {
      tangent.setUndefined();
      return;
    }

    // calc the tangent;
    double fa = f.evaluate(a);
    double slope = deriv.evaluate(a);
    tangent.setCoords(-slope, 1.0, a * slope - fa);
    T.setCoords(a, fa, 1.0);
  }
  @Override
  public void compute() {
    int degInt;
    GeoList coefs = null;
    fv.setVarString(f.getVarString(StringTemplate.defaultTemplate));
    // px^2+qx+r; p+q+r=s;
    double r = f.evaluate(0);
    double s = f.evaluate(1);
    double p = 0.5 * (s + f.evaluate(-1)) - r;
    double q = s - p - r;
    boolean isQuadratic = !f.isGeoFunctionConditional();
    double[] checkpoints = {1000, -1000, Math.PI, Math.E};
    for (int i = 0; i < checkpoints.length; i++) {
      double x = checkpoints[i];
      if (!Kernel.isZero(p * x * x + q * x + r - f.evaluate(x))) {
        // App.debug(p + "," + q + "," + r + ","
        // + (p * x * x + q * x + r - f.evaluate(x)));
        isQuadratic = false;
      }
    }
    if (!isQuadratic) {
      if (algoCoef == null) {
        algoCoef = new AlgoCoefficients(cons, f);
        algoCoef.setProtectedInput(true);
        algoCoef.remove();
      } else {
        algoCoef.compute();
      }
      coefs = algoCoef.getResult();

      degInt = coefs.size() - 1;
      isQuadratic = coefs.isDefined() && coefs.get(0).isDefined();
      for (int i = 1; i < degInt; i++) {
        if (2 * i != degInt && !Kernel.isZero(((GeoNumeric) coefs.get(i)).getDouble())) {
          isQuadratic = false;
        }
        p = ((GeoNumeric) coefs.get(0)).getDouble();
        q = ((GeoNumeric) coefs.get(degInt / 2)).getDouble();
        r = ((GeoNumeric) coefs.get(degInt)).getDouble();
      }
    } else {
      degInt = 2;
    }

    if (degInt % 2 == 1 || degInt < 2 || !isQuadratic || Kernel.isZero(p)) {
      square.setUndefined();
      return;
    }

    if (lastDeg != degInt) {
      ExpressionNode squareE;
      ExpressionValue fvPower;
      if (degInt == 2) fvPower = fv;
      else
        fvPower = new ExpressionNode(kernel, fv, Operation.POWER, new MyDouble(kernel, degInt / 2));
      squareE =
          new ExpressionNode(
              kernel,
              new ExpressionNode(
                  kernel,
                  a,
                  Operation.MULTIPLY,
                  new ExpressionNode(kernel, fvPower, Operation.MINUS, h)
                      .power(new MyDouble(kernel, 2))),
              Operation.PLUS,
              k);

      square.getFunction().setExpression(squareE);
    }
    lastDeg = degInt;
    fv.setVarString(f.getVarString(StringTemplate.defaultTemplate));

    // if one is undefined, others are as well
    square.setDefined(!Double.isNaN(r));
    a.set(p);
    h.set(-q / (2 * p));
    k.set(r - q * q / (p * 4));
  }