/* GeoGebra - Dynamic Mathematics for Everyone http://www.geogebra.org This file is part of GeoGebra. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. */ package org.geogebra.common.kernel.algos; import org.geogebra.common.kernel.Construction; import org.geogebra.common.kernel.algos.AlgoIterationList.Type; import org.geogebra.common.kernel.commands.Commands; import org.geogebra.common.kernel.geos.GeoElement; import org.geogebra.common.kernel.geos.GeoFunction; import org.geogebra.common.kernel.geos.GeoFunctionNVar; import org.geogebra.common.kernel.geos.GeoList; import org.geogebra.common.kernel.geos.GeoNumberValue; import org.geogebra.common.kernel.geos.GeoNumeric; import org.geogebra.common.util.debug.Log; /** * Iteration[ f(x), x0, n ] * * @author Markus Hohenwarter * @version 15-07-2007 */ public class AlgoIteration extends AlgoElement { private GeoFunction f; // input private GeoNumberValue startValue, n; private GeoElement startValueGeo, nGeo; private GeoElement result; // output private GeoFunctionNVar fNVar; public AlgoIteration(Construction cons, String label, GeoFunction f, GeoNumberValue startValue, GeoNumberValue n) { super(cons); this.f = f; this.startValue = startValue; startValueGeo = startValue.toGeoElement(); this.n = n; nGeo = n.toGeoElement(); result = new GeoNumeric(cons); type = Type.SIMPLE; setInputOutput(); compute(); result.setLabel(label); } public AlgoIteration(Construction cons, String label, GeoFunctionNVar f, GeoList startValue, GeoNumberValue n) { super(cons); this.fNVar = f; // this.startValue = startValue; startValueGeo = startValue.toGeoElement(); this.n = n; nGeo = n.toGeoElement(); result = new GeoNumeric(cons); type = Type.DOUBLE; setInputOutput(); compute(); result.setLabel(label); } private GeoElement expression; // input expression dependent on var private GeoElement[] vars; // input: local variable private int varCount; private GeoList[] over; private boolean isEmpty; private AlgoElement expressionParentAlgo; AlgoIterationList.Type type; public AlgoIteration(Construction cons, GeoElement expression, GeoElement[] vars, GeoList[] over, GeoNumberValue n) { super(cons); this.expression = expression; this.vars = vars; this.over = over; this.n = n; this.nGeo = n.toGeoElement(); type = Type.DEFAULT; varCount = vars.length; expressionParentAlgo = expression.getParentAlgorithm(); result = expression.copy(); setInputOutput(); // for AlgoElement compute(); } @Override public Commands getClassName() { return Commands.Iteration; } @Override protected void setInputOutput() { if (type == Type.SIMPLE) { input = new GeoElement[3]; input[0] = f; input[1] = startValueGeo; input[2] = nGeo; // done by AlgoElement } else if (type == Type.DOUBLE) { input = new GeoElement[3]; input[0] = fNVar; input[1] = startValueGeo; input[2] = nGeo; // done by AlgoElement } else { input = new GeoElement[3 + varCount]; input[0] = expression; for (int i = 0; i < varCount; i++) { input[i + 1] = vars[i]; } input[1 + varCount] = over[0]; input[2 + varCount] = nGeo; } // done by AlgoElement super.setOutputLength(1); super.setOutput(0, result); setDependencies(); } public GeoElement getResult() { return result; } public final void computeSimple() { if (!f.isDefined() || !startValueGeo.isDefined() || !nGeo.isDefined()) { result.setUndefined(); return; } int iterations = (int) Math.round(n.getDouble()); if (iterations < 0) { result.setUndefined(); return; } // perform iteration f(f(f(...(startValue)))) double val = startValue.getDouble(); for (int i = 0; i < iterations; i++) { val = f.value(val); } ((GeoNumeric) result).setValue(val); } public final void computeDouble() { if (!fNVar.isDefined() || !startValueGeo.isDefined() || !nGeo.isDefined() || ((GeoList) startValueGeo).size() != 2) { result.setUndefined(); return; } int iterations = (int) Math.round(n.getDouble()); if (iterations < 0) { result.setUndefined(); return; } // perform iteration f(f(f(...(startValue)))) double val = ((GeoList) startValueGeo).get(0).evaluateDouble(); double offset = Math .round(((GeoList) startValueGeo).get(1).evaluateDouble()); for (int i = 0; i < iterations; i++) { val = fNVar.evaluate(offset + i, val); } ((GeoNumeric) result).setValue(val); } boolean updateRunning = false; @Override public final void compute() { if (type == Type.SIMPLE) { computeSimple(); return; } if (type == Type.DOUBLE) { computeDouble(); return; } Log.debug(nGeo.isLabelSet() + "label"); if (updateRunning) { return; } updateRunning = true; for (int i = 2; i < input.length - 1; i += 2) { if (!input[i].isDefined()) { result.setUndefined(); updateRunning = false; return; } } // list.setDefined(true); int iterations = (int) Math.round(n.getDouble()); if (iterations < 0 || varCount > over[0].size()) { updateRunning = false; result.setUndefined(); return; } isEmpty = over[0].size() == 0; boolean oldSuppressLabels = cons.isSuppressLabelsActive(); cons.setSuppressLabelCreation(true); // update list updateListItems(); // revert label creation setting cons.setSuppressLabelCreation(oldSuppressLabels); updateRunning = false; } @Override public GeoElement[] getInputForUpdateSetPropagation() { if (type != Type.DEFAULT) { return super.getInputForUpdateSetPropagation(); } GeoElement[] realInput = new GeoElement[3]; realInput[0] = expression; realInput[1] = over[0]; realInput[2] = nGeo; return realInput; } private void updateListItems() { if (isEmpty) { return; } int listSize = (int) Math.round(n.getDouble()); if (listSize < over[0].size()) { result.set(over[0].get(listSize)); return; } for (int j = 1; j < varCount; j++) { vars[j].set(over[0].get(over[0].size() - varCount + j - 1)); } GeoElement listElement = over[0].get(over[0].size() - 1) .copyInternal(cons); int i = over[0].size(); while (i <= listSize) { // check we haven't run out of memory // set local var value // updateLocalVar(currentVal); updateLocalVar(i, listElement); // copy expression value to listElement // if it's undefined, just copy the undefined property if (expression.isDefined()) { // if (listElement.isGeoList()) { for (int j = 0; j < varCount; j++) { ((GeoList) listElement) .replaceChildrenByValues(vars[j]); } } } else { listElement.setUndefined(); } if (listElement instanceof GeoNumeric && listElement .getDrawAlgorithm() instanceof DrawInformationAlgo) { listElement.setDrawAlgorithm( ((DrawInformationAlgo) expression.getDrawAlgorithm()) .copy()); listElement.setEuclidianVisible(true); } listElement.update(); i++; } result.set(listElement); } private void updateLocalVar(int index, GeoElement listElement) { // set local variable to given value if (index == 0) { return; } for (int i = 0; i < varCount - 1; i++) { vars[i].set(vars[i + 1]); } vars[varCount - 1].set(listElement); // update var's algorithms until we reach expression if (expressionParentAlgo != null) { // update all dependent algorithms of the local variable var this.setStopUpdateCascade(true); for (int i = 0; i < varCount; i++) { vars[i].getAlgoUpdateSet().updateAllUntil(expressionParentAlgo); } this.setStopUpdateCascade(false); expressionParentAlgo.update(); listElement.set(expression); } } }