// 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);
  }
  public AlgoCompleteSquare(Construction cons, String label, GeoFunction f) {
    super(cons);
    this.f = f;
    a = new MyDouble(kernel);
    h = new MyDouble(kernel);
    k = new MyDouble(kernel);

    fv = new FunctionVariable(kernel);
    ExpressionNode squareE = new ExpressionNode(kernel, a);
    Function squareF = new Function(squareE, fv);
    squareF.initFunction();
    square = new GeoFunction(cons);
    setInputOutput();
    square.setFunction(squareF);
    compute();
    lastDeg = 0;
    square.setLabel(label);
  }
 @Override
 protected void applyCasCommand(StringTemplate tpl) {
   StringBuilder sb = new StringBuilder();
   sb.append("TrigCombine[%");
   if (target != null) {
     sb.append(',');
     sb.append(target.toValueString(tpl));
   }
   sb.append(']');
   g.setUsingCasCommand(sb.toString(), f, true, arbconst);
 }
 /**
  * @param cons construction
  * @param label label for output
  * @param degree maximal degree
  * @param min minimal coefficient vlaue
  * @param max maximal coefficient value
  */
 public AlgoRandomPolynomial(
     Construction cons, String label, NumberValue degree, NumberValue min, NumberValue max) {
   super(cons);
   this.degree = degree;
   this.min = min;
   this.max = max;
   fv = new FunctionVariable(kernel);
   f = new Function(new ExpressionNode(kernel, fv), fv);
   polynomial = new GeoFunction(cons);
   setInputOutput();
   compute();
   polynomial.setLabel(label);
 }
 @Override
 public void compute() {
   // cover undefined cases
   if (!degree.isDefined() || !min.isDefined() || !max.isDefined() || degree.getDouble() < 0) {
     polynomial.setUndefined();
     return;
   }
   int lower = (int) Math.ceil(min.getDouble());
   int upper = (int) Math.floor(max.getDouble());
   if (lower > upper || (lower == 0 && upper == 0)) {
     polynomial.setUndefined();
     return;
   }
   // input is sane, we can do the computation
   int deg = (int) Math.floor(degree.getDouble());
   ExpressionNode varExpr = new ExpressionNode(kernel, fv);
   ExpressionNode newExpr = randomCoef(deg != 0);
   for (int i = 1; i <= deg; i++) {
     newExpr = varExpr.power(new MyDouble(kernel, i)).multiply(randomCoef(i != deg)).plus(newExpr);
   }
   f.setExpression(newExpr, fv);
   polynomial.setFunction(f);
 }
  @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));
  }
 @Override
 public final String toString(StringTemplate tpl) {
   // Michael Borcherds 2008-03-30
   // simplified to allow better Chinese translation
   return getLoc().getPlain("TangentToAatB", f.getLabel(tpl), "x = " + ngeo.getLabel(tpl));
 }