package org.opengion.penguin.math.statistics;

import java.util.Arrays;
import org.apache.commons.math3.stat.regression.SimpleRegression;

/**
 * apache.commons.mathを利用した線形単回帰計算のクラスです。
 * f(x)=ax+bの形で線形回帰を行います。
 */
// public class HybsSimpleRegression implements HybsSingleRegression {
public class HybsSimpleRegression implements HybsRegression {
	private final double[] cnst = new double[3] ;		// 係数(０次、１次、２次は常に０)

//	private double slope;		// 傾き
//	private double intercept;	// 切片
	private double rsquare;		// 決定係数

	/**
	 * コンストラクタ。
	 * 与えた二次元データを元に回帰直線を計算します。
	 * {x,y}の配列でデータを与えます。
	 *
	 * @param data xとyの組み合わせの配列
	 */
	public HybsSimpleRegression( final double[][] data ) {
		// ここで単回帰計算
		train( data );
	}

//	/**
//	 * コンストラクタ。
//	 * 計算後の傾き、切片を与えられるようにしておきます。
//	 * （以前に計算したものを利用）
//	 * @param slope 傾き
//	 * @param intercept 切片
//	 * 
//	 */
//	public HybsSimpleRegression( final double slope, final double intercept ) {
//		this.slope = slope;
//		this.intercept = intercept;
//	}

//	/**
//	 * コンストラクタ
//	 * このコンストラクタを利用した場合はtrainを実施して学習するか、setCoefficientで係数をセットする。
//	 */
//	public HybsSimpleRegression() {
//		super();
//		//何もしない
//	}

	/**
	 * dataを与えて回帰直線を求める。
	 *
	 * @param data {x,y}の配列
	 */
	private void train( final double[][] data ) {
		final SimpleRegression regression = new SimpleRegression();
		regression.addData( data );
//		slope = regression.getSlope();
//		intercept = regression.getIntercept();

		cnst[2] = 0 ;							// ２次係数は、常に０
		cnst[1] = regression.getSlope();
		cnst[0] = regression.getIntercept();

		rsquare = regression.getRSquare();
	}

//	/**
//	 * このクラスでは未使用。
//	 *
//	 * @param opt オプション
//	 */
//	@Override
//	public void setOption( final double[] opt ) {
//		// 特にオプションなし
//	}

//	/**
//	 * 傾きの取得。
//	 * @return 傾き
//	 */
//	public double getSlope() {
//		return slope;
//	}
//
//	/**
//	 * 切片の取得。
//	 * @return 切片
//	 */
//	public double getIntercept() {
//		return intercept;
//	}

	/**
	 * 決定係数の取得。
	 *
	 * @return 決定係数
	 */
	@Override
	public double getRSquare() {
		return rsquare;
	}

	/**
	 * 係数(０次、１次、２次は常に０)の順にセットした配列を返します。
	 *
	 * @return 係数の配列
	 */
	@Override
	public double[] getCoefficient() {
//		final double[] rtn = {slope,intercept,rsquare};
//		return rtn;
		return Arrays.copyOf( cnst,cnst.length );
	}

//	/**
//	 * 傾き、切片の順に配列の内容をセットします。
//	 * 3番目に決定係数が入っていても無視します。
//	 * 
//	 * @param cV 係数配列
//	 */
//	public void setCoefficient(final double[] cV) {
//		slope = cV[0];
//		intercept = cV[1];
//	}

	/**
	 * a + bxを計算。
	 *
	 * @param in_x 必要な大きさの変数配列
	 * @return 計算結果
	 */
//	public double predict(final double xV) {
	@Override
	public double predict( final double... in_x ) {
//		return intercept + slope * xV;
		return cnst[1] * in_x[0] + cnst[0];
	}

	//************** ここまでが本体 **************
	/**
	 * ここからテスト用mainメソッド 。
	 *
	 * @param args 引数
	 */
	public static void main( final String[] args ) {
		final double[][] data = {{1, 2.3}, {2, 3.4}, {3, 6.1}, {4, 8.2}}; 

		final HybsSimpleRegression sr = new HybsSimpleRegression(data);

		final double[] cnst = sr.getCoefficient();

		System.out.println(cnst[2]);
		System.out.println(cnst[1]);
		System.out.println(cnst[0]);

//		System.out.println(sr.getIntercept());
//		System.out.println(sr.getSlope());
//		System.out.println(sr.getRSquare());

		System.out.println(sr.predict( 5 ));
	}
}

