summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorxuri <xuri.me@gmail.com>2022-03-08 00:03:02 +0800
committerxuri <xuri.me@gmail.com>2022-03-08 00:03:02 +0800
commit56aa6b82637b3210be470a8ebac1fdec2b2a6a30 (patch)
tree419760c01fe4dbc4bca48a378461702e37d82d87
parent61eb265c29685957bcf16b25dba3d389c548dfee (diff)
ref #65, new formula functions and read boolean data type cell value support
* added 3 new formula functions: BETAINV, BETA.INV, F.INV.RT
-rw-r--r--calc.go706
-rw-r--r--calc_test.go46
-rw-r--r--excelize_test.go9
-rw-r--r--rows.go10
-rw-r--r--rows_test.go4
5 files changed, 461 insertions, 314 deletions
diff --git a/calc.go b/calc.go
index e3db882..ada8496 100644
--- a/calc.go
+++ b/calc.go
@@ -333,6 +333,8 @@ type formulaFuncs struct {
// BESSELJ
// BESSELK
// BESSELY
+// BETAINV
+// BETA.INV
// BIN2DEC
// BIN2HEX
// BIN2OCT
@@ -415,6 +417,7 @@ type formulaFuncs struct {
// FALSE
// FIND
// FINDB
+// F.INV.RT
// FINV
// FISHER
// FISHERINV
@@ -5187,6 +5190,375 @@ func (fn *formulaFuncs) AVERAGEIF(argsList *list.List) formulaArg {
return newNumberFormulaArg(sum / count)
}
+// d1mach returns double precision real machine constants.
+func d1mach(i int) float64 {
+ arr := []float64{
+ 2.2250738585072014e-308,
+ 1.7976931348623158e+308,
+ 1.1102230246251565e-16,
+ 2.2204460492503131e-16,
+ 0.301029995663981195,
+ }
+ if i > len(arr) {
+ return 0
+ }
+ return arr[i-1]
+}
+
+// chebyshevInit determines the number of terms for the double precision
+// orthogonal series "dos" needed to insure the error is no larger
+// than "eta". Ordinarily eta will be chosen to be one-tenth machine
+// precision.
+func chebyshevInit(nos int, eta float64, dos []float64) int {
+ i, e := 0, 0.0
+ if nos < 1 {
+ return 0
+ }
+ for ii := 1; ii <= nos; ii++ {
+ i = nos - ii
+ e += math.Abs(dos[i])
+ if e > eta {
+ return i
+ }
+ }
+ return i
+}
+
+// chebyshevEval evaluates the n-term Chebyshev series "a" at "x".
+func chebyshevEval(n int, x float64, a []float64) float64 {
+ if n < 1 || n > 1000 || x < -1.1 || x > 1.1 {
+ return math.NaN()
+ }
+ twox, b0, b1, b2 := x*2, 0.0, 0.0, 0.0
+ for i := 1; i <= n; i++ {
+ b2 = b1
+ b1 = b0
+ b0 = twox*b1 - b2 + a[n-i]
+ }
+ return (b0 - b2) * 0.5
+}
+
+// lgammacor is an implementation for the log(gamma) correction.
+func lgammacor(x float64) float64 {
+ algmcs := []float64{
+ 0.1666389480451863247205729650822, -0.1384948176067563840732986059135e-4,
+ 0.9810825646924729426157171547487e-8, -0.1809129475572494194263306266719e-10,
+ 0.6221098041892605227126015543416e-13, -0.3399615005417721944303330599666e-15,
+ 0.2683181998482698748957538846666e-17, -0.2868042435334643284144622399999e-19,
+ 0.3962837061046434803679306666666e-21, -0.6831888753985766870111999999999e-23,
+ 0.1429227355942498147573333333333e-24, -0.3547598158101070547199999999999e-26,
+ 0.1025680058010470912000000000000e-27, -0.3401102254316748799999999999999e-29,
+ 0.1276642195630062933333333333333e-30,
+ }
+ nalgm := chebyshevInit(15, d1mach(3), algmcs)
+ xbig := 1.0 / math.Sqrt(d1mach(3))
+ xmax := math.Exp(math.Min(math.Log(d1mach(2)/12.0), -math.Log(12.0*d1mach(1))))
+ if x < 10.0 {
+ return math.NaN()
+ } else if x >= xmax {
+ return 4.930380657631324e-32
+ } else if x < xbig {
+ tmp := 10.0 / x
+ return chebyshevEval(nalgm, tmp*tmp*2.0-1.0, algmcs) / x
+ }
+ return 1.0 / (x * 12.0)
+}
+
+// logrelerr compute the relative error logarithm.
+func logrelerr(x float64) float64 {
+ alnrcs := []float64{
+ 0.10378693562743769800686267719098e+1, -0.13364301504908918098766041553133,
+ 0.19408249135520563357926199374750e-1, -0.30107551127535777690376537776592e-2,
+ 0.48694614797154850090456366509137e-3, -0.81054881893175356066809943008622e-4,
+ 0.13778847799559524782938251496059e-4, -0.23802210894358970251369992914935e-5,
+ 0.41640416213865183476391859901989e-6, -0.73595828378075994984266837031998e-7,
+ 0.13117611876241674949152294345011e-7, -0.23546709317742425136696092330175e-8,
+ 0.42522773276034997775638052962567e-9, -0.77190894134840796826108107493300e-10,
+ 0.14075746481359069909215356472191e-10, -0.25769072058024680627537078627584e-11,
+ 0.47342406666294421849154395005938e-12, -0.87249012674742641745301263292675e-13,
+ 0.16124614902740551465739833119115e-13, -0.29875652015665773006710792416815e-14,
+ 0.55480701209082887983041321697279e-15, -0.10324619158271569595141333961932e-15,
+ 0.19250239203049851177878503244868e-16, -0.35955073465265150011189707844266e-17,
+ 0.67264542537876857892194574226773e-18, -0.12602624168735219252082425637546e-18,
+ 0.23644884408606210044916158955519e-19, -0.44419377050807936898878389179733e-20,
+ 0.83546594464034259016241293994666e-21, -0.15731559416479562574899253521066e-21,
+ 0.29653128740247422686154369706666e-22, -0.55949583481815947292156013226666e-23,
+ 0.10566354268835681048187284138666e-23, -0.19972483680670204548314999466666e-24,
+ 0.37782977818839361421049855999999e-25, -0.71531586889081740345038165333333e-26,
+ 0.13552488463674213646502024533333e-26, -0.25694673048487567430079829333333e-27,
+ 0.48747756066216949076459519999999e-28, -0.92542112530849715321132373333333e-29,
+ 0.17578597841760239233269760000000e-29, -0.33410026677731010351377066666666e-30,
+ 0.63533936180236187354180266666666e-31,
+ }
+ nlnrel := chebyshevInit(43, 0.1*d1mach(3), alnrcs)
+ if x <= -1 {
+ return math.NaN()
+ }
+ if math.Abs(x) <= 0.375 {
+ return x * (1.0 - x*chebyshevEval(nlnrel, x/0.375, alnrcs))
+ }
+ return math.Log(x + 1.0)
+}
+
+// logBeta is an implementation for the log of the beta distribution
+// function.
+func logBeta(a, b float64) float64 {
+ corr, p, q := 0.0, a, a
+ if b < p {
+ p = b
+ }
+ if b > q {
+ q = b
+ }
+ if p < 0 {
+ return math.NaN()
+ }
+ if p == 0 {
+ return math.MaxFloat64
+ }
+ if p >= 10.0 {
+ corr = lgammacor(p) + lgammacor(q) - lgammacor(p+q)
+ return math.Log(q)*-0.5 + 0.918938533204672741780329736406 + corr + (p-0.5)*math.Log(p/(p+q)) + q*logrelerr(-p/(p+q))
+ }
+ if q >= 10 {
+ corr = lgammacor(q) - lgammacor(p+q)
+ val, _ := math.Lgamma(p)
+ return val + corr + p - p*math.Log(p+q) + (q-0.5)*logrelerr(-p/(p+q))
+ }
+ return math.Log(math.Gamma(p) * (math.Gamma(q) / math.Gamma(p+q)))
+}
+
+// pbetaRaw is a part of pbeta for the beta distribution.
+func pbetaRaw(alnsml, ans, eps, p, pin, q, sml, x, y float64) float64 {
+ if q > 1.0 {
+ xb := p*math.Log(y) + q*math.Log(1.0-y) - logBeta(p, q) - math.Log(q)
+ ib := int(math.Max(xb/alnsml, 0.0))
+ term := math.Exp(xb - float64(ib)*alnsml)
+ c := 1.0 / (1.0 - y)
+ p1 := q * c / (p + q - 1.0)
+ finsum := 0.0
+ n := int(q)
+ if q == float64(n) {
+ n = n - 1
+ }
+ for i := 1; i <= n; i++ {
+ if p1 <= 1 && term/eps <= finsum {
+ break
+ }
+ xi := float64(i)
+ term = (q - xi + 1.0) * c * term / (p + q - xi)
+ if term > 1.0 {
+ ib = ib - 1
+ term = term * sml
+ }
+ if ib == 0 {
+ finsum = finsum + term
+ }
+ }
+ ans = ans + finsum
+ }
+ if y != x || p != pin {
+ ans = 1.0 - ans
+ }
+ ans = math.Max(math.Min(ans, 1.0), 0.0)
+ return ans
+}
+
+// pbeta returns distribution function of the beta distribution.
+func pbeta(x, pin, qin float64) (ans float64) {
+ eps := d1mach(3)
+ alneps := math.Log(eps)
+ sml := d1mach(1)
+ alnsml := math.Log(sml)
+ y := x
+ p := pin
+ q := qin
+ if p/(p+q) < x {
+ y = 1.0 - y
+ p = qin
+ q = pin
+ }
+ if (p+q)*y/(p+1.0) < eps {
+ xb := p*math.Log(math.Max(y, sml)) - math.Log(p) - logBeta(p, q)
+ if xb > alnsml && y != 0.0 {
+ ans = math.Exp(xb)
+ }
+ if y != x || p != pin {
+ ans = 1.0 - ans
+ }
+ } else {
+ ps := q - math.Floor(q)
+ if ps == 0.0 {
+ ps = 1.0
+ }
+ xb := p*math.Log(y) - logBeta(ps, p) - math.Log(p)
+ if xb >= alnsml {
+ ans = math.Exp(xb)
+ term := ans * p
+ if ps != 1.0 {
+ n := int(math.Max(alneps/math.Log(y), 4.0))
+ for i := 1; i <= n; i++ {
+ xi := float64(i)
+ term = term * (xi - ps) * y / xi
+ ans = ans + term/(p+xi)
+ }
+ }
+ }
+ ans = pbetaRaw(alnsml, ans, eps, p, pin, q, sml, x, y)
+ }
+ return ans
+}
+
+// betainvProbIterator is a part of betainv for the inverse of the beta
+// function.
+func betainvProbIterator(alpha1, alpha3, beta1, beta2, beta3, logbeta, lower, maxCumulative, prob1, prob2, upper float64, needSwap bool) float64 {
+ var i, j, prev, prop4 float64
+ j = 1
+ for prob := 0; prob < 1000; prob++ {
+ prop3 := pbeta(beta3, alpha1, beta1)
+ prop3 = (prop3 - prob1) * math.Exp(logbeta+prob2*math.Log(beta3)+beta2*math.Log(1.0-beta3))
+ if prop3*prop4 <= 0 {
+ prev = math.Max(math.Abs(j), maxCumulative)
+ }
+ h := 1.0
+ for iteratorCount := 0; iteratorCount < 1000; iteratorCount++ {
+ j = h * prop3
+ if math.Abs(j) < prev {
+ i = beta3 - j
+ if i >= 0 && i <= 1.0 {
+ if prev <= alpha3 {
+ return beta3
+ }
+ if math.Abs(prop3) <= alpha3 {
+ return beta3
+ }
+ if i != 0 && i != 1.0 {
+ break
+ }
+ }
+ }
+ h /= 3.0
+ }
+ if i == beta3 {
+ return beta3
+ }
+ beta3, prop4 = i, prop3
+ }
+ return beta3
+}
+
+// calcBetainv is an implementation for the quantile of the beta
+// distribution.
+func calcBetainv(probability, alpha, beta, lower, upper float64) float64 {
+ minCumulative, maxCumulative := 1.0e-300, 3.0e-308
+ lowerBound, upperBound := maxCumulative, 1.0-2.22e-16
+ needSwap := false
+ var alpha1, alpha2, beta1, beta2, beta3, prob1, x, y float64
+ if probability <= 0.5 {
+ prob1, alpha1, beta1 = probability, alpha, beta
+ } else {
+ prob1, alpha1, beta1, needSwap = 1.0-probability, beta, alpha, true
+ }
+ logbeta := logBeta(alpha, beta)
+ prob2 := math.Sqrt(-math.Log(prob1 * prob1))
+ prob3 := prob2 - (prob2*0.27061+2.3075)/(prob2*(prob2*0.04481+0.99229)+1)
+ if alpha1 > 1 && beta1 > 1 {
+ alpha2, beta2, prob2 = 1/(alpha1+alpha1-1), 1/(beta1+beta1-1), (prob3*prob3-3)/6
+ x = 2 / (alpha2 + beta2)
+ y = prob3*math.Sqrt(x+prob2)/x - (beta2-alpha2)*(prob2+5/6.0-2/(x*3))
+ beta3 = alpha1 / (alpha1 + beta1*math.Exp(y+y))
+ } else {
+ beta2, prob2 = 1/(beta1*9), beta1+beta1
+ beta2 = prob2 * math.Pow(1-beta2+prob3*math.Sqrt(beta2), 3)
+ if beta2 <= 0 {
+ beta3 = 1 - math.Exp((math.Log((1-prob1)*beta1)+logbeta)/beta1)
+ } else {
+ beta2 = (prob2 + alpha1*4 - 2) / beta2
+ if beta2 <= 1 {
+ beta3 = math.Exp((logbeta + math.Log(alpha1*prob1)) / alpha1)
+ } else {
+ beta3 = 1 - 2/(beta2+1)
+ }
+ }
+ }
+ beta2, prob2 = 1-beta1, 1-alpha1
+ if beta3 < lowerBound {
+ beta3 = lowerBound
+ } else if beta3 > upperBound {
+ beta3 = upperBound
+ }
+ alpha3 := math.Max(minCumulative, math.Pow(10.0, -13.0-2.5/(alpha1*alpha1)-0.5/(prob1*prob1)))
+ beta3 = betainvProbIterator(alpha1, alpha3, beta1, beta2, beta3, logbeta, lower, maxCumulative, prob1, prob2, upper, needSwap)
+ if needSwap {
+ beta3 = 1.0 - beta3
+ }
+ return (upper-lower)*beta3 + lower
+}
+
+// betainv is an implementation of the formula functions BETAINV and
+// BETA.INV.
+func (fn *formulaFuncs) betainv(name string, argsList *list.List) formulaArg {
+ if argsList.Len() < 3 {
+ return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires at least 3 arguments", name))
+ }
+ if argsList.Len() > 5 {
+ return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires at most 5 arguments", name))
+ }
+ probability := argsList.Front().Value.(formulaArg).ToNumber()
+ if probability.Type != ArgNumber {
+ return probability
+ }
+ if probability.Number <= 0 || probability.Number >= 1 {
+ return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)
+ }
+ alpha := argsList.Front().Next().Value.(formulaArg).ToNumber()
+ if alpha.Type != ArgNumber {
+ return alpha
+ }
+ beta := argsList.Front().Next().Next().Value.(formulaArg).ToNumber()
+ if beta.Type != ArgNumber {
+ return beta
+ }
+ if alpha.Number <= 0 || beta.Number <= 0 {
+ return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)
+ }
+ a, b := newNumberFormulaArg(0), newNumberFormulaArg(1)
+ if argsList.Len() > 3 {
+ if a = argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber(); a.Type != ArgNumber {
+ return a
+ }
+ }
+ if argsList.Len() == 5 {
+ if b = argsList.Back().Value.(formulaArg).ToNumber(); b.Type != ArgNumber {
+ return b
+ }
+ }
+ if a.Number == b.Number {
+ return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)
+ }
+ return newNumberFormulaArg(calcBetainv(probability.Number, alpha.Number, beta.Number, a.Number, b.Number))
+}
+
+// BETAINV function uses an iterative procedure to calculate the inverse of
+// the cumulative beta probability density function for a supplied
+// probability. The syntax of the function is:
+//
+// BETAINV(probability,alpha,beta,[A],[B])
+//
+func (fn *formulaFuncs) BETAINV(argsList *list.List) formulaArg {
+ return fn.betainv("BETAINV", argsList)
+}
+
+// BETAdotINV function uses an iterative procedure to calculate the inverse of
+// the cumulative beta probability density function for a supplied
+// probability. The syntax of the function is:
+//
+// BETA.INV(probability,alpha,beta,[A],[B])
+//
+func (fn *formulaFuncs) BETAdotINV(argsList *list.List) formulaArg {
+ return fn.betainv("BETA.INV", argsList)
+}
+
// incompleteGamma is an implementation of the incomplete gamma function.
func incompleteGamma(a, x float64) float64 {
max := 32
@@ -5836,317 +6208,10 @@ func (fn *formulaFuncs) EXPONDIST(argsList *list.List) formulaArg {
return newNumberFormulaArg(lambda.Number * math.Exp(-lambda.Number*x.Number))
}
-// d1mach returns double precision real machine constants.
-func d1mach(i int) float64 {
- arr := []float64{
- 2.2250738585072014e-308,
- 1.7976931348623158e+308,
- 1.1102230246251565e-16,
- 2.2204460492503131e-16,
- 0.301029995663981195,
- }
- if i > len(arr) {
- return 0
- }
- return arr[i-1]
-}
-
-// chebyshevInit determines the number of terms for the double precision
-// orthogonal series "dos" needed to insure the error is no larger
-// than "eta". Ordinarily eta will be chosen to be one-tenth machine
-// precision.
-func chebyshevInit(nos int, eta float64, dos []float64) int {
- i, e := 0, 0.0
- if nos < 1 {
- return 0
- }
- for ii := 1; ii <= nos; ii++ {
- i = nos - ii
- e += math.Abs(dos[i])
- if e > eta {
- return i
- }
- }
- return i
-}
-
-// chebyshevEval evaluates the n-term Chebyshev series "a" at "x".
-func chebyshevEval(n int, x float64, a []float64) float64 {
- if n < 1 || n > 1000 || x < -1.1 || x > 1.1 {
- return math.NaN()
- }
- twox, b0, b1, b2 := x*2, 0.0, 0.0, 0.0
- for i := 1; i <= n; i++ {
- b2 = b1
- b1 = b0
- b0 = twox*b1 - b2 + a[n-i]
- }
- return (b0 - b2) * 0.5
-}
-
-// lgammacor is an implementation for the log(gamma) correction.
-func lgammacor(x float64) float64 {
- algmcs := []float64{
- 0.1666389480451863247205729650822, -0.1384948176067563840732986059135e-4,
- 0.9810825646924729426157171547487e-8, -0.1809129475572494194263306266719e-10,
- 0.6221098041892605227126015543416e-13, -0.3399615005417721944303330599666e-15,
- 0.2683181998482698748957538846666e-17, -0.2868042435334643284144622399999e-19,
- 0.3962837061046434803679306666666e-21, -0.6831888753985766870111999999999e-23,
- 0.1429227355942498147573333333333e-24, -0.3547598158101070547199999999999e-26,
- 0.1025680058010470912000000000000e-27, -0.3401102254316748799999999999999e-29,
- 0.1276642195630062933333333333333e-30,
- }
- nalgm := chebyshevInit(15, d1mach(3), algmcs)
- xbig := 1.0 / math.Sqrt(d1mach(3))
- xmax := math.Exp(math.Min(math.Log(d1mach(2)/12.0), -math.Log(12.0*d1mach(1))))
- if x < 10.0 {
- return math.NaN()
- } else if x >= xmax {
- return 4.930380657631324e-32
- } else if x < xbig {
- tmp := 10.0 / x
- return chebyshevEval(nalgm, tmp*tmp*2.0-1.0, algmcs) / x
- }
- return 1.0 / (x * 12.0)
-}
-
-// logrelerr compute the relative error logarithm.
-func logrelerr(x float64) float64 {
- alnrcs := []float64{
- 0.10378693562743769800686267719098e+1, -0.13364301504908918098766041553133,
- 0.19408249135520563357926199374750e-1, -0.30107551127535777690376537776592e-2,
- 0.48694614797154850090456366509137e-3, -0.81054881893175356066809943008622e-4,
- 0.13778847799559524782938251496059e-4, -0.23802210894358970251369992914935e-5,
- 0.41640416213865183476391859901989e-6, -0.73595828378075994984266837031998e-7,
- 0.13117611876241674949152294345011e-7, -0.23546709317742425136696092330175e-8,
- 0.42522773276034997775638052962567e-9, -0.77190894134840796826108107493300e-10,
- 0.14075746481359069909215356472191e-10, -0.25769072058024680627537078627584e-11,
- 0.47342406666294421849154395005938e-12, -0.87249012674742641745301263292675e-13,
- 0.16124614902740551465739833119115e-13, -0.29875652015665773006710792416815e-14,
- 0.55480701209082887983041321697279e-15, -0.10324619158271569595141333961932e-15,
- 0.19250239203049851177878503244868e-16, -0.35955073465265150011189707844266e-17,
- 0.67264542537876857892194574226773e-18, -0.12602624168735219252082425637546e-18,
- 0.23644884408606210044916158955519e-19, -0.44419377050807936898878389179733e-20,
- 0.83546594464034259016241293994666e-21, -0.15731559416479562574899253521066e-21,
- 0.29653128740247422686154369706666e-22, -0.55949583481815947292156013226666e-23,
- 0.10566354268835681048187284138666e-23, -0.19972483680670204548314999466666e-24,
- 0.37782977818839361421049855999999e-25, -0.71531586889081740345038165333333e-26,
- 0.13552488463674213646502024533333e-26, -0.25694673048487567430079829333333e-27,
- 0.48747756066216949076459519999999e-28, -0.92542112530849715321132373333333e-29,
- 0.17578597841760239233269760000000e-29, -0.33410026677731010351377066666666e-30,
- 0.63533936180236187354180266666666e-31,
- }
- nlnrel := chebyshevInit(43, 0.1*d1mach(3), alnrcs)
- if x <= -1 {
- return math.NaN()
- }
- if math.Abs(x) <= 0.375 {
- return x * (1.0 - x*chebyshevEval(nlnrel, x/0.375, alnrcs))
- }
- return math.Log(x + 1.0)
-}
-
-// logBeta is an implementation for the log of the beta distribution
-// function.
-func logBeta(a, b float64) float64 {
- corr, p, q := 0.0, a, a
- if b < p {
- p = b
- }
- if b > q {
- q = b
- }
- if p < 0 {
- return math.NaN()
- }
- if p == 0 {
- return math.MaxFloat64
- }
- if p >= 10.0 {
- corr = lgammacor(p) + lgammacor(q) - lgammacor(p+q)
- return math.Log(q)*-0.5 + 0.918938533204672741780329736406 + corr + (p-0.5)*math.Log(p/(p+q)) + q*logrelerr(-p/(p+q))
- }
- if q >= 10 {
- corr = lgammacor(q) - lgammacor(p+q)
- val, _ := math.Lgamma(p)
- return val + corr + p - p*math.Log(p+q) + (q-0.5)*logrelerr(-p/(p+q))
- }
- return math.Log(math.Gamma(p) * (math.Gamma(q) / math.Gamma(p+q)))
-}
-
-// pbetaRaw is a part of pbeta for the beta distribution.
-func pbetaRaw(alnsml, ans, eps, p, pin, q, sml, x, y float64) float64 {
- if q > 1.0 {
- xb := p*math.Log(y) + q*math.Log(1.0-y) - logBeta(p, q) - math.Log(q)
- ib := int(math.Max(xb/alnsml, 0.0))
- term := math.Exp(xb - float64(ib)*alnsml)
- c := 1.0 / (1.0 - y)
- p1 := q * c / (p + q - 1.0)
- finsum := 0.0
- n := int(q)
- if q == float64(n) {
- n = n - 1
- }
- for i := 1; i <= n; i++ {
- if p1 <= 1 && term/eps <= finsum {
- break
- }
- xi := float64(i)
- term = (q - xi + 1.0) * c * term / (p + q - xi)
- if term > 1.0 {
- ib = ib - 1
- term = term * sml
- }
- if ib == 0 {
- finsum = finsum + term
- }
- }
- ans = ans + finsum
- }
- if y != x || p != pin {
- ans = 1.0 - ans
- }
- ans = math.Max(math.Min(ans, 1.0), 0.0)
- return ans
-}
-
-// pbeta returns distribution function of the beta distribution.
-func pbeta(x, pin, qin float64) (ans float64) {
- eps := d1mach(3)
- alneps := math.Log(eps)
- sml := d1mach(1)
- alnsml := math.Log(sml)
- y := x
- p := pin
- q := qin
- if p/(p+q) < x {
- y = 1.0 - y
- p = qin
- q = pin
- }
- if (p+q)*y/(p+1.0) < eps {
- xb := p*math.Log(math.Max(y, sml)) - math.Log(p) - logBeta(p, q)
- if xb > alnsml && y != 0.0 {
- ans = math.Exp(xb)
- }
- if y != x || p != pin {
- ans = 1.0 - ans
- }
- } else {
- ps := q - math.Floor(q)
- if ps == 0.0 {
- ps = 1.0
- }
- xb := p*math.Log(y) - logBeta(ps, p) - math.Log(p)
- if xb >= alnsml {
- ans = math.Exp(xb)
- term := ans * p
- if ps != 1.0 {
- n := int(math.Max(alneps/math.Log(y), 4.0))
- for i := 1; i <= n; i++ {
- xi := float64(i)
- term = term * (xi - ps) * y / xi
- ans = ans + term/(p+xi)
- }
- }
- }
- ans = pbetaRaw(alnsml, ans, eps, p, pin, q, sml, x, y)
- }
- return ans
-}
-
-// betainvProbIterator is a part of betainv for the inverse of the beta function.
-func betainvProbIterator(alpha1, alpha3, beta1, beta2, beta3, logbeta, lower, maxCumulative, prob1, prob2, upper float64, needSwap bool) float64 {
- var i, j, prev, prop4 float64
- j = 1
- for prob := 0; prob < 1000; prob++ {
- prop3 := pbeta(beta3, alpha1, beta1)
- prop3 = (prop3 - prob1) * math.Exp(logbeta+prob2*math.Log(beta3)+beta2*math.Log(1.0-beta3))
- if prop3*prop4 <= 0 {
- prev = math.Max(math.Abs(j), maxCumulative)
- }
- h := 1.0
- for iteratorCount := 0; iteratorCount < 1000; iteratorCount++ {
- j = h * prop3
- if math.Abs(j) < prev {
- i = beta3 - j
- if i >= 0 && i <= 1.0 {
- if prev <= alpha3 {
- return beta3
- }
- if math.Abs(prop3) <= alpha3 {
- return beta3
- }
- if i != 0 && i != 1.0 {
- break
- }
- }
- }
- h /= 3.0
- }
- if i == beta3 {
- return beta3
- }
- beta3, prop4 = i, prop3
- }
- return beta3
-}
-
-// betainv is an implementation for the quantile of the beta distribution.
-func betainv(probability, alpha, beta, lower, upper float64) float64 {
- minCumulative, maxCumulative := 1.0e-300, 3.0e-308
- lowerBound, upperBound := maxCumulative, 1.0-2.22e-16
- needSwap := false
- var alpha1, alpha2, beta1, beta2, beta3, prob1, x, y float64
- if probability <= 0.5 {
- prob1, alpha1, beta1 = probability, alpha, beta
- } else {
- prob1, alpha1, beta1, needSwap = 1.0-probability, beta, alpha, true
- }
- logbeta := logBeta(alpha, beta)
- prob2 := math.Sqrt(-math.Log(prob1 * prob1))
- prob3 := prob2 - (prob2*0.27061+2.3075)/(prob2*(prob2*0.04481+0.99229)+1)
- if alpha1 > 1 && beta1 > 1 {
- alpha2, beta2, prob2 = 1/(alpha1+alpha1-1), 1/(beta1+beta1-1), (prob3*prob3-3)/6
- x = 2 / (alpha2 + beta2)
- y = prob3*math.Sqrt(x+prob2)/x - (beta2-alpha2)*(prob2+5/6.0-2/(x*3))
- beta3 = alpha1 / (alpha1 + beta1*math.Exp(y+y))
- } else {
- beta2, prob2 = 1/(beta1*9), beta1+beta1
- beta2 = prob2 * math.Pow(1-beta2+prob3*math.Sqrt(beta2), 3)
- if beta2 <= 0 {
- beta3 = 1 - math.Exp((math.Log((1-prob1)*beta1)+logbeta)/beta1)
- } else {
- beta2 = (prob2 + alpha1*4 - 2) / beta2
- if beta2 <= 1 {
- beta3 = math.Exp((logbeta + math.Log(alpha1*prob1)) / alpha1)
- } else {
- beta3 = 1 - 2/(beta2+1)
- }
- }
- }
- beta2, prob2 = 1-beta1, 1-alpha1
- if beta3 < lowerBound {
- beta3 = lowerBound
- } else if beta3 > upperBound {
- beta3 = upperBound
- }
- alpha3 := math.Max(minCumulative, math.Pow(10.0, -13.0-2.5/(alpha1*alpha1)-0.5/(prob1*prob1)))
- beta3 = betainvProbIterator(alpha1, alpha3, beta1, beta2, beta3, logbeta, lower, maxCumulative, prob1, prob2, upper, needSwap)
- if needSwap {
- beta3 = 1.0 - beta3
- }
- return (upper-lower)*beta3 + lower
-}
-
-// FINV function calculates the inverse of the (right-tailed) F Probability
-// Distribution for a supplied probability. The syntax of the function is:
-//
-// FINV(probability,deg_freedom1,deg_freedom2)
-//
-func (fn *formulaFuncs) FINV(argsList *list.List) formulaArg {
+// finv is an implementation of the formula functions F.INV.RT and FINV.
+func (fn *formulaFuncs) finv(name string, argsList *list.List) formulaArg {
if argsList.Len() != 3 {
- return newErrorFormulaArg(formulaErrorVALUE, "FINV requires 3 arguments")
+ return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires 3 arguments", name))
}
var probability, d1, d2 formulaArg
if probability = argsList.Front().Value.(formulaArg).ToNumber(); probability.Type != ArgNumber {
@@ -6167,7 +6232,26 @@ func (fn *formulaFuncs) FINV(argsList *list.List) formulaArg {
if d2.Number < 1 || d2.Number >= math.Pow10(10) {
return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)
}
- return newNumberFormulaArg((1/betainv(1.0-(1.0-probability.Number), d2.Number/2, d1.Number/2, 0, 1) - 1.0) * (d2.Number / d1.Number))
+ return newNumberFormulaArg((1/calcBetainv(1.0-(1.0-probability.Number), d2.Number/2, d1.Number/2, 0, 1) - 1.0) * (d2.Number / d1.Number))
+}
+
+// FdotINVdotRT function calculates the inverse of the (right-tailed) F
+// Probability Distribution for a supplied probability. The syntax of the
+// function is:
+//
+// F.INV.RT(probability,deg_freedom1,deg_freedom2)
+//
+func (fn *formulaFuncs) FdotINVdotRT(argsList *list.List) formulaArg {
+ return fn.finv("F.INV.RT", argsList)
+}
+
+// FINV function calculates the inverse of the (right-tailed) F Probability
+// Distribution for a supplied probability. The syntax of the function is:
+//
+// FINV(probability,deg_freedom1,deg_freedom2)
+//
+func (fn *formulaFuncs) FINV(argsList *list.List) formulaArg {
+ return fn.finv("FINV", argsList)
}
// NORMdotDIST function calculates the Normal Probability Density Function or
diff --git a/calc_test.go b/calc_test.go
index 80ba8ef..63e5984 100644
--- a/calc_test.go
+++ b/calc_test.go
@@ -784,6 +784,10 @@ func TestCalcCellValue(t *testing.T) {
"=AVERAGEA(A1)": "1",
"=AVERAGEA(A1:A2)": "1.5",
"=AVERAGEA(D2:F9)": "12671.375",
+ // BETAINV
+ "=BETAINV(0.2,4,5,0,1)": "0.303225844664082",
+ // BETA.INV
+ "=BETA.INV(0.2,4,5,0,1)": "0.303225844664082",
// CHIDIST
"=CHIDIST(0.5,3)": "0.918891411654676",
"=CHIDIST(8,3)": "0.0460117056892315",
@@ -859,6 +863,14 @@ func TestCalcCellValue(t *testing.T) {
"=FINV(0.5,4,8)": "0.914645355977072",
"=FINV(0.1,79,86)": "1.32646097270444",
"=FINV(1,40,5)": "0",
+ // F.INV.RT
+ "=F.INV.RT(0.2,1,2)": "3.55555555555555",
+ "=F.INV.RT(0.6,1,2)": "0.380952380952381",
+ "=F.INV.RT(0.6,2,2)": "0.666666666666667",
+ "=F.INV.RT(0.6,4,4)": "0.763454070045235",
+ "=F.INV.RT(0.5,4,8)": "0.914645355977072",
+ "=F.INV.RT(0.1,79,86)": "1.32646097270444",
+ "=F.INV.RT(1,40,5)": "0",
// NORM.DIST
"=NORM.DIST(0.8,1,0.3,TRUE)": "0.252492537546923",
"=NORM.DIST(50,40,20,FALSE)": "0.017603266338215",
@@ -2282,6 +2294,32 @@ func TestCalcCellValue(t *testing.T) {
"=AVERAGE(H1)": "AVERAGE divide by zero",
// AVERAGEA
"=AVERAGEA(H1)": "AVERAGEA divide by zero",
+ // BETAINV
+ "=BETAINV()": "BETAINV requires at least 3 arguments",
+ "=BETAINV(0.2,4,5,0,1,0)": "BETAINV requires at most 5 arguments",
+ "=BETAINV(\"\",4,5,0,1)": "strconv.ParseFloat: parsing \"\": invalid syntax",
+ "=BETAINV(0.2,\"\",5,0,1)": "strconv.ParseFloat: parsing \"\": invalid syntax",
+ "=BETAINV(0.2,4,\"\",0,1)": "strconv.ParseFloat: parsing \"\": invalid syntax",
+ "=BETAINV(0.2,4,5,\"\",1)": "strconv.ParseFloat: parsing \"\": invalid syntax",
+ "=BETAINV(0.2,4,5,0,\"\")": "strconv.ParseFloat: parsing \"\": invalid syntax",
+ "=BETAINV(0,4,5,0,1)": "#NUM!",
+ "=BETAINV(1,4,5,0,1)": "#NUM!",
+ "=BETAINV(0.2,0,5,0,1)": "#NUM!",
+ "=BETAINV(0.2,4,0,0,1)": "#NUM!",
+ "=BETAINV(0.2,4,5,2,2)": "#NUM!",
+ // BETA.INV
+ "=BETA.INV()": "BETA.INV requires at least 3 arguments",
+ "=BETA.INV(0.2,4,5,0,1,0)": "BETA.INV requires at most 5 arguments",
+ "=BETA.INV(\"\",4,5,0,1)": "strconv.ParseFloat: parsing \"\": invalid syntax",
+ "=BETA.INV(0.2,\"\",5,0,1)": "strconv.ParseFloat: parsing \"\": invalid syntax",
+ "=BETA.INV(0.2,4,\"\",0,1)": "strconv.ParseFloat: parsing \"\": invalid syntax",
+ "=BETA.INV(0.2,4,5,\"\",1)": "strconv.ParseFloat: parsing \"\": invalid syntax",
+ "=BETA.INV(0.2,4,5,0,\"\")": "strconv.ParseFloat: parsing \"\": invalid syntax",
+ "=BETA.INV(0,4,5,0,1)": "#NUM!",
+ "=BETA.INV(1,4,5,0,1)": "#NUM!",
+ "=BETA.INV(0.2,0,5,0,1)": "#NUM!",
+ "=BETA.INV(0.2,4,0,0,1)": "#NUM!",
+ "=BETA.INV(0.2,4,5,2,2)": "#NUM!",
// AVERAGEIF
"=AVERAGEIF()": "AVERAGEIF requires at least 2 arguments",
"=AVERAGEIF(H1,\"\")": "AVERAGEIF divide by zero",
@@ -2375,6 +2413,14 @@ func TestCalcCellValue(t *testing.T) {
"=FINV(0,1,2)": "#NUM!",
"=FINV(0.2,0.5,2)": "#NUM!",
"=FINV(0.2,1,0.5)": "#NUM!",
+ // F.INV.RT
+ "=F.INV.RT()": "F.INV.RT requires 3 arguments",
+ "=F.INV.RT(\"\",1,2)": "strconv.ParseFloat: parsing \"\": invalid syntax",
+ "=F.INV.RT(0.2,\"\",2)": "strconv.ParseFloat: parsing \"\": invalid syntax",
+ "=F.INV.RT(0.2,1,\"\")": "strconv.ParseFloat: parsing \"\": invalid syntax",
+ "=F.INV.RT(0,1,2)": "#NUM!",
+ "=F.INV.RT(0.2,0.5,2)": "#NUM!",
+ "=F.INV.RT(0.2,1,0.5)": "#NUM!",
// NORM.DIST
"=NORM.DIST()": "NORM.DIST requires 4 arguments",
// NORMDIST
diff --git a/excelize_test.go b/excelize_test.go
index bafd446..7d38304 100644
--- a/excelize_test.go
+++ b/excelize_test.go
@@ -130,14 +130,17 @@ func TestOpenFile(t *testing.T) {
// Test boolean write
booltest := []struct {
value bool
+ raw bool
expected string
}{
- {false, "0"},
- {true, "1"},
+ {false, true, "0"},
+ {true, true, "1"},
+ {false, false, "FALSE"},
+ {true, false, "TRUE"},
}
for _, test := range booltest {
assert.NoError(t, f.SetCellValue("Sheet2", "F16", test.value))
- val, err := f.GetCellValue("Sheet2", "F16")
+ val, err := f.GetCellValue("Sheet2", "F16", Options{RawCellValue: test.raw})
assert.NoError(t, err)
assert.Equal(t, test.expected, val)
}
diff --git a/rows.go b/rows.go
index 0d2490c..81eaeeb 100644
--- a/rows.go
+++ b/rows.go
@@ -429,6 +429,16 @@ func (c *xlsxC) getValueFrom(f *File, d *xlsxSST, raw bool) (string, error) {
f.Lock()
defer f.Unlock()
switch c.T {
+ case "b":
+ if !raw {
+ if c.V == "1" {
+ return "TRUE", nil
+ }
+ if c.V == "0" {
+ return "FALSE", nil
+ }
+ }
+ return f.formattedValue(c.S, c.V, raw), nil
case "s":
if c.V != "" {
xlsxSI := 0
diff --git a/rows_test.go b/rows_test.go
index 208b2de..22b038a 100644
--- a/rows_test.go
+++ b/rows_test.go
@@ -950,6 +950,10 @@ func TestNumberFormats(t *testing.T) {
assert.NoError(t, f.Close())
}
+func TestRoundPrecision(t *testing.T) {
+ assert.Equal(t, "text", roundPrecision("text", 0))
+}
+
func BenchmarkRows(b *testing.B) {
f, _ := OpenFile(filepath.Join("test", "Book1.xlsx"))
for i := 0; i < b.N; i++ {