diff options
-rw-r--r-- | calc.go | 520 | ||||
-rw-r--r-- | calc_test.go | 33 |
2 files changed, 393 insertions, 160 deletions
@@ -19,6 +19,7 @@ import ( "math" "math/rand" "reflect" + "regexp" "strconv" "strings" "time" @@ -53,10 +54,39 @@ type cellRange struct { To cellRef } +// formula criteria condition enumeration. +const ( + _ byte = iota + criteriaEq + criteriaLe + criteriaGe + criteriaL + criteriaG + criteriaBeg + criteriaEnd +) + +// formulaCriteria defined formula criteria parser result. +type formulaCriteria struct { + Type byte + Condition string +} + +// ArgType is the type if formula argument type. +type ArgType byte + +// Formula argument types enumeration. +const ( + ArgUnknown ArgType = iota + ArgString + ArgMatrix +) + // formulaArg is the argument of a formula or function. type formulaArg struct { - Value string - Matrix [][]string + String string + Matrix [][]formulaArg + Type ArgType } // formulaFuncs is the type of the formula functions. @@ -150,36 +180,30 @@ func (f *File) evalInfixExp(sheet string, tokens []efp.Token) (efp.Token, error) if token.TSubType == efp.TokenSubTypeRange { if !opftStack.Empty() { // parse reference: must reference at here - result, _, err := f.parseReference(sheet, token.TValue) + result, err := f.parseReference(sheet, token.TValue) if err != nil { return efp.Token{TValue: formulaErrorNAME}, err } - if len(result) != 1 { + if result.Type != ArgString { return efp.Token{}, errors.New(formulaErrorVALUE) } opfdStack.Push(efp.Token{ TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber, - TValue: result[0], + TValue: result.String, }) continue } if nextToken.TType == efp.TokenTypeArgument || nextToken.TType == efp.TokenTypeFunction { // parse reference: reference or range at here - result, matrix, err := f.parseReference(sheet, token.TValue) + result, err := f.parseReference(sheet, token.TValue) if err != nil { return efp.Token{TValue: formulaErrorNAME}, err } - for idx, val := range result { - arg := formulaArg{Value: val} - if idx == 0 { - arg.Matrix = matrix - } - argsList.PushBack(arg) - } - if len(result) == 0 { + if result.Type == ArgUnknown { return efp.Token{}, errors.New(formulaErrorVALUE) } + argsList.PushBack(result) continue } } @@ -201,7 +225,8 @@ func (f *File) evalInfixExp(sheet string, tokens []efp.Token) (efp.Token, error) } if !opfdStack.Empty() { argsList.PushBack(formulaArg{ - Value: opfdStack.Pop().(efp.Token).TValue, + String: opfdStack.Pop().(efp.Token).TValue, + Type: ArgString, }) } continue @@ -214,7 +239,8 @@ func (f *File) evalInfixExp(sheet string, tokens []efp.Token) (efp.Token, error) // current token is text if token.TType == efp.TokenTypeOperand && token.TSubType == efp.TokenSubTypeText { argsList.PushBack(formulaArg{ - Value: token.TValue, + String: token.TValue, + Type: ArgString, }) } @@ -232,7 +258,8 @@ func (f *File) evalInfixExp(sheet string, tokens []efp.Token) (efp.Token, error) // push opfd to args if opfdStack.Len() > 0 { argsList.PushBack(formulaArg{ - Value: opfdStack.Pop().(efp.Token).TValue, + String: opfdStack.Pop().(efp.Token).TValue, + Type: ArgString, }) } // call formula function to evaluate @@ -430,14 +457,14 @@ func isOperatorPrefixToken(token efp.Token) bool { func (f *File) parseToken(sheet string, token efp.Token, opdStack, optStack *Stack) error { // parse reference: must reference at here if token.TSubType == efp.TokenSubTypeRange { - result, _, err := f.parseReference(sheet, token.TValue) + result, err := f.parseReference(sheet, token.TValue) if err != nil { return errors.New(formulaErrorNAME) } - if len(result) != 1 { + if result.Type != ArgString { return errors.New(formulaErrorVALUE) } - token.TValue = result[0] + token.TValue = result.String token.TType = efp.TokenTypeOperand token.TSubType = efp.TokenSubTypeNumber } @@ -468,7 +495,7 @@ func (f *File) parseToken(sheet string, token efp.Token, opdStack, optStack *Sta // parseReference parse reference and extract values by given reference // characters and default sheet name. -func (f *File) parseReference(sheet, reference string) (result []string, matrix [][]string, err error) { +func (f *File) parseReference(sheet, reference string) (arg formulaArg, err error) { reference = strings.Replace(reference, "$", "", -1) refs, cellRanges, cellRefs := list.New(), list.New(), list.New() for _, ref := range strings.Split(reference, ":") { @@ -507,39 +534,38 @@ func (f *File) parseReference(sheet, reference string) (result []string, matrix cellRefs.PushBack(e.Value.(cellRef)) refs.Remove(e) } - - result, matrix, err = f.rangeResolver(cellRefs, cellRanges) + arg, err = f.rangeResolver(cellRefs, cellRanges) return } // prepareValueRange prepare value range. func prepareValueRange(cr cellRange, valueRange []int) { - if cr.From.Row < valueRange[0] { + if cr.From.Row < valueRange[0] || valueRange[0] == 0 { valueRange[0] = cr.From.Row } - if cr.From.Col < valueRange[2] { + if cr.From.Col < valueRange[2] || valueRange[2] == 0 { valueRange[2] = cr.From.Col } - if cr.To.Row > valueRange[0] { + if cr.To.Row > valueRange[1] || valueRange[1] == 0 { valueRange[1] = cr.To.Row } - if cr.To.Col > valueRange[3] { + if cr.To.Col > valueRange[3] || valueRange[3] == 0 { valueRange[3] = cr.To.Col } } // prepareValueRef prepare value reference. func prepareValueRef(cr cellRef, valueRange []int) { - if cr.Row < valueRange[0] { + if cr.Row < valueRange[0] || valueRange[0] == 0 { valueRange[0] = cr.Row } - if cr.Col < valueRange[2] { + if cr.Col < valueRange[2] || valueRange[2] == 0 { valueRange[2] = cr.Col } - if cr.Row > valueRange[0] { + if cr.Row > valueRange[1] || valueRange[1] == 0 { valueRange[1] = cr.Row } - if cr.Col > valueRange[3] { + if cr.Col > valueRange[3] || valueRange[3] == 0 { valueRange[3] = cr.Col } } @@ -547,11 +573,10 @@ func prepareValueRef(cr cellRef, valueRange []int) { // rangeResolver extract value as string from given reference and range list. // This function will not ignore the empty cell. For example, A1:A2:A2:B3 will // be reference A1:B3. -func (f *File) rangeResolver(cellRefs, cellRanges *list.List) (result []string, matrix [][]string, err error) { +func (f *File) rangeResolver(cellRefs, cellRanges *list.List) (arg formulaArg, err error) { // value range order: from row, to row, from column, to column - valueRange := []int{1, 1, 1, 1} + valueRange := []int{0, 0, 0, 0} var sheet string - filter := map[string]string{} // prepare value range for temp := cellRanges.Front(); temp != nil; temp = temp.Next() { cr := temp.Value.(cellRange) @@ -560,6 +585,7 @@ func (f *File) rangeResolver(cellRefs, cellRanges *list.List) (result []string, } rng := []int{cr.From.Col, cr.From.Row, cr.To.Col, cr.To.Row} sortCoordinates(rng) + cr.From.Col, cr.From.Row, cr.To.Col, cr.To.Row = rng[0], rng[1], rng[2], rng[3] prepareValueRange(cr, valueRange) if cr.From.Sheet != "" { sheet = cr.From.Sheet @@ -574,8 +600,9 @@ func (f *File) rangeResolver(cellRefs, cellRanges *list.List) (result []string, } // extract value from ranges if cellRanges.Len() > 0 { + arg.Type = ArgMatrix for row := valueRange[0]; row <= valueRange[1]; row++ { - var matrixRow = []string{} + var matrixRow = []formulaArg{} for col := valueRange[2]; col <= valueRange[3]; col++ { var cell, value string if cell, err = CoordinatesToCellName(col, row); err != nil { @@ -584,11 +611,12 @@ func (f *File) rangeResolver(cellRefs, cellRanges *list.List) (result []string, if value, err = f.GetCellValue(sheet, cell); err != nil { return } - filter[cell] = value - matrixRow = append(matrixRow, value) - result = append(result, value) + matrixRow = append(matrixRow, formulaArg{ + String: value, + Type: ArgString, + }) } - matrix = append(matrix, matrixRow) + arg.Matrix = append(arg.Matrix, matrixRow) } return } @@ -599,13 +627,10 @@ func (f *File) rangeResolver(cellRefs, cellRanges *list.List) (result []string, if cell, err = CoordinatesToCellName(cr.Col, cr.Row); err != nil { return } - if filter[cell], err = f.GetCellValue(cr.Sheet, cell); err != nil { + if arg.String, err = f.GetCellValue(cr.Sheet, cell); err != nil { return } - } - - for _, val := range filter { - result = append(result, val) + arg.Type = ArgString } return } @@ -630,6 +655,90 @@ func callFuncByName(receiver interface{}, name string, params []reflect.Value) ( return } +// formulaCriteriaParser parse formula criteria. +func formulaCriteriaParser(exp string) (fc *formulaCriteria) { + fc = &formulaCriteria{} + if exp == "" { + return + } + if match := regexp.MustCompile(`^([0-9]+)$`).FindStringSubmatch(exp); len(match) > 1 { + fc.Type, fc.Condition = criteriaEq, match[1] + return + } + if match := regexp.MustCompile(`^=(.*)$`).FindStringSubmatch(exp); len(match) > 1 { + fc.Type, fc.Condition = criteriaEq, match[1] + return + } + if match := regexp.MustCompile(`^<(.*)$`).FindStringSubmatch(exp); len(match) > 1 { + fc.Type, fc.Condition = criteriaLe, match[1] + return + } + if match := regexp.MustCompile(`^>(.*)$`).FindStringSubmatch(exp); len(match) > 1 { + fc.Type, fc.Condition = criteriaGe, match[1] + return + } + if match := regexp.MustCompile(`^<=(.*)$`).FindStringSubmatch(exp); len(match) > 1 { + fc.Type, fc.Condition = criteriaL, match[1] + return + } + if match := regexp.MustCompile(`^>=(.*)$`).FindStringSubmatch(exp); len(match) > 1 { + fc.Type, fc.Condition = criteriaG, match[1] + return + } + if strings.Contains(exp, "*") { + if strings.HasPrefix(exp, "*") { + fc.Type, fc.Condition = criteriaEnd, strings.TrimPrefix(exp, "*") + } + if strings.HasSuffix(exp, "*") { + fc.Type, fc.Condition = criteriaBeg, strings.TrimSuffix(exp, "*") + } + return + } + fc.Type, fc.Condition = criteriaEq, exp + return +} + +// formulaCriteriaEval evaluate formula criteria expression. +func formulaCriteriaEval(val string, criteria *formulaCriteria) (result bool, err error) { + var value, expected float64 + var prepareValue = func(val, cond string) (value float64, expected float64, err error) { + value, _ = strconv.ParseFloat(val, 64) + if expected, err = strconv.ParseFloat(criteria.Condition, 64); err != nil { + return + } + return + } + switch criteria.Type { + case criteriaEq: + return val == criteria.Condition, err + case criteriaLe: + if value, expected, err = prepareValue(val, criteria.Condition); err != nil { + return + } + return value <= expected, err + case criteriaGe: + if value, expected, err = prepareValue(val, criteria.Condition); err != nil { + return + } + return value >= expected, err + case criteriaL: + if value, expected, err = prepareValue(val, criteria.Condition); err != nil { + return + } + return value < expected, err + case criteriaG: + if value, expected, err = prepareValue(val, criteria.Condition); err != nil { + return + } + return value > expected, err + case criteriaBeg: + return strings.HasPrefix(val, criteria.Condition), err + case criteriaEnd: + return strings.HasSuffix(val, criteria.Condition), err + } + return +} + // Math and Trigonometric functions // ABS function returns the absolute value of any supplied number. The syntax @@ -643,7 +752,7 @@ func (fn *formulaFuncs) ABS(argsList *list.List) (result string, err error) { return } var val float64 - if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -663,7 +772,7 @@ func (fn *formulaFuncs) ACOS(argsList *list.List) (result string, err error) { return } var val float64 - if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -682,7 +791,7 @@ func (fn *formulaFuncs) ACOSH(argsList *list.List) (result string, err error) { return } var val float64 - if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -702,7 +811,7 @@ func (fn *formulaFuncs) ACOT(argsList *list.List) (result string, err error) { return } var val float64 - if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -721,7 +830,7 @@ func (fn *formulaFuncs) ACOTH(argsList *list.List) (result string, err error) { return } var val float64 - if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -741,7 +850,7 @@ func (fn *formulaFuncs) ARABIC(argsList *list.List) (result string, err error) { } charMap := map[rune]float64{'I': 1, 'V': 5, 'X': 10, 'L': 50, 'C': 100, 'D': 500, 'M': 1000} val, last, prefix := 0.0, 0.0, 1.0 - for _, char := range argsList.Front().Value.(formulaArg).Value { + for _, char := range argsList.Front().Value.(formulaArg).String { digit := 0.0 if char == '-' { prefix = -1 @@ -778,7 +887,7 @@ func (fn *formulaFuncs) ASIN(argsList *list.List) (result string, err error) { return } var val float64 - if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -797,7 +906,7 @@ func (fn *formulaFuncs) ASINH(argsList *list.List) (result string, err error) { return } var val float64 - if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -817,7 +926,7 @@ func (fn *formulaFuncs) ATAN(argsList *list.List) (result string, err error) { return } var val float64 - if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -836,7 +945,7 @@ func (fn *formulaFuncs) ATANH(argsList *list.List) (result string, err error) { return } var val float64 - if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -856,11 +965,11 @@ func (fn *formulaFuncs) ATAN2(argsList *list.List) (result string, err error) { return } var x, y float64 - if x, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).Value, 64); err != nil { + if x, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } - if y, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if y, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -884,11 +993,11 @@ func (fn *formulaFuncs) BASE(argsList *list.List) (result string, err error) { } var number float64 var radix, minLength int - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } - if radix, err = strconv.Atoi(argsList.Front().Next().Value.(formulaArg).Value); err != nil { + if radix, err = strconv.Atoi(argsList.Front().Next().Value.(formulaArg).String); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -897,7 +1006,7 @@ func (fn *formulaFuncs) BASE(argsList *list.List) (result string, err error) { return } if argsList.Len() > 2 { - if minLength, err = strconv.Atoi(argsList.Back().Value.(formulaArg).Value); err != nil { + if minLength, err = strconv.Atoi(argsList.Back().Value.(formulaArg).String); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -925,7 +1034,7 @@ func (fn *formulaFuncs) CEILING(argsList *list.List) (result string, err error) return } number, significance, res := 0.0, 1.0, 0.0 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -933,7 +1042,7 @@ func (fn *formulaFuncs) CEILING(argsList *list.List) (result string, err error) significance = -1 } if argsList.Len() > 1 { - if significance, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).Value, 64); err != nil { + if significance, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -969,7 +1078,7 @@ func (fn *formulaFuncs) CEILINGMATH(argsList *list.List) (result string, err err return } number, significance, mode := 0.0, 1.0, 1.0 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -977,7 +1086,7 @@ func (fn *formulaFuncs) CEILINGMATH(argsList *list.List) (result string, err err significance = -1 } if argsList.Len() > 1 { - if significance, err = strconv.ParseFloat(argsList.Front().Next().Value.(formulaArg).Value, 64); err != nil { + if significance, err = strconv.ParseFloat(argsList.Front().Next().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -987,7 +1096,7 @@ func (fn *formulaFuncs) CEILINGMATH(argsList *list.List) (result string, err err return } if argsList.Len() > 2 { - if mode, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).Value, 64); err != nil { + if mode, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -1020,7 +1129,7 @@ func (fn *formulaFuncs) CEILINGPRECISE(argsList *list.List) (result string, err return } number, significance := 0.0, 1.0 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -1032,7 +1141,7 @@ func (fn *formulaFuncs) CEILINGPRECISE(argsList *list.List) (result string, err return } if argsList.Len() > 1 { - if significance, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).Value, 64); err != nil { + if significance, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -1063,11 +1172,11 @@ func (fn *formulaFuncs) COMBIN(argsList *list.List) (result string, err error) { return } number, chosen, val := 0.0, 0.0, 1.0 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } - if chosen, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).Value, 64); err != nil { + if chosen, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -1098,11 +1207,11 @@ func (fn *formulaFuncs) COMBINA(argsList *list.List) (result string, err error) return } var number, chosen float64 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } - if chosen, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).Value, 64); err != nil { + if chosen, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -1117,10 +1226,12 @@ func (fn *formulaFuncs) COMBINA(argsList *list.List) (result string, err error) } args := list.New() args.PushBack(formulaArg{ - Value: fmt.Sprintf("%g", number+chosen-1), + String: fmt.Sprintf("%g", number+chosen-1), + Type: ArgString, }) args.PushBack(formulaArg{ - Value: fmt.Sprintf("%g", number-1), + String: fmt.Sprintf("%g", number-1), + Type: ArgString, }) return fn.COMBIN(args) } @@ -1136,7 +1247,7 @@ func (fn *formulaFuncs) COS(argsList *list.List) (result string, err error) { return } var val float64 - if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -1155,7 +1266,7 @@ func (fn *formulaFuncs) COSH(argsList *list.List) (result string, err error) { return } var val float64 - if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -1174,7 +1285,7 @@ func (fn *formulaFuncs) COT(argsList *list.List) (result string, err error) { return } var val float64 - if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -1197,7 +1308,7 @@ func (fn *formulaFuncs) COTH(argsList *list.List) (result string, err error) { return } var val float64 - if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -1220,7 +1331,7 @@ func (fn *formulaFuncs) CSC(argsList *list.List) (result string, err error) { return } var val float64 - if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -1243,7 +1354,7 @@ func (fn *formulaFuncs) CSCH(argsList *list.List) (result string, err error) { return } var val float64 - if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -1265,9 +1376,9 @@ func (fn *formulaFuncs) DECIMAL(argsList *list.List) (result string, err error) err = errors.New("DECIMAL requires 2 numeric arguments") return } - var text = argsList.Front().Value.(formulaArg).Value + var text = argsList.Front().Value.(formulaArg).String var radix int - if radix, err = strconv.Atoi(argsList.Back().Value.(formulaArg).Value); err != nil { + if radix, err = strconv.Atoi(argsList.Back().Value.(formulaArg).String); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -1294,7 +1405,7 @@ func (fn *formulaFuncs) DEGREES(argsList *list.List) (result string, err error) return } var val float64 - if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -1318,7 +1429,7 @@ func (fn *formulaFuncs) EVEN(argsList *list.List) (result string, err error) { return } var number float64 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -1347,7 +1458,7 @@ func (fn *formulaFuncs) EXP(argsList *list.List) (result string, err error) { return } var number float64 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -1375,7 +1486,7 @@ func (fn *formulaFuncs) FACT(argsList *list.List) (result string, err error) { return } var number float64 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -1397,7 +1508,7 @@ func (fn *formulaFuncs) FACTDOUBLE(argsList *list.List) (result string, err erro return } number, val := 0.0, 1.0 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -1423,11 +1534,11 @@ func (fn *formulaFuncs) FLOOR(argsList *list.List) (result string, err error) { return } var number, significance float64 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } - if significance, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).Value, 64); err != nil { + if significance, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -1461,7 +1572,7 @@ func (fn *formulaFuncs) FLOORMATH(argsList *list.List) (result string, err error return } number, significance, mode := 0.0, 1.0, 1.0 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -1469,7 +1580,7 @@ func (fn *formulaFuncs) FLOORMATH(argsList *list.List) (result string, err error significance = -1 } if argsList.Len() > 1 { - if significance, err = strconv.ParseFloat(argsList.Front().Next().Value.(formulaArg).Value, 64); err != nil { + if significance, err = strconv.ParseFloat(argsList.Front().Next().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -1479,7 +1590,7 @@ func (fn *formulaFuncs) FLOORMATH(argsList *list.List) (result string, err error return } if argsList.Len() > 2 { - if mode, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).Value, 64); err != nil { + if mode, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -1507,7 +1618,7 @@ func (fn *formulaFuncs) FLOORPRECISE(argsList *list.List) (result string, err er return } var number, significance float64 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -1519,7 +1630,7 @@ func (fn *formulaFuncs) FLOORPRECISE(argsList *list.List) (result string, err er return } if argsList.Len() > 1 { - if significance, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).Value, 64); err != nil { + if significance, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -1573,7 +1684,7 @@ func (fn *formulaFuncs) GCD(argsList *list.List) (result string, err error) { nums = []float64{} ) for arg := argsList.Front(); arg != nil; arg = arg.Next() { - token := arg.Value.(formulaArg).Value + token := arg.Value.(formulaArg).String if token == "" { continue } @@ -1614,7 +1725,7 @@ func (fn *formulaFuncs) INT(argsList *list.List) (result string, err error) { return } var number float64 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -1642,7 +1753,7 @@ func (fn *formulaFuncs) ISOCEILING(argsList *list.List) (result string, err erro return } var number, significance float64 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -1654,7 +1765,7 @@ func (fn *formulaFuncs) ISOCEILING(argsList *list.List) (result string, err erro return } if argsList.Len() > 1 { - if significance, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).Value, 64); err != nil { + if significance, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -1699,7 +1810,7 @@ func (fn *formulaFuncs) LCM(argsList *list.List) (result string, err error) { nums = []float64{} ) for arg := argsList.Front(); arg != nil; arg = arg.Next() { - token := arg.Value.(formulaArg).Value + token := arg.Value.(formulaArg).String if token == "" { continue } @@ -1740,7 +1851,7 @@ func (fn *formulaFuncs) LN(argsList *list.List) (result string, err error) { return } var number float64 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -1763,12 +1874,12 @@ func (fn *formulaFuncs) LOG(argsList *list.List) (result string, err error) { return } number, base := 0.0, 10.0 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } if argsList.Len() > 1 { - if base, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).Value, 64); err != nil { + if base, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -1800,7 +1911,7 @@ func (fn *formulaFuncs) LOG10(argsList *list.List) (result string, err error) { return } var number float64 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -1863,7 +1974,7 @@ func (fn *formulaFuncs) MDETERM(argsList *list.List) (result string, err error) } numRow := []float64{} for _, ele := range row { - if num, err = strconv.ParseFloat(ele, 64); err != nil { + if num, err = strconv.ParseFloat(ele.String, 64); err != nil { return } numRow = append(numRow, num) @@ -1885,11 +1996,11 @@ func (fn *formulaFuncs) MOD(argsList *list.List) (result string, err error) { return } var number, divisor float64 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } - if divisor, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).Value, 64); err != nil { + if divisor, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -1916,11 +2027,11 @@ func (fn *formulaFuncs) MROUND(argsList *list.List) (result string, err error) { return } var number, multiple float64 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } - if multiple, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).Value, 64); err != nil { + if multiple, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -1951,10 +2062,10 @@ func (fn *formulaFuncs) MULTINOMIAL(argsList *list.List) (result string, err err val, num, denom := 0.0, 0.0, 1.0 for arg := argsList.Front(); arg != nil; arg = arg.Next() { token := arg.Value.(formulaArg) - if token.Value == "" { + if token.String == "" { continue } - if val, err = strconv.ParseFloat(token.Value, 64); err != nil { + if val, err = strconv.ParseFloat(token.String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -1976,7 +2087,7 @@ func (fn *formulaFuncs) MUNIT(argsList *list.List) (result string, err error) { return } var dimension int - if dimension, err = strconv.Atoi(argsList.Front().Value.(formulaArg).Value); err != nil { + if dimension, err = strconv.Atoi(argsList.Front().Value.(formulaArg).String); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -2007,7 +2118,7 @@ func (fn *formulaFuncs) ODD(argsList *list.List) (result string, err error) { return } var number float64 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -2054,11 +2165,11 @@ func (fn *formulaFuncs) POWER(argsList *list.List) (result string, err error) { return } var x, y float64 - if x, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if x, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } - if y, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).Value, 64); err != nil { + if y, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -2083,14 +2194,32 @@ func (fn *formulaFuncs) PRODUCT(argsList *list.List) (result string, err error) val, product := 0.0, 1.0 for arg := argsList.Front(); arg != nil; arg = arg.Next() { token := arg.Value.(formulaArg) - if token.Value == "" { + switch token.Type { + case ArgUnknown: continue + case ArgString: + if token.String == "" { + continue + } + if val, err = strconv.ParseFloat(token.String, 64); err != nil { + err = errors.New(formulaErrorVALUE) + return + } + product = product * val + case ArgMatrix: + for _, row := range token.Matrix { + for _, value := range row { + if value.String == "" { + continue + } + if val, err = strconv.ParseFloat(value.String, 64); err != nil { + err = errors.New(formulaErrorVALUE) + return + } + product = product * val + } + } } - if val, err = strconv.ParseFloat(token.Value, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return - } - product = product * val } result = fmt.Sprintf("%g", product) return @@ -2107,11 +2236,11 @@ func (fn *formulaFuncs) QUOTIENT(argsList *list.List) (result string, err error) return } var x, y float64 - if x, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if x, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } - if y, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).Value, 64); err != nil { + if y, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -2133,7 +2262,7 @@ func (fn *formulaFuncs) RADIANS(argsList *list.List) (result string, err error) return } var angle float64 - if angle, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if angle, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -2166,11 +2295,11 @@ func (fn *formulaFuncs) RANDBETWEEN(argsList *list.List) (result string, err err return } var bottom, top int64 - if bottom, err = strconv.ParseInt(argsList.Front().Value.(formulaArg).Value, 10, 64); err != nil { + if bottom, err = strconv.ParseInt(argsList.Front().Value.(formulaArg).String, 10, 64); err != nil { err = errors.New(formulaErrorVALUE) return } - if top, err = strconv.ParseInt(argsList.Back().Value.(formulaArg).Value, 10, 64); err != nil { + if top, err = strconv.ParseInt(argsList.Back().Value.(formulaArg).String, 10, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -2213,12 +2342,12 @@ func (fn *formulaFuncs) ROMAN(argsList *list.List) (result string, err error) { } var number float64 var form int - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } if argsList.Len() > 1 { - if form, err = strconv.Atoi(argsList.Back().Value.(formulaArg).Value); err != nil { + if form, err = strconv.Atoi(argsList.Back().Value.(formulaArg).String); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -2298,11 +2427,11 @@ func (fn *formulaFuncs) ROUND(argsList *list.List) (result string, err error) { return } var number, digits float64 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } - if digits, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).Value, 64); err != nil { + if digits, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -2321,11 +2450,11 @@ func (fn *formulaFuncs) ROUNDDOWN(argsList *list.List) (result string, err error return } var number, digits float64 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } - if digits, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).Value, 64); err != nil { + if digits, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -2344,11 +2473,11 @@ func (fn *formulaFuncs) ROUNDUP(argsList *list.List) (result string, err error) return } var number, digits float64 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } - if digits, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).Value, 64); err != nil { + if digits, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -2367,7 +2496,7 @@ func (fn *formulaFuncs) SEC(argsList *list.List) (result string, err error) { return } var number float64 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -2386,7 +2515,7 @@ func (fn *formulaFuncs) SECH(argsList *list.List) (result string, err error) { return } var number float64 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -2407,7 +2536,7 @@ func (fn *formulaFuncs) SIGN(argsList *list.List) (result string, err error) { return } var val float64 - if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -2434,7 +2563,7 @@ func (fn *formulaFuncs) SIN(argsList *list.List) (result string, err error) { return } var number float64 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -2453,7 +2582,7 @@ func (fn *formulaFuncs) SINH(argsList *list.List) (result string, err error) { return } var number float64 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -2472,7 +2601,7 @@ func (fn *formulaFuncs) SQRT(argsList *list.List) (result string, err error) { return } var res float64 - var value = argsList.Front().Value.(formulaArg).Value + var value = argsList.Front().Value.(formulaArg).String if value == "" { result = "0" return @@ -2500,7 +2629,7 @@ func (fn *formulaFuncs) SQRTPI(argsList *list.List) (result string, err error) { return } var number float64 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -2517,14 +2646,79 @@ func (fn *formulaFuncs) SUM(argsList *list.List) (result string, err error) { var val, sum float64 for arg := argsList.Front(); arg != nil; arg = arg.Next() { token := arg.Value.(formulaArg) - if token.Value == "" { + switch token.Type { + case ArgUnknown: continue + case ArgString: + if token.String == "" { + continue + } + if val, err = strconv.ParseFloat(token.String, 64); err != nil { + err = errors.New(formulaErrorVALUE) + return + } + sum += val + case ArgMatrix: + for _, row := range token.Matrix { + for _, value := range row { + if value.String == "" { + continue + } + if val, err = strconv.ParseFloat(value.String, 64); err != nil { + err = errors.New(formulaErrorVALUE) + return + } + sum += val + } + } } - if val, err = strconv.ParseFloat(token.Value, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + } + result = fmt.Sprintf("%g", sum) + return +} + +// SUMIF function finds the values in a supplied array, that satisfy a given +// criteria, and returns the sum of the corresponding values in a second +// supplied array. The syntax of the function is: +// +// SUMIF(range,criteria,[sum_range]) +// +func (fn *formulaFuncs) SUMIF(argsList *list.List) (result string, err error) { + if argsList.Len() < 2 { + err = errors.New("SUMIF requires at least 2 argument") + return + } + var criteria = formulaCriteriaParser(argsList.Front().Next().Value.(formulaArg).String) + var rangeMtx = argsList.Front().Value.(formulaArg).Matrix + var sumRange [][]formulaArg + if argsList.Len() == 3 { + sumRange = argsList.Back().Value.(formulaArg).Matrix + } + var sum, val float64 + for rowIdx, row := range rangeMtx { + for colIdx, col := range row { + var ok bool + fromVal := col.String + if col.String == "" { + continue + } + if ok, err = formulaCriteriaEval(fromVal, criteria); err != nil { + return + } + if ok { + if argsList.Len() == 3 { + if len(sumRange) <= rowIdx || len(sumRange[rowIdx]) <= colIdx { + continue + } + fromVal = sumRange[rowIdx][colIdx].String + } + if val, err = strconv.ParseFloat(fromVal, 64); err != nil { + err = errors.New(formulaErrorVALUE) + return + } + sum += val + } } - sum += val } result = fmt.Sprintf("%g", sum) return @@ -2539,14 +2733,30 @@ func (fn *formulaFuncs) SUMSQ(argsList *list.List) (result string, err error) { var val, sq float64 for arg := argsList.Front(); arg != nil; arg = arg.Next() { token := arg.Value.(formulaArg) - if token.Value == "" { - continue - } - if val, err = strconv.ParseFloat(token.Value, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + switch token.Type { + case ArgString: + if token.String == "" { + continue + } + if val, err = strconv.ParseFloat(token.String, 64); err != nil { + err = errors.New(formulaErrorVALUE) + return + } + sq += val * val + case ArgMatrix: + for _, row := range token.Matrix { + for _, value := range row { + if value.String == "" { + continue + } + if val, err = strconv.ParseFloat(value.String, 64); err != nil { + err = errors.New(formulaErrorVALUE) + return + } + sq += val * val + } + } } - sq += val * val } result = fmt.Sprintf("%g", sq) return @@ -2563,7 +2773,7 @@ func (fn *formulaFuncs) TAN(argsList *list.List) (result string, err error) { return } var number float64 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -2582,7 +2792,7 @@ func (fn *formulaFuncs) TANH(argsList *list.List) (result string, err error) { return } var number float64 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -2601,12 +2811,12 @@ func (fn *formulaFuncs) TRUNC(argsList *list.List) (result string, err error) { return } var number, digits, adjust, rtrim float64 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } if argsList.Len() > 1 { - if digits, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).Value, 64); err != nil { + if digits, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } @@ -2628,3 +2838,5 @@ func (fn *formulaFuncs) TRUNC(argsList *list.List) (result string, err error) { } // Statistical functions + +// Information functions diff --git a/calc_test.go b/calc_test.go index 7592078..3639af7 100644 --- a/calc_test.go +++ b/calc_test.go @@ -1,20 +1,32 @@ package excelize import ( + "path/filepath" "testing" "github.com/stretchr/testify/assert" ) func TestCalcCellValue(t *testing.T) { + cellData := [][]interface{}{ + {1, 4, nil, "Month", "Team", "Sales"}, + {2, 5, nil, "Jan", "North 1", 36693}, + {3, nil, nil, "Jan", "North 2", 22100}, + {0, nil, nil, "Jan", "South 1", 53321}, + {nil, nil, nil, "Jan", "South 2", 34440}, + {nil, nil, nil, "Feb", "North 1", 29889}, + {nil, nil, nil, "Feb", "North 2", 50090}, + {nil, nil, nil, "Feb", "South 1", 32080}, + {nil, nil, nil, "Feb", "South 2", 45500}, + } prepareData := func() *File { f := NewFile() - f.SetCellValue("Sheet1", "A1", 1) - f.SetCellValue("Sheet1", "A2", 2) - f.SetCellValue("Sheet1", "A3", 3) - f.SetCellValue("Sheet1", "A4", 0) - f.SetCellValue("Sheet1", "B1", 4) - f.SetCellValue("Sheet1", "B2", 5) + for r, row := range cellData { + for c, value := range row { + cell, _ := CoordinatesToCellName(c+1, r+1) + assert.NoError(t, f.SetCellValue("Sheet1", cell, value)) + } + } return f } @@ -348,6 +360,12 @@ func TestCalcCellValue(t *testing.T) { "=((3+5*2)+3)/5+(-6)/4*2+3": "3.2", "=1+SUM(SUM(1,2*3),4)*-4/2+5+(4+2)*3": "2", "=1+SUM(SUM(1,2*3),4)*4/3+5+(4+2)*3": "38.666666666666664", + // SUMIF + `=SUMIF(F1:F5, ">100")`: "146554", + `=SUMIF(D3:D7,"Jan",F2:F5)`: "112114", + `=SUMIF(D2:D9,"Feb",F2:F9)`: "157559", + `=SUMIF(E2:E9,"North 1",F2:F9)`: "66582", + `=SUMIF(E2:E9,"North*",F2:F9)`: "138772", // SUMSQ "=SUMSQ(A1:A4)": "14", "=SUMSQ(A1,B1,A2,B2,6)": "82", @@ -627,6 +645,8 @@ func TestCalcCellValue(t *testing.T) { "=SUM(1*)": "formula not valid", "=SUM(1/)": "formula not valid", `=SUM("X")`: "#VALUE!", + // SUMIF + "=SUMIF()": "SUMIF requires at least 2 argument", // SUMSQ `=SUMSQ("X")`: "#VALUE!", // TAN @@ -711,4 +731,5 @@ func TestCalcCellValue(t *testing.T) { assert.NoError(t, f.SetCellFormula("Sheet1", "A1", "=UNSUPPORT(A1)")) _, err = f.CalcCellValue("Sheet1", "A1") assert.EqualError(t, err, "not support UNSUPPORT function") + assert.NoError(t, f.SaveAs(filepath.Join("test", "TestCalcCellValue.xlsx"))) } |