From aee7bdd3a07cbf7f436bf79461b170a3823c3fa1 Mon Sep 17 00:00:00 2001 From: xuri Date: Thu, 10 Mar 2022 00:05:20 +0800 Subject: [PATCH] ref #65, new formula functions: F.INV and GAUSS --- calc.go | 59 +++++++++++++++++++++++++++++++++++++++++++++++----- calc_test.go | 18 ++++++++++++++++ 2 files changed, 72 insertions(+), 5 deletions(-) diff --git a/calc.go b/calc.go index ada84963..2d1aa0e6 100644 --- a/calc.go +++ b/calc.go @@ -417,6 +417,7 @@ type formulaFuncs struct { // FALSE // FIND // FINDB +// F.INV // F.INV.RT // FINV // FISHER @@ -430,6 +431,7 @@ type formulaFuncs struct { // FVSCHEDULE // GAMMA // GAMMALN +// GAUSS // GCD // GEOMEAN // GESTEP @@ -6064,6 +6066,28 @@ func (fn *formulaFuncs) GAMMALN(argsList *list.List) formulaArg { return newErrorFormulaArg(formulaErrorVALUE, "GAMMALN requires 1 numeric argument") } +// GAUSS function returns the probability that a member of a standard normal +// population will fall between the mean and a specified number of standard +// deviations from the mean. The syntax of the function is: +// +// GAUSS(z) +// +func (fn *formulaFuncs) GAUSS(argsList *list.List) formulaArg { + if argsList.Len() != 1 { + return newErrorFormulaArg(formulaErrorVALUE, "GAUSS requires 1 numeric argument") + } + args := list.New().Init() + args.PushBack(argsList.Front().Value.(formulaArg)) + args.PushBack(formulaArg{Type: ArgNumber, Number: 0}) + args.PushBack(formulaArg{Type: ArgNumber, Number: 1}) + args.PushBack(newBoolFormulaArg(true)) + normdist := fn.NORMDIST(args) + if normdist.Type != ArgNumber { + return normdist + } + return newNumberFormulaArg(normdist.Number - 0.5) +} + // GEOMEAN function calculates the geometric mean of a supplied set of values. // The syntax of the function is: // @@ -6208,8 +6232,9 @@ func (fn *formulaFuncs) EXPONDIST(argsList *list.List) formulaArg { return newNumberFormulaArg(lambda.Number * math.Exp(-lambda.Number*x.Number)) } -// finv is an implementation of the formula functions F.INV.RT and FINV. -func (fn *formulaFuncs) finv(name string, argsList *list.List) formulaArg { +// prepareFinvArgs checking and prepare arguments for the formula function +// F.INV, F.INV.RT and FINV. +func (fn *formulaFuncs) prepareFinvArgs(name string, argsList *list.List) formulaArg { if argsList.Len() != 3 { return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires 3 arguments", name)) } @@ -6232,7 +6257,21 @@ func (fn *formulaFuncs) finv(name string, argsList *list.List) formulaArg { if d2.Number < 1 || d2.Number >= math.Pow10(10) { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } - return newNumberFormulaArg((1/calcBetainv(1.0-(1.0-probability.Number), d2.Number/2, d1.Number/2, 0, 1) - 1.0) * (d2.Number / d1.Number)) + return newListFormulaArg([]formulaArg{probability, d1, d2}) +} + +// FdotINV function calculates the inverse of the Cumulative F Distribution +// for a supplied probability. The syntax of the F.Inv function is: +// +// F.INV(probability,deg_freedom1,deg_freedom2) +// +func (fn *formulaFuncs) FdotINV(argsList *list.List) formulaArg { + args := fn.prepareFinvArgs("F.INV", argsList) + if args.Type != ArgList { + return args + } + probability, d1, d2 := args.List[0], args.List[1], args.List[2] + return newNumberFormulaArg((1/calcBetainv(1-probability.Number, d2.Number/2, d1.Number/2, 0, 1) - 1) * (d2.Number / d1.Number)) } // FdotINVdotRT function calculates the inverse of the (right-tailed) F @@ -6242,7 +6281,12 @@ func (fn *formulaFuncs) finv(name string, argsList *list.List) formulaArg { // F.INV.RT(probability,deg_freedom1,deg_freedom2) // func (fn *formulaFuncs) FdotINVdotRT(argsList *list.List) formulaArg { - return fn.finv("F.INV.RT", argsList) + args := fn.prepareFinvArgs("F.INV.RT", argsList) + if args.Type != ArgList { + return args + } + probability, d1, d2 := args.List[0], args.List[1], args.List[2] + return newNumberFormulaArg((1/calcBetainv(1-(1-probability.Number), d2.Number/2, d1.Number/2, 0, 1) - 1) * (d2.Number / d1.Number)) } // FINV function calculates the inverse of the (right-tailed) F Probability @@ -6251,7 +6295,12 @@ func (fn *formulaFuncs) FdotINVdotRT(argsList *list.List) formulaArg { // FINV(probability,deg_freedom1,deg_freedom2) // func (fn *formulaFuncs) FINV(argsList *list.List) formulaArg { - return fn.finv("FINV", argsList) + args := fn.prepareFinvArgs("FINV", argsList) + if args.Type != ArgList { + return args + } + probability, d1, d2 := args.List[0], args.List[1], args.List[2] + return newNumberFormulaArg((1/calcBetainv(1-(1-probability.Number), d2.Number/2, d1.Number/2, 0, 1) - 1) * (d2.Number / d1.Number)) } // NORMdotDIST function calculates the Normal Probability Density Function or diff --git a/calc_test.go b/calc_test.go index 63e5984e..9804cc9f 100644 --- a/calc_test.go +++ b/calc_test.go @@ -838,6 +838,11 @@ func TestCalcCellValue(t *testing.T) { // GAMMALN "=GAMMALN(4.5)": "2.45373657084244", "=GAMMALN(INT(1))": "0", + // GAUSS + "=GAUSS(-5)": "-0.499999713348428", + "=GAUSS(0)": "0", + "=GAUSS(0.1)": "0.039827837277029", + "=GAUSS(2.5)": "0.493790334674224", // GEOMEAN "=GEOMEAN(2.5,3,0.5,1,3)": "1.6226711115996", // HARMEAN @@ -855,6 +860,8 @@ func TestCalcCellValue(t *testing.T) { "=EXPONDIST(0.5,1,TRUE)": "0.393469340287367", "=EXPONDIST(0.5,1,FALSE)": "0.606530659712633", "=EXPONDIST(2,1,TRUE)": "0.864664716763387", + // F.INV + "=F.INV(0.9,2,5)": "3.77971607877395", // FINV "=FINV(0.2,1,2)": "3.55555555555555", "=FINV(0.6,1,2)": "0.380952380952381", @@ -2380,6 +2387,9 @@ func TestCalcCellValue(t *testing.T) { "=GAMMALN(F1)": "GAMMALN requires 1 numeric argument", "=GAMMALN(0)": "#N/A", "=GAMMALN(INT(0))": "#N/A", + // GAUSS + "=GAUSS()": "GAUSS requires 1 numeric argument", + "=GAUSS(\"\")": "strconv.ParseFloat: parsing \"\": invalid syntax", // GEOMEAN "=GEOMEAN()": "GEOMEAN requires at least 1 numeric argument", "=GEOMEAN(0)": "#NUM!", @@ -2405,6 +2415,14 @@ func TestCalcCellValue(t *testing.T) { "=EXPONDIST(0,1,\"\")": "strconv.ParseBool: parsing \"\": invalid syntax", "=EXPONDIST(-1,1,TRUE)": "#NUM!", "=EXPONDIST(1,0,TRUE)": "#NUM!", + // F.INV + "=F.INV()": "F.INV requires 3 arguments", + "=F.INV(\"\",1,2)": "strconv.ParseFloat: parsing \"\": invalid syntax", + "=F.INV(0.2,\"\",2)": "strconv.ParseFloat: parsing \"\": invalid syntax", + "=F.INV(0.2,1,\"\")": "strconv.ParseFloat: parsing \"\": invalid syntax", + "=F.INV(0,1,2)": "#NUM!", + "=F.INV(0.2,0.5,2)": "#NUM!", + "=F.INV(0.2,1,0.5)": "#NUM!", // FINV "=FINV()": "FINV requires 3 arguments", "=FINV(\"\",1,2)": "strconv.ParseFloat: parsing \"\": invalid syntax",