' 發展單位:風禹科技驗證有限公司
' 撰寫人:鄭子璉(Tzu-Lien, Cheng, 璉璉) ,成大水利博肄,微軟最有價值專家
' Web: http://tlcheng.twbbs.org/TLCheng/ E-Mail: qvb3377@ms5.hinet.net
' --------------------------------------------------------------------------------------

Module modStatistic
	Public Enum DataGroup As Integer
		Sample = 0
		[Default] = Sample
		Population = 1
	End Enum

	Public Function GetMaxPercentError(ByVal arrSimulate() As Double, ByVal arrObserved() As Double, Optional ByVal LowerBound As Integer = 0) As Double
		' 最大誤差百分比
		Dim ub, nb, i As Integer
		Dim maxError, nowError As Double

		ub = UBound(arrSimulate)
		nb = ub - LowerBound + 1

		maxError = 0
		For i = LowerBound To ub
			nowError = Math.Abs(arrObserved(i) - arrSimulate(i)) / arrObserved(i)
			If maxError < nowError Then maxError = nowError
		Next

		Return maxError
	End Function

	Public Function GetCoefEfficiency(ByVal arrSimulate() As Double, ByVal arrObserved() As Double, Optional ByVal LowerBound As Integer = 0) As Double
		' 效率係數, Coefficient of Efficiency, CE
		Dim ub, nb, i As Integer
		Dim oMean, summy, summy1, summy2 As Double

		ub = UBound(arrSimulate)
		nb = ub - LowerBound + 1

		oMean = GetAverage(arrObserved, LowerBound)
		For i = LowerBound To ub
			summy1 += (arrObserved(i) - arrSimulate(i)) ^ 2
			summy2 += (arrObserved(i) - oMean) ^ 2
		Next
		summy = 1 - summy1 / summy2

		Return summy
	End Function

	Public Function GetPeekWeightRMSE(ByVal arrSimulate() As Double, ByVal arrObserved() As Double, Optional ByVal LowerBound As Integer = 0) As Double
		' 洪峰均方根物差
		' Peak-weighted root mean square error. This function is identical to
		'the calibration objective function included in computer program HEC-1(USACE, 1998).
		Dim ub, nb, i As Integer
		Dim oMean, summy As Double

		ub = UBound(arrSimulate)
		nb = ub - LowerBound + 1

		oMean = GetAverage(arrObserved)
		For i = LowerBound To ub
			summy += (arrSimulate(i) - arrObserved(i)) ^ 2 * (arrObserved(i) + oMean) / (2 * oMean)
		Next
		summy = Math.Sqrt(summy / nb)
		Return summy
	End Function

	Public Function GetRMSE(ByVal arrSimulate() As Double, ByVal arrObserved() As Double, Optional ByVal LowerBound As Integer = 0) As Double
		' 均方根誤差
		Dim ub, nb, i As Integer
		Dim oMean, summy As Double

		ub = UBound(arrSimulate)
		nb = ub - LowerBound + 1

		For i = LowerBound To ub
			summy += (arrSimulate(i) - arrObserved(i)) ^ 2
		Next
		summy = Math.Sqrt(summy) / nb
		Return summy
	End Function

	Public Function GetLeastSquare(ByVal arrSimulate() As Double, ByVal arrObserved() As Double, Optional ByVal LowerBound As Integer = 0) As Double
		' 最小平方法
		Dim ub, nb, i As Integer
		Dim oMean, summy As Double

		ub = UBound(arrSimulate)

		For i = LowerBound To ub
			summy += (arrSimulate(i) - arrObserved(i)) ^ 2
		Next
		Return summy
	End Function

	Public Function GetCorrelationCoefficient(ByVal arrSimulate() As Double, ByVal arrObserved() As Double, Optional ByVal LowerBound As Integer = 0) As Double
		' 相關係數
		Dim ub, nb, i As Integer
		Dim ax, ay, sx, sy, mxy, v_sum As Double

		ub = UBound(arrSimulate)
		nb = ub - LowerBound + 1

		ax = GetAverage(arrSimulate)
		ay = GetAverage(arrObserved)
		sx = GetStandardDeviation(arrSimulate, , , ax)
		sy = GetStandardDeviation(arrObserved, , , ay)
		mxy = ax * ay

		For i = LowerBound To ub
			v_sum += arrSimulate(i) * arrObserved(i) - mxy
		Next

		Return v_sum / ((nb - 1) * sx * sy)
	End Function

	Public Function LogRegression(ByVal arrX() As Double, ByVal arrY() As Double, Optional ByVal LogBase As Double = 0, Optional ByVal LowerBound As Integer = 0) As Double()
		' y=b*x^a => ln(y) = a*ln(x) + ln(b)
		Dim ub, i, nb As Integer
		Dim rtnArray() As Double

		ub = UBound(arrX)
		Dim logX(ub), logY(ub) As Double

		For i = LowerBound To ub
			nb += 1
			If LogBase > 0 Then
				logX(nb) = Math.Log(arrX(i), LogBase)
				logY(nb) = Math.Log(arrY(i), LogBase)
			Else ' 以自然數為底
				logX(nb) = Math.Log(arrX(i))
				logY(nb) = Math.Log(arrY(i))
			End If
			If Double.IsNegativeInfinity(logX(nb)) OrElse Double.IsNegativeInfinity(logY(nb)) Then nb -= 1
		Next

		ReDim Preserve logX(nb), logY(nb)
		rtnArray = LinearRegression(logX, logY, LowerBound)
		If LogBase > 0 Then ' 計算 b
			rtnArray(1) = LogBase ^ (rtnArray(1))
		Else
			rtnArray(1) = Math.Exp(rtnArray(1))
		End If

		Return rtnArray
	End Function

	Public Function LinearRegression(ByVal arrX() As Double, ByVal arrY() As Double, Optional ByVal LowerBound As Integer = 0) As Double()
		' y=ax+b
		Dim ub, nb, i As Integer
		Dim ax, ay, a_up, a_down, a, b As Double

		ub = UBound(arrX)

		ax = GetAverage(arrX, LowerBound)
		ay = GetAverage(arrY, LowerBound)

		For i = LowerBound To ub
			a_up += (arrX(i) - ax) * (arrY(i) - ay)
			a_down += (arrX(i) - ax) ^ 2
		Next

		a = a_up / a_down
		b = ay - a * ax

		Return New Double() {a, b}
	End Function

	Public Function GetAverage(ByVal arrValue() As Double, Optional ByVal LowerBound As Integer = 0) As Double
		' 平均
		Dim ub, nb, i As Integer
		Dim v_sum As Double

		ub = UBound(arrValue)
		nb = ub - LowerBound + 1

		For i = LowerBound To ub
			v_sum += arrValue(i)
		Next

		Return v_sum / nb
	End Function

	Public Function GetStandardDeviation(ByVal arrValue() As Double, Optional ByVal DataGroupFlag As DataGroup = DataGroup.Default, Optional ByVal LowerBound As Integer = 0, Optional ByVal AverageOfValue As Object = Nothing) As Double
		Dim ub, nb, i As Integer
		Dim av, v_sum As Double

		ub = UBound(arrValue)
		nb = ub - LowerBound + 1

		If IsNothing(AverageOfValue) Then
			av = GetAverage(arrValue)
		Else
			av = AverageOfValue
		End If

		For i = LowerBound To ub
			v_sum = v_sum + (arrValue(i) - av) ^ 2
		Next

		Select Case DataGroupFlag
			Case DataGroup.Default, DataGroup.Sample
				Return Math.Sqrt(v_sum / (nb - 1))
			Case DataGroup.Population
				Return Math.Sqrt(v_sum / nb)
		End Select
	End Function

End Module