/**
   * 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);
  }
  @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() {

    String testType;
    if (tail.getTextString().equals("<")) {
      testType = "left";
    } else if (tail.getTextString().equals(">")) {
      testType = "right";
    } else if (StringUtil.isNotEqual(tail.getTextString())) {
      testType = "two";
    } else {
      result.setUndefined();
      return;
    }

    double n1 = n.getDouble();
    double phat1 = proportion.getDouble();
    double n2 = n_2.getDouble();
    double phat2 = proportion2.getDouble();

    double x1 = phat1 * n1;
    double x2 = phat2 * n2;
    double phatTotal = (x1 + x2) / (n1 + n2);
    se = Math.sqrt(phatTotal * (1 - phatTotal) * (1 / n1 + 1 / n2));
    double testStatistic = (phat1 - phat2) / se;

    NormalDistributionImpl normalDist = new NormalDistributionImpl(0, 1);
    double P = 0;
    try {
      P = normalDist.cumulativeProbability(testStatistic);
    } catch (Exception e) {
      result.setUndefined();
      return;
    }

    if ("right".equals(testType)) {
      P = 1 - P;
    } else if ("two".equals(testType)) {
      if (testStatistic < 0) {
        P = 2 * P;
      } else {
        P = 2 * (1 - P);
      }
    }

    // put these results into the output list
    result.clear();
    result.addNumber(P, null);
    result.addNumber(testStatistic, null);
  }
  @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();
    }
  }
  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);
  }
 /**
  * @param cons
  * @param label
  * @param proportion
  * @param n
  * @param proportion2
  * @param n_2
  * @param tail
  */
 public AlgoZProportion2Test(
     Construction cons,
     String label,
     GeoNumeric proportion,
     GeoNumeric n,
     GeoNumeric proportion2,
     GeoNumeric n_2,
     GeoText tail) {
   this(cons, proportion, n, proportion2, n_2, tail);
   result.setLabel(label);
 }
  @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));
  }
 /**
  * Proves the given statement and gives some details in a list.
  *
  * @param cons The construction
  * @param label Label for the output
  * @param root Input statement
  */
 public AlgoProveDetails(Construction cons, String label, GeoElement root) {
   this(cons, root);
   list.setLabel(label);
 }
  /** Heavy computation of the proof. */
  public final void initialCompute() {

    // Create and initialize the prover
    Prover p = UtilFactory.getPrototype().newProver();
    ProverSettings proverSettings = ProverSettings.get();
    if ("OpenGeoProver".equalsIgnoreCase(proverSettings.proverEngine)) {
      if ("Wu".equalsIgnoreCase(proverSettings.proverMethod))
        p.setProverEngine(ProverEngine.OPENGEOPROVER_WU);
      else if ("Area".equalsIgnoreCase(proverSettings.proverMethod))
        p.setProverEngine(ProverEngine.OPENGEOPROVER_AREA);
    } else if ("Botana".equalsIgnoreCase(proverSettings.proverEngine))
      p.setProverEngine(ProverEngine.BOTANAS_PROVER);
    else if ("Recio".equalsIgnoreCase(proverSettings.proverEngine))
      p.setProverEngine(ProverEngine.RECIOS_PROVER);
    else if ("PureSymbolic".equalsIgnoreCase(proverSettings.proverEngine))
      p.setProverEngine(ProverEngine.PURE_SYMBOLIC_PROVER);
    else if ("Auto".equalsIgnoreCase(proverSettings.proverEngine))
      p.setProverEngine(ProverEngine.AUTO);
    p.setTimeout(proverSettings.proverTimeout);
    p.setConstruction(cons);
    p.setStatement(root);
    // Compute extra NDG's:
    p.setReturnExtraNDGs(true);

    // Adding benchmarking:
    double startTime = cons.getApplication().getMillisecondTime();
    p.compute(); // the computation of the proof
    int elapsedTime = (int) (cons.getApplication().getMillisecondTime() - startTime);
    /*
     * Don't remove this. It is needed for automated testing. (String match
     * is assumed.)
     */
    Log.debug("Benchmarking: " + elapsedTime + " ms");

    ProofResult proofresult = p.getProofResult();
    ExtendedBoolean result = p.getYesNoAnswer();

    Log.debug("STATEMENT IS " + proofresult + " (yes/no: " + result + ")");

    if (proofresult == ProofResult.PROCESSING) {
      list.setUndefined();
      return;
    }

    list.setDefined(true);
    list.clear();

    if (!ExtendedBoolean.UNKNOWN.equals(result)) {
      Boolean unreadable = null;

      if (proofresult == ProofResult.TRUE_NDG_UNREADABLE) {
        unreadable = true;
      }
      if (proofresult == ProofResult.TRUE) {
        unreadable = false;
      }

      GeoBoolean answer = new GeoBoolean(cons);
      answer.setValue(result.boolVal());
      list.add(answer);
      if (result.boolVal()) {
        HashSet<NDGCondition> ndgresult = p.getNDGConditions();
        GeoList ndgConditionsList = new GeoList(cons);
        ndgConditionsList.clear();
        ndgConditionsList.setDrawAsComboBox(true);
        Iterator<NDGCondition> it = ndgresult.iterator();
        TreeSet<GeoText> sortedSet = new TreeSet<GeoText>(GeoText.getComparator());

        // Collecting the set of NDG conditions.
        // The OGP data collector may left some unreadable conditions
        // so we make sure if the condition is readable.
        while (!unreadable && it.hasNext()) {
          GeoText ndgConditionText = new GeoText(cons);
          NDGCondition ndgc = it.next();
          // Do not print unnecessary conditions:
          if (ndgc.getReadability() > 0) {
            ndgc.rewrite(cons);
            String s = null;

            if (relTool) {
              String cond = ndgc.getCondition();
              if ("AreParallel".equals(cond)) {
                // non-parallism in 2D means intersecting
                // FIXME: this is not true for 3D
                s =
                    RelationNumerical.intersectString(
                        ndgc.getGeos()[0], ndgc.getGeos()[1], true, getLoc());
              } else if ("AreCollinear".equals(cond)) {
                s =
                    RelationNumerical.triangleNonDegenerateString(
                        (GeoPoint) ndgc.getGeos()[0],
                        (GeoPoint) ndgc.getGeos()[1],
                        (GeoPoint) ndgc.getGeos()[2],
                        getLoc());
              } else if ("AreEqual".equals(cond)) {
                s =
                    RelationNumerical.equalityString(
                        ndgc.getGeos()[0], ndgc.getGeos()[1], false, getLoc());
              } else if ("ArePerpendicular".equals(cond)) {
                s =
                    RelationNumerical.perpendicularString(
                        (GeoLine) ndgc.getGeos()[0], (GeoLine) ndgc.getGeos()[1], false, getLoc());
              } else if ("AreCongruent".equals(cond)) {
                s =
                    RelationNumerical.congruentSegmentString(
                        ndgc.getGeos()[0], ndgc.getGeos()[1], false, getLoc());
              }
            }
            if (s == null || !relTool) {
              GeoElement[] geos = ndgc.getGeos();
              if (geos == null) { // formula with quantities
                s = ndgc.getCondition();
              } else {
                s = getLoc().getCommand(ndgc.getCondition());
                s += "[";
                for (int i = 0; i < ndgc.getGeos().length; ++i) {
                  if (i > 0) {
                    s += ',';
                  }
                  /*
                   * There can be a case when the underlying
                   * prover sends such objects which cannot be
                   * understood by GeoGebra. In this case we
                   * use the "Objects" word. In this case we
                   * normally return ProveResult.UNKNOWN to
                   * not confuse the student, but for sure, we
                   * still do the check here as well.
                   */
                  GeoElement geo = ndgc.getGeos()[i];
                  if (geo != null) s += ndgc.getGeos()[i].getLabelSimple();
                  else s += "...";
                }
                s += "]";
                if (relTool) {
                  s = getLoc().getPlain("not") + " " + s;
                }
              }
            }

            ndgConditionText.setTextString(s);
            ndgConditionText.setLabelVisible(false);
            ndgConditionText.setEuclidianVisible(false);
            sortedSet.add(ndgConditionText);
          }
          // For alphabetically ordering, we need a sorted set here:
        }
        // Copy the sorted list into the output:
        Iterator<GeoText> it2 = sortedSet.iterator();
        while (it2.hasNext()) {
          ndgConditionsList.add(it2.next());
        }

        if (unreadable) {
          GeoText ndgConditionText = new GeoText(cons);
          String cond = "...";
          ndgConditionText.setTextString(cond);
          ndgConditionText.setLabelVisible(false);
          ndgConditionText.setEuclidianVisible(false);
          sortedSet.add(ndgConditionText);
          ndgConditionsList.add(ndgConditionText);
        }

        // Put this list to the final output (if non-empty):
        if (ndgConditionsList.size() > 0) list.add(ndgConditionsList);
      }
    }

    /*
     * Don't remove this. It is needed for testing the web platform. (String
     * match is assumed.)
     */
    Log.debug("OUTPUT for ProveDetails: " + list);
  }