// FittingPolynomial
/*
------------------------------------------------------
作者   : Black Ghost
日期   : 2018-12-11
版本   : 0.0.0
------------------------------------------------------
    多项式拟合
理论：
    对于单自变量单因变量的N个数据对
    假设其一个低于N-1次的多项式为：
    y(x) = a0 + a1x + a2x^2 + ... + amx^m (m < N-1)
    建立矛盾方程组Ax=b，即

     N
    Sum ai*xj^i = bj   (i=0, 1, 2, ..., m)
    j=1

    求解ai (i=0, 1, 2, ..., m)代入多项式即得拟合函数

    参考 李信真, 车刚明, 欧阳洁, 等. 计算方法. 西北工业大学
       出版社, 2000, pp 136-138.
------------------------------------------------------
输入   :
    xy      单自变量单因变量的N个数据对，Nx2
    m       多项式次数，m < N-1
输出   :
    sol     解向量，从0到m对应a0到am
    RMS     均方误差
    MaxErr  最大误差
    err     解出标志：false-未解出或达到步数上限；
                     true-全部解出
------------------------------------------------------
扩展   ：
    可以修改为适应log、exp、sin等拟合方法
------------------------------------------------------
*/

package goNum

import (
	"math"
)

// FittingPolynomial 多项式拟合
func FittingPolynomial(xy Matrix, m int) (Matrix, float64, float64, bool) {
	/*
		多项式拟合
		输入   :
		    xy      单自变量单因变量的N个数据对，Nx2
		    m       多项式次数，m < N-1
		输出   :
		    sol     解向量，从0到m对应a0到am
		    RMS     均方误差
		    MaxErr  最大误差
		    err     解出标志：false-未解出或达到步数上限；
		                     true-全部解出
	*/
	//判断m是否小于N-1
	if (m > xy.Rows-2) || (m < 0) {
		panic("Error in goNum.FittingPolynomial: Order m is wrong number")
	}

	N := xy.Rows

	//构建矛盾方程组系数矩阵A, b=xy.ColumnOfMatrix(1)
	A := ZeroMatrix(N, m+1)
	for i := 0; i < N; i++ {
		A.SetMatrix(i, 0, 1.0)
		for j := 1; j < m+1; j++ {
			temp := xy.GetFromMatrix(i, 0)
			A.SetMatrix(i, j, math.Pow(temp, float64(j)))
		}
	}
	//求解矛盾方程组
	sol, err := InconsistentLSQ(A, Slices1ToMatrix(xy.ColumnOfMatrix(1)))
	//判断结果
	if err != true {
		panic("Error in goNum.FittingPolynomial: Solve error")
	}

	errSub := make([]float64, N)
	var RMS float64
	for i := 0; i < N; i++ {
		fit := sol.Data[0]
		for j := 1; j < m+1; j++ {
			fit += sol.Data[j] * math.Pow(xy.GetFromMatrix(i, 0), float64(j))
		}
		errSub[i] = fit - xy.GetFromMatrix(i, 1)
		RMS += errSub[i] * errSub[i]
	}
	RMS = math.Sqrt(RMS)
	MaxErr, _, _ := MaxAbs(errSub)

	return sol, RMS, math.Abs(MaxErr), err
}
