/* 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.statistics; import org.geogebra.common.euclidian.EuclidianConstants; import org.geogebra.common.kernel.Construction; import org.geogebra.common.kernel.Kernel; import org.geogebra.common.kernel.Matrix.Coords; import org.geogebra.common.kernel.algos.AlgoElement; import org.geogebra.common.kernel.commands.Commands; import org.geogebra.common.kernel.geos.GeoElement; import org.geogebra.common.kernel.geos.GeoLine; import org.geogebra.common.kernel.geos.GeoList; import org.geogebra.common.kernel.geos.GeoPoint; import org.geogebra.common.kernel.kernelND.GeoPointND; /** * FitLineY of a list. adapted from AlgoListMax * * @author Michael Borcherds * @version 14-01-2008 */ public class AlgoFitLineY extends AlgoElement { private GeoList geoList; // input private GeoLine g; // output public AlgoFitLineY(Construction cons, String label, GeoList geoList) { this(cons, geoList); g.setLabel(label); } public AlgoFitLineY(Construction cons, GeoList geoList) { super(cons); this.geoList = geoList; g = new GeoLine(cons); // ignore default (implicit) // for FitXXX we always want "y=..." g.setToExplicit(); setInputOutput(); // for AlgoElement compute(); } @Override public Commands getClassName() { return Commands.FitLineY; } @Override public int getRelatedModeID() { return EuclidianConstants.MODE_FITLINE; } @Override protected void setInputOutput() { input = new GeoElement[1]; input[0] = geoList; setOnlyOutput(g); setDependencies(); // done by AlgoElement } public GeoLine getFitLineY() { return g; } @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) = (Sxy / Sxx)*(x - sigmax / n) // rearranged to eliminate all divisions // g.x = (sigmax * sigmay - size * sigmaxy); // g.y = (size * sigmaxx - sigmax * sigmax); // g.z = (sigmax * sigmaxy - sigmaxx * sigmay); // (g.x)x + (g.y)y + g.z = 0 // more accurate, see #5230 double Sxy = sigmaxy - sigmax * sigmay / size; double Sxx = sigmaxx - sigmax * sigmax / size; double mux = sigmax / size; double muy = sigmay / size; g.x = Sxy; g.y = -Sxx; g.z = -Sxy * mux + Sxx * muy; // #5294 if (Kernel.isZero(g.x) || Kernel.isZero(g.y) || Kernel.isZero(g.z)) { return; } // normalize coefficients (copied from // GeoLine.getnormalizedCoefficients()) // #5230 while (Math.abs(g.x) < 0.5 && Math.abs(g.y) < 0.5 && Math.abs(g.z) < 0.5) { g.x *= 2; g.y *= 2; g.z *= 2; } while (Math.abs(g.x) > 1 && Math.abs(g.y) > 1 && Math.abs(g.z) > 1) { g.x /= 2; g.y /= 2; g.z /= 2; } } }