From e1d660dda7c02475a8529a252e6c1fd171065429 Mon Sep 17 00:00:00 2001 From: xuri Date: Sat, 12 Mar 2022 00:45:27 +0800 Subject: [PATCH] ref #65, new formula functions: GAMMA.INV and GAMMAINV and format code --- calc.go | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++ calc_test.go | 24 ++++++++++++++++++ drawing.go | 2 +- 3 files changed, 96 insertions(+), 1 deletion(-) diff --git a/calc.go b/calc.go index 1f1408e..e64d260 100644 --- a/calc.go +++ b/calc.go @@ -432,6 +432,8 @@ type formulaFuncs struct { // GAMMA // GAMMA.DIST // GAMMADIST +// GAMMA.INV +// GAMMAINV // GAMMALN // GAUSS // GCD @@ -6088,6 +6090,75 @@ func (fn *formulaFuncs) GAMMADIST(argsList *list.List) formulaArg { return newNumberFormulaArg((1 / (math.Pow(beta.Number, alpha.Number) * math.Gamma(alpha.Number))) * math.Pow(x.Number, (alpha.Number-1)) * math.Exp(0-(x.Number/beta.Number))) } +// gammainv returns the inverse of the Gamma distribution for the specified +// value. +func gammainv(probability, alpha, beta float64) float64 { + xLo, xHi := 0.0, alpha*beta*5 + dx, x, xNew, result := 1024.0, 1.0, 1.0, 0.0 + for i := 0; math.Abs(dx) > 8.88e-016 && i <= 256; i++ { + result = incompleteGamma(alpha, x/beta) / math.Gamma(alpha) + error := result - probability + if error == 0 { + dx = 0 + } else if error < 0 { + xLo = x + } else { + xHi = x + } + pdf := (1 / (math.Pow(beta, alpha) * math.Gamma(alpha))) * math.Pow(x, (alpha-1)) * math.Exp(0-(x/beta)) + if pdf != 0 { + dx = error / pdf + xNew = x - dx + } + if xNew < xLo || xNew > xHi || pdf == 0 { + xNew = (xLo + xHi) / 2 + dx = xNew - x + } + x = xNew + } + return x +} + +// GAMMAdotINV function returns the inverse of the Gamma Cumulative +// Distribution. The syntax of the function is: +// +// GAMMA.INV(probability,alpha,beta) +// +func (fn *formulaFuncs) GAMMAdotINV(argsList *list.List) formulaArg { + if argsList.Len() != 3 { + return newErrorFormulaArg(formulaErrorVALUE, "GAMMA.INV requires 3 arguments") + } + return fn.GAMMAINV(argsList) +} + +// GAMMAINV function returns the inverse of the Gamma Cumulative Distribution. +// The syntax of the function is: +// +// GAMMAINV(probability,alpha,beta) +// +func (fn *formulaFuncs) GAMMAINV(argsList *list.List) formulaArg { + if argsList.Len() != 3 { + return newErrorFormulaArg(formulaErrorVALUE, "GAMMAINV requires 3 arguments") + } + var probability, alpha, beta formulaArg + if probability = argsList.Front().Value.(formulaArg).ToNumber(); probability.Type != ArgNumber { + return probability + } + if probability.Number < 0 || probability.Number >= 1 { + return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) + } + if alpha = argsList.Front().Next().Value.(formulaArg).ToNumber(); alpha.Type != ArgNumber { + return alpha + } + if beta = argsList.Back().Value.(formulaArg).ToNumber(); beta.Type != ArgNumber { + return beta + } + if alpha.Number <= 0 || beta.Number <= 0 { + return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) + } + return newNumberFormulaArg(gammainv(probability.Number, alpha.Number, beta.Number)) +} + // GAMMALN function returns the natural logarithm of the Gamma Function, Γ // (n). The syntax of the function is: // diff --git a/calc_test.go b/calc_test.go index 0c9fb2e..6407685 100644 --- a/calc_test.go +++ b/calc_test.go @@ -841,6 +841,12 @@ func TestCalcCellValue(t *testing.T) { // GAMMADIST "=GAMMADIST(6,3,2,FALSE)": "0.112020903827694", "=GAMMADIST(6,3,2,TRUE)": "0.576809918873156", + // GAMMA.INV + "=GAMMA.INV(0.5,3,2)": "5.348120627447122", + "=GAMMA.INV(0.5,0.5,1)": "0.227468211559786", + // GAMMAINV + "=GAMMAINV(0.5,3,2)": "5.348120627447122", + "=GAMMAINV(0.5,0.5,1)": "0.227468211559786", // GAMMALN "=GAMMALN(4.5)": "2.45373657084244", "=GAMMALN(INT(1))": "0", @@ -2406,6 +2412,24 @@ func TestCalcCellValue(t *testing.T) { "=GAMMADIST(-1,3,2,FALSE)": "#NUM!", "=GAMMADIST(6,0,2,FALSE)": "#NUM!", "=GAMMADIST(6,3,0,FALSE)": "#NUM!", + // GAMMA.INV + "=GAMMA.INV()": "GAMMA.INV requires 3 arguments", + "=GAMMA.INV(\"\",3,2)": "strconv.ParseFloat: parsing \"\": invalid syntax", + "=GAMMA.INV(0.5,\"\",2)": "strconv.ParseFloat: parsing \"\": invalid syntax", + "=GAMMA.INV(0.5,3,\"\")": "strconv.ParseFloat: parsing \"\": invalid syntax", + "=GAMMA.INV(-1,3,2)": "#NUM!", + "=GAMMA.INV(2,3,2)": "#NUM!", + "=GAMMA.INV(0.5,0,2)": "#NUM!", + "=GAMMA.INV(0.5,3,0)": "#NUM!", + // GAMMAINV + "=GAMMAINV()": "GAMMAINV requires 3 arguments", + "=GAMMAINV(\"\",3,2)": "strconv.ParseFloat: parsing \"\": invalid syntax", + "=GAMMAINV(0.5,\"\",2)": "strconv.ParseFloat: parsing \"\": invalid syntax", + "=GAMMAINV(0.5,3,\"\")": "strconv.ParseFloat: parsing \"\": invalid syntax", + "=GAMMAINV(-1,3,2)": "#NUM!", + "=GAMMAINV(2,3,2)": "#NUM!", + "=GAMMAINV(0.5,0,2)": "#NUM!", + "=GAMMAINV(0.5,3,0)": "#NUM!", // GAMMALN "=GAMMALN()": "GAMMALN requires 1 numeric argument", "=GAMMALN(F1)": "GAMMALN requires 1 numeric argument", diff --git a/drawing.go b/drawing.go index 3af789f..e3e7fa8 100644 --- a/drawing.go +++ b/drawing.go @@ -514,7 +514,7 @@ func (f *File) drawBaseChart(formatSet *formatChart) *cPlotArea { // doughnut chart by given format sets. func (f *File) drawDoughnutChart(formatSet *formatChart) *cPlotArea { holeSize := 75 - if formatSet.HoleSize > 0 && formatSet.HoleSize <= 90{ + if formatSet.HoleSize > 0 && formatSet.HoleSize <= 90 { holeSize = formatSet.HoleSize }