summaryrefslogtreecommitdiff
path: root/calc.go
diff options
context:
space:
mode:
Diffstat (limited to 'calc.go')
-rw-r--r--calc.go94
1 files changed, 89 insertions, 5 deletions
diff --git a/calc.go b/calc.go
index 796ca16..c600aaa 100644
--- a/calc.go
+++ b/calc.go
@@ -339,6 +339,7 @@ type formulaFuncs struct {
// ACOT
// ACOTH
// ADDRESS
+// AGGREGATE
// AMORDEGRC
// AMORLINC
// AND
@@ -700,6 +701,7 @@ type formulaFuncs struct {
// STDEVPA
// STEYX
// SUBSTITUTE
+// SUBTOTAL
// SUM
// SUMIF
// SUMIFS
@@ -872,7 +874,6 @@ func (f *File) evalInfixExp(ctx *calcContext, sheet, cell string, tokens []efp.T
var err error
opdStack, optStack, opfStack, opfdStack, opftStack, argsStack := NewStack(), NewStack(), NewStack(), NewStack(), NewStack(), NewStack()
var inArray, inArrayRow bool
- var arrayRow []formulaArg
for i := 0; i < len(tokens); i++ {
token := tokens[i]
@@ -981,7 +982,6 @@ func (f *File) evalInfixExp(ctx *calcContext, sheet, cell string, tokens []efp.T
argsStack.Peek().(*list.List).PushBack(newStringFormulaArg(token.TValue))
}
if inArrayRow && isOperand(token) {
- arrayRow = append(arrayRow, tokenToFormulaArg(token))
continue
}
if inArrayRow && isFunctionStopToken(token) {
@@ -990,7 +990,7 @@ func (f *File) evalInfixExp(ctx *calcContext, sheet, cell string, tokens []efp.T
}
if inArray && isFunctionStopToken(token) {
argsStack.Peek().(*list.List).PushBack(opfdStack.Pop())
- arrayRow, inArray = []formulaArg{}, false
+ inArray = false
continue
}
if err = f.evalInfixExpFunc(ctx, sheet, cell, token, nextToken, opfStack, opdStack, opftStack, opfdStack, argsStack); err != nil {
@@ -3559,6 +3559,56 @@ func (fn *formulaFuncs) ACOTH(argsList *list.List) formulaArg {
return newNumberFormulaArg(math.Atanh(1 / arg.Number))
}
+// AGGREGATE function returns the result of a specified operation or function,
+// applied to a list or database of values. The syntax of the function is:
+//
+// AGGREGATE(function_num,options,ref1,[ref2],...)
+func (fn *formulaFuncs) AGGREGATE(argsList *list.List) formulaArg {
+ if argsList.Len() < 2 {
+ return newErrorFormulaArg(formulaErrorVALUE, "AGGREGATE requires at least 3 arguments")
+ }
+ var fnNum, opts formulaArg
+ if fnNum = argsList.Front().Value.(formulaArg).ToNumber(); fnNum.Type != ArgNumber {
+ return fnNum
+ }
+ subFn, ok := map[int]func(argsList *list.List) formulaArg{
+ 1: fn.AVERAGE,
+ 2: fn.COUNT,
+ 3: fn.COUNTA,
+ 4: fn.MAX,
+ 5: fn.MIN,
+ 6: fn.PRODUCT,
+ 7: fn.STDEVdotS,
+ 8: fn.STDEVdotP,
+ 9: fn.SUM,
+ 10: fn.VARdotS,
+ 11: fn.VARdotP,
+ 12: fn.MEDIAN,
+ 13: fn.MODEdotSNGL,
+ 14: fn.LARGE,
+ 15: fn.SMALL,
+ 16: fn.PERCENTILEdotINC,
+ 17: fn.QUARTILEdotINC,
+ 18: fn.PERCENTILEdotEXC,
+ 19: fn.QUARTILEdotEXC,
+ }[int(fnNum.Number)]
+ if !ok {
+ return newErrorFormulaArg(formulaErrorVALUE, "AGGREGATE has invalid function_num")
+ }
+ if opts = argsList.Front().Next().Value.(formulaArg).ToNumber(); opts.Type != ArgNumber {
+ return opts
+ }
+ // TODO: apply option argument values to be ignored during the calculation
+ if int(opts.Number) < 0 || int(opts.Number) > 7 {
+ return newErrorFormulaArg(formulaErrorVALUE, "AGGREGATE has invalid options")
+ }
+ subArgList := list.New().Init()
+ for arg := argsList.Front().Next().Next(); arg != nil; arg = arg.Next() {
+ subArgList.PushBack(arg.Value.(formulaArg))
+ }
+ return subFn(subArgList)
+}
+
// ARABIC function converts a Roman numeral into an Arabic numeral. The syntax
// of the function is:
//
@@ -5555,6 +5605,41 @@ func (fn *formulaFuncs) POISSON(argsList *list.List) formulaArg {
return newNumberFormulaArg(math.Exp(0-mean.Number) * math.Pow(mean.Number, x.Number) / fact(x.Number))
}
+// SUBTOTAL function performs a specified calculation (e.g. the sum, product,
+// average, etc.) for a supplied set of values. The syntax of the function is:
+//
+// SUBTOTAL(function_num,ref1,[ref2],...)
+func (fn *formulaFuncs) SUBTOTAL(argsList *list.List) formulaArg {
+ if argsList.Len() < 2 {
+ return newErrorFormulaArg(formulaErrorVALUE, "SUBTOTAL requires at least 2 arguments")
+ }
+ var fnNum formulaArg
+ if fnNum = argsList.Front().Value.(formulaArg).ToNumber(); fnNum.Type != ArgNumber {
+ return fnNum
+ }
+ subFn, ok := map[int]func(argsList *list.List) formulaArg{
+ 1: fn.AVERAGE, 101: fn.AVERAGE,
+ 2: fn.COUNT, 102: fn.COUNT,
+ 3: fn.COUNTA, 103: fn.COUNTA,
+ 4: fn.MAX, 104: fn.MAX,
+ 5: fn.MIN, 105: fn.MIN,
+ 6: fn.PRODUCT, 106: fn.PRODUCT,
+ 7: fn.STDEV, 107: fn.STDEV,
+ 8: fn.STDEVP, 108: fn.STDEVP,
+ 9: fn.SUM, 109: fn.SUM,
+ 10: fn.VAR, 110: fn.VAR,
+ 11: fn.VARP, 111: fn.VARP,
+ }[int(fnNum.Number)]
+ if !ok {
+ return newErrorFormulaArg(formulaErrorVALUE, "SUBTOTAL has invalid function_num")
+ }
+ subArgList := list.New().Init()
+ for arg := argsList.Front().Next(); arg != nil; arg = arg.Next() {
+ subArgList.PushBack(arg.Value.(formulaArg))
+ }
+ return subFn(subArgList)
+}
+
// SUM function adds together a supplied set of numbers and returns the sum of
// these values. The syntax of the function is:
//
@@ -11622,8 +11707,7 @@ func (fn *formulaFuncs) OR(argsList *list.List) formulaArg {
}
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
case ArgNumber:
- or = token.Number != 0
- if or {
+ if or = token.Number != 0; or {
return newStringFormulaArg(strings.ToUpper(strconv.FormatBool(or)))
}
case ArgMatrix: