From d9b5afc1ac4e085b7f2e6838cb13df6ae6962b7f Mon Sep 17 00:00:00 2001 From: xuri Date: Fri, 1 Apr 2022 00:09:36 +0800 Subject: [PATCH] ref #65, new formula functions: NEGBINOM.DIST and NEGBINOMDIST --- calc.go | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++ calc_test.go | 29 ++++++++++++++++++++++++ 2 files changed, 92 insertions(+) diff --git a/calc.go b/calc.go index f279b34..5bad72a 100644 --- a/calc.go +++ b/calc.go @@ -555,6 +555,8 @@ type formulaFuncs struct { // MUNIT // N // NA +// NEGBINOM.DIST +// NEGBINOMDIST // NOMINAL // NORM.DIST // NORMDIST @@ -7983,6 +7985,67 @@ func (fn *formulaFuncs) LOGNORMDIST(argsList *list.List) formulaArg { return fn.NORMSDIST(args) } +// NEGBINOMdotDIST function calculates the probability mass function or the +// cumulative distribution function for the Negative Binomial Distribution. +// This gives the probability that there will be a given number of failures +// before a required number of successes is achieved. The syntax of the +// function is: +// +// NEGBINOM.DIST(number_f,number_s,probability_s,cumulative) +// +func (fn *formulaFuncs) NEGBINOMdotDIST(argsList *list.List) formulaArg { + if argsList.Len() != 4 { + return newErrorFormulaArg(formulaErrorVALUE, "NEGBINOM.DIST requires 4 arguments") + } + var f, s, probability, cumulative formulaArg + if f = argsList.Front().Value.(formulaArg).ToNumber(); f.Type != ArgNumber { + return f + } + if s = argsList.Front().Next().Value.(formulaArg).ToNumber(); s.Type != ArgNumber { + return s + } + if probability = argsList.Front().Next().Next().Value.(formulaArg).ToNumber(); probability.Type != ArgNumber { + return probability + } + if cumulative = argsList.Back().Value.(formulaArg).ToBool(); cumulative.Type != ArgNumber { + return cumulative + } + if f.Number < 0 || s.Number < 1 || probability.Number < 0 || probability.Number > 1 { + return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) + } + if cumulative.Number == 1 { + return newNumberFormulaArg(1 - getBetaDist(1-probability.Number, f.Number+1, s.Number)) + } + return newNumberFormulaArg(binomCoeff(f.Number+s.Number-1, s.Number-1) * math.Pow(probability.Number, s.Number) * math.Pow(1-probability.Number, f.Number)) +} + +// NEGBINOMDIST function calculates the Negative Binomial Distribution for a +// given set of parameters. This gives the probability that there will be a +// specified number of failures before a required number of successes is +// achieved. The syntax of the function is: +// +// NEGBINOMDIST(number_f,number_s,probability_s) +// +func (fn *formulaFuncs) NEGBINOMDIST(argsList *list.List) formulaArg { + if argsList.Len() != 3 { + return newErrorFormulaArg(formulaErrorVALUE, "NEGBINOMDIST requires 3 arguments") + } + var f, s, probability formulaArg + if f = argsList.Front().Value.(formulaArg).ToNumber(); f.Type != ArgNumber { + return f + } + if s = argsList.Front().Next().Value.(formulaArg).ToNumber(); s.Type != ArgNumber { + return s + } + if probability = argsList.Back().Value.(formulaArg).ToNumber(); probability.Type != ArgNumber { + return probability + } + if f.Number < 0 || s.Number < 1 || probability.Number < 0 || probability.Number > 1 { + return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) + } + return newNumberFormulaArg(binomCoeff(f.Number+s.Number-1, s.Number-1) * math.Pow(probability.Number, s.Number) * math.Pow(1-probability.Number, f.Number)) +} + // NORMdotDIST function calculates the Normal Probability Density Function or // the Cumulative Normal Distribution. Function for a supplied set of // parameters. The syntax of the function is: diff --git a/calc_test.go b/calc_test.go index 43f6a29..2b76ed3 100644 --- a/calc_test.go +++ b/calc_test.go @@ -1015,6 +1015,16 @@ func TestCalcCellValue(t *testing.T) { "=LOGNORM.DIST(12,10,5,TRUE)": "0.0664171147992078", // LOGNORMDIST "=LOGNORMDIST(12,10,5)": "0.0664171147992078", + // NEGBINOM.DIST + "=NEGBINOM.DIST(6,12,0.5,FALSE)": "0.047210693359375", + "=NEGBINOM.DIST(12,12,0.5,FALSE)": "0.0805901288986206", + "=NEGBINOM.DIST(15,12,0.5,FALSE)": "0.057564377784729", + "=NEGBINOM.DIST(12,12,0.5,TRUE)": "0.580590128898621", + "=NEGBINOM.DIST(15,12,0.5,TRUE)": "0.778965830802917", + // NEGBINOMDIST + "=NEGBINOMDIST(6,12,0.5)": "0.047210693359375", + "=NEGBINOMDIST(12,12,0.5)": "0.0805901288986206", + "=NEGBINOMDIST(15,12,0.5)": "0.057564377784729", // NORM.DIST "=NORM.DIST(0.8,1,0.3,TRUE)": "0.252492537546923", "=NORM.DIST(50,40,20,FALSE)": "0.017603266338215", @@ -2835,6 +2845,25 @@ func TestCalcCellValue(t *testing.T) { "=LOGNORMDIST(12,10,\"\")": "strconv.ParseFloat: parsing \"\": invalid syntax", "=LOGNORMDIST(0,2,5)": "#NUM!", "=LOGNORMDIST(12,10,0)": "#NUM!", + // NEGBINOM.DIST + "=NEGBINOM.DIST()": "NEGBINOM.DIST requires 4 arguments", + "=NEGBINOM.DIST(\"\",12,0.5,TRUE)": "strconv.ParseFloat: parsing \"\": invalid syntax", + "=NEGBINOM.DIST(6,\"\",0.5,TRUE)": "strconv.ParseFloat: parsing \"\": invalid syntax", + "=NEGBINOM.DIST(6,12,\"\",TRUE)": "strconv.ParseFloat: parsing \"\": invalid syntax", + "=NEGBINOM.DIST(6,12,0.5,\"\")": "strconv.ParseBool: parsing \"\": invalid syntax", + "=NEGBINOM.DIST(-1,12,0.5,TRUE)": "#NUM!", + "=NEGBINOM.DIST(6,0,0.5,TRUE)": "#NUM!", + "=NEGBINOM.DIST(6,12,-1,TRUE)": "#NUM!", + "=NEGBINOM.DIST(6,12,2,TRUE)": "#NUM!", + // NEGBINOMDIST + "=NEGBINOMDIST()": "NEGBINOMDIST requires 3 arguments", + "=NEGBINOMDIST(\"\",12,0.5)": "strconv.ParseFloat: parsing \"\": invalid syntax", + "=NEGBINOMDIST(6,\"\",0.5)": "strconv.ParseFloat: parsing \"\": invalid syntax", + "=NEGBINOMDIST(6,12,\"\")": "strconv.ParseFloat: parsing \"\": invalid syntax", + "=NEGBINOMDIST(-1,12,0.5)": "#NUM!", + "=NEGBINOMDIST(6,0,0.5)": "#NUM!", + "=NEGBINOMDIST(6,12,-1)": "#NUM!", + "=NEGBINOMDIST(6,12,2)": "#NUM!", // NORM.DIST "=NORM.DIST()": "NORM.DIST requires 4 arguments", // NORMDIST