/**
   * Creates new random element algo
   *
   * @param cons
   * @param label
   * @param geoList
   */
  public AlgoRandomElement(Construction cons, String label, GeoList geoList) {
    super(cons);
    this.geoList = geoList;

    // init return element as copy of first list element
    if (geoList.size() > 0) {
      element = geoList.get(0).copyInternal(cons);
    } else if (geoList.getTypeStringForXML() != null) {
      // if the list was non-empty at some point before saving, get the
      // same type of geo
      // saved in XML from 4.1.131.0
      element = kernel.createGeoElement(cons, geoList.getTypeStringForXML());
    }

    // desperate case: empty list
    else {
      // saved in XML from 4.0.18.0
      element = cons.getOutputGeo();
    }

    setInputOutput();
    compute();
    element.setLabel(label);
    cons.addRandomGeo(element);
  }
  protected void transformList(GeoList ageo2, GeoList bgeo2) {
    for (int i = bgeo2.size() - 1; i >= ageo2.size(); i--) bgeo2.remove(i);

    for (int i = 0; i < ageo2.size(); i++) {
      GeoElement trans = null;
      if (i < bgeo2.size()) {
        setTransformedObject(ageo2.get(i), bgeo2.get(i));
        compute();
      } else {
        trans = getResultTemplate(ageo2.get(i));

        setTransformedObject(ageo2.get(i), trans);
        compute();
        bgeo2.add(trans);
      }
    }
    setTransformedObject(ageo2, bgeo2);
  }
  @Override
  public final void compute() {
    int size = geoList.size();
    if (!geoList.isDefined() || size <= 1) {
      g.setUndefined();
      return;
    }

    double sigmax = 0;
    double sigmay = 0;
    double sigmaxx = 0;
    // double sigmayy=0; not needed
    double sigmaxy = 0;

    for (int i = 0; i < size; i++) {
      GeoElement geo = geoList.get(i);
      if (geo.isGeoPoint()) {
        double x;
        double y;
        if (geo.isGeoElement3D()) {
          Coords coords = ((GeoPointND) geo).getInhomCoordsInD3();
          if (!Kernel.isZero(coords.getZ())) {
            g.setUndefined();
            return;
          }
          x = coords.getX();
          y = coords.getY();
        } else {
          double xy[] = new double[2];
          ((GeoPoint) geo).getInhomCoords(xy);
          x = xy[0];
          y = xy[1];
        }

        sigmax += x;
        sigmay += y;
        sigmaxx += x * x;
        sigmaxy += x * y;
        // sigmayy+=y*y; not needed
      } else {
        g.setUndefined();
        return;
      }
    }
    // y on x regression line
    // (y - sigmay / n) = (Sxx / Sxy)*(x - sigmax / n)
    // rearranged to eliminate all divisions
    g.x = size * sigmax * sigmay - size * size * sigmaxy;
    g.y = size * size * sigmaxx - size * sigmax * sigmax;
    g.z = size * sigmax * sigmaxy - size * sigmaxx * sigmay; // (g.x)x +
    // (g.y)y +
    // g.z = 0
  }
  @Override
  public final void compute() {
    if (!geoList.isDefined() || geoList.size() == 0) {
      element.setUndefined();
      return;
    }

    GeoElement randElement =
        geoList.get((int) Math.floor((cons.getApplication().getRandomNumber() * geoList.size())));

    // check type:
    if (randElement.getGeoClassType() == element.getGeoClassType()) {
      element.set(randElement);
    } else {
      element.setUndefined();
    }
  }
  @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));
  }