forked from p30928647/excelize
ref #65, new formula functions: MINVERSE and MMULT
This commit is contained in:
parent
396cf99d45
commit
c0d341706d
174
calc.go
174
calc.go
|
@ -549,7 +549,9 @@ type formulaFuncs struct {
|
|||
// MINA
|
||||
// MINIFS
|
||||
// MINUTE
|
||||
// MINVERSE
|
||||
// MIRR
|
||||
// MMULT
|
||||
// MOD
|
||||
// MODE
|
||||
// MODE.MULT
|
||||
|
@ -4042,39 +4044,169 @@ func det(sqMtx [][]float64) float64 {
|
|||
return res
|
||||
}
|
||||
|
||||
// newNumberMatrix converts a formula arguments matrix to a number matrix.
|
||||
func newNumberMatrix(arg formulaArg, phalanx bool) (numMtx [][]float64, ele formulaArg) {
|
||||
rows := len(arg.Matrix)
|
||||
for r, row := range arg.Matrix {
|
||||
if phalanx && len(row) != rows {
|
||||
ele = newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
|
||||
return
|
||||
}
|
||||
numMtx = append(numMtx, make([]float64, len(row)))
|
||||
for c, cell := range row {
|
||||
if ele = cell.ToNumber(); ele.Type != ArgNumber {
|
||||
return
|
||||
}
|
||||
numMtx[r][c] = ele.Number
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// newFormulaArgMatrix converts the number formula arguments matrix to a
|
||||
// formula arguments matrix.
|
||||
func newFormulaArgMatrix(numMtx [][]float64) (arg [][]formulaArg) {
|
||||
for r, row := range numMtx {
|
||||
arg = append(arg, make([]formulaArg, len(row)))
|
||||
for c, cell := range row {
|
||||
arg[r][c] = newNumberFormulaArg(cell)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MDETERM calculates the determinant of a square matrix. The
|
||||
// syntax of the function is:
|
||||
//
|
||||
// MDETERM(array)
|
||||
//
|
||||
func (fn *formulaFuncs) MDETERM(argsList *list.List) (result formulaArg) {
|
||||
var (
|
||||
num float64
|
||||
numMtx [][]float64
|
||||
err error
|
||||
strMtx [][]formulaArg
|
||||
)
|
||||
if argsList.Len() < 1 {
|
||||
return newErrorFormulaArg(formulaErrorVALUE, "MDETERM requires at least 1 argument")
|
||||
return newErrorFormulaArg(formulaErrorVALUE, "MDETERM requires 1 argument")
|
||||
}
|
||||
strMtx = argsList.Front().Value.(formulaArg).Matrix
|
||||
rows := len(strMtx)
|
||||
for _, row := range argsList.Front().Value.(formulaArg).Matrix {
|
||||
if len(row) != rows {
|
||||
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
|
||||
}
|
||||
var numRow []float64
|
||||
for _, ele := range row {
|
||||
if num, err = strconv.ParseFloat(ele.String, 64); err != nil {
|
||||
return newErrorFormulaArg(formulaErrorVALUE, err.Error())
|
||||
}
|
||||
numRow = append(numRow, num)
|
||||
}
|
||||
numMtx = append(numMtx, numRow)
|
||||
numMtx, errArg := newNumberMatrix(argsList.Front().Value.(formulaArg), true)
|
||||
if errArg.Type == ArgError {
|
||||
return errArg
|
||||
}
|
||||
return newNumberFormulaArg(det(numMtx))
|
||||
}
|
||||
|
||||
// cofactorMatrix returns the matrix A of cofactors.
|
||||
func cofactorMatrix(i, j int, A [][]float64) float64 {
|
||||
N, sign := len(A), -1.0
|
||||
if (i+j)%2 == 0 {
|
||||
sign = 1
|
||||
}
|
||||
var B [][]float64
|
||||
for _, row := range A {
|
||||
B = append(B, row)
|
||||
}
|
||||
for m := 0; m < N; m++ {
|
||||
for n := j + 1; n < N; n++ {
|
||||
B[m][n-1] = B[m][n]
|
||||
}
|
||||
B[m] = B[m][:len(B[m])-1]
|
||||
}
|
||||
for k := i + 1; k < N; k++ {
|
||||
B[k-1] = B[k]
|
||||
}
|
||||
B = B[:len(B)-1]
|
||||
return sign * det(B)
|
||||
}
|
||||
|
||||
// adjugateMatrix returns transpose of the cofactor matrix A with Cramer's
|
||||
// rule.
|
||||
func adjugateMatrix(A [][]float64) (adjA [][]float64) {
|
||||
N := len(A)
|
||||
var B [][]float64
|
||||
for i := 0; i < N; i++ {
|
||||
adjA = append(adjA, make([]float64, N))
|
||||
for j := 0; j < N; j++ {
|
||||
for m := 0; m < N; m++ {
|
||||
for n := 0; n < N; n++ {
|
||||
for x := len(B); x <= m; x++ {
|
||||
B = append(B, []float64{})
|
||||
}
|
||||
for k := len(B[m]); k <= n; k++ {
|
||||
B[m] = append(B[m], 0)
|
||||
}
|
||||
B[m][n] = A[m][n]
|
||||
}
|
||||
}
|
||||
adjA[i][j] = cofactorMatrix(j, i, B)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MINVERSE function calculates the inverse of a square matrix. The syntax of
|
||||
// the function is:
|
||||
//
|
||||
// MINVERSE(array)
|
||||
//
|
||||
func (fn *formulaFuncs) MINVERSE(argsList *list.List) formulaArg {
|
||||
if argsList.Len() != 1 {
|
||||
return newErrorFormulaArg(formulaErrorVALUE, "MINVERSE requires 1 argument")
|
||||
}
|
||||
numMtx, errArg := newNumberMatrix(argsList.Front().Value.(formulaArg), true)
|
||||
if errArg.Type == ArgError {
|
||||
return errArg
|
||||
}
|
||||
if detM := det(numMtx); detM != 0 {
|
||||
datM, invertM := 1/detM, adjugateMatrix(numMtx)
|
||||
for i := 0; i < len(invertM); i++ {
|
||||
for j := 0; j < len(invertM[i]); j++ {
|
||||
invertM[i][j] *= datM
|
||||
}
|
||||
}
|
||||
return newMatrixFormulaArg(newFormulaArgMatrix(invertM))
|
||||
}
|
||||
return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)
|
||||
}
|
||||
|
||||
// MMULT function calculates the matrix product of two arrays
|
||||
// (representing matrices). The syntax of the function is:
|
||||
//
|
||||
// MMULT(array1,array2)
|
||||
//
|
||||
func (fn *formulaFuncs) MMULT(argsList *list.List) formulaArg {
|
||||
if argsList.Len() != 2 {
|
||||
return newErrorFormulaArg(formulaErrorVALUE, "MMULT requires 2 argument")
|
||||
}
|
||||
numMtx1, errArg1 := newNumberMatrix(argsList.Front().Value.(formulaArg), false)
|
||||
if errArg1.Type == ArgError {
|
||||
return errArg1
|
||||
}
|
||||
numMtx2, errArg2 := newNumberMatrix(argsList.Back().Value.(formulaArg), false)
|
||||
if errArg2.Type == ArgError {
|
||||
return errArg2
|
||||
}
|
||||
array2Rows, array2Cols := len(numMtx2), len(numMtx2[0])
|
||||
if len(numMtx1[0]) != array2Rows {
|
||||
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
|
||||
}
|
||||
var numMtx [][]float64
|
||||
var row1, row []float64
|
||||
var sum float64
|
||||
for i := 0; i < len(numMtx1); i++ {
|
||||
numMtx = append(numMtx, []float64{})
|
||||
row = []float64{}
|
||||
row1 = numMtx1[i]
|
||||
for j := 0; j < array2Cols; j++ {
|
||||
sum = 0
|
||||
for k := 0; k < array2Rows; k++ {
|
||||
sum += row1[k] * numMtx2[k][j]
|
||||
}
|
||||
for l := len(row); l <= j; l++ {
|
||||
row = append(row, 0)
|
||||
}
|
||||
row[j] = sum
|
||||
numMtx[i] = row
|
||||
}
|
||||
}
|
||||
return newMatrixFormulaArg(newFormulaArgMatrix(numMtx))
|
||||
}
|
||||
|
||||
// MOD function returns the remainder of a division between two supplied
|
||||
// numbers. The syntax of the function is:
|
||||
//
|
||||
|
|
16
calc_test.go
16
calc_test.go
|
@ -571,6 +571,10 @@ func TestCalcCellValue(t *testing.T) {
|
|||
"=IMPRODUCT(\"1-i\",\"5+10i\",2)": "30+10i",
|
||||
"=IMPRODUCT(COMPLEX(5,2),COMPLEX(0,1))": "-2+5i",
|
||||
"=IMPRODUCT(A1:C1)": "4",
|
||||
// MINVERSE
|
||||
"=MINVERSE(A1:B2)": "",
|
||||
// MMULT
|
||||
"=MMULT(A4:A4,A4:A4)": "",
|
||||
// MOD
|
||||
"=MOD(6,4)": "2",
|
||||
"=MOD(6,3)": "0",
|
||||
|
@ -2336,7 +2340,17 @@ func TestCalcCellValue(t *testing.T) {
|
|||
"=LOG10()": "LOG10 requires 1 numeric argument",
|
||||
`=LOG10("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax",
|
||||
// MDETERM
|
||||
"MDETERM()": "MDETERM requires at least 1 argument",
|
||||
"=MDETERM()": "MDETERM requires 1 argument",
|
||||
// MINVERSE
|
||||
"=MINVERSE()": "MINVERSE requires 1 argument",
|
||||
"=MINVERSE(B3:C4)": "strconv.ParseFloat: parsing \"\": invalid syntax",
|
||||
"=MINVERSE(A1:C2)": "#VALUE!",
|
||||
"=MINVERSE(A4:A4)": "#NUM!",
|
||||
// MMULT
|
||||
"=MMULT()": "MMULT requires 2 argument",
|
||||
"=MMULT(A1:B2,B3:C4)": "strconv.ParseFloat: parsing \"\": invalid syntax",
|
||||
"=MMULT(B3:C4,A1:B2)": "strconv.ParseFloat: parsing \"\": invalid syntax",
|
||||
"=MMULT(A1:A2,B1:B2)": "#VALUE!",
|
||||
// MOD
|
||||
"=MOD()": "MOD requires 2 numeric arguments",
|
||||
"=MOD(6,0)": "MOD divide by zero",
|
||||
|
|
Loading…
Reference in New Issue