// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of // this source code is governed by a BSD-style license that can be found in // the LICENSE file. // // Package excelize providing a set of functions that allow you to write to // and read from XLSX / XLSM / XLTM files. Supports reading and writing // spreadsheet documents generated by Microsoft Exce™ 2007 and later. Supports // complex components by high compatibility, and provided streaming API for // generating or reading data from a worksheet with huge amounts of data. This // library needs Go version 1.10 or later. package excelize import ( "bytes" "container/list" "errors" "fmt" "math" "math/rand" "reflect" "regexp" "sort" "strconv" "strings" "time" "github.com/xuri/efp" ) // Excel formula errors const ( formulaErrorDIV = "#DIV/0!" formulaErrorNAME = "#NAME?" formulaErrorNA = "#N/A" formulaErrorNUM = "#NUM!" formulaErrorVALUE = "#VALUE!" formulaErrorREF = "#REF!" formulaErrorNULL = "#NULL" formulaErrorSPILL = "#SPILL!" formulaErrorCALC = "#CALC!" formulaErrorGETTINGDATA = "#GETTING_DATA" ) // cellRef defines the structure of a cell reference. type cellRef struct { Col int Row int Sheet string } // cellRef defines the structure of a cell range. type cellRange struct { From cellRef 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 { String string Matrix [][]formulaArg Type ArgType } // formulaFuncs is the type of the formula functions. type formulaFuncs struct{} // CalcCellValue provides a function to get calculated cell value. This // feature is currently in working processing. Array formula, table formula // and some other formulas are not supported currently. // // Supported formulas: // // ABS, ACOS, ACOSH, ACOT, ACOTH, ARABIC, ASIN, ASINH, ATAN2, ATANH, BASE, // CEILING, CEILING.MATH, CEILING.PRECISE, COMBIN, COMBINA, COS, COSH, COT, // COTH, COUNTA, CSC, CSCH, DECIMAL, DEGREES, EVEN, EXP, FACT, FACTDOUBLE, // FLOOR, FLOOR.MATH, FLOOR.PRECISE, GCD, INT, ISBLANK, ISERR, ISERROR, // ISEVEN, ISNA, ISNONTEXT, ISNUMBER, ISO.CEILING, ISODD, LCM, LN, LOG, // LOG10, MDETERM, MEDIAN, MOD, MROUND, MULTINOMIAL, MUNIT, NA, ODD, PI, // POWER, PRODUCT, QUOTIENT, RADIANS, RAND, RANDBETWEEN, ROUND, ROUNDDOWN, // ROUNDUP, SEC, SECH, SIGN, SIN, SINH, SQRT, SQRTPI, SUM, SUMIF, SUMSQ, // TAN, TANH, TRUNC // func (f *File) CalcCellValue(sheet, cell string) (result string, err error) { var ( formula string token efp.Token ) if formula, err = f.GetCellFormula(sheet, cell); err != nil { return } ps := efp.ExcelParser() tokens := ps.Parse(formula) if tokens == nil { return } if token, err = f.evalInfixExp(sheet, tokens); err != nil { return } result = token.TValue return } // getPriority calculate arithmetic operator priority. func getPriority(token efp.Token) (pri int) { var priority = map[string]int{ "*": 2, "/": 2, "+": 1, "-": 1, } pri, _ = priority[token.TValue] if token.TValue == "-" && token.TType == efp.TokenTypeOperatorPrefix { pri = 3 } if token.TSubType == efp.TokenSubTypeStart && token.TType == efp.TokenTypeSubexpression { // ( pri = 0 } return } // evalInfixExp evaluate syntax analysis by given infix expression after // lexical analysis. Evaluate an infix expression containing formulas by // stacks: // // opd - Operand // opt - Operator // opf - Operation formula // opfd - Operand of the operation formula // opft - Operator of the operation formula // // Evaluate arguments of the operation formula by list: // // args - Arguments of the operation formula // // TODO: handle subtypes: Nothing, Text, Logical, Error, Concatenation, Intersection, Union // func (f *File) evalInfixExp(sheet string, tokens []efp.Token) (efp.Token, error) { var err error opdStack, optStack, opfStack, opfdStack, opftStack := NewStack(), NewStack(), NewStack(), NewStack(), NewStack() argsList := list.New() for i := 0; i < len(tokens); i++ { token := tokens[i] // out of function stack if opfStack.Len() == 0 { if err = f.parseToken(sheet, token, opdStack, optStack); err != nil { return efp.Token{}, err } } // function start if token.TType == efp.TokenTypeFunction && token.TSubType == efp.TokenSubTypeStart { opfStack.Push(token) continue } // in function stack, walk 2 token at once if opfStack.Len() > 0 { var nextToken efp.Token if i+1 < len(tokens) { nextToken = tokens[i+1] } // current token is args or range, skip next token, order required: parse reference first if token.TSubType == efp.TokenSubTypeRange { if !opftStack.Empty() { // parse reference: must reference at here result, err := f.parseReference(sheet, token.TValue) if err != nil { return efp.Token{TValue: formulaErrorNAME}, err } if result.Type != ArgString { return efp.Token{}, errors.New(formulaErrorVALUE) } opfdStack.Push(efp.Token{ TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber, TValue: result.String, }) continue } if nextToken.TType == efp.TokenTypeArgument || nextToken.TType == efp.TokenTypeFunction { // parse reference: reference or range at here result, err := f.parseReference(sheet, token.TValue) if err != nil { return efp.Token{TValue: formulaErrorNAME}, err } if result.Type == ArgUnknown { return efp.Token{}, errors.New(formulaErrorVALUE) } argsList.PushBack(result) continue } } // check current token is opft if err = f.parseToken(sheet, token, opfdStack, opftStack); err != nil { return efp.Token{}, err } // current token is arg if token.TType == efp.TokenTypeArgument { for !opftStack.Empty() { // calculate trigger topOpt := opftStack.Peek().(efp.Token) if err := calculate(opfdStack, topOpt); err != nil { return efp.Token{}, err } opftStack.Pop() } if !opfdStack.Empty() { argsList.PushBack(formulaArg{ String: opfdStack.Pop().(efp.Token).TValue, Type: ArgString, }) } continue } // current token is logical if token.TType == efp.OperatorsInfix && token.TSubType == efp.TokenSubTypeLogical { } // current token is text if token.TType == efp.TokenTypeOperand && token.TSubType == efp.TokenSubTypeText { argsList.PushBack(formulaArg{ String: token.TValue, Type: ArgString, }) } // current token is function stop if token.TType == efp.TokenTypeFunction && token.TSubType == efp.TokenSubTypeStop { for !opftStack.Empty() { // calculate trigger topOpt := opftStack.Peek().(efp.Token) if err := calculate(opfdStack, topOpt); err != nil { return efp.Token{}, err } opftStack.Pop() } // push opfd to args if opfdStack.Len() > 0 { argsList.PushBack(formulaArg{ String: opfdStack.Pop().(efp.Token).TValue, Type: ArgString, }) } // call formula function to evaluate result, err := callFuncByName(&formulaFuncs{}, strings.NewReplacer( "_xlfn", "", ".", "").Replace(opfStack.Peek().(efp.Token).TValue), []reflect.Value{reflect.ValueOf(argsList)}) if err != nil { return efp.Token{}, err } argsList.Init() opfStack.Pop() if opfStack.Len() > 0 { // still in function stack opfdStack.Push(efp.Token{TValue: result, TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber}) } else { opdStack.Push(efp.Token{TValue: result, TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber}) } } } } for optStack.Len() != 0 { topOpt := optStack.Peek().(efp.Token) if err = calculate(opdStack, topOpt); err != nil { return efp.Token{}, err } optStack.Pop() } if opdStack.Len() == 0 { return efp.Token{}, errors.New("formula not valid") } return opdStack.Peek().(efp.Token), err } // calcAdd evaluate addition arithmetic operations. func calcAdd(opdStack *Stack) error { if opdStack.Len() < 2 { return errors.New("formula not valid") } rOpd := opdStack.Pop().(efp.Token) lOpd := opdStack.Pop().(efp.Token) lOpdVal, err := strconv.ParseFloat(lOpd.TValue, 64) if err != nil { return err } rOpdVal, err := strconv.ParseFloat(rOpd.TValue, 64) if err != nil { return err } result := lOpdVal + rOpdVal opdStack.Push(efp.Token{TValue: fmt.Sprintf("%g", result), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber}) return nil } // calcSubtract evaluate subtraction arithmetic operations. func calcSubtract(opdStack *Stack) error { if opdStack.Len() < 2 { return errors.New("formula not valid") } rOpd := opdStack.Pop().(efp.Token) lOpd := opdStack.Pop().(efp.Token) lOpdVal, err := strconv.ParseFloat(lOpd.TValue, 64) if err != nil { return err } rOpdVal, err := strconv.ParseFloat(rOpd.TValue, 64) if err != nil { return err } result := lOpdVal - rOpdVal opdStack.Push(efp.Token{TValue: fmt.Sprintf("%g", result), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber}) return nil } // calcMultiply evaluate multiplication arithmetic operations. func calcMultiply(opdStack *Stack) error { if opdStack.Len() < 2 { return errors.New("formula not valid") } rOpd := opdStack.Pop().(efp.Token) lOpd := opdStack.Pop().(efp.Token) lOpdVal, err := strconv.ParseFloat(lOpd.TValue, 64) if err != nil { return err } rOpdVal, err := strconv.ParseFloat(rOpd.TValue, 64) if err != nil { return err } result := lOpdVal * rOpdVal opdStack.Push(efp.Token{TValue: fmt.Sprintf("%g", result), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber}) return nil } // calcDivide evaluate division arithmetic operations. func calcDivide(opdStack *Stack) error { if opdStack.Len() < 2 { return errors.New("formula not valid") } rOpd := opdStack.Pop().(efp.Token) lOpd := opdStack.Pop().(efp.Token) lOpdVal, err := strconv.ParseFloat(lOpd.TValue, 64) if err != nil { return err } rOpdVal, err := strconv.ParseFloat(rOpd.TValue, 64) if err != nil { return err } result := lOpdVal / rOpdVal if rOpdVal == 0 { return errors.New(formulaErrorDIV) } opdStack.Push(efp.Token{TValue: fmt.Sprintf("%g", result), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber}) return nil } // calculate evaluate basic arithmetic operations. func calculate(opdStack *Stack, opt efp.Token) error { if opt.TValue == "-" && opt.TType == efp.TokenTypeOperatorPrefix { if opdStack.Len() < 1 { return errors.New("formula not valid") } opd := opdStack.Pop().(efp.Token) opdVal, err := strconv.ParseFloat(opd.TValue, 64) if err != nil { return err } result := 0 - opdVal opdStack.Push(efp.Token{TValue: fmt.Sprintf("%g", result), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber}) } if opt.TValue == "+" { if err := calcAdd(opdStack); err != nil { return err } } if opt.TValue == "-" && opt.TType == efp.TokenTypeOperatorInfix { if err := calcSubtract(opdStack); err != nil { return err } } if opt.TValue == "*" { if err := calcMultiply(opdStack); err != nil { return err } } if opt.TValue == "/" { if err := calcDivide(opdStack); err != nil { return err } } return nil } // parseOperatorPrefixToken parse operator prefix token. func (f *File) parseOperatorPrefixToken(optStack, opdStack *Stack, token efp.Token) (err error) { if optStack.Len() == 0 { optStack.Push(token) } else { tokenPriority := getPriority(token) topOpt := optStack.Peek().(efp.Token) topOptPriority := getPriority(topOpt) if tokenPriority > topOptPriority { optStack.Push(token) } else { for tokenPriority <= topOptPriority { optStack.Pop() if err = calculate(opdStack, topOpt); err != nil { return } if optStack.Len() > 0 { topOpt = optStack.Peek().(efp.Token) topOptPriority = getPriority(topOpt) continue } break } optStack.Push(token) } } return } // isOperatorPrefixToken determine if the token is parse operator prefix // token. func isOperatorPrefixToken(token efp.Token) bool { if (token.TValue == "-" && token.TType == efp.TokenTypeOperatorPrefix) || token.TValue == "+" || token.TValue == "-" || token.TValue == "*" || token.TValue == "/" { return true } return false } func (f *File) getDefinedNameRefTo(definedNameName string, currentSheet string) (refTo string) { for _, definedName := range f.GetDefinedName() { if definedName.Name == definedNameName { refTo = definedName.RefersTo // worksheet scope takes precedence over scope workbook when both definedNames exist if definedName.Scope == currentSheet { break } } } return refTo } // parseToken parse basic arithmetic operator priority and evaluate based on // operators and operands. func (f *File) parseToken(sheet string, token efp.Token, opdStack, optStack *Stack) error { // parse reference: must reference at here if token.TSubType == efp.TokenSubTypeRange { refTo := f.getDefinedNameRefTo(token.TValue, sheet) if refTo != "" { token.TValue = refTo } result, err := f.parseReference(sheet, token.TValue) if err != nil { return errors.New(formulaErrorNAME) } if result.Type != ArgString { return errors.New(formulaErrorVALUE) } token.TValue = result.String token.TType = efp.TokenTypeOperand token.TSubType = efp.TokenSubTypeNumber } if isOperatorPrefixToken(token) { if err := f.parseOperatorPrefixToken(optStack, opdStack, token); err != nil { return err } } if token.TType == efp.TokenTypeSubexpression && token.TSubType == efp.TokenSubTypeStart { // ( optStack.Push(token) } if token.TType == efp.TokenTypeSubexpression && token.TSubType == efp.TokenSubTypeStop { // ) for optStack.Peek().(efp.Token).TSubType != efp.TokenSubTypeStart && optStack.Peek().(efp.Token).TType != efp.TokenTypeSubexpression { // != ( topOpt := optStack.Peek().(efp.Token) if err := calculate(opdStack, topOpt); err != nil { return err } optStack.Pop() } optStack.Pop() } // opd if token.TType == efp.TokenTypeOperand && token.TSubType == efp.TokenSubTypeNumber { opdStack.Push(token) } return nil } // parseReference parse reference and extract values by given reference // characters and default sheet name. 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, ":") { tokens := strings.Split(ref, "!") cr := cellRef{} if len(tokens) == 2 { // have a worksheet name cr.Sheet = tokens[0] if cr.Col, cr.Row, err = CellNameToCoordinates(tokens[1]); err != nil { return } if refs.Len() > 0 { e := refs.Back() cellRefs.PushBack(e.Value.(cellRef)) refs.Remove(e) } refs.PushBack(cr) continue } if cr.Col, cr.Row, err = CellNameToCoordinates(tokens[0]); err != nil { return } e := refs.Back() if e == nil { cr.Sheet = sheet refs.PushBack(cr) continue } cellRanges.PushBack(cellRange{ From: e.Value.(cellRef), To: cr, }) refs.Remove(e) } if refs.Len() > 0 { e := refs.Back() cellRefs.PushBack(e.Value.(cellRef)) refs.Remove(e) } arg, err = f.rangeResolver(cellRefs, cellRanges) return } // prepareValueRange prepare value range. func prepareValueRange(cr cellRange, valueRange []int) { if cr.From.Row < valueRange[0] || valueRange[0] == 0 { valueRange[0] = cr.From.Row } if cr.From.Col < valueRange[2] || valueRange[2] == 0 { valueRange[2] = cr.From.Col } if cr.To.Row > valueRange[1] || valueRange[1] == 0 { valueRange[1] = cr.To.Row } 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] || valueRange[0] == 0 { valueRange[0] = cr.Row } if cr.Col < valueRange[2] || valueRange[2] == 0 { valueRange[2] = cr.Col } if cr.Row > valueRange[1] || valueRange[1] == 0 { valueRange[1] = cr.Row } if cr.Col > valueRange[3] || valueRange[3] == 0 { valueRange[3] = cr.Col } } // 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) (arg formulaArg, err error) { // value range order: from row, to row, from column, to column valueRange := []int{0, 0, 0, 0} var sheet string // prepare value range for temp := cellRanges.Front(); temp != nil; temp = temp.Next() { cr := temp.Value.(cellRange) if cr.From.Sheet != cr.To.Sheet { err = errors.New(formulaErrorVALUE) } 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 } } for temp := cellRefs.Front(); temp != nil; temp = temp.Next() { cr := temp.Value.(cellRef) if cr.Sheet != "" { sheet = cr.Sheet } prepareValueRef(cr, valueRange) } // extract value from ranges if cellRanges.Len() > 0 { arg.Type = ArgMatrix for row := valueRange[0]; row <= valueRange[1]; row++ { var matrixRow = []formulaArg{} for col := valueRange[2]; col <= valueRange[3]; col++ { var cell, value string if cell, err = CoordinatesToCellName(col, row); err != nil { return } if value, err = f.GetCellValue(sheet, cell); err != nil { return } matrixRow = append(matrixRow, formulaArg{ String: value, Type: ArgString, }) } arg.Matrix = append(arg.Matrix, matrixRow) } return } // extract value from references for temp := cellRefs.Front(); temp != nil; temp = temp.Next() { cr := temp.Value.(cellRef) var cell string if cell, err = CoordinatesToCellName(cr.Col, cr.Row); err != nil { return } if arg.String, err = f.GetCellValue(cr.Sheet, cell); err != nil { return } arg.Type = ArgString } return } // callFuncByName calls the no error or only error return function with // reflect by given receiver, name and parameters. func callFuncByName(receiver interface{}, name string, params []reflect.Value) (result string, err error) { function := reflect.ValueOf(receiver).MethodByName(name) if function.IsValid() { rt := function.Call(params) if len(rt) == 0 { return } if !rt[1].IsNil() { err = rt[1].Interface().(error) return } result = rt[0].Interface().(string) return } err = fmt.Errorf("not support %s function", name) 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 // of the function is: // // ABS(number) // func (fn *formulaFuncs) ABS(argsList *list.List) (result string, err error) { if argsList.Len() != 1 { err = errors.New("ABS requires 1 numeric argument") return } var val float64 if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } result = fmt.Sprintf("%g", math.Abs(val)) return } // ACOS function calculates the arccosine (i.e. the inverse cosine) of a given // number, and returns an angle, in radians, between 0 and π. The syntax of // the function is: // // ACOS(number) // func (fn *formulaFuncs) ACOS(argsList *list.List) (result string, err error) { if argsList.Len() != 1 { err = errors.New("ACOS requires 1 numeric argument") return } var val float64 if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } result = fmt.Sprintf("%g", math.Acos(val)) return } // ACOSH function calculates the inverse hyperbolic cosine of a supplied number. // of the function is: // // ACOSH(number) // func (fn *formulaFuncs) ACOSH(argsList *list.List) (result string, err error) { if argsList.Len() != 1 { err = errors.New("ACOSH requires 1 numeric argument") return } var val float64 if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } result = fmt.Sprintf("%g", math.Acosh(val)) return } // ACOT function calculates the arccotangent (i.e. the inverse cotangent) of a // given number, and returns an angle, in radians, between 0 and π. The syntax // of the function is: // // ACOT(number) // func (fn *formulaFuncs) ACOT(argsList *list.List) (result string, err error) { if argsList.Len() != 1 { err = errors.New("ACOT requires 1 numeric argument") return } var val float64 if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } result = fmt.Sprintf("%g", math.Pi/2-math.Atan(val)) return } // ACOTH function calculates the hyperbolic arccotangent (coth) of a supplied // value. The syntax of the function is: // // ACOTH(number) // func (fn *formulaFuncs) ACOTH(argsList *list.List) (result string, err error) { if argsList.Len() != 1 { err = errors.New("ACOTH requires 1 numeric argument") return } var val float64 if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } result = fmt.Sprintf("%g", math.Atanh(1/val)) return } // ARABIC function converts a Roman numeral into an Arabic numeral. The syntax // of the function is: // // ARABIC(text) // func (fn *formulaFuncs) ARABIC(argsList *list.List) (result string, err error) { if argsList.Len() != 1 { err = errors.New("ARABIC requires 1 numeric argument") return } 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).String { digit := 0.0 if char == '-' { prefix = -1 continue } digit, _ = charMap[char] val += digit switch { case last == digit && (last == 5 || last == 50 || last == 500): result = formulaErrorVALUE return case 2*last == digit: result = formulaErrorVALUE return } if last < digit { val -= 2 * last } last = digit } result = fmt.Sprintf("%g", prefix*val) return } // ASIN function calculates the arcsine (i.e. the inverse sine) of a given // number, and returns an angle, in radians, between -π/2 and π/2. The syntax // of the function is: // // ASIN(number) // func (fn *formulaFuncs) ASIN(argsList *list.List) (result string, err error) { if argsList.Len() != 1 { err = errors.New("ASIN requires 1 numeric argument") return } var val float64 if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } result = fmt.Sprintf("%g", math.Asin(val)) return } // ASINH function calculates the inverse hyperbolic sine of a supplied number. // The syntax of the function is: // // ASINH(number) // func (fn *formulaFuncs) ASINH(argsList *list.List) (result string, err error) { if argsList.Len() != 1 { err = errors.New("ASINH requires 1 numeric argument") return } var val float64 if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } result = fmt.Sprintf("%g", math.Asinh(val)) return } // ATAN function calculates the arctangent (i.e. the inverse tangent) of a // given number, and returns an angle, in radians, between -π/2 and +π/2. The // syntax of the function is: // // ATAN(number) // func (fn *formulaFuncs) ATAN(argsList *list.List) (result string, err error) { if argsList.Len() != 1 { err = errors.New("ATAN requires 1 numeric argument") return } var val float64 if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } result = fmt.Sprintf("%g", math.Atan(val)) return } // ATANH function calculates the inverse hyperbolic tangent of a supplied // number. The syntax of the function is: // // ATANH(number) // func (fn *formulaFuncs) ATANH(argsList *list.List) (result string, err error) { if argsList.Len() != 1 { err = errors.New("ATANH requires 1 numeric argument") return } var val float64 if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } result = fmt.Sprintf("%g", math.Atanh(val)) return } // ATAN2 function calculates the arctangent (i.e. the inverse tangent) of a // given set of x and y coordinates, and returns an angle, in radians, between // -π/2 and +π/2. The syntax of the function is: // // ATAN2(x_num,y_num) // func (fn *formulaFuncs) ATAN2(argsList *list.List) (result string, err error) { if argsList.Len() != 2 { err = errors.New("ATAN2 requires 2 numeric arguments") return } var x, y float64 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).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } result = fmt.Sprintf("%g", math.Atan2(x, y)) return } // BASE function converts a number into a supplied base (radix), and returns a // text representation of the calculated value. The syntax of the function is: // // BASE(number,radix,[min_length]) // func (fn *formulaFuncs) BASE(argsList *list.List) (result string, err error) { if argsList.Len() < 2 { err = errors.New("BASE requires at least 2 arguments") return } if argsList.Len() > 3 { err = errors.New("BASE allows at most 3 arguments") return } var number float64 var radix, minLength int 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).String); err != nil { err = errors.New(formulaErrorVALUE) return } if radix < 2 || radix > 36 { err = errors.New("radix must be an integer >= 2 and <= 36") return } if argsList.Len() > 2 { if minLength, err = strconv.Atoi(argsList.Back().Value.(formulaArg).String); err != nil { err = errors.New(formulaErrorVALUE) return } } result = strconv.FormatInt(int64(number), radix) if len(result) < minLength { result = strings.Repeat("0", minLength-len(result)) + result } result = strings.ToUpper(result) return } // CEILING function rounds a supplied number away from zero, to the nearest // multiple of a given number. The syntax of the function is: // // CEILING(number,significance) // func (fn *formulaFuncs) CEILING(argsList *list.List) (result string, err error) { if argsList.Len() == 0 { err = errors.New("CEILING requires at least 1 argument") return } if argsList.Len() > 2 { err = errors.New("CEILING allows at most 2 arguments") return } number, significance, res := 0.0, 1.0, 0.0 if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } if number < 0 { significance = -1 } if argsList.Len() > 1 { if significance, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } } if significance < 0 && number > 0 { err = errors.New("negative sig to CEILING invalid") return } if argsList.Len() == 1 { result = fmt.Sprintf("%g", math.Ceil(number)) return } number, res = math.Modf(number / significance) if res > 0 { number++ } result = fmt.Sprintf("%g", number*significance) return } // CEILINGMATH function rounds a supplied number up to a supplied multiple of // significance. The syntax of the function is: // // CEILING.MATH(number,[significance],[mode]) // func (fn *formulaFuncs) CEILINGMATH(argsList *list.List) (result string, err error) { if argsList.Len() == 0 { err = errors.New("CEILING.MATH requires at least 1 argument") return } if argsList.Len() > 3 { err = errors.New("CEILING.MATH allows at most 3 arguments") return } number, significance, mode := 0.0, 1.0, 1.0 if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } if number < 0 { significance = -1 } if argsList.Len() > 1 { if significance, err = strconv.ParseFloat(argsList.Front().Next().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } } if argsList.Len() == 1 { result = fmt.Sprintf("%g", math.Ceil(number)) return } if argsList.Len() > 2 { if mode, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } } val, res := math.Modf(number / significance) if res != 0 { if number > 0 { val++ } else if mode < 0 { val-- } } result = fmt.Sprintf("%g", val*significance) return } // CEILINGPRECISE function rounds a supplied number up (regardless of the // number's sign), to the nearest multiple of a given number. The syntax of // the function is: // // CEILING.PRECISE(number,[significance]) // func (fn *formulaFuncs) CEILINGPRECISE(argsList *list.List) (result string, err error) { if argsList.Len() == 0 { err = errors.New("CEILING.PRECISE requires at least 1 argument") return } if argsList.Len() > 2 { err = errors.New("CEILING.PRECISE allows at most 2 arguments") return } number, significance := 0.0, 1.0 if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } if number < 0 { significance = -1 } if argsList.Len() == 1 { result = fmt.Sprintf("%g", math.Ceil(number)) return } if argsList.Len() > 1 { if significance, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } significance = math.Abs(significance) if significance == 0 { result = "0" return } } val, res := math.Modf(number / significance) if res != 0 { if number > 0 { val++ } } result = fmt.Sprintf("%g", val*significance) return } // COMBIN function calculates the number of combinations (in any order) of a // given number objects from a set. The syntax of the function is: // // COMBIN(number,number_chosen) // func (fn *formulaFuncs) COMBIN(argsList *list.List) (result string, err error) { if argsList.Len() != 2 { err = errors.New("COMBIN requires 2 argument") return } number, chosen, val := 0.0, 0.0, 1.0 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).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } number, chosen = math.Trunc(number), math.Trunc(chosen) if chosen > number { err = errors.New("COMBIN requires number >= number_chosen") return } if chosen == number || chosen == 0 { result = "1" return } for c := float64(1); c <= chosen; c++ { val *= (number + 1 - c) / c } result = fmt.Sprintf("%g", math.Ceil(val)) return } // COMBINA function calculates the number of combinations, with repetitions, // of a given number objects from a set. The syntax of the function is: // // COMBINA(number,number_chosen) // func (fn *formulaFuncs) COMBINA(argsList *list.List) (result string, err error) { if argsList.Len() != 2 { err = errors.New("COMBINA requires 2 argument") return } var number, chosen float64 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).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } number, chosen = math.Trunc(number), math.Trunc(chosen) if number < chosen { err = errors.New("COMBINA requires number > number_chosen") return } if number == 0 { result = "0" return } args := list.New() args.PushBack(formulaArg{ String: fmt.Sprintf("%g", number+chosen-1), Type: ArgString, }) args.PushBack(formulaArg{ String: fmt.Sprintf("%g", number-1), Type: ArgString, }) return fn.COMBIN(args) } // COS function calculates the cosine of a given angle. The syntax of the // function is: // // COS(number) // func (fn *formulaFuncs) COS(argsList *list.List) (result string, err error) { if argsList.Len() != 1 { err = errors.New("COS requires 1 numeric argument") return } var val float64 if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } result = fmt.Sprintf("%g", math.Cos(val)) return } // COSH function calculates the hyperbolic cosine (cosh) of a supplied number. // The syntax of the function is: // // COSH(number) // func (fn *formulaFuncs) COSH(argsList *list.List) (result string, err error) { if argsList.Len() != 1 { err = errors.New("COSH requires 1 numeric argument") return } var val float64 if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } result = fmt.Sprintf("%g", math.Cosh(val)) return } // COT function calculates the cotangent of a given angle. The syntax of the // function is: // // COT(number) // func (fn *formulaFuncs) COT(argsList *list.List) (result string, err error) { if argsList.Len() != 1 { err = errors.New("COT requires 1 numeric argument") return } var val float64 if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } if val == 0 { err = errors.New(formulaErrorDIV) return } result = fmt.Sprintf("%g", math.Tan(val)) return } // COTH function calculates the hyperbolic cotangent (coth) of a supplied // angle. The syntax of the function is: // // COTH(number) // func (fn *formulaFuncs) COTH(argsList *list.List) (result string, err error) { if argsList.Len() != 1 { err = errors.New("COTH requires 1 numeric argument") return } var val float64 if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } if val == 0 { err = errors.New(formulaErrorDIV) return } result = fmt.Sprintf("%g", math.Tanh(val)) return } // CSC function calculates the cosecant of a given angle. The syntax of the // function is: // // CSC(number) // func (fn *formulaFuncs) CSC(argsList *list.List) (result string, err error) { if argsList.Len() != 1 { err = errors.New("CSC requires 1 numeric argument") return } var val float64 if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } if val == 0 { err = errors.New(formulaErrorDIV) return } result = fmt.Sprintf("%g", 1/math.Sin(val)) return } // CSCH function calculates the hyperbolic cosecant (csch) of a supplied // angle. The syntax of the function is: // // CSCH(number) // func (fn *formulaFuncs) CSCH(argsList *list.List) (result string, err error) { if argsList.Len() != 1 { err = errors.New("CSCH requires 1 numeric argument") return } var val float64 if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } if val == 0 { err = errors.New(formulaErrorDIV) return } result = fmt.Sprintf("%g", 1/math.Sinh(val)) return } // DECIMAL function converts a text representation of a number in a specified // base, into a decimal value. The syntax of the function is: // // DECIMAL(text,radix) // func (fn *formulaFuncs) DECIMAL(argsList *list.List) (result string, err error) { if argsList.Len() != 2 { err = errors.New("DECIMAL requires 2 numeric arguments") return } var text = argsList.Front().Value.(formulaArg).String var radix int if radix, err = strconv.Atoi(argsList.Back().Value.(formulaArg).String); err != nil { err = errors.New(formulaErrorVALUE) return } if len(text) > 2 && (strings.HasPrefix(text, "0x") || strings.HasPrefix(text, "0X")) { text = text[2:] } val, err := strconv.ParseInt(text, radix, 64) if err != nil { err = errors.New(formulaErrorVALUE) return } result = fmt.Sprintf("%g", float64(val)) return } // DEGREES function converts radians into degrees. The syntax of the function // is: // // DEGREES(angle) // func (fn *formulaFuncs) DEGREES(argsList *list.List) (result string, err error) { if argsList.Len() != 1 { err = errors.New("DEGREES requires 1 numeric argument") return } var val float64 if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } if val == 0 { err = errors.New(formulaErrorDIV) return } result = fmt.Sprintf("%g", 180.0/math.Pi*val) return } // EVEN function rounds a supplied number away from zero (i.e. rounds a // positive number up and a negative number down), to the next even number. // The syntax of the function is: // // EVEN(number) // func (fn *formulaFuncs) EVEN(argsList *list.List) (result string, err error) { if argsList.Len() != 1 { err = errors.New("EVEN requires 1 numeric argument") return } var number float64 if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } sign := math.Signbit(number) m, frac := math.Modf(number / 2) val := m * 2 if frac != 0 { if !sign { val += 2 } else { val -= 2 } } result = fmt.Sprintf("%g", val) return } // EXP function calculates the value of the mathematical constant e, raised to // the power of a given number. The syntax of the function is: // // EXP(number) // func (fn *formulaFuncs) EXP(argsList *list.List) (result string, err error) { if argsList.Len() != 1 { err = errors.New("EXP requires 1 numeric argument") return } var number float64 if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } result = strings.ToUpper(fmt.Sprintf("%g", math.Exp(number))) return } // fact returns the factorial of a supplied number. func fact(number float64) float64 { val := float64(1) for i := float64(2); i <= number; i++ { val *= i } return val } // FACT function returns the factorial of a supplied number. The syntax of the // function is: // // FACT(number) // func (fn *formulaFuncs) FACT(argsList *list.List) (result string, err error) { if argsList.Len() != 1 { err = errors.New("FACT requires 1 numeric argument") return } var number float64 if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } if number < 0 { err = errors.New(formulaErrorNUM) } result = strings.ToUpper(fmt.Sprintf("%g", fact(number))) return } // FACTDOUBLE function returns the double factorial of a supplied number. The // syntax of the function is: // // FACTDOUBLE(number) // func (fn *formulaFuncs) FACTDOUBLE(argsList *list.List) (result string, err error) { if argsList.Len() != 1 { err = errors.New("FACTDOUBLE requires 1 numeric argument") return } number, val := 0.0, 1.0 if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } if number < 0 { err = errors.New(formulaErrorNUM) return } for i := math.Trunc(number); i > 1; i -= 2 { val *= i } result = strings.ToUpper(fmt.Sprintf("%g", val)) return } // FLOOR function rounds a supplied number towards zero to the nearest // multiple of a specified significance. The syntax of the function is: // // FLOOR(number,significance) // func (fn *formulaFuncs) FLOOR(argsList *list.List) (result string, err error) { if argsList.Len() != 2 { err = errors.New("FLOOR requires 2 numeric arguments") return } var number, significance float64 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).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } if significance < 0 && number >= 0 { err = errors.New(formulaErrorNUM) return } val := number val, res := math.Modf(val / significance) if res != 0 { if number < 0 && res < 0 { val-- } } result = strings.ToUpper(fmt.Sprintf("%g", val*significance)) return } // FLOORMATH function rounds a supplied number down to a supplied multiple of // significance. The syntax of the function is: // // FLOOR.MATH(number,[significance],[mode]) // func (fn *formulaFuncs) FLOORMATH(argsList *list.List) (result string, err error) { if argsList.Len() == 0 { err = errors.New("FLOOR.MATH requires at least 1 argument") return } if argsList.Len() > 3 { err = errors.New("FLOOR.MATH allows at most 3 arguments") return } number, significance, mode := 0.0, 1.0, 1.0 if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } if number < 0 { significance = -1 } if argsList.Len() > 1 { if significance, err = strconv.ParseFloat(argsList.Front().Next().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } } if argsList.Len() == 1 { result = fmt.Sprintf("%g", math.Floor(number)) return } if argsList.Len() > 2 { if mode, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } } val, res := math.Modf(number / significance) if res != 0 && number < 0 && mode > 0 { val-- } result = fmt.Sprintf("%g", val*significance) return } // FLOORPRECISE function rounds a supplied number down to a supplied multiple // of significance. The syntax of the function is: // // FLOOR.PRECISE(number,[significance]) // func (fn *formulaFuncs) FLOORPRECISE(argsList *list.List) (result string, err error) { if argsList.Len() == 0 { err = errors.New("FLOOR.PRECISE requires at least 1 argument") return } if argsList.Len() > 2 { err = errors.New("FLOOR.PRECISE allows at most 2 arguments") return } var number, significance float64 if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } if number < 0 { significance = -1 } if argsList.Len() == 1 { result = fmt.Sprintf("%g", math.Floor(number)) return } if argsList.Len() > 1 { if significance, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } significance = math.Abs(significance) if significance == 0 { result = "0" return } } val, res := math.Modf(number / significance) if res != 0 { if number < 0 { val-- } } result = fmt.Sprintf("%g", val*significance) return } // gcd returns the greatest common divisor of two supplied integers. func gcd(x, y float64) float64 { x, y = math.Trunc(x), math.Trunc(y) if x == 0 { return y } if y == 0 { return x } for x != y { if x > y { x = x - y } else { y = y - x } } return x } // GCD function returns the greatest common divisor of two or more supplied // integers. The syntax of the function is: // // GCD(number1,[number2],...) // func (fn *formulaFuncs) GCD(argsList *list.List) (result string, err error) { if argsList.Len() == 0 { err = errors.New("GCD requires at least 1 argument") return } var ( val float64 nums = []float64{} ) for arg := argsList.Front(); arg != nil; arg = arg.Next() { token := arg.Value.(formulaArg).String if token == "" { continue } if val, err = strconv.ParseFloat(token, 64); err != nil { err = errors.New(formulaErrorVALUE) return } nums = append(nums, val) } if nums[0] < 0 { err = errors.New("GCD only accepts positive arguments") return } if len(nums) == 1 { result = fmt.Sprintf("%g", nums[0]) return } cd := nums[0] for i := 1; i < len(nums); i++ { if nums[i] < 0 { err = errors.New("GCD only accepts positive arguments") return } cd = gcd(cd, nums[i]) } result = fmt.Sprintf("%g", cd) return } // INT function truncates a supplied number down to the closest integer. The // syntax of the function is: // // INT(number) // func (fn *formulaFuncs) INT(argsList *list.List) (result string, err error) { if argsList.Len() != 1 { err = errors.New("INT requires 1 numeric argument") return } var number float64 if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } val, frac := math.Modf(number) if frac < 0 { val-- } result = fmt.Sprintf("%g", val) return } // ISOCEILING function rounds a supplied number up (regardless of the number's // sign), to the nearest multiple of a supplied significance. The syntax of // the function is: // // ISO.CEILING(number,[significance]) // func (fn *formulaFuncs) ISOCEILING(argsList *list.List) (result string, err error) { if argsList.Len() == 0 { err = errors.New("ISO.CEILING requires at least 1 argument") return } if argsList.Len() > 2 { err = errors.New("ISO.CEILING allows at most 2 arguments") return } var number, significance float64 if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } if number < 0 { significance = -1 } if argsList.Len() == 1 { result = fmt.Sprintf("%g", math.Ceil(number)) return } if argsList.Len() > 1 { if significance, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } significance = math.Abs(significance) if significance == 0 { result = "0" return } } val, res := math.Modf(number / significance) if res != 0 { if number > 0 { val++ } } result = fmt.Sprintf("%g", val*significance) return } // lcm returns the least common multiple of two supplied integers. func lcm(a, b float64) float64 { a = math.Trunc(a) b = math.Trunc(b) if a == 0 && b == 0 { return 0 } return a * b / gcd(a, b) } // LCM function returns the least common multiple of two or more supplied // integers. The syntax of the function is: // // LCM(number1,[number2],...) // func (fn *formulaFuncs) LCM(argsList *list.List) (result string, err error) { if argsList.Len() == 0 { err = errors.New("LCM requires at least 1 argument") return } var ( val float64 nums = []float64{} ) for arg := argsList.Front(); arg != nil; arg = arg.Next() { token := arg.Value.(formulaArg).String if token == "" { continue } if val, err = strconv.ParseFloat(token, 64); err != nil { err = errors.New(formulaErrorVALUE) return } nums = append(nums, val) } if nums[0] < 0 { err = errors.New("LCM only accepts positive arguments") return } if len(nums) == 1 { result = fmt.Sprintf("%g", nums[0]) return } cm := nums[0] for i := 1; i < len(nums); i++ { if nums[i] < 0 { err = errors.New("LCM only accepts positive arguments") return } cm = lcm(cm, nums[i]) } result = fmt.Sprintf("%g", cm) return } // LN function calculates the natural logarithm of a given number. The syntax // of the function is: // // LN(number) // func (fn *formulaFuncs) LN(argsList *list.List) (result string, err error) { if argsList.Len() != 1 { err = errors.New("LN requires 1 numeric argument") return } var number float64 if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } result = fmt.Sprintf("%g", math.Log(number)) return } // LOG function calculates the logarithm of a given number, to a supplied // base. The syntax of the function is: // // LOG(number,[base]) // func (fn *formulaFuncs) LOG(argsList *list.List) (result string, err error) { if argsList.Len() == 0 { err = errors.New("LOG requires at least 1 argument") return } if argsList.Len() > 2 { err = errors.New("LOG allows at most 2 arguments") return } number, base := 0.0, 10.0 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).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } } if number == 0 { err = errors.New(formulaErrorNUM) return } if base == 0 { err = errors.New(formulaErrorNUM) return } if base == 1 { err = errors.New(formulaErrorDIV) return } result = fmt.Sprintf("%g", math.Log(number)/math.Log(base)) return } // LOG10 function calculates the base 10 logarithm of a given number. The // syntax of the function is: // // LOG10(number) // func (fn *formulaFuncs) LOG10(argsList *list.List) (result string, err error) { if argsList.Len() != 1 { err = errors.New("LOG10 requires 1 numeric argument") return } var number float64 if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } result = fmt.Sprintf("%g", math.Log10(number)) return } func minor(sqMtx [][]float64, idx int) [][]float64 { ret := [][]float64{} for i := range sqMtx { if i == 0 { continue } row := []float64{} for j := range sqMtx { if j == idx { continue } row = append(row, sqMtx[i][j]) } ret = append(ret, row) } return ret } // det determinant of the 2x2 matrix. func det(sqMtx [][]float64) float64 { if len(sqMtx) == 2 { m00 := sqMtx[0][0] m01 := sqMtx[0][1] m10 := sqMtx[1][0] m11 := sqMtx[1][1] return m00*m11 - m10*m01 } var res, sgn float64 = 0, 1 for j := range sqMtx { res += sgn * sqMtx[0][j] * det(minor(sqMtx, j)) sgn *= -1 } return res } // MDETERM calculates the determinant of a square matrix. The // syntax of the function is: // // MDETERM(array) // func (fn *formulaFuncs) MDETERM(argsList *list.List) (result string, err error) { var num float64 var numMtx = [][]float64{} var strMtx = argsList.Front().Value.(formulaArg).Matrix if argsList.Len() < 1 { return } var rows = len(strMtx) for _, row := range argsList.Front().Value.(formulaArg).Matrix { if len(row) != rows { err = errors.New(formulaErrorVALUE) return } numRow := []float64{} for _, ele := range row { if num, err = strconv.ParseFloat(ele.String, 64); err != nil { return } numRow = append(numRow, num) } numMtx = append(numMtx, numRow) } result = fmt.Sprintf("%g", det(numMtx)) return } // MOD function returns the remainder of a division between two supplied // numbers. The syntax of the function is: // // MOD(number,divisor) // func (fn *formulaFuncs) MOD(argsList *list.List) (result string, err error) { if argsList.Len() != 2 { err = errors.New("MOD requires 2 numeric arguments") return } var number, divisor float64 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).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } if divisor == 0 { err = errors.New(formulaErrorDIV) return } trunc, rem := math.Modf(number / divisor) if rem < 0 { trunc-- } result = fmt.Sprintf("%g", number-divisor*trunc) return } // MROUND function rounds a supplied number up or down to the nearest multiple // of a given number. The syntax of the function is: // // MOD(number,multiple) // func (fn *formulaFuncs) MROUND(argsList *list.List) (result string, err error) { if argsList.Len() != 2 { err = errors.New("MROUND requires 2 numeric arguments") return } var number, multiple float64 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).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } if multiple == 0 { err = errors.New(formulaErrorNUM) return } if multiple < 0 && number > 0 || multiple > 0 && number < 0 { err = errors.New(formulaErrorNUM) return } number, res := math.Modf(number / multiple) if math.Trunc(res+0.5) > 0 { number++ } result = fmt.Sprintf("%g", number*multiple) return } // MULTINOMIAL function calculates the ratio of the factorial of a sum of // supplied values to the product of factorials of those values. The syntax of // the function is: // // MULTINOMIAL(number1,[number2],...) // func (fn *formulaFuncs) MULTINOMIAL(argsList *list.List) (result string, err error) { val, num, denom := 0.0, 0.0, 1.0 for arg := argsList.Front(); arg != nil; arg = arg.Next() { token := arg.Value.(formulaArg) if token.String == "" { continue } if val, err = strconv.ParseFloat(token.String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } num += val denom *= fact(val) } result = fmt.Sprintf("%g", fact(num)/denom) return } // MUNIT function returns the unit matrix for a specified dimension. The // syntax of the function is: // // MUNIT(dimension) // func (fn *formulaFuncs) MUNIT(argsList *list.List) (result string, err error) { if argsList.Len() != 1 { err = errors.New("MUNIT requires 1 numeric argument") return } var dimension int if dimension, err = strconv.Atoi(argsList.Front().Value.(formulaArg).String); err != nil { err = errors.New(formulaErrorVALUE) return } matrix := make([][]float64, 0, dimension) for i := 0; i < dimension; i++ { row := make([]float64, dimension) for j := 0; j < dimension; j++ { if i == j { row[j] = float64(1.0) } else { row[j] = float64(0.0) } } matrix = append(matrix, row) } return } // ODD function ounds a supplied number away from zero (i.e. rounds a positive // number up and a negative number down), to the next odd number. The syntax // of the function is: // // ODD(number) // func (fn *formulaFuncs) ODD(argsList *list.List) (result string, err error) { if argsList.Len() != 1 { err = errors.New("ODD requires 1 numeric argument") return } var number float64 if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } if number == 0 { result = "1" return } sign := math.Signbit(number) m, frac := math.Modf((number - 1) / 2) val := m*2 + 1 if frac != 0 { if !sign { val += 2 } else { val -= 2 } } result = fmt.Sprintf("%g", val) return } // PI function returns the value of the mathematical constant π (pi), accurate // to 15 digits (14 decimal places). The syntax of the function is: // // PI() // func (fn *formulaFuncs) PI(argsList *list.List) (result string, err error) { if argsList.Len() != 0 { err = errors.New("PI accepts no arguments") return } result = fmt.Sprintf("%g", math.Pi) return } // POWER function calculates a given number, raised to a supplied power. // The syntax of the function is: // // POWER(number,power) // func (fn *formulaFuncs) POWER(argsList *list.List) (result string, err error) { if argsList.Len() != 2 { err = errors.New("POWER requires 2 numeric arguments") return } var x, y float64 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).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } if x == 0 && y == 0 { err = errors.New(formulaErrorNUM) return } if x == 0 && y < 0 { err = errors.New(formulaErrorDIV) return } result = fmt.Sprintf("%g", math.Pow(x, y)) return } // PRODUCT function returns the product (multiplication) of a supplied set of // numerical values. The syntax of the function is: // // PRODUCT(number1,[number2],...) // 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) 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 } } } } result = fmt.Sprintf("%g", product) return } // QUOTIENT function returns the integer portion of a division between two // supplied numbers. The syntax of the function is: // // QUOTIENT(numerator,denominator) // func (fn *formulaFuncs) QUOTIENT(argsList *list.List) (result string, err error) { if argsList.Len() != 2 { err = errors.New("QUOTIENT requires 2 numeric arguments") return } var x, y float64 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).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } if y == 0 { err = errors.New(formulaErrorDIV) return } result = fmt.Sprintf("%g", math.Trunc(x/y)) return } // RADIANS function converts radians into degrees. The syntax of the function is: // // RADIANS(angle) // func (fn *formulaFuncs) RADIANS(argsList *list.List) (result string, err error) { if argsList.Len() != 1 { err = errors.New("RADIANS requires 1 numeric argument") return } var angle float64 if angle, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } result = fmt.Sprintf("%g", math.Pi/180.0*angle) return } // RAND function generates a random real number between 0 and 1. The syntax of // the function is: // // RAND() // func (fn *formulaFuncs) RAND(argsList *list.List) (result string, err error) { if argsList.Len() != 0 { err = errors.New("RAND accepts no arguments") return } result = fmt.Sprintf("%g", rand.New(rand.NewSource(time.Now().UnixNano())).Float64()) return } // RANDBETWEEN function generates a random integer between two supplied // integers. The syntax of the function is: // // RANDBETWEEN(bottom,top) // func (fn *formulaFuncs) RANDBETWEEN(argsList *list.List) (result string, err error) { if argsList.Len() != 2 { err = errors.New("RANDBETWEEN requires 2 numeric arguments") return } var bottom, top int64 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).String, 10, 64); err != nil { err = errors.New(formulaErrorVALUE) return } if top < bottom { err = errors.New(formulaErrorNUM) return } result = fmt.Sprintf("%g", float64(rand.New(rand.NewSource(time.Now().UnixNano())).Int63n(top-bottom+1)+bottom)) return } // romanNumerals defined a numeral system that originated in ancient Rome and // remained the usual way of writing numbers throughout Europe well into the // Late Middle Ages. type romanNumerals struct { n float64 s string } var romanTable = [][]romanNumerals{{{1000, "M"}, {900, "CM"}, {500, "D"}, {400, "CD"}, {100, "C"}, {90, "XC"}, {50, "L"}, {40, "XL"}, {10, "X"}, {9, "IX"}, {5, "V"}, {4, "IV"}, {1, "I"}}, {{1000, "M"}, {950, "LM"}, {900, "CM"}, {500, "D"}, {450, "LD"}, {400, "CD"}, {100, "C"}, {95, "VC"}, {90, "XC"}, {50, "L"}, {45, "VL"}, {40, "XL"}, {10, "X"}, {9, "IX"}, {5, "V"}, {4, "IV"}, {1, "I"}}, {{1000, "M"}, {990, "XM"}, {950, "LM"}, {900, "CM"}, {500, "D"}, {490, "XD"}, {450, "LD"}, {400, "CD"}, {100, "C"}, {99, "IC"}, {90, "XC"}, {50, "L"}, {45, "VL"}, {40, "XL"}, {10, "X"}, {9, "IX"}, {5, "V"}, {4, "IV"}, {1, "I"}}, {{1000, "M"}, {995, "VM"}, {990, "XM"}, {950, "LM"}, {900, "CM"}, {500, "D"}, {495, "VD"}, {490, "XD"}, {450, "LD"}, {400, "CD"}, {100, "C"}, {99, "IC"}, {90, "XC"}, {50, "L"}, {45, "VL"}, {40, "XL"}, {10, "X"}, {9, "IX"}, {5, "V"}, {4, "IV"}, {1, "I"}}, {{1000, "M"}, {999, "IM"}, {995, "VM"}, {990, "XM"}, {950, "LM"}, {900, "CM"}, {500, "D"}, {499, "ID"}, {495, "VD"}, {490, "XD"}, {450, "LD"}, {400, "CD"}, {100, "C"}, {99, "IC"}, {90, "XC"}, {50, "L"}, {45, "VL"}, {40, "XL"}, {10, "X"}, {9, "IX"}, {5, "V"}, {4, "IV"}, {1, "I"}}} // ROMAN function converts an arabic number to Roman. I.e. for a supplied // integer, the function returns a text string depicting the roman numeral // form of the number. The syntax of the function is: // // ROMAN(number,[form]) // func (fn *formulaFuncs) ROMAN(argsList *list.List) (result string, err error) { if argsList.Len() == 0 { err = errors.New("ROMAN requires at least 1 argument") return } if argsList.Len() > 2 { err = errors.New("ROMAN allows at most 2 arguments") return } var number float64 var form int 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).String); err != nil { err = errors.New(formulaErrorVALUE) return } if form < 0 { form = 0 } else if form > 4 { form = 4 } } decimalTable := romanTable[0] switch form { case 1: decimalTable = romanTable[1] case 2: decimalTable = romanTable[2] case 3: decimalTable = romanTable[3] case 4: decimalTable = romanTable[4] } val := math.Trunc(number) buf := bytes.Buffer{} for _, r := range decimalTable { for val >= r.n { buf.WriteString(r.s) val -= r.n } } result = buf.String() return } type roundMode byte const ( closest roundMode = iota down up ) // round rounds a supplied number up or down. func (fn *formulaFuncs) round(number, digits float64, mode roundMode) float64 { var significance float64 if digits > 0 { significance = math.Pow(1/10.0, digits) } else { significance = math.Pow(10.0, -digits) } val, res := math.Modf(number / significance) switch mode { case closest: const eps = 0.499999999 if res >= eps { val++ } else if res <= -eps { val-- } case down: case up: if res > 0 { val++ } else if res < 0 { val-- } } return val * significance } // ROUND function rounds a supplied number up or down, to a specified number // of decimal places. The syntax of the function is: // // ROUND(number,num_digits) // func (fn *formulaFuncs) ROUND(argsList *list.List) (result string, err error) { if argsList.Len() != 2 { err = errors.New("ROUND requires 2 numeric arguments") return } var number, digits float64 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).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } result = fmt.Sprintf("%g", fn.round(number, digits, closest)) return } // ROUNDDOWN function rounds a supplied number down towards zero, to a // specified number of decimal places. The syntax of the function is: // // ROUNDDOWN(number,num_digits) // func (fn *formulaFuncs) ROUNDDOWN(argsList *list.List) (result string, err error) { if argsList.Len() != 2 { err = errors.New("ROUNDDOWN requires 2 numeric arguments") return } var number, digits float64 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).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } result = fmt.Sprintf("%g", fn.round(number, digits, down)) return } // ROUNDUP function rounds a supplied number up, away from zero, to a // specified number of decimal places. The syntax of the function is: // // ROUNDUP(number,num_digits) // func (fn *formulaFuncs) ROUNDUP(argsList *list.List) (result string, err error) { if argsList.Len() != 2 { err = errors.New("ROUNDUP requires 2 numeric arguments") return } var number, digits float64 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).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } result = fmt.Sprintf("%g", fn.round(number, digits, up)) return } // SEC function calculates the secant of a given angle. The syntax of the // function is: // // SEC(number) // func (fn *formulaFuncs) SEC(argsList *list.List) (result string, err error) { if argsList.Len() != 1 { err = errors.New("SEC requires 1 numeric argument") return } var number float64 if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } result = fmt.Sprintf("%g", math.Cos(number)) return } // SECH function calculates the hyperbolic secant (sech) of a supplied angle. // The syntax of the function is: // // SECH(number) // func (fn *formulaFuncs) SECH(argsList *list.List) (result string, err error) { if argsList.Len() != 1 { err = errors.New("SECH requires 1 numeric argument") return } var number float64 if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } result = fmt.Sprintf("%g", 1/math.Cosh(number)) return } // SIGN function returns the arithmetic sign (+1, -1 or 0) of a supplied // number. I.e. if the number is positive, the Sign function returns +1, if // the number is negative, the function returns -1 and if the number is 0 // (zero), the function returns 0. The syntax of the function is: // // SIGN(number) // func (fn *formulaFuncs) SIGN(argsList *list.List) (result string, err error) { if argsList.Len() != 1 { err = errors.New("SIGN requires 1 numeric argument") return } var val float64 if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } if val < 0 { result = "-1" return } if val > 0 { result = "1" return } result = "0" return } // SIN function calculates the sine of a given angle. The syntax of the // function is: // // SIN(number) // func (fn *formulaFuncs) SIN(argsList *list.List) (result string, err error) { if argsList.Len() != 1 { err = errors.New("SIN requires 1 numeric argument") return } var number float64 if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } result = fmt.Sprintf("%g", math.Sin(number)) return } // SINH function calculates the hyperbolic sine (sinh) of a supplied number. // The syntax of the function is: // // SINH(number) // func (fn *formulaFuncs) SINH(argsList *list.List) (result string, err error) { if argsList.Len() != 1 { err = errors.New("SINH requires 1 numeric argument") return } var number float64 if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } result = fmt.Sprintf("%g", math.Sinh(number)) return } // SQRT function calculates the positive square root of a supplied number. The // syntax of the function is: // // SQRT(number) // func (fn *formulaFuncs) SQRT(argsList *list.List) (result string, err error) { if argsList.Len() != 1 { err = errors.New("SQRT requires 1 numeric argument") return } var res float64 var value = argsList.Front().Value.(formulaArg).String if value == "" { result = "0" return } if res, err = strconv.ParseFloat(value, 64); err != nil { err = errors.New(formulaErrorVALUE) return } if res < 0 { err = errors.New(formulaErrorNUM) return } result = fmt.Sprintf("%g", math.Sqrt(res)) return } // SQRTPI function returns the square root of a supplied number multiplied by // the mathematical constant, π. The syntax of the function is: // // SQRTPI(number) // func (fn *formulaFuncs) SQRTPI(argsList *list.List) (result string, err error) { if argsList.Len() != 1 { err = errors.New("SQRTPI requires 1 numeric argument") return } var number float64 if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } result = fmt.Sprintf("%g", math.Sqrt(number*math.Pi)) return } // SUM function adds together a supplied set of numbers and returns the sum of // these values. The syntax of the function is: // // SUM(number1,[number2],...) // 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) 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 } } } } 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 } } } result = fmt.Sprintf("%g", sum) return } // SUMSQ function returns the sum of squares of a supplied set of values. The // syntax of the function is: // // SUMSQ(number1,[number2],...) // 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) 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 } } } } result = fmt.Sprintf("%g", sq) return } // TAN function calculates the tangent of a given angle. The syntax of the // function is: // // TAN(number) // func (fn *formulaFuncs) TAN(argsList *list.List) (result string, err error) { if argsList.Len() != 1 { err = errors.New("TAN requires 1 numeric argument") return } var number float64 if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } result = fmt.Sprintf("%g", math.Tan(number)) return } // TANH function calculates the hyperbolic tangent (tanh) of a supplied // number. The syntax of the function is: // // TANH(number) // func (fn *formulaFuncs) TANH(argsList *list.List) (result string, err error) { if argsList.Len() != 1 { err = errors.New("TANH requires 1 numeric argument") return } var number float64 if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } result = fmt.Sprintf("%g", math.Tanh(number)) return } // TRUNC function truncates a supplied number to a specified number of decimal // places. The syntax of the function is: // // TRUNC(number,[number_digits]) // func (fn *formulaFuncs) TRUNC(argsList *list.List) (result string, err error) { if argsList.Len() == 0 { err = errors.New("TRUNC requires at least 1 argument") return } var number, digits, adjust, rtrim float64 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).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } digits = math.Floor(digits) } adjust = math.Pow(10, digits) x := int((math.Abs(number) - math.Abs(float64(int(number)))) * adjust) if x != 0 { if rtrim, err = strconv.ParseFloat(strings.TrimRight(strconv.Itoa(x), "0"), 64); err != nil { return } } if (digits > 0) && (rtrim < adjust/10) { result = fmt.Sprintf("%g", number) return } result = fmt.Sprintf("%g", float64(int(number*adjust))/adjust) return } // Statistical functions // COUNTA function returns the number of non-blanks within a supplied set of // cells or values. The syntax of the function is: // // COUNTA(value1,[value2],...) // func (fn *formulaFuncs) COUNTA(argsList *list.List) (result string, err error) { var count int for token := argsList.Front(); token != nil; token = token.Next() { arg := token.Value.(formulaArg) switch arg.Type { case ArgString: if arg.String != "" { count++ } case ArgMatrix: for _, row := range arg.Matrix { for _, value := range row { if value.String != "" { count++ } } } } } result = fmt.Sprintf("%d", count) return } // MEDIAN function returns the statistical median (the middle value) of a list // of supplied numbers. The syntax of the function is: // // MEDIAN(number1,[number2],...) // func (fn *formulaFuncs) MEDIAN(argsList *list.List) (result string, err error) { if argsList.Len() == 0 { err = errors.New("MEDIAN requires at least 1 argument") return } values := []float64{} var median, digits float64 for token := argsList.Front(); token != nil; token = token.Next() { arg := token.Value.(formulaArg) switch arg.Type { case ArgString: if digits, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } values = append(values, digits) case ArgMatrix: for _, row := range arg.Matrix { for _, value := range row { if value.String == "" { continue } if digits, err = strconv.ParseFloat(value.String, 64); err != nil { err = errors.New(formulaErrorVALUE) return } values = append(values, digits) } } } } sort.Float64s(values) if len(values)%2 == 0 { median = (values[len(values)/2-1] + values[len(values)/2]) / 2 } else { median = values[len(values)/2] } result = fmt.Sprintf("%g", median) return } // Information functions // ISBLANK function tests if a specified cell is blank (empty) and if so, // returns TRUE; Otherwise the function returns FALSE. The syntax of the // function is: // // ISBLANK(value) // func (fn *formulaFuncs) ISBLANK(argsList *list.List) (result string, err error) { if argsList.Len() != 1 { err = errors.New("ISBLANK requires 1 argument") return } token := argsList.Front().Value.(formulaArg) result = "FALSE" switch token.Type { case ArgUnknown: result = "TRUE" case ArgString: if token.String == "" { result = "TRUE" } } return } // ISERR function tests if an initial supplied expression (or value) returns // any Excel Error, except the #N/A error. If so, the function returns the // logical value TRUE; If the supplied value is not an error or is the #N/A // error, the ISERR function returns FALSE. The syntax of the function is: // // ISERR(value) // func (fn *formulaFuncs) ISERR(argsList *list.List) (result string, err error) { if argsList.Len() != 1 { err = errors.New("ISERR requires 1 argument") return } token := argsList.Front().Value.(formulaArg) result = "FALSE" if token.Type == ArgString { for _, errType := range []string{formulaErrorDIV, formulaErrorNAME, formulaErrorNUM, formulaErrorVALUE, formulaErrorREF, formulaErrorNULL, formulaErrorSPILL, formulaErrorCALC, formulaErrorGETTINGDATA} { if errType == token.String { result = "TRUE" } } } return } // ISERROR function tests if an initial supplied expression (or value) returns // an Excel Error, and if so, returns the logical value TRUE; Otherwise the // function returns FALSE. The syntax of the function is: // // ISERROR(value) // func (fn *formulaFuncs) ISERROR(argsList *list.List) (result string, err error) { if argsList.Len() != 1 { err = errors.New("ISERROR requires 1 argument") return } token := argsList.Front().Value.(formulaArg) result = "FALSE" if token.Type == ArgString { for _, errType := range []string{formulaErrorDIV, formulaErrorNAME, formulaErrorNA, formulaErrorNUM, formulaErrorVALUE, formulaErrorREF, formulaErrorNULL, formulaErrorSPILL, formulaErrorCALC, formulaErrorGETTINGDATA} { if errType == token.String { result = "TRUE" } } } return } // ISEVEN function tests if a supplied number (or numeric expression) // evaluates to an even number, and if so, returns TRUE; Otherwise, the // function returns FALSE. The syntax of the function is: // // ISEVEN(value) // func (fn *formulaFuncs) ISEVEN(argsList *list.List) (result string, err error) { if argsList.Len() != 1 { err = errors.New("ISEVEN requires 1 argument") return } token := argsList.Front().Value.(formulaArg) result = "FALSE" var numeric int if token.Type == ArgString { if numeric, err = strconv.Atoi(token.String); err != nil { err = errors.New(formulaErrorVALUE) return } if numeric == numeric/2*2 { result = "TRUE" return } } return } // ISNA function tests if an initial supplied expression (or value) returns // the Excel #N/A Error, and if so, returns TRUE; Otherwise the function // returns FALSE. The syntax of the function is: // // ISNA(value) // func (fn *formulaFuncs) ISNA(argsList *list.List) (result string, err error) { if argsList.Len() != 1 { err = errors.New("ISNA requires 1 argument") return } token := argsList.Front().Value.(formulaArg) result = "FALSE" if token.Type == ArgString && token.String == formulaErrorNA { result = "TRUE" } return } // ISNONTEXT function function tests if a supplied value is text. If not, the // function returns TRUE; If the supplied value is text, the function returns // FALSE. The syntax of the function is: // // ISNONTEXT(value) // func (fn *formulaFuncs) ISNONTEXT(argsList *list.List) (result string, err error) { if argsList.Len() != 1 { err = errors.New("ISNONTEXT requires 1 argument") return } token := argsList.Front().Value.(formulaArg) result = "TRUE" if token.Type == ArgString && token.String != "" { result = "FALSE" } return } // ISNUMBER function function tests if a supplied value is a number. If so, // the function returns TRUE; Otherwise it returns FALSE. The syntax of the // function is: // // ISNUMBER(value) // func (fn *formulaFuncs) ISNUMBER(argsList *list.List) (result string, err error) { if argsList.Len() != 1 { err = errors.New("ISNUMBER requires 1 argument") return } token := argsList.Front().Value.(formulaArg) result = "FALSE" if token.Type == ArgString && token.String != "" { if _, err = strconv.Atoi(token.String); err == nil { result = "TRUE" } err = nil } return } // ISODD function tests if a supplied number (or numeric expression) evaluates // to an odd number, and if so, returns TRUE; Otherwise, the function returns // FALSE. The syntax of the function is: // // ISODD(value) // func (fn *formulaFuncs) ISODD(argsList *list.List) (result string, err error) { if argsList.Len() != 1 { err = errors.New("ISODD requires 1 argument") return } token := argsList.Front().Value.(formulaArg) result = "FALSE" var numeric int if token.Type == ArgString { if numeric, err = strconv.Atoi(token.String); err != nil { err = errors.New(formulaErrorVALUE) return } if numeric != numeric/2*2 { result = "TRUE" return } } return } // NA function returns the Excel #N/A error. This error message has the // meaning 'value not available' and is produced when an Excel Formula is // unable to find a value that it needs. The syntax of the function is: // // NA() // func (fn *formulaFuncs) NA(argsList *list.List) (result string, err error) { if argsList.Len() != 0 { err = errors.New("NA accepts no arguments") return } result = formulaErrorNA return }