1<?php
2/**
3 * PHPExcel
4 *
5 * Copyright (c) 2006 - 2014 PHPExcel
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
20 *
21 * @category   PHPExcel
22 * @package    PHPExcel_Shared_Trend
23 * @copyright  Copyright (c) 2006 - 2014 PHPExcel (http://www.codeplex.com/PHPExcel)
24 * @license    http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt	LGPL
25 * @version    ##VERSION##, ##DATE##
26 */
27
28
29require_once PHPEXCEL_ROOT . 'PHPExcel/Shared/trend/bestFitClass.php';
30require_once PHPEXCEL_ROOT . 'PHPExcel/Shared/JAMA/Matrix.php';
31
32
33/**
34 * PHPExcel_Polynomial_Best_Fit
35 *
36 * @category   PHPExcel
37 * @package    PHPExcel_Shared_Trend
38 * @copyright  Copyright (c) 2006 - 2014 PHPExcel (http://www.codeplex.com/PHPExcel)
39 */
40class PHPExcel_Polynomial_Best_Fit extends PHPExcel_Best_Fit
41{
42	/**
43	 * Algorithm type to use for best-fit
44	 * (Name of this trend class)
45	 *
46	 * @var	string
47	 **/
48	protected $_bestFitType		= 'polynomial';
49
50	/**
51	 * Polynomial order
52	 *
53	 * @protected
54	 * @var	int
55	 **/
56	protected $_order			= 0;
57
58
59	/**
60	 * Return the order of this polynomial
61	 *
62	 * @return	 int
63	 **/
64	public function getOrder() {
65		return $this->_order;
66	}	//	function getOrder()
67
68
69	/**
70	 * Return the Y-Value for a specified value of X
71	 *
72	 * @param	 float		$xValue			X-Value
73	 * @return	 float						Y-Value
74	 **/
75	public function getValueOfYForX($xValue) {
76		$retVal = $this->getIntersect();
77		$slope = $this->getSlope();
78		foreach($slope as $key => $value) {
79			if ($value != 0.0) {
80				$retVal += $value * pow($xValue, $key + 1);
81			}
82		}
83		return $retVal;
84	}	//	function getValueOfYForX()
85
86
87	/**
88	 * Return the X-Value for a specified value of Y
89	 *
90	 * @param	 float		$yValue			Y-Value
91	 * @return	 float						X-Value
92	 **/
93	public function getValueOfXForY($yValue) {
94		return ($yValue - $this->getIntersect()) / $this->getSlope();
95	}	//	function getValueOfXForY()
96
97
98	/**
99	 * Return the Equation of the best-fit line
100	 *
101	 * @param	 int		$dp		Number of places of decimal precision to display
102	 * @return	 string
103	 **/
104	public function getEquation($dp=0) {
105		$slope = $this->getSlope($dp);
106		$intersect = $this->getIntersect($dp);
107
108		$equation = 'Y = '.$intersect;
109		foreach($slope as $key => $value) {
110			if ($value != 0.0) {
111				$equation .= ' + '.$value.' * X';
112				if ($key > 0) {
113					$equation .= '^'.($key + 1);
114				}
115			}
116		}
117		return $equation;
118	}	//	function getEquation()
119
120
121	/**
122	 * Return the Slope of the line
123	 *
124	 * @param	 int		$dp		Number of places of decimal precision to display
125	 * @return	 string
126	 **/
127	public function getSlope($dp=0) {
128		if ($dp != 0) {
129			$coefficients = array();
130			foreach($this->_slope as $coefficient) {
131				$coefficients[] = round($coefficient,$dp);
132			}
133			return $coefficients;
134		}
135		return $this->_slope;
136	}	//	function getSlope()
137
138
139	public function getCoefficients($dp=0) {
140		return array_merge(array($this->getIntersect($dp)),$this->getSlope($dp));
141	}	//	function getCoefficients()
142
143
144	/**
145	 * Execute the regression and calculate the goodness of fit for a set of X and Y data values
146	 *
147	 * @param	int			$order		Order of Polynomial for this regression
148	 * @param	float[]		$yValues	The set of Y-values for this regression
149	 * @param	float[]		$xValues	The set of X-values for this regression
150	 * @param	boolean		$const
151	 */
152	private function _polynomial_regression($order, $yValues, $xValues, $const) {
153		// calculate sums
154		$x_sum = array_sum($xValues);
155		$y_sum = array_sum($yValues);
156		$xx_sum = $xy_sum = 0;
157		for($i = 0; $i < $this->_valueCount; ++$i) {
158			$xy_sum += $xValues[$i] * $yValues[$i];
159			$xx_sum += $xValues[$i] * $xValues[$i];
160			$yy_sum += $yValues[$i] * $yValues[$i];
161		}
162		/*
163		 *	This routine uses logic from the PHP port of polyfit version 0.1
164		 *	written by Michael Bommarito and Paul Meagher
165		 *
166		 *	The function fits a polynomial function of order $order through
167		 *	a series of x-y data points using least squares.
168		 *
169		 */
170		for ($i = 0; $i < $this->_valueCount; ++$i) {
171			for ($j = 0; $j <= $order; ++$j) {
172				$A[$i][$j] = pow($xValues[$i], $j);
173			}
174		}
175		for ($i=0; $i < $this->_valueCount; ++$i) {
176			$B[$i] = array($yValues[$i]);
177		}
178		$matrixA = new Matrix($A);
179		$matrixB = new Matrix($B);
180		$C = $matrixA->solve($matrixB);
181
182		$coefficients = array();
183		for($i = 0; $i < $C->m; ++$i) {
184			$r = $C->get($i, 0);
185			if (abs($r) <= pow(10, -9)) {
186				$r = 0;
187			}
188			$coefficients[] = $r;
189		}
190
191		$this->_intersect = array_shift($coefficients);
192		$this->_slope = $coefficients;
193
194		$this->_calculateGoodnessOfFit($x_sum,$y_sum,$xx_sum,$yy_sum,$xy_sum);
195		foreach($this->_xValues as $xKey => $xValue) {
196			$this->_yBestFitValues[$xKey] = $this->getValueOfYForX($xValue);
197		}
198	}	//	function _polynomial_regression()
199
200
201	/**
202	 * Define the regression and calculate the goodness of fit for a set of X and Y data values
203	 *
204	 * @param	int			$order		Order of Polynomial for this regression
205	 * @param	float[]		$yValues	The set of Y-values for this regression
206	 * @param	float[]		$xValues	The set of X-values for this regression
207	 * @param	boolean		$const
208	 */
209	function __construct($order, $yValues, $xValues=array(), $const=True) {
210		if (parent::__construct($yValues, $xValues) !== False) {
211			if ($order < $this->_valueCount) {
212				$this->_bestFitType .= '_'.$order;
213				$this->_order = $order;
214				$this->_polynomial_regression($order, $yValues, $xValues, $const);
215				if (($this->getGoodnessOfFit() < 0.0) || ($this->getGoodnessOfFit() > 1.0)) {
216					$this->_error = True;
217				}
218			} else {
219				$this->_error = True;
220			}
221		}
222	}	//	function __construct()
223
224}	//	class polynomialBestFit