// Copyright 2016 - 2022 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 XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and // writing spreadsheet documents generated by Microsoft Excel™ 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.15 or later. package excelize import ( "bytes" "container/list" "errors" "fmt" "math" "math/big" "math/cmplx" "math/rand" "net/url" "reflect" "regexp" "sort" "strconv" "strings" "time" "unicode" "unsafe" "github.com/xuri/efp" "golang.org/x/text/language" "golang.org/x/text/message" ) const ( // Excel formula errors formulaErrorDIV = "#DIV/0!" formulaErrorNAME = "#NAME?" formulaErrorNA = "#N/A" formulaErrorNUM = "#NUM!" formulaErrorVALUE = "#VALUE!" formulaErrorREF = "#REF!" formulaErrorNULL = "#NULL!" formulaErrorSPILL = "#SPILL!" formulaErrorCALC = "#CALC!" formulaErrorGETTINGDATA = "#GETTING_DATA" // formula criteria condition enumeration. _ byte = iota criteriaEq criteriaLe criteriaGe criteriaNe criteriaL criteriaG criteriaErr criteriaRegexp matchModeExact = 0 matchModeMinGreater = 1 matchModeMaxLess = -1 matchModeWildcard = 2 searchModeLinear = 1 searchModeReverseLinear = -1 searchModeAscBinary = 2 searchModeDescBinary = -2 maxFinancialIterations = 128 financialPercision = 1.0e-08 // Date and time format regular expressions monthRe = `((jan|january)|(feb|february)|(mar|march)|(apr|april)|(may)|(jun|june)|(jul|july)|(aug|august)|(sep|september)|(oct|october)|(nov|november)|(dec|december))` df1 = `(([0-9])+)/(([0-9])+)/(([0-9])+)` df2 = monthRe + ` (([0-9])+), (([0-9])+)` df3 = `(([0-9])+)-(([0-9])+)-(([0-9])+)` df4 = `(([0-9])+)-` + monthRe + `-(([0-9])+)` datePrefix = `^((` + df1 + `|` + df2 + `|` + df3 + `|` + df4 + `) )?` tfhh = `(([0-9])+) (am|pm)` tfhhmm = `(([0-9])+):(([0-9])+)( (am|pm))?` tfmmss = `(([0-9])+):(([0-9])+\.([0-9])+)( (am|pm))?` tfhhmmss = `(([0-9])+):(([0-9])+):(([0-9])+(\.([0-9])+)?)( (am|pm))?` timeSuffix = `( (` + tfhh + `|` + tfhhmm + `|` + tfmmss + `|` + tfhhmmss + `))?$` ) var ( // tokenPriority defined basic arithmetic operator priority. tokenPriority = map[string]int{ "^": 5, "*": 4, "/": 4, "+": 3, "-": 3, "=": 2, "<>": 2, "<": 2, "<=": 2, ">": 2, ">=": 2, "&": 1, } month2num = map[string]int{ "january": 1, "february": 2, "march": 3, "april": 4, "may": 5, "june": 6, "july": 7, "august": 8, "september": 9, "october": 10, "november": 11, "december": 12, "jan": 1, "feb": 2, "mar": 3, "apr": 4, "jun": 6, "jul": 7, "aug": 8, "sep": 9, "oct": 10, "nov": 11, "dec": 12, } dateFormats = map[string]*regexp.Regexp{ "mm/dd/yy": regexp.MustCompile(`^` + df1 + timeSuffix), "mm dd, yy": regexp.MustCompile(`^` + df2 + timeSuffix), "yy-mm-dd": regexp.MustCompile(`^` + df3 + timeSuffix), "yy-mmStr-dd": regexp.MustCompile(`^` + df4 + timeSuffix), } timeFormats = map[string]*regexp.Regexp{ "hh": regexp.MustCompile(datePrefix + tfhh + `$`), "hh:mm": regexp.MustCompile(datePrefix + tfhhmm + `$`), "mm:ss": regexp.MustCompile(datePrefix + tfmmss + `$`), "hh:mm:ss": regexp.MustCompile(datePrefix + tfhhmmss + `$`), } dateOnlyFormats = []*regexp.Regexp{ regexp.MustCompile(`^` + df1 + `$`), regexp.MustCompile(`^` + df2 + `$`), regexp.MustCompile(`^` + df3 + `$`), regexp.MustCompile(`^` + df4 + `$`), } addressFmtMaps = map[string]func(col, row int) (string, error){ "1_TRUE": func(col, row int) (string, error) { return CoordinatesToCellName(col, row, true) }, "1_FALSE": func(col, row int) (string, error) { return fmt.Sprintf("R%dC%d", row, col), nil }, "2_TRUE": func(col, row int) (string, error) { column, err := ColumnNumberToName(col) if err != nil { return "", err } return fmt.Sprintf("%s$%d", column, row), nil }, "2_FALSE": func(col, row int) (string, error) { return fmt.Sprintf("R%dC[%d]", row, col), nil }, "3_TRUE": func(col, row int) (string, error) { column, err := ColumnNumberToName(col) if err != nil { return "", err } return fmt.Sprintf("$%s%d", column, row), nil }, "3_FALSE": func(col, row int) (string, error) { return fmt.Sprintf("R[%d]C%d", row, col), nil }, "4_TRUE": func(col, row int) (string, error) { return CoordinatesToCellName(col, row, false) }, "4_FALSE": func(col, row int) (string, error) { return fmt.Sprintf("R[%d]C[%d]", row, col), nil }, } ) // 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 } // formulaCriteria defined formula criteria parser result. type formulaCriteria struct { Type byte Condition string } // ArgType is the type of formula argument type. type ArgType byte // Formula argument types enumeration. const ( ArgUnknown ArgType = iota ArgNumber ArgString ArgList ArgMatrix ArgError ArgEmpty ) // formulaArg is the argument of a formula or function. type formulaArg struct { SheetName string Number float64 String string List []formulaArg Matrix [][]formulaArg Boolean bool Error string Type ArgType cellRefs, cellRanges *list.List } // Value returns a string data type of the formula argument. func (fa formulaArg) Value() (value string) { switch fa.Type { case ArgNumber: if fa.Boolean { if fa.Number == 0 { return "FALSE" } return "TRUE" } return fmt.Sprintf("%g", fa.Number) case ArgString: return fa.String case ArgError: return fa.Error } return } // ToNumber returns a formula argument with number data type. func (fa formulaArg) ToNumber() formulaArg { var n float64 var err error switch fa.Type { case ArgString: n, err = strconv.ParseFloat(fa.String, 64) if err != nil { return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } case ArgNumber: n = fa.Number } return newNumberFormulaArg(n) } // ToBool returns a formula argument with boolean data type. func (fa formulaArg) ToBool() formulaArg { var b bool var err error switch fa.Type { case ArgString: b, err = strconv.ParseBool(fa.String) if err != nil { return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } case ArgNumber: if fa.Boolean && fa.Number == 1 { b = true } } return newBoolFormulaArg(b) } // ToList returns a formula argument with array data type. func (fa formulaArg) ToList() []formulaArg { switch fa.Type { case ArgMatrix: list := []formulaArg{} for _, row := range fa.Matrix { list = append(list, row...) } return list case ArgList: return fa.List case ArgNumber, ArgString, ArgError, ArgUnknown: return []formulaArg{fa} } return nil } // formulaFuncs is the type of the formula functions. type formulaFuncs struct { f *File sheet, cell string } // 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 formula functions: // // ABS // ACCRINT // ACCRINTM // ACOS // ACOSH // ACOT // ACOTH // ADDRESS // AMORDEGRC // AMORLINC // AND // ARABIC // ASIN // ASINH // ATAN // ATAN2 // ATANH // AVEDEV // AVERAGE // AVERAGEA // AVERAGEIF // AVERAGEIFS // BASE // BESSELI // BESSELJ // BESSELK // BESSELY // BETADIST // BETA.DIST // BETAINV // BETA.INV // BIN2DEC // BIN2HEX // BIN2OCT // BINOMDIST // BINOM.DIST // BINOM.DIST.RANGE // BINOM.INV // BITAND // BITLSHIFT // BITOR // BITRSHIFT // BITXOR // CEILING // CEILING.MATH // CEILING.PRECISE // CHAR // CHIDIST // CHIINV // CHOOSE // CLEAN // CODE // COLUMN // COLUMNS // COMBIN // COMBINA // COMPLEX // CONCAT // CONCATENATE // CONFIDENCE // CONFIDENCE.NORM // CORREL // COS // COSH // COT // COTH // COUNT // COUNTA // COUNTBLANK // COUNTIF // COUNTIFS // COUPDAYBS // COUPDAYS // COUPDAYSNC // COUPNCD // COUPNUM // COUPPCD // COVAR // COVARIANCE.P // CRITBINOM // CSC // CSCH // CUMIPMT // CUMPRINC // DATE // DATEDIF // DATEVALUE // DAY // DAYS // DB // DDB // DEC2BIN // DEC2HEX // DEC2OCT // DECIMAL // DEGREES // DELTA // DEVSQ // DISC // DOLLARDE // DOLLARFR // DURATION // EFFECT // ENCODEURL // ERF // ERF.PRECISE // ERFC // ERFC.PRECISE // ERROR.TYPE // EVEN // EXACT // EXP // EXPON.DIST // EXPONDIST // FACT // FACTDOUBLE // FALSE // FIND // FINDB // F.INV // F.INV.RT // FINV // FISHER // FISHERINV // FIXED // FLOOR // FLOOR.MATH // FLOOR.PRECISE // FORMULATEXT // FV // FVSCHEDULE // GAMMA // GAMMA.DIST // GAMMADIST // GAMMA.INV // GAMMAINV // GAMMALN // GAUSS // GCD // GEOMEAN // GESTEP // HARMEAN // HEX2BIN // HEX2DEC // HEX2OCT // HLOOKUP // HOUR // IF // IFERROR // IFNA // IFS // IMABS // IMAGINARY // IMARGUMENT // IMCONJUGATE // IMCOS // IMCOSH // IMCOT // IMCSC // IMCSCH // IMDIV // IMEXP // IMLN // IMLOG10 // IMLOG2 // IMPOWER // IMPRODUCT // IMREAL // IMSEC // IMSECH // IMSIN // IMSINH // IMSQRT // IMSUB // IMSUM // IMTAN // INDEX // INDIRECT // INT // INTRATE // IPMT // IRR // ISBLANK // ISERR // ISERROR // ISEVEN // ISFORMULA // ISLOGICAL // ISNA // ISNONTEXT // ISNUMBER // ISODD // ISREF // ISTEXT // ISO.CEILING // ISOWEEKNUM // ISPMT // KURT // LARGE // LCM // LEFT // LEFTB // LEN // LENB // LN // LOG // LOG10 // LOGINV // LOGNORM.DIST // LOGNORMDIST // LOGNORM.INV // LOOKUP // LOWER // MATCH // MAX // MAXA // MAXIFS // MDETERM // MDURATION // MEDIAN // MID // MIDB // MIN // MINA // MINIFS // MINUTE // MIRR // MOD // MONTH // MROUND // MULTINOMIAL // MUNIT // N // NA // NOMINAL // NORM.DIST // NORMDIST // NORM.INV // NORMINV // NORM.S.DIST // NORMSDIST // NORM.S.INV // NORMSINV // NOT // NOW // NPER // NPV // OCT2BIN // OCT2DEC // OCT2HEX // ODD // ODDFPRICE // OR // PDURATION // PERCENTILE.EXC // PERCENTILE.INC // PERCENTILE // PERCENTRANK.EXC // PERCENTRANK.INC // PERCENTRANK // PERMUT // PERMUTATIONA // PI // PMT // POISSON.DIST // POISSON // POWER // PPMT // PRICE // PRICEDISC // PRICEMAT // PRODUCT // PROPER // PV // QUARTILE // QUARTILE.EXC // QUARTILE.INC // QUOTIENT // RADIANS // RAND // RANDBETWEEN // RANK // RANK.EQ // RATE // RECEIVED // REPLACE // REPLACEB // REPT // RIGHT // RIGHTB // ROMAN // ROUND // ROUNDDOWN // ROUNDUP // ROW // ROWS // RRI // SEC // SECH // SECOND // SHEET // SHEETS // SIGN // SIN // SINH // SKEW // SLN // SMALL // SQRT // SQRTPI // STANDARDIZE // STDEV // STDEV.P // STDEV.S // STDEVA // STDEVP // SUBSTITUTE // SUM // SUMIF // SUMIFS // SUMPRODUCT // SUMSQ // SUMX2MY2 // SUMX2PY2 // SUMXMY2 // SWITCH // SYD // T // TAN // TANH // TBILLEQ // TBILLPRICE // TBILLYIELD // TEXTJOIN // TIME // TIMEVALUE // TODAY // TRANSPOSE // TRIM // TRIMMEAN // TRUE // TRUNC // TYPE // UNICHAR // UNICODE // UPPER // VALUE // VAR // VAR.P // VAR.S // VARA // VARP // VARPA // VDB // VLOOKUP // WEEKDAY // WEIBULL // WEIBULL.DIST // XIRR // XLOOKUP // XNPV // XOR // YEAR // YEARFRAC // YIELD // YIELDDISC // YIELDMAT // Z.TEST // ZTEST // 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, cell, tokens); err != nil { return } result = token.TValue isNum, precision := isNumeric(result) if isNum && (precision > 15 || precision == 0) { num := roundPrecision(result, -1) result = strings.ToUpper(num) } return } // getPriority calculate arithmetic operator priority. func getPriority(token efp.Token) (pri int) { pri = tokenPriority[token.TValue] if token.TValue == "-" && token.TType == efp.TokenTypeOperatorPrefix { pri = 6 } if isBeginParenthesesToken(token) { // ( pri = 0 } return } // newNumberFormulaArg constructs a number formula argument. func newNumberFormulaArg(n float64) formulaArg { if math.IsNaN(n) { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } return formulaArg{Type: ArgNumber, Number: n} } // newStringFormulaArg constructs a string formula argument. func newStringFormulaArg(s string) formulaArg { return formulaArg{Type: ArgString, String: s} } // newMatrixFormulaArg constructs a matrix formula argument. func newMatrixFormulaArg(m [][]formulaArg) formulaArg { return formulaArg{Type: ArgMatrix, Matrix: m} } // newListFormulaArg create a list formula argument. func newListFormulaArg(l []formulaArg) formulaArg { return formulaArg{Type: ArgList, List: l} } // newBoolFormulaArg constructs a boolean formula argument. func newBoolFormulaArg(b bool) formulaArg { var n float64 if b { n = 1 } return formulaArg{Type: ArgNumber, Number: n, Boolean: true} } // newErrorFormulaArg create an error formula argument of a given type with a // specified error message. func newErrorFormulaArg(formulaError, msg string) formulaArg { return formulaArg{Type: ArgError, String: formulaError, Error: msg} } // newEmptyFormulaArg create an empty formula argument. func newEmptyFormulaArg() formulaArg { return formulaArg{Type: ArgEmpty} } // 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 // args - Arguments list of the operation formula // // TODO: handle subtypes: Nothing, Text, Logical, Error, Concatenation, Intersection, Union // func (f *File) evalInfixExp(sheet, cell string, tokens []efp.Token) (efp.Token, error) { var err error opdStack, optStack, opfStack, opfdStack, opftStack, argsStack := NewStack(), NewStack(), NewStack(), NewStack(), NewStack(), NewStack() 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 isFunctionStartToken(token) { opfStack.Push(token) argsStack.Push(list.New().Init()) opftStack.Push(token) // to know which operators belong to a function use the function as a separator 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.Peek().(efp.Token) != opfStack.Peek().(efp.Token) { refTo := f.getDefinedNameRefTo(token.TValue, sheet) if refTo != "" { token.TValue = refTo } // 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 refTo := f.getDefinedNameRefTo(token.TValue, sheet) if refTo != "" { token.TValue = refTo } 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) } argsStack.Peek().(*list.List).PushBack(result) continue } } if isEndParenthesesToken(token) && isBeginParenthesesToken(opftStack.Peek().(efp.Token)) { if arg := argsStack.Peek().(*list.List).Back(); arg != nil { opfdStack.Push(efp.Token{ TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber, TValue: arg.Value.(formulaArg).Value(), }) argsStack.Peek().(*list.List).Remove(arg) } } // 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.Peek().(efp.Token) != opfStack.Peek().(efp.Token) { // calculate trigger topOpt := opftStack.Peek().(efp.Token) if err := calculate(opfdStack, topOpt); err != nil { argsStack.Peek().(*list.List).PushFront(newErrorFormulaArg(formulaErrorVALUE, err.Error())) } opftStack.Pop() } if !opfdStack.Empty() { argsStack.Peek().(*list.List).PushBack(newStringFormulaArg(opfdStack.Pop().(efp.Token).TValue)) } continue } // current token is logical if token.TType == efp.TokenTypeOperand && token.TSubType == efp.TokenSubTypeLogical { argsStack.Peek().(*list.List).PushBack(newStringFormulaArg(token.TValue)) } if err = f.evalInfixExpFunc(sheet, cell, token, nextToken, opfStack, opdStack, opftStack, opfdStack, argsStack); err != nil { return efp.Token{}, err } } } 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{}, ErrInvalidFormula } return opdStack.Peek().(efp.Token), err } // evalInfixExpFunc evaluate formula function in the infix expression. func (f *File) evalInfixExpFunc(sheet, cell string, token, nextToken efp.Token, opfStack, opdStack, opftStack, opfdStack, argsStack *Stack) error { if !isFunctionStopToken(token) { return nil } prepareEvalInfixExp(opfStack, opftStack, opfdStack, argsStack) // call formula function to evaluate arg := callFuncByName(&formulaFuncs{f: f, sheet: sheet, cell: cell}, strings.NewReplacer( "_xlfn.", "", ".", "dot").Replace(opfStack.Peek().(efp.Token).TValue), []reflect.Value{reflect.ValueOf(argsStack.Peek().(*list.List))}) if arg.Type == ArgError && opfStack.Len() == 1 { return errors.New(arg.Value()) } argsStack.Pop() opftStack.Pop() // remove current function separator opfStack.Pop() if opfStack.Len() > 0 { // still in function stack if nextToken.TType == efp.TokenTypeOperatorInfix || (opftStack.Len() > 1 && opfdStack.Len() > 0) { // mathematics calculate in formula function if arg.Type == ArgError { opfdStack.Push(efp.Token{TValue: arg.Value(), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeError}) } else { opfdStack.Push(efp.Token{TValue: arg.Value(), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber}) } } else { argsStack.Peek().(*list.List).PushBack(arg) } } else { opdStack.Push(efp.Token{TValue: arg.Value(), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber}) } return nil } // prepareEvalInfixExp check the token and stack state for formula function // evaluate. func prepareEvalInfixExp(opfStack, opftStack, opfdStack, argsStack *Stack) { // current token is function stop for opftStack.Peek().(efp.Token) != opfStack.Peek().(efp.Token) { // calculate trigger topOpt := opftStack.Peek().(efp.Token) if err := calculate(opfdStack, topOpt); err != nil { argsStack.Peek().(*list.List).PushBack(newErrorFormulaArg(err.Error(), err.Error())) opftStack.Pop() continue } opftStack.Pop() } argument := true if opftStack.Len() > 2 && opfdStack.Len() == 1 { topOpt := opftStack.Pop() if opftStack.Peek().(efp.Token).TType == efp.TokenTypeOperatorInfix { argument = false } opftStack.Push(topOpt) } // push opfd to args if argument && opfdStack.Len() > 0 { argsStack.Peek().(*list.List).PushBack(newStringFormulaArg(opfdStack.Pop().(efp.Token).TValue)) } } // calcPow evaluate exponentiation arithmetic operations. func calcPow(rOpd, lOpd efp.Token, opdStack *Stack) error { 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 := math.Pow(lOpdVal, rOpdVal) opdStack.Push(efp.Token{TValue: fmt.Sprintf("%g", result), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber}) return nil } // calcEq evaluate equal arithmetic operations. func calcEq(rOpd, lOpd efp.Token, opdStack *Stack) error { opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(rOpd.TValue == lOpd.TValue)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber}) return nil } // calcNEq evaluate not equal arithmetic operations. func calcNEq(rOpd, lOpd efp.Token, opdStack *Stack) error { opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(rOpd.TValue != lOpd.TValue)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber}) return nil } // calcL evaluate less than arithmetic operations. func calcL(rOpd, lOpd efp.Token, opdStack *Stack) error { if rOpd.TSubType == efp.TokenSubTypeNumber && lOpd.TSubType == efp.TokenSubTypeNumber { lOpdVal, _ := strconv.ParseFloat(lOpd.TValue, 64) rOpdVal, _ := strconv.ParseFloat(rOpd.TValue, 64) opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(lOpdVal < rOpdVal)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber}) } if rOpd.TSubType == efp.TokenSubTypeText && lOpd.TSubType == efp.TokenSubTypeText { opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(strings.Compare(lOpd.TValue, rOpd.TValue) == -1)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber}) } if rOpd.TSubType == efp.TokenSubTypeNumber && lOpd.TSubType == efp.TokenSubTypeText { opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(false)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber}) } if rOpd.TSubType == efp.TokenSubTypeText && lOpd.TSubType == efp.TokenSubTypeNumber { opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(true)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber}) } return nil } // calcLe evaluate less than or equal arithmetic operations. func calcLe(rOpd, lOpd efp.Token, opdStack *Stack) error { if rOpd.TSubType == efp.TokenSubTypeNumber && lOpd.TSubType == efp.TokenSubTypeNumber { lOpdVal, _ := strconv.ParseFloat(lOpd.TValue, 64) rOpdVal, _ := strconv.ParseFloat(rOpd.TValue, 64) opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(lOpdVal <= rOpdVal)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber}) } if rOpd.TSubType == efp.TokenSubTypeText && lOpd.TSubType == efp.TokenSubTypeText { opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(strings.Compare(lOpd.TValue, rOpd.TValue) != 1)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber}) } if rOpd.TSubType == efp.TokenSubTypeNumber && lOpd.TSubType == efp.TokenSubTypeText { opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(false)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber}) } if rOpd.TSubType == efp.TokenSubTypeText && lOpd.TSubType == efp.TokenSubTypeNumber { opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(true)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber}) } return nil } // calcG evaluate greater than or equal arithmetic operations. func calcG(rOpd, lOpd efp.Token, opdStack *Stack) error { if rOpd.TSubType == efp.TokenSubTypeNumber && lOpd.TSubType == efp.TokenSubTypeNumber { lOpdVal, _ := strconv.ParseFloat(lOpd.TValue, 64) rOpdVal, _ := strconv.ParseFloat(rOpd.TValue, 64) opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(lOpdVal > rOpdVal)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber}) } if rOpd.TSubType == efp.TokenSubTypeText && lOpd.TSubType == efp.TokenSubTypeText { opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(strings.Compare(lOpd.TValue, rOpd.TValue) == 1)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber}) } if rOpd.TSubType == efp.TokenSubTypeNumber && lOpd.TSubType == efp.TokenSubTypeText { opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(true)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber}) } if rOpd.TSubType == efp.TokenSubTypeText && lOpd.TSubType == efp.TokenSubTypeNumber { opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(false)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber}) } return nil } // calcGe evaluate greater than or equal arithmetic operations. func calcGe(rOpd, lOpd efp.Token, opdStack *Stack) error { if rOpd.TSubType == efp.TokenSubTypeNumber && lOpd.TSubType == efp.TokenSubTypeNumber { lOpdVal, _ := strconv.ParseFloat(lOpd.TValue, 64) rOpdVal, _ := strconv.ParseFloat(rOpd.TValue, 64) opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(lOpdVal >= rOpdVal)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber}) } if rOpd.TSubType == efp.TokenSubTypeText && lOpd.TSubType == efp.TokenSubTypeText { opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(strings.Compare(lOpd.TValue, rOpd.TValue) != -1)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber}) } if rOpd.TSubType == efp.TokenSubTypeNumber && lOpd.TSubType == efp.TokenSubTypeText { opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(true)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber}) } if rOpd.TSubType == efp.TokenSubTypeText && lOpd.TSubType == efp.TokenSubTypeNumber { opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(false)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber}) } return nil } // calcSplice evaluate splice '&' operations. func calcSplice(rOpd, lOpd efp.Token, opdStack *Stack) error { opdStack.Push(efp.Token{TValue: lOpd.TValue + rOpd.TValue, TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber}) return nil } // calcAdd evaluate addition arithmetic operations. func calcAdd(rOpd, lOpd efp.Token, opdStack *Stack) error { 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(rOpd, lOpd efp.Token, opdStack *Stack) error { 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(rOpd, lOpd efp.Token, opdStack *Stack) error { 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 } // calcDiv evaluate division arithmetic operations. func calcDiv(rOpd, lOpd efp.Token, opdStack *Stack) error { 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 ErrInvalidFormula } 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 == "-" && opt.TType == efp.TokenTypeOperatorInfix { if opdStack.Len() < 2 { return ErrInvalidFormula } rOpd := opdStack.Pop().(efp.Token) lOpd := opdStack.Pop().(efp.Token) if err := calcSubtract(rOpd, lOpd, opdStack); err != nil { return err } } tokenCalcFunc := map[string]func(rOpd, lOpd efp.Token, opdStack *Stack) error{ "^": calcPow, "*": calcMultiply, "/": calcDiv, "+": calcAdd, "=": calcEq, "<>": calcNEq, "<": calcL, "<=": calcLe, ">": calcG, ">=": calcGe, "&": calcSplice, } fn, ok := tokenCalcFunc[opt.TValue] if ok { if opdStack.Len() < 2 { return ErrInvalidFormula } rOpd := opdStack.Pop().(efp.Token) lOpd := opdStack.Pop().(efp.Token) if rOpd.TSubType == efp.TokenSubTypeError { return errors.New(rOpd.TValue) } if lOpd.TSubType == efp.TokenSubTypeError { return errors.New(lOpd.TValue) } if err := fn(rOpd, lOpd, 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 } // isFunctionStartToken determine if the token is function start. func isFunctionStartToken(token efp.Token) bool { return token.TType == efp.TokenTypeFunction && token.TSubType == efp.TokenSubTypeStart } // isFunctionStopToken determine if the token is function stop. func isFunctionStopToken(token efp.Token) bool { return token.TType == efp.TokenTypeFunction && token.TSubType == efp.TokenSubTypeStop } // isBeginParenthesesToken determine if the token is begin parentheses: (. func isBeginParenthesesToken(token efp.Token) bool { return token.TType == efp.TokenTypeSubexpression && token.TSubType == efp.TokenSubTypeStart } // isEndParenthesesToken determine if the token is end parentheses: ). func isEndParenthesesToken(token efp.Token) bool { return token.TType == efp.TokenTypeSubexpression && token.TSubType == efp.TokenSubTypeStop } // isOperatorPrefixToken determine if the token is parse operator prefix // token. func isOperatorPrefixToken(token efp.Token) bool { _, ok := tokenPriority[token.TValue] return (token.TValue == "-" && token.TType == efp.TokenTypeOperatorPrefix) || (ok && token.TType == efp.TokenTypeOperatorInfix) } // isOperand determine if the token is parse operand perand. func isOperand(token efp.Token) bool { return token.TType == efp.TokenTypeOperand && (token.TSubType == efp.TokenSubTypeNumber || token.TSubType == efp.TokenSubTypeText) } // getDefinedNameRefTo convert defined name to reference range. func (f *File) getDefinedNameRefTo(definedNameName string, currentSheet string) (refTo string) { var workbookRefTo, worksheetRefTo string for _, definedName := range f.GetDefinedName() { if definedName.Name == definedNameName { // worksheet scope takes precedence over scope workbook when both definedNames exist if definedName.Scope == "Workbook" { workbookRefTo = definedName.RefersTo } if definedName.Scope == currentSheet { worksheetRefTo = definedName.RefersTo } } } refTo = workbookRefTo if worksheetRefTo != "" { refTo = worksheetRefTo } return } // 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 isBeginParenthesesToken(token) { // ( optStack.Push(token) } if isEndParenthesesToken(token) { // ) for !isBeginParenthesesToken(optStack.Peek().(efp.Token)) { // != ( topOpt := optStack.Peek().(efp.Token) if err := calculate(opdStack, topOpt); err != nil { return err } optStack.Pop() } optStack.Pop() } if token.TType == efp.TokenTypeOperatorPostfix && !opdStack.Empty() { topOpd := opdStack.Pop().(efp.Token) opd, err := strconv.ParseFloat(topOpd.TValue, 64) topOpd.TValue = strconv.FormatFloat(opd/100, 'f', -1, 64) opdStack.Push(topOpd) return err } // opd if isOperand(token) { 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] // cast to cell coordinates if cr.Col, cr.Row, err = CellNameToCoordinates(tokens[1]); err != nil { // cast to column if cr.Col, err = ColumnNameToNumber(tokens[1]); err != nil { // cast to row if cr.Row, err = strconv.Atoi(tokens[1]); err != nil { err = newInvalidColumnNameError(tokens[1]) return } cr.Col = TotalColumns } } if refs.Len() > 0 { e := refs.Back() cellRefs.PushBack(e.Value.(cellRef)) refs.Remove(e) } refs.PushBack(cr) continue } // cast to cell coordinates if cr.Col, cr.Row, err = CellNameToCoordinates(tokens[0]); err != nil { // cast to column if cr.Col, err = ColumnNameToNumber(tokens[0]); err != nil { // cast to row if cr.Row, err = strconv.Atoi(tokens[0]); err != nil { err = newInvalidColumnNameError(tokens[0]) return } cr.Col = TotalColumns } cellRanges.PushBack(cellRange{ From: cellRef{Sheet: sheet, Col: cr.Col, Row: 1}, To: cellRef{Sheet: sheet, Col: cr.Col, Row: TotalRows}, }) cellRefs.Init() arg, err = f.rangeResolver(cellRefs, cellRanges) 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) { arg.cellRefs, arg.cellRanges = cellRefs, cellRanges // 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++ { 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, Options{RawCellValue: true}); 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, Options{RawCellValue: true}); 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) (arg formulaArg) { function := reflect.ValueOf(receiver).MethodByName(name) if function.IsValid() { rt := function.Call(params) if len(rt) == 0 { return } arg = rt[0].Interface().(formulaArg) return } return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("not support %s function", name)) } // 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 = criteriaNe, 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, "?") { exp = strings.ReplaceAll(exp, "?", ".") } if strings.Contains(exp, "*") { exp = strings.ReplaceAll(exp, "*", ".*") } fc.Type, fc.Condition = criteriaRegexp, exp return } // formulaCriteriaEval evaluate formula criteria expression. func formulaCriteriaEval(val string, criteria *formulaCriteria) (result bool, err error) { var value, expected float64 var e error prepareValue := func(val, cond string) (value float64, expected float64, err error) { if value, err = strconv.ParseFloat(val, 64); err != nil { return } if expected, err = strconv.ParseFloat(criteria.Condition, 64); err != nil { return } return } switch criteria.Type { case criteriaEq: return val == criteria.Condition, err case criteriaLe: value, expected, e = prepareValue(val, criteria.Condition) return value <= expected && e == nil, err case criteriaGe: value, expected, e = prepareValue(val, criteria.Condition) return value >= expected && e == nil, err case criteriaNe: return val != criteria.Condition, err case criteriaL: value, expected, e = prepareValue(val, criteria.Condition) return value < expected && e == nil, err case criteriaG: value, expected, e = prepareValue(val, criteria.Condition) return value > expected && e == nil, err case criteriaRegexp: return regexp.MatchString(criteria.Condition, val) } return } // Engineering Functions // BESSELI function the modified Bessel function, which is equivalent to the // Bessel function evaluated for purely imaginary arguments. The syntax of // the Besseli function is: // // BESSELI(x,n) // func (fn *formulaFuncs) BESSELI(argsList *list.List) formulaArg { if argsList.Len() != 2 { return newErrorFormulaArg(formulaErrorVALUE, "BESSELI requires 2 numeric arguments") } return fn.bassel(argsList, true) } // BESSELJ function returns the Bessel function, Jn(x), for a specified order // and value of x. The syntax of the function is: // // BESSELJ(x,n) // func (fn *formulaFuncs) BESSELJ(argsList *list.List) formulaArg { if argsList.Len() != 2 { return newErrorFormulaArg(formulaErrorVALUE, "BESSELJ requires 2 numeric arguments") } return fn.bassel(argsList, false) } // bassel is an implementation of the formula functions BESSELI and BESSELJ. func (fn *formulaFuncs) bassel(argsList *list.List, modfied bool) formulaArg { x, n := argsList.Front().Value.(formulaArg).ToNumber(), argsList.Back().Value.(formulaArg).ToNumber() if x.Type != ArgNumber { return x } if n.Type != ArgNumber { return n } max, x1 := 100, x.Number*0.5 x2 := x1 * x1 x1 = math.Pow(x1, n.Number) n1, n2, n3, n4, add := fact(n.Number), 1.0, 0.0, n.Number, false result := x1 / n1 t := result * 0.9 for result != t && max != 0 { x1 *= x2 n3++ n1 *= n3 n4++ n2 *= n4 t = result if modfied || add { result += (x1 / n1 / n2) } else { result -= (x1 / n1 / n2) } max-- add = !add } return newNumberFormulaArg(result) } // BESSELK function calculates the modified Bessel functions, Kn(x), which are // also known as the hyperbolic Bessel Functions. These are the equivalent of // the Bessel functions, evaluated for purely imaginary arguments. The syntax // of the function is: // // BESSELK(x,n) // func (fn *formulaFuncs) BESSELK(argsList *list.List) formulaArg { if argsList.Len() != 2 { return newErrorFormulaArg(formulaErrorVALUE, "BESSELK requires 2 numeric arguments") } x, n := argsList.Front().Value.(formulaArg).ToNumber(), argsList.Back().Value.(formulaArg).ToNumber() if x.Type != ArgNumber { return x } if n.Type != ArgNumber { return n } if x.Number <= 0 || n.Number < 0 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } var result float64 switch math.Floor(n.Number) { case 0: result = fn.besselK0(x) case 1: result = fn.besselK1(x) default: result = fn.besselK2(x, n) } return newNumberFormulaArg(result) } // besselK0 is an implementation of the formula function BESSELK. func (fn *formulaFuncs) besselK0(x formulaArg) float64 { var y float64 if x.Number <= 2 { n2 := x.Number * 0.5 y = n2 * n2 args := list.New() args.PushBack(x) args.PushBack(newNumberFormulaArg(0)) return -math.Log(n2)*fn.BESSELI(args).Number + (-0.57721566 + y*(0.42278420+y*(0.23069756+y*(0.3488590e-1+y*(0.262698e-2+y* (0.10750e-3+y*0.74e-5)))))) } y = 2 / x.Number return math.Exp(-x.Number) / math.Sqrt(x.Number) * (1.25331414 + y*(-0.7832358e-1+y*(0.2189568e-1+y*(-0.1062446e-1+y* (0.587872e-2+y*(-0.251540e-2+y*0.53208e-3)))))) } // besselK1 is an implementation of the formula function BESSELK. func (fn *formulaFuncs) besselK1(x formulaArg) float64 { var n2, y float64 if x.Number <= 2 { n2 = x.Number * 0.5 y = n2 * n2 args := list.New() args.PushBack(x) args.PushBack(newNumberFormulaArg(1)) return math.Log(n2)*fn.BESSELI(args).Number + (1+y*(0.15443144+y*(-0.67278579+y*(-0.18156897+y*(-0.1919402e-1+y*(-0.110404e-2+y*(-0.4686e-4)))))))/x.Number } y = 2 / x.Number return math.Exp(-x.Number) / math.Sqrt(x.Number) * (1.25331414 + y*(0.23498619+y*(-0.3655620e-1+y*(0.1504268e-1+y*(-0.780353e-2+y* (0.325614e-2+y*(-0.68245e-3))))))) } // besselK2 is an implementation of the formula function BESSELK. func (fn *formulaFuncs) besselK2(x, n formulaArg) float64 { tox, bkm, bk, bkp := 2/x.Number, fn.besselK0(x), fn.besselK1(x), 0.0 for i := 1.0; i < n.Number; i++ { bkp = bkm + i*tox*bk bkm = bk bk = bkp } return bk } // BESSELY function returns the Bessel function, Yn(x), (also known as the // Weber function or the Neumann function), for a specified order and value // of x. The syntax of the function is: // // BESSELY(x,n) // func (fn *formulaFuncs) BESSELY(argsList *list.List) formulaArg { if argsList.Len() != 2 { return newErrorFormulaArg(formulaErrorVALUE, "BESSELY requires 2 numeric arguments") } x, n := argsList.Front().Value.(formulaArg).ToNumber(), argsList.Back().Value.(formulaArg).ToNumber() if x.Type != ArgNumber { return x } if n.Type != ArgNumber { return n } if x.Number <= 0 || n.Number < 0 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } var result float64 switch math.Floor(n.Number) { case 0: result = fn.besselY0(x) case 1: result = fn.besselY1(x) default: result = fn.besselY2(x, n) } return newNumberFormulaArg(result) } // besselY0 is an implementation of the formula function BESSELY. func (fn *formulaFuncs) besselY0(x formulaArg) float64 { var y float64 if x.Number < 8 { y = x.Number * x.Number f1 := -2957821389.0 + y*(7062834065.0+y*(-512359803.6+y*(10879881.29+y* (-86327.92757+y*228.4622733)))) f2 := 40076544269.0 + y*(745249964.8+y*(7189466.438+y* (47447.26470+y*(226.1030244+y)))) args := list.New() args.PushBack(x) args.PushBack(newNumberFormulaArg(0)) return f1/f2 + 0.636619772*fn.BESSELJ(args).Number*math.Log(x.Number) } z := 8.0 / x.Number y = z * z xx := x.Number - 0.785398164 f1 := 1 + y*(-0.1098628627e-2+y*(0.2734510407e-4+y*(-0.2073370639e-5+y*0.2093887211e-6))) f2 := -0.1562499995e-1 + y*(0.1430488765e-3+y*(-0.6911147651e-5+y*(0.7621095161e-6+y* (-0.934945152e-7)))) return math.Sqrt(0.636619772/x.Number) * (math.Sin(xx)*f1 + z*math.Cos(xx)*f2) } // besselY1 is an implementation of the formula function BESSELY. func (fn *formulaFuncs) besselY1(x formulaArg) float64 { if x.Number < 8 { y := x.Number * x.Number f1 := x.Number * (-0.4900604943e13 + y*(0.1275274390e13+y*(-0.5153438139e11+y* (0.7349264551e9+y*(-0.4237922726e7+y*0.8511937935e4))))) f2 := 0.2499580570e14 + y*(0.4244419664e12+y*(0.3733650367e10+y*(0.2245904002e8+y* (0.1020426050e6+y*(0.3549632885e3+y))))) args := list.New() args.PushBack(x) args.PushBack(newNumberFormulaArg(1)) return f1/f2 + 0.636619772*(fn.BESSELJ(args).Number*math.Log(x.Number)-1/x.Number) } return math.Sqrt(0.636619772/x.Number) * math.Sin(x.Number-2.356194491) } // besselY2 is an implementation of the formula function BESSELY. func (fn *formulaFuncs) besselY2(x, n formulaArg) float64 { tox, bym, by, byp := 2/x.Number, fn.besselY0(x), fn.besselY1(x), 0.0 for i := 1.0; i < n.Number; i++ { byp = i*tox*by - bym bym = by by = byp } return by } // BIN2DEC function converts a Binary (a base-2 number) into a decimal number. // The syntax of the function is: // // BIN2DEC(number) // func (fn *formulaFuncs) BIN2DEC(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "BIN2DEC requires 1 numeric argument") } token := argsList.Front().Value.(formulaArg) number := token.ToNumber() if number.Type != ArgNumber { return newErrorFormulaArg(formulaErrorVALUE, number.Error) } return fn.bin2dec(token.Value()) } // BIN2HEX function converts a Binary (Base 2) number into a Hexadecimal // (Base 16) number. The syntax of the function is: // // BIN2HEX(number,[places]) // func (fn *formulaFuncs) BIN2HEX(argsList *list.List) formulaArg { if argsList.Len() < 1 { return newErrorFormulaArg(formulaErrorVALUE, "BIN2HEX requires at least 1 argument") } if argsList.Len() > 2 { return newErrorFormulaArg(formulaErrorVALUE, "BIN2HEX allows at most 2 arguments") } token := argsList.Front().Value.(formulaArg) number := token.ToNumber() if number.Type != ArgNumber { return newErrorFormulaArg(formulaErrorVALUE, number.Error) } decimal, newList := fn.bin2dec(token.Value()), list.New() if decimal.Type != ArgNumber { return decimal } newList.PushBack(decimal) if argsList.Len() == 2 { newList.PushBack(argsList.Back().Value.(formulaArg)) } return fn.dec2x("BIN2HEX", newList) } // BIN2OCT function converts a Binary (Base 2) number into an Octal (Base 8) // number. The syntax of the function is: // // BIN2OCT(number,[places]) // func (fn *formulaFuncs) BIN2OCT(argsList *list.List) formulaArg { if argsList.Len() < 1 { return newErrorFormulaArg(formulaErrorVALUE, "BIN2OCT requires at least 1 argument") } if argsList.Len() > 2 { return newErrorFormulaArg(formulaErrorVALUE, "BIN2OCT allows at most 2 arguments") } token := argsList.Front().Value.(formulaArg) number := token.ToNumber() if number.Type != ArgNumber { return newErrorFormulaArg(formulaErrorVALUE, number.Error) } decimal, newList := fn.bin2dec(token.Value()), list.New() if decimal.Type != ArgNumber { return decimal } newList.PushBack(decimal) if argsList.Len() == 2 { newList.PushBack(argsList.Back().Value.(formulaArg)) } return fn.dec2x("BIN2OCT", newList) } // bin2dec is an implementation of the formula function BIN2DEC. func (fn *formulaFuncs) bin2dec(number string) formulaArg { decimal, length := 0.0, len(number) for i := length; i > 0; i-- { s := string(number[length-i]) if i == 10 && s == "1" { decimal += math.Pow(-2.0, float64(i-1)) continue } if s == "1" { decimal += math.Pow(2.0, float64(i-1)) continue } if s != "0" { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } } return newNumberFormulaArg(decimal) } // BITAND function returns the bitwise 'AND' for two supplied integers. The // syntax of the function is: // // BITAND(number1,number2) // func (fn *formulaFuncs) BITAND(argsList *list.List) formulaArg { return fn.bitwise("BITAND", argsList) } // BITLSHIFT function returns a supplied integer, shifted left by a specified // number of bits. The syntax of the function is: // // BITLSHIFT(number1,shift_amount) // func (fn *formulaFuncs) BITLSHIFT(argsList *list.List) formulaArg { return fn.bitwise("BITLSHIFT", argsList) } // BITOR function returns the bitwise 'OR' for two supplied integers. The // syntax of the function is: // // BITOR(number1,number2) // func (fn *formulaFuncs) BITOR(argsList *list.List) formulaArg { return fn.bitwise("BITOR", argsList) } // BITRSHIFT function returns a supplied integer, shifted right by a specified // number of bits. The syntax of the function is: // // BITRSHIFT(number1,shift_amount) // func (fn *formulaFuncs) BITRSHIFT(argsList *list.List) formulaArg { return fn.bitwise("BITRSHIFT", argsList) } // BITXOR function returns the bitwise 'XOR' (exclusive 'OR') for two supplied // integers. The syntax of the function is: // // BITXOR(number1,number2) // func (fn *formulaFuncs) BITXOR(argsList *list.List) formulaArg { return fn.bitwise("BITXOR", argsList) } // bitwise is an implementation of the formula functions BITAND, BITLSHIFT, // BITOR, BITRSHIFT and BITXOR. func (fn *formulaFuncs) bitwise(name string, argsList *list.List) formulaArg { if argsList.Len() != 2 { return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires 2 numeric arguments", name)) } num1, num2 := argsList.Front().Value.(formulaArg).ToNumber(), argsList.Back().Value.(formulaArg).ToNumber() if num1.Type != ArgNumber || num2.Type != ArgNumber { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } max := math.Pow(2, 48) - 1 if num1.Number < 0 || num1.Number > max || num2.Number < 0 || num2.Number > max { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } bitwiseFuncMap := map[string]func(a, b int) int{ "BITAND": func(a, b int) int { return a & b }, "BITLSHIFT": func(a, b int) int { return a << uint(b) }, "BITOR": func(a, b int) int { return a | b }, "BITRSHIFT": func(a, b int) int { return a >> uint(b) }, "BITXOR": func(a, b int) int { return a ^ b }, } bitwiseFunc := bitwiseFuncMap[name] return newNumberFormulaArg(float64(bitwiseFunc(int(num1.Number), int(num2.Number)))) } // COMPLEX function takes two arguments, representing the real and the // imaginary coefficients of a complex number, and from these, creates a // complex number. The syntax of the function is: // // COMPLEX(real_num,i_num,[suffix]) // func (fn *formulaFuncs) COMPLEX(argsList *list.List) formulaArg { if argsList.Len() < 2 { return newErrorFormulaArg(formulaErrorVALUE, "COMPLEX requires at least 2 arguments") } if argsList.Len() > 3 { return newErrorFormulaArg(formulaErrorVALUE, "COMPLEX allows at most 3 arguments") } real, i, suffix := argsList.Front().Value.(formulaArg).ToNumber(), argsList.Front().Next().Value.(formulaArg).ToNumber(), "i" if real.Type != ArgNumber { return real } if i.Type != ArgNumber { return i } if argsList.Len() == 3 { if suffix = strings.ToLower(argsList.Back().Value.(formulaArg).Value()); suffix != "i" && suffix != "j" { return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } } return newStringFormulaArg(cmplx2str(complex(real.Number, i.Number), suffix)) } // cmplx2str replace complex number string characters. func cmplx2str(num complex128, suffix string) string { c := fmt.Sprint(num) realPart, imagPart := fmt.Sprint(real(num)), fmt.Sprint(imag(num)) isNum, i := isNumeric(realPart) if isNum && i > 15 { realPart = roundPrecision(realPart, -1) } isNum, i = isNumeric(imagPart) if isNum && i > 15 { imagPart = roundPrecision(imagPart, -1) } c = realPart if imag(num) > 0 { c += "+" } if imag(num) != 0 { c += imagPart + "i" } c = strings.TrimPrefix(c, "(") c = strings.TrimPrefix(c, "+0+") c = strings.TrimPrefix(c, "-0+") c = strings.TrimSuffix(c, ")") c = strings.TrimPrefix(c, "0+") if strings.HasPrefix(c, "0-") { c = "-" + strings.TrimPrefix(c, "0-") } c = strings.TrimPrefix(c, "0+") c = strings.TrimSuffix(c, "+0i") c = strings.TrimSuffix(c, "-0i") c = strings.NewReplacer("+1i", "+i", "-1i", "-i").Replace(c) c = strings.Replace(c, "i", suffix, -1) return c } // str2cmplx convert complex number string characters. func str2cmplx(c string) string { c = strings.Replace(c, "j", "i", -1) if c == "i" { c = "1i" } c = strings.NewReplacer("+i", "+1i", "-i", "-1i").Replace(c) return c } // DEC2BIN function converts a decimal number into a Binary (Base 2) number. // The syntax of the function is: // // DEC2BIN(number,[places]) // func (fn *formulaFuncs) DEC2BIN(argsList *list.List) formulaArg { return fn.dec2x("DEC2BIN", argsList) } // DEC2HEX function converts a decimal number into a Hexadecimal (Base 16) // number. The syntax of the function is: // // DEC2HEX(number,[places]) // func (fn *formulaFuncs) DEC2HEX(argsList *list.List) formulaArg { return fn.dec2x("DEC2HEX", argsList) } // DEC2OCT function converts a decimal number into an Octal (Base 8) number. // The syntax of the function is: // // DEC2OCT(number,[places]) // func (fn *formulaFuncs) DEC2OCT(argsList *list.List) formulaArg { return fn.dec2x("DEC2OCT", argsList) } // dec2x is an implementation of the formula functions DEC2BIN, DEC2HEX and // DEC2OCT. func (fn *formulaFuncs) dec2x(name string, argsList *list.List) formulaArg { if argsList.Len() < 1 { return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires at least 1 argument", name)) } if argsList.Len() > 2 { return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s allows at most 2 arguments", name)) } decimal := argsList.Front().Value.(formulaArg).ToNumber() if decimal.Type != ArgNumber { return newErrorFormulaArg(formulaErrorVALUE, decimal.Error) } maxLimitMap := map[string]float64{ "DEC2BIN": 511, "HEX2BIN": 511, "OCT2BIN": 511, "BIN2HEX": 549755813887, "DEC2HEX": 549755813887, "OCT2HEX": 549755813887, "BIN2OCT": 536870911, "DEC2OCT": 536870911, "HEX2OCT": 536870911, } minLimitMap := map[string]float64{ "DEC2BIN": -512, "HEX2BIN": -512, "OCT2BIN": -512, "BIN2HEX": -549755813888, "DEC2HEX": -549755813888, "OCT2HEX": -549755813888, "BIN2OCT": -536870912, "DEC2OCT": -536870912, "HEX2OCT": -536870912, } baseMap := map[string]int{ "DEC2BIN": 2, "HEX2BIN": 2, "OCT2BIN": 2, "BIN2HEX": 16, "DEC2HEX": 16, "OCT2HEX": 16, "BIN2OCT": 8, "DEC2OCT": 8, "HEX2OCT": 8, } maxLimit, minLimit := maxLimitMap[name], minLimitMap[name] base := baseMap[name] if decimal.Number < minLimit || decimal.Number > maxLimit { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } n := int64(decimal.Number) binary := strconv.FormatUint(*(*uint64)(unsafe.Pointer(&n)), base) if argsList.Len() == 2 { places := argsList.Back().Value.(formulaArg).ToNumber() if places.Type != ArgNumber { return newErrorFormulaArg(formulaErrorVALUE, places.Error) } binaryPlaces := len(binary) if places.Number < 0 || places.Number > 10 || binaryPlaces > int(places.Number) { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } return newStringFormulaArg(strings.ToUpper(fmt.Sprintf("%s%s", strings.Repeat("0", int(places.Number)-binaryPlaces), binary))) } if decimal.Number < 0 && len(binary) > 10 { return newStringFormulaArg(strings.ToUpper(binary[len(binary)-10:])) } return newStringFormulaArg(strings.ToUpper(binary)) } // DELTA function tests two numbers for equality and returns the Kronecker // Delta. i.e. the function returns 1 if the two supplied numbers are equal // and 0 otherwise. The syntax of the function is: // // DELTA(number1,[number2]) // func (fn *formulaFuncs) DELTA(argsList *list.List) formulaArg { if argsList.Len() < 1 { return newErrorFormulaArg(formulaErrorVALUE, "DELTA requires at least 1 argument") } if argsList.Len() > 2 { return newErrorFormulaArg(formulaErrorVALUE, "DELTA allows at most 2 arguments") } number1 := argsList.Front().Value.(formulaArg).ToNumber() if number1.Type != ArgNumber { return number1 } number2 := newNumberFormulaArg(0) if argsList.Len() == 2 { if number2 = argsList.Back().Value.(formulaArg).ToNumber(); number2.Type != ArgNumber { return number2 } } return newBoolFormulaArg(number1.Number == number2.Number).ToNumber() } // ERF function calculates the Error Function, integrated between two supplied // limits. The syntax of the function is: // // ERF(lower_limit,[upper_limit]) // func (fn *formulaFuncs) ERF(argsList *list.List) formulaArg { if argsList.Len() < 1 { return newErrorFormulaArg(formulaErrorVALUE, "ERF requires at least 1 argument") } if argsList.Len() > 2 { return newErrorFormulaArg(formulaErrorVALUE, "ERF allows at most 2 arguments") } lower := argsList.Front().Value.(formulaArg).ToNumber() if lower.Type != ArgNumber { return lower } if argsList.Len() == 2 { upper := argsList.Back().Value.(formulaArg).ToNumber() if upper.Type != ArgNumber { return upper } return newNumberFormulaArg(math.Erf(upper.Number) - math.Erf(lower.Number)) } return newNumberFormulaArg(math.Erf(lower.Number)) } // ERFdotPRECISE function calculates the Error Function, integrated between a // supplied lower or upper limit and 0. The syntax of the function is: // // ERF.PRECISE(x) // func (fn *formulaFuncs) ERFdotPRECISE(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "ERF.PRECISE requires 1 argument") } x := argsList.Front().Value.(formulaArg).ToNumber() if x.Type != ArgNumber { return x } return newNumberFormulaArg(math.Erf(x.Number)) } // erfc is an implementation of the formula functions ERFC and ERFC.PRECISE. func (fn *formulaFuncs) erfc(name string, argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires 1 argument", name)) } x := argsList.Front().Value.(formulaArg).ToNumber() if x.Type != ArgNumber { return x } return newNumberFormulaArg(math.Erfc(x.Number)) } // ERFC function calculates the Complementary Error Function, integrated // between a supplied lower limit and infinity. The syntax of the function // is: // // ERFC(x) // func (fn *formulaFuncs) ERFC(argsList *list.List) formulaArg { return fn.erfc("ERFC", argsList) } // ERFC.PRECISE function calculates the Complementary Error Function, // integrated between a supplied lower limit and infinity. The syntax of the // function is: // // ERFC(x) // func (fn *formulaFuncs) ERFCdotPRECISE(argsList *list.List) formulaArg { return fn.erfc("ERFC.PRECISE", argsList) } // GESTEP unction tests whether a supplied number is greater than a supplied // step size and returns. The syntax of the function is: // // GESTEP(number,[step]) // func (fn *formulaFuncs) GESTEP(argsList *list.List) formulaArg { if argsList.Len() < 1 { return newErrorFormulaArg(formulaErrorVALUE, "GESTEP requires at least 1 argument") } if argsList.Len() > 2 { return newErrorFormulaArg(formulaErrorVALUE, "GESTEP allows at most 2 arguments") } number := argsList.Front().Value.(formulaArg).ToNumber() if number.Type != ArgNumber { return number } step := newNumberFormulaArg(0) if argsList.Len() == 2 { if step = argsList.Back().Value.(formulaArg).ToNumber(); step.Type != ArgNumber { return step } } return newBoolFormulaArg(number.Number >= step.Number).ToNumber() } // HEX2BIN function converts a Hexadecimal (Base 16) number into a Binary // (Base 2) number. The syntax of the function is: // // HEX2BIN(number,[places]) // func (fn *formulaFuncs) HEX2BIN(argsList *list.List) formulaArg { if argsList.Len() < 1 { return newErrorFormulaArg(formulaErrorVALUE, "HEX2BIN requires at least 1 argument") } if argsList.Len() > 2 { return newErrorFormulaArg(formulaErrorVALUE, "HEX2BIN allows at most 2 arguments") } decimal, newList := fn.hex2dec(argsList.Front().Value.(formulaArg).Value()), list.New() if decimal.Type != ArgNumber { return decimal } newList.PushBack(decimal) if argsList.Len() == 2 { newList.PushBack(argsList.Back().Value.(formulaArg)) } return fn.dec2x("HEX2BIN", newList) } // HEX2DEC function converts a hexadecimal (a base-16 number) into a decimal // number. The syntax of the function is: // // HEX2DEC(number) // func (fn *formulaFuncs) HEX2DEC(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "HEX2DEC requires 1 numeric argument") } return fn.hex2dec(argsList.Front().Value.(formulaArg).Value()) } // HEX2OCT function converts a Hexadecimal (Base 16) number into an Octal // (Base 8) number. The syntax of the function is: // // HEX2OCT(number,[places]) // func (fn *formulaFuncs) HEX2OCT(argsList *list.List) formulaArg { if argsList.Len() < 1 { return newErrorFormulaArg(formulaErrorVALUE, "HEX2OCT requires at least 1 argument") } if argsList.Len() > 2 { return newErrorFormulaArg(formulaErrorVALUE, "HEX2OCT allows at most 2 arguments") } decimal, newList := fn.hex2dec(argsList.Front().Value.(formulaArg).Value()), list.New() if decimal.Type != ArgNumber { return decimal } newList.PushBack(decimal) if argsList.Len() == 2 { newList.PushBack(argsList.Back().Value.(formulaArg)) } return fn.dec2x("HEX2OCT", newList) } // hex2dec is an implementation of the formula function HEX2DEC. func (fn *formulaFuncs) hex2dec(number string) formulaArg { decimal, length := 0.0, len(number) for i := length; i > 0; i-- { num, err := strconv.ParseInt(string(number[length-i]), 16, 64) if err != nil { return newErrorFormulaArg(formulaErrorNUM, err.Error()) } if i == 10 && string(number[length-i]) == "F" { decimal += math.Pow(-16.0, float64(i-1)) continue } decimal += float64(num) * math.Pow(16.0, float64(i-1)) } return newNumberFormulaArg(decimal) } // IMABS function returns the absolute value (the modulus) of a complex // number. The syntax of the function is: // // IMABS(inumber) // func (fn *formulaFuncs) IMABS(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "IMABS requires 1 argument") } value := argsList.Front().Value.(formulaArg).Value() inumber, err := strconv.ParseComplex(str2cmplx(value), 128) if err != nil { return newErrorFormulaArg(formulaErrorNUM, err.Error()) } return newNumberFormulaArg(cmplx.Abs(inumber)) } // IMAGINARY function returns the imaginary coefficient of a supplied complex // number. The syntax of the function is: // // IMAGINARY(inumber) // func (fn *formulaFuncs) IMAGINARY(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "IMAGINARY requires 1 argument") } value := argsList.Front().Value.(formulaArg).Value() inumber, err := strconv.ParseComplex(str2cmplx(value), 128) if err != nil { return newErrorFormulaArg(formulaErrorNUM, err.Error()) } return newNumberFormulaArg(imag(inumber)) } // IMARGUMENT function returns the phase (also called the argument) of a // supplied complex number. The syntax of the function is: // // IMARGUMENT(inumber) // func (fn *formulaFuncs) IMARGUMENT(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "IMARGUMENT requires 1 argument") } value := argsList.Front().Value.(formulaArg).Value() inumber, err := strconv.ParseComplex(str2cmplx(value), 128) if err != nil { return newErrorFormulaArg(formulaErrorNUM, err.Error()) } return newNumberFormulaArg(cmplx.Phase(inumber)) } // IMCONJUGATE function returns the complex conjugate of a supplied complex // number. The syntax of the function is: // // IMCONJUGATE(inumber) // func (fn *formulaFuncs) IMCONJUGATE(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "IMCONJUGATE requires 1 argument") } value := argsList.Front().Value.(formulaArg).Value() inumber, err := strconv.ParseComplex(str2cmplx(value), 128) if err != nil { return newErrorFormulaArg(formulaErrorNUM, err.Error()) } return newStringFormulaArg(cmplx2str(cmplx.Conj(inumber), value[len(value)-1:])) } // IMCOS function returns the cosine of a supplied complex number. The syntax // of the function is: // // IMCOS(inumber) // func (fn *formulaFuncs) IMCOS(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "IMCOS requires 1 argument") } value := argsList.Front().Value.(formulaArg).Value() inumber, err := strconv.ParseComplex(str2cmplx(value), 128) if err != nil { return newErrorFormulaArg(formulaErrorNUM, err.Error()) } return newStringFormulaArg(cmplx2str(cmplx.Cos(inumber), value[len(value)-1:])) } // IMCOSH function returns the hyperbolic cosine of a supplied complex number. The syntax // of the function is: // // IMCOSH(inumber) // func (fn *formulaFuncs) IMCOSH(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "IMCOSH requires 1 argument") } value := argsList.Front().Value.(formulaArg).Value() inumber, err := strconv.ParseComplex(str2cmplx(value), 128) if err != nil { return newErrorFormulaArg(formulaErrorNUM, err.Error()) } return newStringFormulaArg(cmplx2str(cmplx.Cosh(inumber), value[len(value)-1:])) } // IMCOT function returns the cotangent of a supplied complex number. The syntax // of the function is: // // IMCOT(inumber) // func (fn *formulaFuncs) IMCOT(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "IMCOT requires 1 argument") } value := argsList.Front().Value.(formulaArg).Value() inumber, err := strconv.ParseComplex(str2cmplx(value), 128) if err != nil { return newErrorFormulaArg(formulaErrorNUM, err.Error()) } return newStringFormulaArg(cmplx2str(cmplx.Cot(inumber), value[len(value)-1:])) } // IMCSC function returns the cosecant of a supplied complex number. The syntax // of the function is: // // IMCSC(inumber) // func (fn *formulaFuncs) IMCSC(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "IMCSC requires 1 argument") } value := argsList.Front().Value.(formulaArg).Value() inumber, err := strconv.ParseComplex(str2cmplx(value), 128) if err != nil { return newErrorFormulaArg(formulaErrorNUM, err.Error()) } num := 1 / cmplx.Sin(inumber) if cmplx.IsInf(num) { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } return newStringFormulaArg(cmplx2str(num, value[len(value)-1:])) } // IMCSCH function returns the hyperbolic cosecant of a supplied complex // number. The syntax of the function is: // // IMCSCH(inumber) // func (fn *formulaFuncs) IMCSCH(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "IMCSCH requires 1 argument") } value := argsList.Front().Value.(formulaArg).Value() inumber, err := strconv.ParseComplex(str2cmplx(value), 128) if err != nil { return newErrorFormulaArg(formulaErrorNUM, err.Error()) } num := 1 / cmplx.Sinh(inumber) if cmplx.IsInf(num) { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } return newStringFormulaArg(cmplx2str(num, value[len(value)-1:])) } // IMDIV function calculates the quotient of two complex numbers (i.e. divides // one complex number by another). The syntax of the function is: // // IMDIV(inumber1,inumber2) // func (fn *formulaFuncs) IMDIV(argsList *list.List) formulaArg { if argsList.Len() != 2 { return newErrorFormulaArg(formulaErrorVALUE, "IMDIV requires 2 arguments") } value := argsList.Front().Value.(formulaArg).Value() inumber1, err := strconv.ParseComplex(str2cmplx(value), 128) if err != nil { return newErrorFormulaArg(formulaErrorNUM, err.Error()) } inumber2, err := strconv.ParseComplex(str2cmplx(argsList.Back().Value.(formulaArg).Value()), 128) if err != nil { return newErrorFormulaArg(formulaErrorNUM, err.Error()) } num := inumber1 / inumber2 if cmplx.IsInf(num) { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } return newStringFormulaArg(cmplx2str(num, value[len(value)-1:])) } // IMEXP function returns the exponential of a supplied complex number. The // syntax of the function is: // // IMEXP(inumber) // func (fn *formulaFuncs) IMEXP(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "IMEXP requires 1 argument") } value := argsList.Front().Value.(formulaArg).Value() inumber, err := strconv.ParseComplex(str2cmplx(value), 128) if err != nil { return newErrorFormulaArg(formulaErrorNUM, err.Error()) } return newStringFormulaArg(cmplx2str(cmplx.Exp(inumber), value[len(value)-1:])) } // IMLN function returns the natural logarithm of a supplied complex number. // The syntax of the function is: // // IMLN(inumber) // func (fn *formulaFuncs) IMLN(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "IMLN requires 1 argument") } value := argsList.Front().Value.(formulaArg).Value() inumber, err := strconv.ParseComplex(str2cmplx(value), 128) if err != nil { return newErrorFormulaArg(formulaErrorNUM, err.Error()) } num := cmplx.Log(inumber) if cmplx.IsInf(num) { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } return newStringFormulaArg(cmplx2str(num, value[len(value)-1:])) } // IMLOG10 function returns the common (base 10) logarithm of a supplied // complex number. The syntax of the function is: // // IMLOG10(inumber) // func (fn *formulaFuncs) IMLOG10(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "IMLOG10 requires 1 argument") } value := argsList.Front().Value.(formulaArg).Value() inumber, err := strconv.ParseComplex(str2cmplx(value), 128) if err != nil { return newErrorFormulaArg(formulaErrorNUM, err.Error()) } num := cmplx.Log10(inumber) if cmplx.IsInf(num) { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } return newStringFormulaArg(cmplx2str(num, value[len(value)-1:])) } // IMLOG2 function calculates the base 2 logarithm of a supplied complex // number. The syntax of the function is: // // IMLOG2(inumber) // func (fn *formulaFuncs) IMLOG2(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "IMLOG2 requires 1 argument") } value := argsList.Front().Value.(formulaArg).Value() inumber, err := strconv.ParseComplex(str2cmplx(value), 128) if err != nil { return newErrorFormulaArg(formulaErrorNUM, err.Error()) } num := cmplx.Log(inumber) if cmplx.IsInf(num) { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } return newStringFormulaArg(cmplx2str(num/cmplx.Log(2), value[len(value)-1:])) } // IMPOWER function returns a supplied complex number, raised to a given // power. The syntax of the function is: // // IMPOWER(inumber,number) // func (fn *formulaFuncs) IMPOWER(argsList *list.List) formulaArg { if argsList.Len() != 2 { return newErrorFormulaArg(formulaErrorVALUE, "IMPOWER requires 2 arguments") } value := argsList.Front().Value.(formulaArg).Value() inumber, err := strconv.ParseComplex(str2cmplx(value), 128) if err != nil { return newErrorFormulaArg(formulaErrorNUM, err.Error()) } number, err := strconv.ParseComplex(str2cmplx(argsList.Back().Value.(formulaArg).Value()), 128) if err != nil { return newErrorFormulaArg(formulaErrorNUM, err.Error()) } if inumber == 0 && number == 0 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } num := cmplx.Pow(inumber, number) if cmplx.IsInf(num) { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } return newStringFormulaArg(cmplx2str(num, value[len(value)-1:])) } // IMPRODUCT function calculates the product of two or more complex numbers. // The syntax of the function is: // // IMPRODUCT(number1,[number2],...) // func (fn *formulaFuncs) IMPRODUCT(argsList *list.List) formulaArg { product := complex128(1) for arg := argsList.Front(); arg != nil; arg = arg.Next() { token := arg.Value.(formulaArg) switch token.Type { case ArgString: if token.Value() == "" { continue } val, err := strconv.ParseComplex(str2cmplx(token.Value()), 128) if err != nil { return newErrorFormulaArg(formulaErrorNUM, err.Error()) } product = product * val case ArgNumber: product = product * complex(token.Number, 0) case ArgMatrix: for _, row := range token.Matrix { for _, value := range row { if value.Value() == "" { continue } val, err := strconv.ParseComplex(str2cmplx(value.Value()), 128) if err != nil { return newErrorFormulaArg(formulaErrorNUM, err.Error()) } product = product * val } } } } return newStringFormulaArg(cmplx2str(product, "i")) } // IMREAL function returns the real coefficient of a supplied complex number. // The syntax of the function is: // // IMREAL(inumber) // func (fn *formulaFuncs) IMREAL(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "IMREAL requires 1 argument") } value := argsList.Front().Value.(formulaArg).Value() inumber, err := strconv.ParseComplex(str2cmplx(value), 128) if err != nil { return newErrorFormulaArg(formulaErrorNUM, err.Error()) } return newStringFormulaArg(fmt.Sprint(real(inumber))) } // IMSEC function returns the secant of a supplied complex number. The syntax // of the function is: // // IMSEC(inumber) // func (fn *formulaFuncs) IMSEC(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "IMSEC requires 1 argument") } value := argsList.Front().Value.(formulaArg).Value() inumber, err := strconv.ParseComplex(str2cmplx(value), 128) if err != nil { return newErrorFormulaArg(formulaErrorNUM, err.Error()) } return newStringFormulaArg(cmplx2str(1/cmplx.Cos(inumber), value[len(value)-1:])) } // IMSECH function returns the hyperbolic secant of a supplied complex number. // The syntax of the function is: // // IMSECH(inumber) // func (fn *formulaFuncs) IMSECH(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "IMSECH requires 1 argument") } value := argsList.Front().Value.(formulaArg).Value() inumber, err := strconv.ParseComplex(str2cmplx(value), 128) if err != nil { return newErrorFormulaArg(formulaErrorNUM, err.Error()) } return newStringFormulaArg(cmplx2str(1/cmplx.Cosh(inumber), value[len(value)-1:])) } // IMSIN function returns the Sine of a supplied complex number. The syntax of // the function is: // // IMSIN(inumber) // func (fn *formulaFuncs) IMSIN(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "IMSIN requires 1 argument") } value := argsList.Front().Value.(formulaArg).Value() inumber, err := strconv.ParseComplex(str2cmplx(value), 128) if err != nil { return newErrorFormulaArg(formulaErrorNUM, err.Error()) } return newStringFormulaArg(cmplx2str(cmplx.Sin(inumber), value[len(value)-1:])) } // IMSINH function returns the hyperbolic sine of a supplied complex number. // The syntax of the function is: // // IMSINH(inumber) // func (fn *formulaFuncs) IMSINH(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "IMSINH requires 1 argument") } value := argsList.Front().Value.(formulaArg).Value() inumber, err := strconv.ParseComplex(str2cmplx(value), 128) if err != nil { return newErrorFormulaArg(formulaErrorNUM, err.Error()) } return newStringFormulaArg(cmplx2str(cmplx.Sinh(inumber), value[len(value)-1:])) } // IMSQRT function returns the square root of a supplied complex number. The // syntax of the function is: // // IMSQRT(inumber) // func (fn *formulaFuncs) IMSQRT(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "IMSQRT requires 1 argument") } value := argsList.Front().Value.(formulaArg).Value() inumber, err := strconv.ParseComplex(str2cmplx(value), 128) if err != nil { return newErrorFormulaArg(formulaErrorNUM, err.Error()) } return newStringFormulaArg(cmplx2str(cmplx.Sqrt(inumber), value[len(value)-1:])) } // IMSUB function calculates the difference between two complex numbers // (i.e. subtracts one complex number from another). The syntax of the // function is: // // IMSUB(inumber1,inumber2) // func (fn *formulaFuncs) IMSUB(argsList *list.List) formulaArg { if argsList.Len() != 2 { return newErrorFormulaArg(formulaErrorVALUE, "IMSUB requires 2 arguments") } i1, err := strconv.ParseComplex(str2cmplx(argsList.Front().Value.(formulaArg).Value()), 128) if err != nil { return newErrorFormulaArg(formulaErrorNUM, err.Error()) } i2, err := strconv.ParseComplex(str2cmplx(argsList.Back().Value.(formulaArg).Value()), 128) if err != nil { return newErrorFormulaArg(formulaErrorNUM, err.Error()) } return newStringFormulaArg(cmplx2str(i1-i2, "i")) } // IMSUM function calculates the sum of two or more complex numbers. The // syntax of the function is: // // IMSUM(inumber1,inumber2,...) // func (fn *formulaFuncs) IMSUM(argsList *list.List) formulaArg { if argsList.Len() < 1 { return newErrorFormulaArg(formulaErrorVALUE, "IMSUM requires at least 1 argument") } var result complex128 for arg := argsList.Front(); arg != nil; arg = arg.Next() { token := arg.Value.(formulaArg) num, err := strconv.ParseComplex(str2cmplx(token.Value()), 128) if err != nil { return newErrorFormulaArg(formulaErrorNUM, err.Error()) } result += num } return newStringFormulaArg(cmplx2str(result, "i")) } // IMTAN function returns the tangent of a supplied complex number. The syntax // of the function is: // // IMTAN(inumber) // func (fn *formulaFuncs) IMTAN(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "IMTAN requires 1 argument") } value := argsList.Front().Value.(formulaArg).Value() inumber, err := strconv.ParseComplex(str2cmplx(value), 128) if err != nil { return newErrorFormulaArg(formulaErrorNUM, err.Error()) } return newStringFormulaArg(cmplx2str(cmplx.Tan(inumber), value[len(value)-1:])) } // OCT2BIN function converts an Octal (Base 8) number into a Binary (Base 2) // number. The syntax of the function is: // // OCT2BIN(number,[places]) // func (fn *formulaFuncs) OCT2BIN(argsList *list.List) formulaArg { if argsList.Len() < 1 { return newErrorFormulaArg(formulaErrorVALUE, "OCT2BIN requires at least 1 argument") } if argsList.Len() > 2 { return newErrorFormulaArg(formulaErrorVALUE, "OCT2BIN allows at most 2 arguments") } token := argsList.Front().Value.(formulaArg) number := token.ToNumber() if number.Type != ArgNumber { return newErrorFormulaArg(formulaErrorVALUE, number.Error) } decimal, newList := fn.oct2dec(token.Value()), list.New() newList.PushBack(decimal) if argsList.Len() == 2 { newList.PushBack(argsList.Back().Value.(formulaArg)) } return fn.dec2x("OCT2BIN", newList) } // OCT2DEC function converts an Octal (a base-8 number) into a decimal number. // The syntax of the function is: // // OCT2DEC(number) // func (fn *formulaFuncs) OCT2DEC(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "OCT2DEC requires 1 numeric argument") } token := argsList.Front().Value.(formulaArg) number := token.ToNumber() if number.Type != ArgNumber { return newErrorFormulaArg(formulaErrorVALUE, number.Error) } return fn.oct2dec(token.Value()) } // OCT2HEX function converts an Octal (Base 8) number into a Hexadecimal // (Base 16) number. The syntax of the function is: // // OCT2HEX(number,[places]) // func (fn *formulaFuncs) OCT2HEX(argsList *list.List) formulaArg { if argsList.Len() < 1 { return newErrorFormulaArg(formulaErrorVALUE, "OCT2HEX requires at least 1 argument") } if argsList.Len() > 2 { return newErrorFormulaArg(formulaErrorVALUE, "OCT2HEX allows at most 2 arguments") } token := argsList.Front().Value.(formulaArg) number := token.ToNumber() if number.Type != ArgNumber { return newErrorFormulaArg(formulaErrorVALUE, number.Error) } decimal, newList := fn.oct2dec(token.Value()), list.New() newList.PushBack(decimal) if argsList.Len() == 2 { newList.PushBack(argsList.Back().Value.(formulaArg)) } return fn.dec2x("OCT2HEX", newList) } // oct2dec is an implementation of the formula function OCT2DEC. func (fn *formulaFuncs) oct2dec(number string) formulaArg { decimal, length := 0.0, len(number) for i := length; i > 0; i-- { num, _ := strconv.Atoi(string(number[length-i])) if i == 10 && string(number[length-i]) == "7" { decimal += math.Pow(-8.0, float64(i-1)) continue } decimal += float64(num) * math.Pow(8.0, float64(i-1)) } return newNumberFormulaArg(decimal) } // 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) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "ABS requires 1 numeric argument") } arg := argsList.Front().Value.(formulaArg).ToNumber() if arg.Type == ArgError { return arg } return newNumberFormulaArg(math.Abs(arg.Number)) } // 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) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "ACOS requires 1 numeric argument") } arg := argsList.Front().Value.(formulaArg).ToNumber() if arg.Type == ArgError { return arg } return newNumberFormulaArg(math.Acos(arg.Number)) } // ACOSH function calculates the inverse hyperbolic cosine of a supplied number. // of the function is: // // ACOSH(number) // func (fn *formulaFuncs) ACOSH(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "ACOSH requires 1 numeric argument") } arg := argsList.Front().Value.(formulaArg).ToNumber() if arg.Type == ArgError { return arg } return newNumberFormulaArg(math.Acosh(arg.Number)) } // 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) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "ACOT requires 1 numeric argument") } arg := argsList.Front().Value.(formulaArg).ToNumber() if arg.Type == ArgError { return arg } return newNumberFormulaArg(math.Pi/2 - math.Atan(arg.Number)) } // 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) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "ACOTH requires 1 numeric argument") } arg := argsList.Front().Value.(formulaArg).ToNumber() if arg.Type == ArgError { return arg } return newNumberFormulaArg(math.Atanh(1 / arg.Number)) } // 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) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "ARABIC requires 1 numeric argument") } text := argsList.Front().Value.(formulaArg).Value() if len(text) > MaxFieldLength { return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } text = strings.ToUpper(text) number, actualStart, index, isNegative := 0, 0, len(text)-1, false startIndex, subtractNumber, currentPartValue, currentCharValue, prevCharValue := 0, 0, 0, 0, -1 for index >= 0 && text[index] == ' ' { index-- } for actualStart <= index && text[actualStart] == ' ' { actualStart++ } if actualStart <= index && text[actualStart] == '-' { isNegative = true actualStart++ } charMap := map[rune]int{'I': 1, 'V': 5, 'X': 10, 'L': 50, 'C': 100, 'D': 500, 'M': 1000} for index >= actualStart { startIndex = index startChar := text[startIndex] index-- for index >= actualStart && (text[index]|' ') == startChar { index-- } currentCharValue = charMap[rune(startChar)] currentPartValue = (startIndex - index) * currentCharValue if currentCharValue >= prevCharValue { number += currentPartValue - subtractNumber prevCharValue = currentCharValue subtractNumber = 0 continue } subtractNumber += currentPartValue } if subtractNumber != 0 { number -= subtractNumber } if isNegative { number = -number } return newNumberFormulaArg(float64(number)) } // 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) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "ASIN requires 1 numeric argument") } arg := argsList.Front().Value.(formulaArg).ToNumber() if arg.Type == ArgError { return arg } return newNumberFormulaArg(math.Asin(arg.Number)) } // 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) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "ASINH requires 1 numeric argument") } arg := argsList.Front().Value.(formulaArg).ToNumber() if arg.Type == ArgError { return arg } return newNumberFormulaArg(math.Asinh(arg.Number)) } // 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) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "ATAN requires 1 numeric argument") } arg := argsList.Front().Value.(formulaArg).ToNumber() if arg.Type == ArgError { return arg } return newNumberFormulaArg(math.Atan(arg.Number)) } // 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) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "ATANH requires 1 numeric argument") } arg := argsList.Front().Value.(formulaArg).ToNumber() if arg.Type == ArgError { return arg } return newNumberFormulaArg(math.Atanh(arg.Number)) } // 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) formulaArg { if argsList.Len() != 2 { return newErrorFormulaArg(formulaErrorVALUE, "ATAN2 requires 2 numeric arguments") } x := argsList.Back().Value.(formulaArg).ToNumber() if x.Type == ArgError { return x } y := argsList.Front().Value.(formulaArg).ToNumber() if y.Type == ArgError { return y } return newNumberFormulaArg(math.Atan2(x.Number, y.Number)) } // 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) formulaArg { if argsList.Len() < 2 { return newErrorFormulaArg(formulaErrorVALUE, "BASE requires at least 2 arguments") } if argsList.Len() > 3 { return newErrorFormulaArg(formulaErrorVALUE, "BASE allows at most 3 arguments") } var minLength int var err error number := argsList.Front().Value.(formulaArg).ToNumber() if number.Type == ArgError { return number } radix := argsList.Front().Next().Value.(formulaArg).ToNumber() if radix.Type == ArgError { return radix } if int(radix.Number) < 2 || int(radix.Number) > 36 { return newErrorFormulaArg(formulaErrorVALUE, "radix must be an integer >= 2 and <= 36") } if argsList.Len() > 2 { if minLength, err = strconv.Atoi(argsList.Back().Value.(formulaArg).String); err != nil { return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } } result := strconv.FormatInt(int64(number.Number), int(radix.Number)) if len(result) < minLength { result = strings.Repeat("0", minLength-len(result)) + result } return newStringFormulaArg(strings.ToUpper(result)) } // 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) formulaArg { if argsList.Len() == 0 { return newErrorFormulaArg(formulaErrorVALUE, "CEILING requires at least 1 argument") } if argsList.Len() > 2 { return newErrorFormulaArg(formulaErrorVALUE, "CEILING allows at most 2 arguments") } number, significance, res := 0.0, 1.0, 0.0 n := argsList.Front().Value.(formulaArg).ToNumber() if n.Type == ArgError { return n } number = n.Number if number < 0 { significance = -1 } if argsList.Len() > 1 { s := argsList.Back().Value.(formulaArg).ToNumber() if s.Type == ArgError { return s } significance = s.Number } if significance < 0 && number > 0 { return newErrorFormulaArg(formulaErrorVALUE, "negative sig to CEILING invalid") } if argsList.Len() == 1 { return newNumberFormulaArg(math.Ceil(number)) } number, res = math.Modf(number / significance) if res > 0 { number++ } return newNumberFormulaArg(number * significance) } // CEILINGdotMATH 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) CEILINGdotMATH(argsList *list.List) formulaArg { if argsList.Len() == 0 { return newErrorFormulaArg(formulaErrorVALUE, "CEILING.MATH requires at least 1 argument") } if argsList.Len() > 3 { return newErrorFormulaArg(formulaErrorVALUE, "CEILING.MATH allows at most 3 arguments") } number, significance, mode := 0.0, 1.0, 1.0 n := argsList.Front().Value.(formulaArg).ToNumber() if n.Type == ArgError { return n } number = n.Number if number < 0 { significance = -1 } if argsList.Len() > 1 { s := argsList.Front().Next().Value.(formulaArg).ToNumber() if s.Type == ArgError { return s } significance = s.Number } if argsList.Len() == 1 { return newNumberFormulaArg(math.Ceil(number)) } if argsList.Len() > 2 { m := argsList.Back().Value.(formulaArg).ToNumber() if m.Type == ArgError { return m } mode = m.Number } val, res := math.Modf(number / significance) if res != 0 { if number > 0 { val++ } else if mode < 0 { val-- } } return newNumberFormulaArg(val * significance) } // CEILINGdotPRECISE 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) CEILINGdotPRECISE(argsList *list.List) formulaArg { if argsList.Len() == 0 { return newErrorFormulaArg(formulaErrorVALUE, "CEILING.PRECISE requires at least 1 argument") } if argsList.Len() > 2 { return newErrorFormulaArg(formulaErrorVALUE, "CEILING.PRECISE allows at most 2 arguments") } number, significance := 0.0, 1.0 n := argsList.Front().Value.(formulaArg).ToNumber() if n.Type == ArgError { return n } number = n.Number if number < 0 { significance = -1 } if argsList.Len() == 1 { return newNumberFormulaArg(math.Ceil(number)) } if argsList.Len() > 1 { s := argsList.Back().Value.(formulaArg).ToNumber() if s.Type == ArgError { return s } significance = s.Number significance = math.Abs(significance) if significance == 0 { return newNumberFormulaArg(significance) } } val, res := math.Modf(number / significance) if res != 0 { if number > 0 { val++ } } return newNumberFormulaArg(val * significance) } // 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) formulaArg { if argsList.Len() != 2 { return newErrorFormulaArg(formulaErrorVALUE, "COMBIN requires 2 argument") } number, chosen, val := 0.0, 0.0, 1.0 n := argsList.Front().Value.(formulaArg).ToNumber() if n.Type == ArgError { return n } number = n.Number c := argsList.Back().Value.(formulaArg).ToNumber() if c.Type == ArgError { return c } chosen = c.Number number, chosen = math.Trunc(number), math.Trunc(chosen) if chosen > number { return newErrorFormulaArg(formulaErrorVALUE, "COMBIN requires number >= number_chosen") } if chosen == number || chosen == 0 { return newNumberFormulaArg(1) } for c := float64(1); c <= chosen; c++ { val *= (number + 1 - c) / c } return newNumberFormulaArg(math.Ceil(val)) } // 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) formulaArg { if argsList.Len() != 2 { return newErrorFormulaArg(formulaErrorVALUE, "COMBINA requires 2 argument") } var number, chosen float64 n := argsList.Front().Value.(formulaArg).ToNumber() if n.Type == ArgError { return n } number = n.Number c := argsList.Back().Value.(formulaArg).ToNumber() if c.Type == ArgError { return c } chosen = c.Number number, chosen = math.Trunc(number), math.Trunc(chosen) if number < chosen { return newErrorFormulaArg(formulaErrorVALUE, "COMBINA requires number > number_chosen") } if number == 0 { return newNumberFormulaArg(number) } 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) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "COS requires 1 numeric argument") } val := argsList.Front().Value.(formulaArg).ToNumber() if val.Type == ArgError { return val } return newNumberFormulaArg(math.Cos(val.Number)) } // 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) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "COSH requires 1 numeric argument") } val := argsList.Front().Value.(formulaArg).ToNumber() if val.Type == ArgError { return val } return newNumberFormulaArg(math.Cosh(val.Number)) } // COT function calculates the cotangent of a given angle. The syntax of the // function is: // // COT(number) // func (fn *formulaFuncs) COT(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "COT requires 1 numeric argument") } val := argsList.Front().Value.(formulaArg).ToNumber() if val.Type == ArgError { return val } if val.Number == 0 { return newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV) } return newNumberFormulaArg(1 / math.Tan(val.Number)) } // 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) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "COTH requires 1 numeric argument") } val := argsList.Front().Value.(formulaArg).ToNumber() if val.Type == ArgError { return val } if val.Number == 0 { return newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV) } return newNumberFormulaArg((math.Exp(val.Number) + math.Exp(-val.Number)) / (math.Exp(val.Number) - math.Exp(-val.Number))) } // CSC function calculates the cosecant of a given angle. The syntax of the // function is: // // CSC(number) // func (fn *formulaFuncs) CSC(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "CSC requires 1 numeric argument") } val := argsList.Front().Value.(formulaArg).ToNumber() if val.Type == ArgError { return val } if val.Number == 0 { return newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV) } return newNumberFormulaArg(1 / math.Sin(val.Number)) } // 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) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "CSCH requires 1 numeric argument") } val := argsList.Front().Value.(formulaArg).ToNumber() if val.Type == ArgError { return val } if val.Number == 0 { return newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV) } return newNumberFormulaArg(1 / math.Sinh(val.Number)) } // 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) formulaArg { if argsList.Len() != 2 { return newErrorFormulaArg(formulaErrorVALUE, "DECIMAL requires 2 numeric arguments") } text := argsList.Front().Value.(formulaArg).String var radix int var err error radix, err = strconv.Atoi(argsList.Back().Value.(formulaArg).String) if err != nil { return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } 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 { return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } return newNumberFormulaArg(float64(val)) } // DEGREES function converts radians into degrees. The syntax of the function // is: // // DEGREES(angle) // func (fn *formulaFuncs) DEGREES(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "DEGREES requires 1 numeric argument") } val := argsList.Front().Value.(formulaArg).ToNumber() if val.Type == ArgError { return val } if val.Number == 0 { return newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV) } return newNumberFormulaArg(180.0 / math.Pi * val.Number) } // 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) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "EVEN requires 1 numeric argument") } number := argsList.Front().Value.(formulaArg).ToNumber() if number.Type == ArgError { return number } sign := math.Signbit(number.Number) m, frac := math.Modf(number.Number / 2) val := m * 2 if frac != 0 { if !sign { val += 2 } else { val -= 2 } } return newNumberFormulaArg(val) } // 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) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "EXP requires 1 numeric argument") } number := argsList.Front().Value.(formulaArg).ToNumber() if number.Type == ArgError { return number } return newStringFormulaArg(strings.ToUpper(fmt.Sprintf("%g", math.Exp(number.Number)))) } // 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) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "FACT requires 1 numeric argument") } number := argsList.Front().Value.(formulaArg).ToNumber() if number.Type == ArgError { return number } if number.Number < 0 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } return newNumberFormulaArg(fact(number.Number)) } // 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) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "FACTDOUBLE requires 1 numeric argument") } val := 1.0 number := argsList.Front().Value.(formulaArg).ToNumber() if number.Type == ArgError { return number } if number.Number < 0 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } for i := math.Trunc(number.Number); i > 1; i -= 2 { val *= i } return newStringFormulaArg(strings.ToUpper(fmt.Sprintf("%g", val))) } // 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) formulaArg { if argsList.Len() != 2 { return newErrorFormulaArg(formulaErrorVALUE, "FLOOR requires 2 numeric arguments") } number := argsList.Front().Value.(formulaArg).ToNumber() if number.Type == ArgError { return number } significance := argsList.Back().Value.(formulaArg).ToNumber() if significance.Type == ArgError { return significance } if significance.Number < 0 && number.Number >= 0 { return newErrorFormulaArg(formulaErrorNUM, "invalid arguments to FLOOR") } val := number.Number val, res := math.Modf(val / significance.Number) if res != 0 { if number.Number < 0 && res < 0 { val-- } } return newStringFormulaArg(strings.ToUpper(fmt.Sprintf("%g", val*significance.Number))) } // FLOORdotMATH 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) FLOORdotMATH(argsList *list.List) formulaArg { if argsList.Len() == 0 { return newErrorFormulaArg(formulaErrorVALUE, "FLOOR.MATH requires at least 1 argument") } if argsList.Len() > 3 { return newErrorFormulaArg(formulaErrorVALUE, "FLOOR.MATH allows at most 3 arguments") } significance, mode := 1.0, 1.0 number := argsList.Front().Value.(formulaArg).ToNumber() if number.Type == ArgError { return number } if number.Number < 0 { significance = -1 } if argsList.Len() > 1 { s := argsList.Front().Next().Value.(formulaArg).ToNumber() if s.Type == ArgError { return s } significance = s.Number } if argsList.Len() == 1 { return newNumberFormulaArg(math.Floor(number.Number)) } if argsList.Len() > 2 { m := argsList.Back().Value.(formulaArg).ToNumber() if m.Type == ArgError { return m } mode = m.Number } val, res := math.Modf(number.Number / significance) if res != 0 && number.Number < 0 && mode > 0 { val-- } return newNumberFormulaArg(val * significance) } // FLOORdotPRECISE 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) FLOORdotPRECISE(argsList *list.List) formulaArg { if argsList.Len() == 0 { return newErrorFormulaArg(formulaErrorVALUE, "FLOOR.PRECISE requires at least 1 argument") } if argsList.Len() > 2 { return newErrorFormulaArg(formulaErrorVALUE, "FLOOR.PRECISE allows at most 2 arguments") } var significance float64 number := argsList.Front().Value.(formulaArg).ToNumber() if number.Type == ArgError { return number } if number.Number < 0 { significance = -1 } if argsList.Len() == 1 { return newNumberFormulaArg(math.Floor(number.Number)) } if argsList.Len() > 1 { s := argsList.Back().Value.(formulaArg).ToNumber() if s.Type == ArgError { return s } significance = s.Number significance = math.Abs(significance) if significance == 0 { return newNumberFormulaArg(significance) } } val, res := math.Modf(number.Number / significance) if res != 0 { if number.Number < 0 { val-- } } return newNumberFormulaArg(val * significance) } // 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) formulaArg { if argsList.Len() == 0 { return newErrorFormulaArg(formulaErrorVALUE, "GCD requires at least 1 argument") } var ( val float64 nums = []float64{} ) for arg := argsList.Front(); arg != nil; arg = arg.Next() { token := arg.Value.(formulaArg) switch token.Type { case ArgString: num := token.ToNumber() if num.Type == ArgError { return num } val = num.Number case ArgNumber: val = token.Number } nums = append(nums, val) } if nums[0] < 0 { return newErrorFormulaArg(formulaErrorVALUE, "GCD only accepts positive arguments") } if len(nums) == 1 { return newNumberFormulaArg(nums[0]) } cd := nums[0] for i := 1; i < len(nums); i++ { if nums[i] < 0 { return newErrorFormulaArg(formulaErrorVALUE, "GCD only accepts positive arguments") } cd = gcd(cd, nums[i]) } return newNumberFormulaArg(cd) } // 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) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "INT requires 1 numeric argument") } number := argsList.Front().Value.(formulaArg).ToNumber() if number.Type == ArgError { return number } val, frac := math.Modf(number.Number) if frac < 0 { val-- } return newNumberFormulaArg(val) } // ISOdotCEILING 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) ISOdotCEILING(argsList *list.List) formulaArg { if argsList.Len() == 0 { return newErrorFormulaArg(formulaErrorVALUE, "ISO.CEILING requires at least 1 argument") } if argsList.Len() > 2 { return newErrorFormulaArg(formulaErrorVALUE, "ISO.CEILING allows at most 2 arguments") } var significance float64 number := argsList.Front().Value.(formulaArg).ToNumber() if number.Type == ArgError { return number } if number.Number < 0 { significance = -1 } if argsList.Len() == 1 { return newNumberFormulaArg(math.Ceil(number.Number)) } if argsList.Len() > 1 { s := argsList.Back().Value.(formulaArg).ToNumber() if s.Type == ArgError { return s } significance = s.Number significance = math.Abs(significance) if significance == 0 { return newNumberFormulaArg(significance) } } val, res := math.Modf(number.Number / significance) if res != 0 { if number.Number > 0 { val++ } } return newNumberFormulaArg(val * significance) } // 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) formulaArg { if argsList.Len() == 0 { return newErrorFormulaArg(formulaErrorVALUE, "LCM requires at least 1 argument") } var ( val float64 nums = []float64{} err error ) 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 { return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } case ArgNumber: val = token.Number } nums = append(nums, val) } if nums[0] < 0 { return newErrorFormulaArg(formulaErrorVALUE, "LCM only accepts positive arguments") } if len(nums) == 1 { return newNumberFormulaArg(nums[0]) } cm := nums[0] for i := 1; i < len(nums); i++ { if nums[i] < 0 { return newErrorFormulaArg(formulaErrorVALUE, "LCM only accepts positive arguments") } cm = lcm(cm, nums[i]) } return newNumberFormulaArg(cm) } // 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) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "LN requires 1 numeric argument") } number := argsList.Front().Value.(formulaArg).ToNumber() if number.Type == ArgError { return number } return newNumberFormulaArg(math.Log(number.Number)) } // 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) formulaArg { if argsList.Len() == 0 { return newErrorFormulaArg(formulaErrorVALUE, "LOG requires at least 1 argument") } if argsList.Len() > 2 { return newErrorFormulaArg(formulaErrorVALUE, "LOG allows at most 2 arguments") } base := 10.0 number := argsList.Front().Value.(formulaArg).ToNumber() if number.Type == ArgError { return number } if argsList.Len() > 1 { b := argsList.Back().Value.(formulaArg).ToNumber() if b.Type == ArgError { return b } base = b.Number } if number.Number == 0 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorDIV) } if base == 0 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorDIV) } if base == 1 { return newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV) } return newNumberFormulaArg(math.Log(number.Number) / math.Log(base)) } // 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) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "LOG10 requires 1 numeric argument") } number := argsList.Front().Value.(formulaArg).ToNumber() if number.Type == ArgError { return number } return newNumberFormulaArg(math.Log10(number.Number)) } // minor function implement a minor of a matrix A is the determinant of some // smaller square matrix. 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 formulaArg) { var ( num float64 numMtx = [][]float64{} err error strMtx [][]formulaArg ) if argsList.Len() < 1 { return newErrorFormulaArg(formulaErrorVALUE, "MDETERM requires at least 1 argument") } strMtx = argsList.Front().Value.(formulaArg).Matrix rows := len(strMtx) for _, row := range argsList.Front().Value.(formulaArg).Matrix { if len(row) != rows { return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } numRow := []float64{} for _, ele := range row { if num, err = strconv.ParseFloat(ele.String, 64); err != nil { return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } numRow = append(numRow, num) } numMtx = append(numMtx, numRow) } return newNumberFormulaArg(det(numMtx)) } // 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) formulaArg { if argsList.Len() != 2 { return newErrorFormulaArg(formulaErrorVALUE, "MOD requires 2 numeric arguments") } number := argsList.Front().Value.(formulaArg).ToNumber() if number.Type == ArgError { return number } divisor := argsList.Back().Value.(formulaArg).ToNumber() if divisor.Type == ArgError { return divisor } if divisor.Number == 0 { return newErrorFormulaArg(formulaErrorDIV, "MOD divide by zero") } trunc, rem := math.Modf(number.Number / divisor.Number) if rem < 0 { trunc-- } return newNumberFormulaArg(number.Number - divisor.Number*trunc) } // MROUND function rounds a supplied number up or down to the nearest multiple // of a given number. The syntax of the function is: // // MROUND(number,multiple) // func (fn *formulaFuncs) MROUND(argsList *list.List) formulaArg { if argsList.Len() != 2 { return newErrorFormulaArg(formulaErrorVALUE, "MROUND requires 2 numeric arguments") } n := argsList.Front().Value.(formulaArg).ToNumber() if n.Type == ArgError { return n } multiple := argsList.Back().Value.(formulaArg).ToNumber() if multiple.Type == ArgError { return multiple } if multiple.Number == 0 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } if multiple.Number < 0 && n.Number > 0 || multiple.Number > 0 && n.Number < 0 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } number, res := math.Modf(n.Number / multiple.Number) if math.Trunc(res+0.5) > 0 { number++ } return newNumberFormulaArg(number * multiple.Number) } // 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) formulaArg { val, num, denom := 0.0, 0.0, 1.0 var err error 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 { return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } case ArgNumber: val = token.Number } num += val denom *= fact(val) } return newNumberFormulaArg(fact(num) / denom) } // 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 formulaArg) { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "MUNIT requires 1 numeric argument") } dimension := argsList.Back().Value.(formulaArg).ToNumber() if dimension.Type == ArgError || dimension.Number < 0 { return newErrorFormulaArg(formulaErrorVALUE, dimension.Error) } matrix := make([][]formulaArg, 0, int(dimension.Number)) for i := 0; i < int(dimension.Number); i++ { row := make([]formulaArg, int(dimension.Number)) for j := 0; j < int(dimension.Number); j++ { if i == j { row[j] = newNumberFormulaArg(1.0) } else { row[j] = newNumberFormulaArg(0.0) } } matrix = append(matrix, row) } return newMatrixFormulaArg(matrix) } // 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) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "ODD requires 1 numeric argument") } number := argsList.Back().Value.(formulaArg).ToNumber() if number.Type == ArgError { return number } if number.Number == 0 { return newNumberFormulaArg(1) } sign := math.Signbit(number.Number) m, frac := math.Modf((number.Number - 1) / 2) val := m*2 + 1 if frac != 0 { if !sign { val += 2 } else { val -= 2 } } return newNumberFormulaArg(val) } // 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) formulaArg { if argsList.Len() != 0 { return newErrorFormulaArg(formulaErrorVALUE, "PI accepts no arguments") } return newNumberFormulaArg(math.Pi) } // 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) formulaArg { if argsList.Len() != 2 { return newErrorFormulaArg(formulaErrorVALUE, "POWER requires 2 numeric arguments") } x := argsList.Front().Value.(formulaArg).ToNumber() if x.Type == ArgError { return x } y := argsList.Back().Value.(formulaArg).ToNumber() if y.Type == ArgError { return y } if x.Number == 0 && y.Number == 0 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } if x.Number == 0 && y.Number < 0 { return newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV) } return newNumberFormulaArg(math.Pow(x.Number, y.Number)) } // 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) formulaArg { val, product := 0.0, 1.0 var err error 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 { return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } product = product * val case ArgNumber: product = product * token.Number 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 { return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } product = product * val } } } } return newNumberFormulaArg(product) } // 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) formulaArg { if argsList.Len() != 2 { return newErrorFormulaArg(formulaErrorVALUE, "QUOTIENT requires 2 numeric arguments") } x := argsList.Front().Value.(formulaArg).ToNumber() if x.Type == ArgError { return x } y := argsList.Back().Value.(formulaArg).ToNumber() if y.Type == ArgError { return y } if y.Number == 0 { return newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV) } return newNumberFormulaArg(math.Trunc(x.Number / y.Number)) } // RADIANS function converts radians into degrees. The syntax of the function is: // // RADIANS(angle) // func (fn *formulaFuncs) RADIANS(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "RADIANS requires 1 numeric argument") } angle := argsList.Front().Value.(formulaArg).ToNumber() if angle.Type == ArgError { return angle } return newNumberFormulaArg(math.Pi / 180.0 * angle.Number) } // 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) formulaArg { if argsList.Len() != 0 { return newErrorFormulaArg(formulaErrorVALUE, "RAND accepts no arguments") } return newNumberFormulaArg(rand.New(rand.NewSource(time.Now().UnixNano())).Float64()) } // 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) formulaArg { if argsList.Len() != 2 { return newErrorFormulaArg(formulaErrorVALUE, "RANDBETWEEN requires 2 numeric arguments") } bottom := argsList.Front().Value.(formulaArg).ToNumber() if bottom.Type == ArgError { return bottom } top := argsList.Back().Value.(formulaArg).ToNumber() if top.Type == ArgError { return top } if top.Number < bottom.Number { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } num := rand.New(rand.NewSource(time.Now().UnixNano())).Int63n(int64(top.Number - bottom.Number + 1)) return newNumberFormulaArg(float64(num + int64(bottom.Number))) } // 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) formulaArg { if argsList.Len() == 0 { return newErrorFormulaArg(formulaErrorVALUE, "ROMAN requires at least 1 argument") } if argsList.Len() > 2 { return newErrorFormulaArg(formulaErrorVALUE, "ROMAN allows at most 2 arguments") } var form int number := argsList.Front().Value.(formulaArg).ToNumber() if number.Type == ArgError { return number } if argsList.Len() > 1 { f := argsList.Back().Value.(formulaArg).ToNumber() if f.Type == ArgError { return f } form = int(f.Number) 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.Number) buf := bytes.Buffer{} for _, r := range decimalTable { for val >= r.n { buf.WriteString(r.s) val -= r.n } } return newStringFormulaArg(buf.String()) } 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) formulaArg { if argsList.Len() != 2 { return newErrorFormulaArg(formulaErrorVALUE, "ROUND requires 2 numeric arguments") } number := argsList.Front().Value.(formulaArg).ToNumber() if number.Type == ArgError { return number } digits := argsList.Back().Value.(formulaArg).ToNumber() if digits.Type == ArgError { return digits } return newNumberFormulaArg(fn.round(number.Number, digits.Number, closest)) } // 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) formulaArg { if argsList.Len() != 2 { return newErrorFormulaArg(formulaErrorVALUE, "ROUNDDOWN requires 2 numeric arguments") } number := argsList.Front().Value.(formulaArg).ToNumber() if number.Type == ArgError { return number } digits := argsList.Back().Value.(formulaArg).ToNumber() if digits.Type == ArgError { return digits } return newNumberFormulaArg(fn.round(number.Number, digits.Number, down)) } // 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) formulaArg { if argsList.Len() != 2 { return newErrorFormulaArg(formulaErrorVALUE, "ROUNDUP requires 2 numeric arguments") } number := argsList.Front().Value.(formulaArg).ToNumber() if number.Type == ArgError { return number } digits := argsList.Back().Value.(formulaArg).ToNumber() if digits.Type == ArgError { return digits } return newNumberFormulaArg(fn.round(number.Number, digits.Number, up)) } // SEC function calculates the secant of a given angle. The syntax of the // function is: // // SEC(number) // func (fn *formulaFuncs) SEC(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "SEC requires 1 numeric argument") } number := argsList.Front().Value.(formulaArg).ToNumber() if number.Type == ArgError { return number } return newNumberFormulaArg(math.Cos(number.Number)) } // 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) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "SECH requires 1 numeric argument") } number := argsList.Front().Value.(formulaArg).ToNumber() if number.Type == ArgError { return number } return newNumberFormulaArg(1 / math.Cosh(number.Number)) } // 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) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "SIGN requires 1 numeric argument") } val := argsList.Front().Value.(formulaArg).ToNumber() if val.Type == ArgError { return val } if val.Number < 0 { return newNumberFormulaArg(-1) } if val.Number > 0 { return newNumberFormulaArg(1) } return newNumberFormulaArg(0) } // SIN function calculates the sine of a given angle. The syntax of the // function is: // // SIN(number) // func (fn *formulaFuncs) SIN(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "SIN requires 1 numeric argument") } number := argsList.Front().Value.(formulaArg).ToNumber() if number.Type == ArgError { return number } return newNumberFormulaArg(math.Sin(number.Number)) } // 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) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "SINH requires 1 numeric argument") } number := argsList.Front().Value.(formulaArg).ToNumber() if number.Type == ArgError { return number } return newNumberFormulaArg(math.Sinh(number.Number)) } // 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) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "SQRT requires 1 numeric argument") } value := argsList.Front().Value.(formulaArg).ToNumber() if value.Type == ArgError { return value } if value.Number < 0 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } return newNumberFormulaArg(math.Sqrt(value.Number)) } // 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) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "SQRTPI requires 1 numeric argument") } number := argsList.Front().Value.(formulaArg).ToNumber() if number.Type == ArgError { return number } return newNumberFormulaArg(math.Sqrt(number.Number * math.Pi)) } // STDEV function calculates the sample standard deviation of a supplied set // of values. The syntax of the function is: // // STDEV(number1,[number2],...) // func (fn *formulaFuncs) STDEV(argsList *list.List) formulaArg { if argsList.Len() < 1 { return newErrorFormulaArg(formulaErrorVALUE, "STDEV requires at least 1 argument") } return fn.stdev(false, argsList) } // STDEVdotS function calculates the sample standard deviation of a supplied // set of values. The syntax of the function is: // // STDEV.S(number1,[number2],...) // func (fn *formulaFuncs) STDEVdotS(argsList *list.List) formulaArg { if argsList.Len() < 1 { return newErrorFormulaArg(formulaErrorVALUE, "STDEV.S requires at least 1 argument") } return fn.stdev(false, argsList) } // STDEVA function estimates standard deviation based on a sample. The // standard deviation is a measure of how widely values are dispersed from // the average value (the mean). The syntax of the function is: // // STDEVA(number1,[number2],...) // func (fn *formulaFuncs) STDEVA(argsList *list.List) formulaArg { if argsList.Len() < 1 { return newErrorFormulaArg(formulaErrorVALUE, "STDEVA requires at least 1 argument") } return fn.stdev(true, argsList) } // calcStdevPow is part of the implementation stdev. func calcStdevPow(result, count float64, n, m formulaArg) (float64, float64) { if result == -1 { result = math.Pow((n.Number - m.Number), 2) } else { result += math.Pow((n.Number - m.Number), 2) } count++ return result, count } // calcStdev is part of the implementation stdev. func calcStdev(stdeva bool, result, count float64, mean, token formulaArg) (float64, float64) { for _, row := range token.ToList() { if row.Type == ArgNumber || row.Type == ArgString { if !stdeva && (row.Value() == "TRUE" || row.Value() == "FALSE") { continue } else if stdeva && (row.Value() == "TRUE" || row.Value() == "FALSE") { num := row.ToBool() if num.Type == ArgNumber { result, count = calcStdevPow(result, count, num, mean) continue } } else { num := row.ToNumber() if num.Type == ArgNumber { result, count = calcStdevPow(result, count, num, mean) } } } } return result, count } // stdev is an implementation of the formula functions STDEV and STDEVA. func (fn *formulaFuncs) stdev(stdeva bool, argsList *list.List) formulaArg { count, result := -1.0, -1.0 var mean formulaArg if stdeva { mean = fn.AVERAGEA(argsList) } else { mean = fn.AVERAGE(argsList) } for arg := argsList.Front(); arg != nil; arg = arg.Next() { token := arg.Value.(formulaArg) switch token.Type { case ArgString, ArgNumber: if !stdeva && (token.Value() == "TRUE" || token.Value() == "FALSE") { continue } else if stdeva && (token.Value() == "TRUE" || token.Value() == "FALSE") { num := token.ToBool() if num.Type == ArgNumber { result, count = calcStdevPow(result, count, num, mean) continue } } else { num := token.ToNumber() if num.Type == ArgNumber { result, count = calcStdevPow(result, count, num, mean) } } case ArgList, ArgMatrix: result, count = calcStdev(stdeva, result, count, mean, token) } } if count > 0 && result >= 0 { return newNumberFormulaArg(math.Sqrt(result / count)) } return newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV) } // POISSONdotDIST function calculates the Poisson Probability Mass Function or // the Cumulative Poisson Probability Function for a supplied set of // parameters. The syntax of the function is: // // POISSON.DIST(x,mean,cumulative) // func (fn *formulaFuncs) POISSONdotDIST(argsList *list.List) formulaArg { if argsList.Len() != 3 { return newErrorFormulaArg(formulaErrorVALUE, "POISSON.DIST requires 3 arguments") } return fn.POISSON(argsList) } // POISSON function calculates the Poisson Probability Mass Function or the // Cumulative Poisson Probability Function for a supplied set of parameters. // The syntax of the function is: // // POISSON(x,mean,cumulative) // func (fn *formulaFuncs) POISSON(argsList *list.List) formulaArg { if argsList.Len() != 3 { return newErrorFormulaArg(formulaErrorVALUE, "POISSON requires 3 arguments") } var x, mean, cumulative formulaArg if x = argsList.Front().Value.(formulaArg).ToNumber(); x.Type != ArgNumber { return x } if mean = argsList.Front().Next().Value.(formulaArg).ToNumber(); mean.Type != ArgNumber { return mean } if cumulative = argsList.Back().Value.(formulaArg).ToBool(); cumulative.Type == ArgError { return cumulative } if x.Number < 0 || mean.Number <= 0 { return newErrorFormulaArg(formulaErrorNA, formulaErrorNA) } if cumulative.Number == 1 { summer := 0.0 floor := math.Floor(x.Number) for i := 0; i <= int(floor); i++ { summer += math.Pow(mean.Number, float64(i)) / fact(float64(i)) } return newNumberFormulaArg(math.Exp(0-mean.Number) * summer) } return newNumberFormulaArg(math.Exp(0-mean.Number) * math.Pow(mean.Number, x.Number) / fact(x.Number)) } // 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) formulaArg { var sum float64 for arg := argsList.Front(); arg != nil; arg = arg.Next() { token := arg.Value.(formulaArg) switch token.Type { case ArgError: return token case ArgString: if num := token.ToNumber(); num.Type == ArgNumber { sum += num.Number } case ArgNumber: sum += token.Number case ArgMatrix: for _, row := range token.Matrix { for _, value := range row { if num := value.ToNumber(); num.Type == ArgNumber { sum += num.Number } } } } } return newNumberFormulaArg(sum) } // 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) formulaArg { if argsList.Len() < 2 { return newErrorFormulaArg(formulaErrorVALUE, "SUMIF requires at least 2 arguments") } criteria := formulaCriteriaParser(argsList.Front().Next().Value.(formulaArg).String) rangeMtx := argsList.Front().Value.(formulaArg).Matrix var sumRange [][]formulaArg if argsList.Len() == 3 { sumRange = argsList.Back().Value.(formulaArg).Matrix } var sum, val float64 var err error for rowIdx, row := range rangeMtx { for colIdx, col := range row { var ok bool fromVal := col.String if col.String == "" { continue } ok, _ = formulaCriteriaEval(fromVal, criteria) 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 { continue } sum += val } } } return newNumberFormulaArg(sum) } // SUMIFS function finds values in one or more supplied arrays, that satisfy a // set of criteria, and returns the sum of the corresponding values in a // further supplied array. The syntax of the function is: // // SUMIFS(sum_range,criteria_range1,criteria1,[criteria_range2,criteria2],...) // func (fn *formulaFuncs) SUMIFS(argsList *list.List) formulaArg { if argsList.Len() < 3 { return newErrorFormulaArg(formulaErrorVALUE, "SUMIFS requires at least 3 arguments") } if argsList.Len()%2 != 1 { return newErrorFormulaArg(formulaErrorNA, formulaErrorNA) } sum, sumRange, args := 0.0, argsList.Front().Value.(formulaArg).Matrix, []formulaArg{} for arg := argsList.Front().Next(); arg != nil; arg = arg.Next() { args = append(args, arg.Value.(formulaArg)) } for _, ref := range formulaIfsMatch(args) { if num := sumRange[ref.Row][ref.Col].ToNumber(); num.Type == ArgNumber { sum += num.Number } } return newNumberFormulaArg(sum) } // sumproduct is an implementation of the formula function SUMPRODUCT. func (fn *formulaFuncs) sumproduct(argsList *list.List) formulaArg { var ( argType ArgType n int res []float64 sum float64 ) for arg := argsList.Front(); arg != nil; arg = arg.Next() { token := arg.Value.(formulaArg) if argType == ArgUnknown { argType = token.Type } if token.Type != argType { return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } switch token.Type { case ArgString, ArgNumber: if num := token.ToNumber(); num.Type == ArgNumber { sum = fn.PRODUCT(argsList).Number continue } return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) case ArgMatrix: args := token.ToList() if res == nil { n = len(args) res = make([]float64, n) for i := range res { res[i] = 1.0 } } if len(args) != n { return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } for i, value := range args { num := value.ToNumber() if num.Type != ArgNumber && value.Value() != "" { return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } res[i] = res[i] * num.Number } } } for _, r := range res { sum += r } return newNumberFormulaArg(sum) } // SUMPRODUCT function returns the sum of the products of the corresponding // values in a set of supplied arrays. The syntax of the function is: // // SUMPRODUCT(array1,[array2],[array3],...) // func (fn *formulaFuncs) SUMPRODUCT(argsList *list.List) formulaArg { if argsList.Len() < 1 { return newErrorFormulaArg(formulaErrorVALUE, "SUMPRODUCT requires at least 1 argument") } for arg := argsList.Front(); arg != nil; arg = arg.Next() { if token := arg.Value.(formulaArg); token.Type == ArgError { return token } } return fn.sumproduct(argsList) } // 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) formulaArg { var val, sq float64 var err error 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 { return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } sq += val * val case ArgNumber: sq += token.Number 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 { return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } sq += val * val } } } } return newNumberFormulaArg(sq) } // sumx is an implementation of the formula functions SUMX2MY2, SUMX2PY2 and // SUMXMY2. func (fn *formulaFuncs) sumx(name string, argsList *list.List) formulaArg { if argsList.Len() != 2 { return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires 2 arguments", name)) } array1 := argsList.Front().Value.(formulaArg) array2 := argsList.Back().Value.(formulaArg) left, right := array1.ToList(), array2.ToList() n := len(left) if n != len(right) { return newErrorFormulaArg(formulaErrorNA, formulaErrorNA) } result := 0.0 for i := 0; i < n; i++ { if lhs, rhs := left[i].ToNumber(), right[i].ToNumber(); lhs.Number != 0 && rhs.Number != 0 { switch name { case "SUMX2MY2": result += lhs.Number*lhs.Number - rhs.Number*rhs.Number case "SUMX2PY2": result += lhs.Number*lhs.Number + rhs.Number*rhs.Number default: result += (lhs.Number - rhs.Number) * (lhs.Number - rhs.Number) } } } return newNumberFormulaArg(result) } // SUMX2MY2 function returns the sum of the differences of squares of two // supplied sets of values. The syntax of the function is: // // SUMX2MY2(array_x,array_y) // func (fn *formulaFuncs) SUMX2MY2(argsList *list.List) formulaArg { return fn.sumx("SUMX2MY2", argsList) } // SUMX2PY2 function returns the sum of the sum of squares of two supplied sets // of values. The syntax of the function is: // // SUMX2PY2(array_x,array_y) // func (fn *formulaFuncs) SUMX2PY2(argsList *list.List) formulaArg { return fn.sumx("SUMX2PY2", argsList) } // SUMXMY2 function returns the sum of the squares of differences between // corresponding values in two supplied arrays. The syntax of the function // is: // // SUMXMY2(array_x,array_y) // func (fn *formulaFuncs) SUMXMY2(argsList *list.List) formulaArg { return fn.sumx("SUMXMY2", argsList) } // TAN function calculates the tangent of a given angle. The syntax of the // function is: // // TAN(number) // func (fn *formulaFuncs) TAN(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "TAN requires 1 numeric argument") } number := argsList.Front().Value.(formulaArg).ToNumber() if number.Type == ArgError { return number } return newNumberFormulaArg(math.Tan(number.Number)) } // 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) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "TANH requires 1 numeric argument") } number := argsList.Front().Value.(formulaArg).ToNumber() if number.Type == ArgError { return number } return newNumberFormulaArg(math.Tanh(number.Number)) } // 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) formulaArg { if argsList.Len() == 0 { return newErrorFormulaArg(formulaErrorVALUE, "TRUNC requires at least 1 argument") } var digits, adjust, rtrim float64 var err error number := argsList.Front().Value.(formulaArg).ToNumber() if number.Type == ArgError { return number } if argsList.Len() > 1 { d := argsList.Back().Value.(formulaArg).ToNumber() if d.Type == ArgError { return d } digits = d.Number digits = math.Floor(digits) } adjust = math.Pow(10, digits) x := int((math.Abs(number.Number) - math.Abs(float64(int(number.Number)))) * adjust) if x != 0 { if rtrim, err = strconv.ParseFloat(strings.TrimRight(strconv.Itoa(x), "0"), 64); err != nil { return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } } if (digits > 0) && (rtrim < adjust/10) { return newNumberFormulaArg(number.Number) } return newNumberFormulaArg(float64(int(number.Number*adjust)) / adjust) } // Statistical Functions // AVEDEV function calculates the average deviation of a supplied set of // values. The syntax of the function is: // // AVEDEV(number1,[number2],...) // func (fn *formulaFuncs) AVEDEV(argsList *list.List) formulaArg { if argsList.Len() == 0 { return newErrorFormulaArg(formulaErrorVALUE, "AVEDEV requires at least 1 argument") } average := fn.AVERAGE(argsList) if average.Type != ArgNumber { return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } result, count := 0.0, 0.0 for arg := argsList.Front(); arg != nil; arg = arg.Next() { num := arg.Value.(formulaArg).ToNumber() if num.Type != ArgNumber { return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } result += math.Abs(num.Number - average.Number) count++ } return newNumberFormulaArg(result / count) } // AVERAGE function returns the arithmetic mean of a list of supplied numbers. // The syntax of the function is: // // AVERAGE(number1,[number2],...) // func (fn *formulaFuncs) AVERAGE(argsList *list.List) formulaArg { args := []formulaArg{} for arg := argsList.Front(); arg != nil; arg = arg.Next() { args = append(args, arg.Value.(formulaArg)) } count, sum := fn.countSum(false, args) if count == 0 { return newErrorFormulaArg(formulaErrorDIV, "AVERAGE divide by zero") } return newNumberFormulaArg(sum / count) } // AVERAGEA function returns the arithmetic mean of a list of supplied numbers // with text cell and zero values. The syntax of the function is: // // AVERAGEA(number1,[number2],...) // func (fn *formulaFuncs) AVERAGEA(argsList *list.List) formulaArg { args := []formulaArg{} for arg := argsList.Front(); arg != nil; arg = arg.Next() { args = append(args, arg.Value.(formulaArg)) } count, sum := fn.countSum(true, args) if count == 0 { return newErrorFormulaArg(formulaErrorDIV, "AVERAGEA divide by zero") } return newNumberFormulaArg(sum / count) } // AVERAGEIF function finds the values in a supplied array that satisfy a // specified criteria, and returns the average (i.e. the statistical mean) of // the corresponding values in a second supplied array. The syntax of the // function is: // // AVERAGEIF(range,criteria,[average_range]) // func (fn *formulaFuncs) AVERAGEIF(argsList *list.List) formulaArg { if argsList.Len() < 2 { return newErrorFormulaArg(formulaErrorVALUE, "AVERAGEIF requires at least 2 arguments") } var ( criteria = formulaCriteriaParser(argsList.Front().Next().Value.(formulaArg).String) rangeMtx = argsList.Front().Value.(formulaArg).Matrix cellRange [][]formulaArg args []formulaArg val float64 err error ok bool ) if argsList.Len() == 3 { cellRange = argsList.Back().Value.(formulaArg).Matrix } for rowIdx, row := range rangeMtx { for colIdx, col := range row { fromVal := col.String if col.String == "" { continue } ok, _ = formulaCriteriaEval(fromVal, criteria) if ok { if argsList.Len() == 3 { if len(cellRange) <= rowIdx || len(cellRange[rowIdx]) <= colIdx { continue } fromVal = cellRange[rowIdx][colIdx].String } if val, err = strconv.ParseFloat(fromVal, 64); err != nil { continue } args = append(args, newNumberFormulaArg(val)) } } } count, sum := fn.countSum(false, args) if count == 0 { return newErrorFormulaArg(formulaErrorDIV, "AVERAGEIF divide by zero") } return newNumberFormulaArg(sum / count) } // AVERAGEIFS function finds entries in one or more arrays, that satisfy a set // of supplied criteria, and returns the average (i.e. the statistical mean) // of the corresponding values in a further supplied array. The syntax of the // function is: // // AVERAGEIFS(average_range,criteria_range1,criteria1,[criteria_range2,criteria2],...) // func (fn *formulaFuncs) AVERAGEIFS(argsList *list.List) formulaArg { if argsList.Len() < 3 { return newErrorFormulaArg(formulaErrorVALUE, "AVERAGEIFS requires at least 3 arguments") } if argsList.Len()%2 != 1 { return newErrorFormulaArg(formulaErrorNA, formulaErrorNA) } sum, sumRange, args := 0.0, argsList.Front().Value.(formulaArg).Matrix, []formulaArg{} for arg := argsList.Front().Next(); arg != nil; arg = arg.Next() { args = append(args, arg.Value.(formulaArg)) } count := 0.0 for _, ref := range formulaIfsMatch(args) { if num := sumRange[ref.Row][ref.Col].ToNumber(); num.Type == ArgNumber { sum += num.Number count++ } } if count == 0 { return newErrorFormulaArg(formulaErrorDIV, "AVERAGEIF divide by zero") } return newNumberFormulaArg(sum / count) } // getBetaHelperContFrac continued fractions for the beta function. func getBetaHelperContFrac(fX, fA, fB float64) float64 { var a1, b1, a2, b2, fnorm, cfnew, cf, rm float64 a1, b1, b2 = 1, 1, 1-(fA+fB)/(fA+1)*fX if b2 == 0 { a2, fnorm, cf = 0, 1, 1 } else { a2, fnorm = 1, 1/b2 cf = a2 * fnorm } cfnew, rm = 1, 1 fMaxIter, fMachEps := 50000.0, 2.22045e-016 bfinished := false for rm < fMaxIter && !bfinished { apl2m := fA + 2*rm d2m := rm * (fB - rm) * fX / ((apl2m - 1) * apl2m) d2m1 := -(fA + rm) * (fA + fB + rm) * fX / (apl2m * (apl2m + 1)) a1 = (a2 + d2m*a1) * fnorm b1 = (b2 + d2m*b1) * fnorm a2 = a1 + d2m1*a2*fnorm b2 = b1 + d2m1*b2*fnorm if b2 != 0 { fnorm = 1 / b2 cfnew = a2 * fnorm bfinished = (math.Abs(cf-cfnew) < math.Abs(cf)*fMachEps) } cf = cfnew rm += 1 } return cf } // getLanczosSum uses a variant of the Lanczos sum with a rational function. func getLanczosSum(fZ float64) float64 { num := []float64{ 23531376880.41075968857200767445163675473, 42919803642.64909876895789904700198885093, 35711959237.35566804944018545154716670596, 17921034426.03720969991975575445893111267, 6039542586.35202800506429164430729792107, 1439720407.311721673663223072794912393972, 248874557.8620541565114603864132294232163, 31426415.58540019438061423162831820536287, 2876370.628935372441225409051620849613599, 186056.2653952234950402949897160456992822, 8071.672002365816210638002902272250613822, 210.8242777515793458725097339207133627117, 2.506628274631000270164908177133837338626, } denom := []float64{ 0, 39916800, 120543840, 150917976, 105258076, 45995730, 13339535, 2637558, 357423, 32670, 1925, 66, 1, } var sumNum, sumDenom, zInv float64 if fZ <= 1 { sumNum = num[12] sumDenom = denom[12] for i := 11; i >= 0; i-- { sumNum *= fZ sumNum += num[i] sumDenom *= fZ sumDenom += denom[i] } } else { zInv = 1 / fZ sumNum = num[0] sumDenom = denom[0] for i := 1; i <= 12; i++ { sumNum *= zInv sumNum += num[i] sumDenom *= zInv sumDenom += denom[i] } } return sumNum / sumDenom } // getBeta return beta distribution. func getBeta(fAlpha, fBeta float64) float64 { var fA, fB float64 if fAlpha > fBeta { fA = fAlpha fB = fBeta } else { fA = fBeta fB = fAlpha } const maxGammaArgument = 171.624376956302 if fA+fB < maxGammaArgument { return math.Gamma(fA) / math.Gamma(fA+fB) * math.Gamma(fB) } fg := 6.024680040776729583740234375 fgm := fg - 0.5 fLanczos := getLanczosSum(fA) fLanczos /= getLanczosSum(fA + fB) fLanczos *= getLanczosSum(fB) fABgm := fA + fB + fgm fLanczos *= math.Sqrt((fABgm / (fA + fgm)) / (fB + fgm)) fTempA := fB / (fA + fgm) fTempB := fA / (fB + fgm) fResult := math.Exp(-fA*math.Log1p(fTempA) - fB*math.Log1p(fTempB) - fgm) fResult *= fLanczos return fResult } // getBetaDistPDF is an implementation for the Beta probability density // function. func getBetaDistPDF(fX, fA, fB float64) float64 { if fX <= 0 || fX >= 1 { return 0 } fLogDblMax, fLogDblMin := math.Log(1.79769e+308), math.Log(2.22507e-308) fLogY := math.Log(0.5 - fX + 0.5) if fX < 0.1 { fLogY = math.Log1p(-fX) } fLogX := math.Log(fX) fAm1LogX := (fA - 1) * fLogX fBm1LogY := (fB - 1) * fLogY fLogBeta := getLogBeta(fA, fB) if fAm1LogX < fLogDblMax && fAm1LogX > fLogDblMin && fBm1LogY < fLogDblMax && fBm1LogY > fLogDblMin && fLogBeta < fLogDblMax && fLogBeta > fLogDblMin && fAm1LogX+fBm1LogY < fLogDblMax && fAm1LogX+fBm1LogY > fLogDblMin { return math.Pow(fX, fA-1) * math.Pow(0.5-fX+0.5, fB-1) / getBeta(fA, fB) } return math.Exp(fAm1LogX + fBm1LogY - fLogBeta) } // getLogBeta return beta with logarithm. func getLogBeta(fAlpha, fBeta float64) float64 { var fA, fB float64 if fAlpha > fBeta { fA, fB = fAlpha, fBeta } else { fA, fB = fBeta, fAlpha } fg := 6.024680040776729583740234375 fgm := fg - 0.5 fLanczos := getLanczosSum(fA) fLanczos /= getLanczosSum(fA + fB) fLanczos *= getLanczosSum(fB) fLogLanczos := math.Log(fLanczos) fABgm := fA + fB + fgm fLogLanczos += 0.5 * (math.Log(fABgm) - math.Log(fA+fgm) - math.Log(fB+fgm)) fTempA := fB / (fA + fgm) fTempB := fA / (fB + fgm) fResult := -fA*math.Log1p(fTempA) - fB*math.Log1p(fTempB) - fgm fResult += fLogLanczos return fResult } // getBetaDist is an implementation for the beta distribution function. func getBetaDist(fXin, fAlpha, fBeta float64) float64 { if fXin <= 0 { return 0 } if fXin >= 1 { return 1 } if fBeta == 1 { return math.Pow(fXin, fAlpha) } if fAlpha == 1 { return -math.Expm1(fBeta * math.Log1p(-fXin)) } var fResult float64 fY, flnY := (0.5-fXin)+0.5, math.Log1p(-fXin) fX, flnX := fXin, math.Log(fXin) fA, fB := fAlpha, fBeta bReflect := fXin > fAlpha/(fAlpha+fBeta) if bReflect { fA = fBeta fB = fAlpha fX = fY fY = fXin flnX = flnY flnY = math.Log(fXin) } fResult = getBetaHelperContFrac(fX, fA, fB) / fA fP, fQ := fA/(fA+fB), fB/(fA+fB) var fTemp float64 if fA > 1 && fB > 1 && fP < 0.97 && fQ < 0.97 { fTemp = getBetaDistPDF(fX, fA, fB) * fX * fY } else { fTemp = math.Exp(fA*flnX + fB*flnY - getLogBeta(fA, fB)) } fResult *= fTemp if bReflect { fResult = 0.5 - fResult + 0.5 } return fResult } // prepareBETAdotDISTArgs checking and prepare arguments for the formula // function BETA.DIST. func (fn *formulaFuncs) prepareBETAdotDISTArgs(argsList *list.List) formulaArg { if argsList.Len() < 4 { return newErrorFormulaArg(formulaErrorVALUE, "BETA.DIST requires at least 4 arguments") } if argsList.Len() > 6 { return newErrorFormulaArg(formulaErrorVALUE, "BETA.DIST requires at most 6 arguments") } x := argsList.Front().Value.(formulaArg).ToNumber() if x.Type != ArgNumber { return x } alpha := argsList.Front().Next().Value.(formulaArg).ToNumber() if alpha.Type != ArgNumber { return alpha } beta := argsList.Front().Next().Next().Value.(formulaArg).ToNumber() if beta.Type != ArgNumber { return beta } if alpha.Number <= 0 || beta.Number <= 0 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } cumulative := argsList.Front().Next().Next().Next().Value.(formulaArg).ToBool() if cumulative.Type != ArgNumber { return cumulative } a, b := newNumberFormulaArg(0), newNumberFormulaArg(1) if argsList.Len() > 4 { if a = argsList.Front().Next().Next().Next().Next().Value.(formulaArg).ToNumber(); a.Type != ArgNumber { return a } } if argsList.Len() == 6 { if b = argsList.Back().Value.(formulaArg).ToNumber(); b.Type != ArgNumber { return b } } return newListFormulaArg([]formulaArg{x, alpha, beta, cumulative, a, b}) } // BETAdotDIST function calculates the cumulative beta distribution function // or the probability density function of the Beta distribution, for a // supplied set of parameters. The syntax of the function is: // // BETA.DIST(x,alpha,beta,cumulative,[A],[B]) // func (fn *formulaFuncs) BETAdotDIST(argsList *list.List) formulaArg { args := fn.prepareBETAdotDISTArgs(argsList) if args.Type != ArgList { return args } x, alpha, beta, cumulative, a, b := args.List[0], args.List[1], args.List[2], args.List[3], args.List[4], args.List[5] if x.Number < a.Number || x.Number > b.Number { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } if a.Number == b.Number { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } fScale := b.Number - a.Number x.Number = (x.Number - a.Number) / fScale if cumulative.Number == 1 { return newNumberFormulaArg(getBetaDist(x.Number, alpha.Number, beta.Number)) } return newNumberFormulaArg(getBetaDistPDF(x.Number, alpha.Number, beta.Number) / fScale) } // BETADIST function calculates the cumulative beta probability density // function for a supplied set of parameters. The syntax of the function is: // // BETADIST(x,alpha,beta,[A],[B]) // func (fn *formulaFuncs) BETADIST(argsList *list.List) formulaArg { if argsList.Len() < 3 { return newErrorFormulaArg(formulaErrorVALUE, "BETADIST requires at least 3 arguments") } if argsList.Len() > 5 { return newErrorFormulaArg(formulaErrorVALUE, "BETADIST requires at most 5 arguments") } x := argsList.Front().Value.(formulaArg).ToNumber() if x.Type != ArgNumber { return x } alpha := argsList.Front().Next().Value.(formulaArg).ToNumber() if alpha.Type != ArgNumber { return alpha } beta := argsList.Front().Next().Next().Value.(formulaArg).ToNumber() if beta.Type != ArgNumber { return beta } if alpha.Number <= 0 || beta.Number <= 0 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } a, b := newNumberFormulaArg(0), newNumberFormulaArg(1) if argsList.Len() > 3 { if a = argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber(); a.Type != ArgNumber { return a } } if argsList.Len() == 5 { if b = argsList.Back().Value.(formulaArg).ToNumber(); b.Type != ArgNumber { return b } } if x.Number < a.Number || x.Number > b.Number { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } if a.Number == b.Number { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } return newNumberFormulaArg(getBetaDist((x.Number-a.Number)/(b.Number-a.Number), alpha.Number, beta.Number)) } // d1mach returns double precision real machine constants. func d1mach(i int) float64 { arr := []float64{ 2.2250738585072014e-308, 1.7976931348623158e+308, 1.1102230246251565e-16, 2.2204460492503131e-16, 0.301029995663981195, } if i > len(arr) { return 0 } return arr[i-1] } // chebyshevInit determines the number of terms for the double precision // orthogonal series "dos" needed to insure the error is no larger // than "eta". Ordinarily eta will be chosen to be one-tenth machine // precision. func chebyshevInit(nos int, eta float64, dos []float64) int { i, e := 0, 0.0 if nos < 1 { return 0 } for ii := 1; ii <= nos; ii++ { i = nos - ii e += math.Abs(dos[i]) if e > eta { return i } } return i } // chebyshevEval evaluates the n-term Chebyshev series "a" at "x". func chebyshevEval(n int, x float64, a []float64) float64 { if n < 1 || n > 1000 || x < -1.1 || x > 1.1 { return math.NaN() } twox, b0, b1, b2 := x*2, 0.0, 0.0, 0.0 for i := 1; i <= n; i++ { b2 = b1 b1 = b0 b0 = twox*b1 - b2 + a[n-i] } return (b0 - b2) * 0.5 } // lgammacor is an implementation for the log(gamma) correction. func lgammacor(x float64) float64 { algmcs := []float64{ 0.1666389480451863247205729650822, -0.1384948176067563840732986059135e-4, 0.9810825646924729426157171547487e-8, -0.1809129475572494194263306266719e-10, 0.6221098041892605227126015543416e-13, -0.3399615005417721944303330599666e-15, 0.2683181998482698748957538846666e-17, -0.2868042435334643284144622399999e-19, 0.3962837061046434803679306666666e-21, -0.6831888753985766870111999999999e-23, 0.1429227355942498147573333333333e-24, -0.3547598158101070547199999999999e-26, 0.1025680058010470912000000000000e-27, -0.3401102254316748799999999999999e-29, 0.1276642195630062933333333333333e-30, } nalgm := chebyshevInit(15, d1mach(3), algmcs) xbig := 1.0 / math.Sqrt(d1mach(3)) xmax := math.Exp(math.Min(math.Log(d1mach(2)/12.0), -math.Log(12.0*d1mach(1)))) if x < 10.0 { return math.NaN() } else if x >= xmax { return 4.930380657631324e-32 } else if x < xbig { tmp := 10.0 / x return chebyshevEval(nalgm, tmp*tmp*2.0-1.0, algmcs) / x } return 1.0 / (x * 12.0) } // logrelerr compute the relative error logarithm. func logrelerr(x float64) float64 { alnrcs := []float64{ 0.10378693562743769800686267719098e+1, -0.13364301504908918098766041553133, 0.19408249135520563357926199374750e-1, -0.30107551127535777690376537776592e-2, 0.48694614797154850090456366509137e-3, -0.81054881893175356066809943008622e-4, 0.13778847799559524782938251496059e-4, -0.23802210894358970251369992914935e-5, 0.41640416213865183476391859901989e-6, -0.73595828378075994984266837031998e-7, 0.13117611876241674949152294345011e-7, -0.23546709317742425136696092330175e-8, 0.42522773276034997775638052962567e-9, -0.77190894134840796826108107493300e-10, 0.14075746481359069909215356472191e-10, -0.25769072058024680627537078627584e-11, 0.47342406666294421849154395005938e-12, -0.87249012674742641745301263292675e-13, 0.16124614902740551465739833119115e-13, -0.29875652015665773006710792416815e-14, 0.55480701209082887983041321697279e-15, -0.10324619158271569595141333961932e-15, 0.19250239203049851177878503244868e-16, -0.35955073465265150011189707844266e-17, 0.67264542537876857892194574226773e-18, -0.12602624168735219252082425637546e-18, 0.23644884408606210044916158955519e-19, -0.44419377050807936898878389179733e-20, 0.83546594464034259016241293994666e-21, -0.15731559416479562574899253521066e-21, 0.29653128740247422686154369706666e-22, -0.55949583481815947292156013226666e-23, 0.10566354268835681048187284138666e-23, -0.19972483680670204548314999466666e-24, 0.37782977818839361421049855999999e-25, -0.71531586889081740345038165333333e-26, 0.13552488463674213646502024533333e-26, -0.25694673048487567430079829333333e-27, 0.48747756066216949076459519999999e-28, -0.92542112530849715321132373333333e-29, 0.17578597841760239233269760000000e-29, -0.33410026677731010351377066666666e-30, 0.63533936180236187354180266666666e-31, } nlnrel := chebyshevInit(43, 0.1*d1mach(3), alnrcs) if x <= -1 { return math.NaN() } if math.Abs(x) <= 0.375 { return x * (1.0 - x*chebyshevEval(nlnrel, x/0.375, alnrcs)) } return math.Log(x + 1.0) } // logBeta is an implementation for the log of the beta distribution // function. func logBeta(a, b float64) float64 { corr, p, q := 0.0, a, a if b < p { p = b } if b > q { q = b } if p < 0 { return math.NaN() } if p == 0 { return math.MaxFloat64 } if p >= 10.0 { corr = lgammacor(p) + lgammacor(q) - lgammacor(p+q) f1 := q * logrelerr(-p/(p+q)) return math.Log(q)*-0.5 + 0.918938533204672741780329736406 + corr + (p-0.5)*math.Log(p/(p+q)) + math.Nextafter(f1, f1) } if q >= 10 { corr = lgammacor(q) - lgammacor(p+q) val, _ := math.Lgamma(p) return val + corr + p - p*math.Log(p+q) + (q-0.5)*logrelerr(-p/(p+q)) } return math.Log(math.Gamma(p) * (math.Gamma(q) / math.Gamma(p+q))) } // pbetaRaw is a part of pbeta for the beta distribution. func pbetaRaw(alnsml, ans, eps, p, pin, q, sml, x, y float64) float64 { if q > 1.0 { xb := p*math.Log(y) + q*math.Log(1.0-y) - logBeta(p, q) - math.Log(q) ib := int(math.Max(xb/alnsml, 0.0)) term := math.Exp(xb - float64(ib)*alnsml) c := 1.0 / (1.0 - y) p1 := q * c / (p + q - 1.0) finsum := 0.0 n := int(q) if q == float64(n) { n = n - 1 } for i := 1; i <= n; i++ { if p1 <= 1 && term/eps <= finsum { break } xi := float64(i) term = (q - xi + 1.0) * c * term / (p + q - xi) if term > 1.0 { ib = ib - 1 term = term * sml } if ib == 0 { finsum = finsum + term } } ans = ans + finsum } if y != x || p != pin { ans = 1.0 - ans } ans = math.Max(math.Min(ans, 1.0), 0.0) return ans } // pbeta returns distribution function of the beta distribution. func pbeta(x, pin, qin float64) (ans float64) { eps := d1mach(3) alneps := math.Log(eps) sml := d1mach(1) alnsml := math.Log(sml) y := x p := pin q := qin if p/(p+q) < x { y = 1.0 - y p = qin q = pin } if (p+q)*y/(p+1.0) < eps { xb := p*math.Log(math.Max(y, sml)) - math.Log(p) - logBeta(p, q) if xb > alnsml && y != 0.0 { ans = math.Exp(xb) } if y != x || p != pin { ans = 1.0 - ans } } else { ps := q - math.Floor(q) if ps == 0.0 { ps = 1.0 } xb := p*math.Log(y) - logBeta(ps, p) - math.Log(p) if xb >= alnsml { ans = math.Exp(xb) term := ans * p if ps != 1.0 { n := int(math.Max(alneps/math.Log(y), 4.0)) for i := 1; i <= n; i++ { xi := float64(i) term = term * (xi - ps) * y / xi ans = ans + term/(p+xi) } } } ans = pbetaRaw(alnsml, ans, eps, p, pin, q, sml, x, y) } return ans } // betainvProbIterator is a part of betainv for the inverse of the beta // function. func betainvProbIterator(alpha1, alpha3, beta1, beta2, beta3, logbeta, lower, maxCumulative, prob1, prob2, upper float64, needSwap bool) float64 { var i, j, prev, prop4 float64 j = 1 for prob := 0; prob < 1000; prob++ { prop3 := pbeta(beta3, alpha1, beta1) prop3 = (prop3 - prob1) * math.Exp(logbeta+prob2*math.Log(beta3)+beta2*math.Log(1.0-beta3)) if prop3*prop4 <= 0 { prev = math.Max(math.Abs(j), maxCumulative) } h := 1.0 for iteratorCount := 0; iteratorCount < 1000; iteratorCount++ { j = h * prop3 if math.Abs(j) < prev { i = beta3 - j if i >= 0 && i <= 1.0 { if prev <= alpha3 { return beta3 } if math.Abs(prop3) <= alpha3 { return beta3 } if i != 0 && i != 1.0 { break } } } h /= 3.0 } if i == beta3 { return beta3 } beta3, prop4 = i, prop3 } return beta3 } // calcBetainv is an implementation for the quantile of the beta // distribution. func calcBetainv(probability, alpha, beta, lower, upper float64) float64 { minCumulative, maxCumulative := 1.0e-300, 3.0e-308 lowerBound, upperBound := maxCumulative, 1.0-2.22e-16 needSwap := false var alpha1, alpha2, beta1, beta2, beta3, prob1, x, y float64 if probability <= 0.5 { prob1, alpha1, beta1 = probability, alpha, beta } else { prob1, alpha1, beta1, needSwap = 1.0-probability, beta, alpha, true } logbeta := logBeta(alpha, beta) prob2 := math.Sqrt(-math.Log(prob1 * prob1)) prob3 := prob2 - (prob2*0.27061+2.3075)/(prob2*(prob2*0.04481+0.99229)+1) if alpha1 > 1 && beta1 > 1 { alpha2, beta2, prob2 = 1/(alpha1+alpha1-1), 1/(beta1+beta1-1), (prob3*prob3-3)/6 x = 2 / (alpha2 + beta2) y = prob3*math.Sqrt(x+prob2)/x - (beta2-alpha2)*(prob2+5/6.0-2/(x*3)) beta3 = alpha1 / (alpha1 + beta1*math.Exp(y+y)) } else { beta2, prob2 = 1/(beta1*9), beta1+beta1 beta2 = prob2 * math.Pow(1-beta2+prob3*math.Sqrt(beta2), 3) if beta2 <= 0 { beta3 = 1 - math.Exp((math.Log((1-prob1)*beta1)+logbeta)/beta1) } else { beta2 = (prob2 + alpha1*4 - 2) / beta2 if beta2 <= 1 { beta3 = math.Exp((logbeta + math.Log(alpha1*prob1)) / alpha1) } else { beta3 = 1 - 2/(beta2+1) } } } beta2, prob2 = 1-beta1, 1-alpha1 if beta3 < lowerBound { beta3 = lowerBound } else if beta3 > upperBound { beta3 = upperBound } alpha3 := math.Max(minCumulative, math.Pow(10.0, -13.0-2.5/(alpha1*alpha1)-0.5/(prob1*prob1))) beta3 = betainvProbIterator(alpha1, alpha3, beta1, beta2, beta3, logbeta, lower, maxCumulative, prob1, prob2, upper, needSwap) if needSwap { beta3 = 1.0 - beta3 } return (upper-lower)*beta3 + lower } // betainv is an implementation of the formula functions BETAINV and // BETA.INV. func (fn *formulaFuncs) betainv(name string, argsList *list.List) formulaArg { if argsList.Len() < 3 { return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires at least 3 arguments", name)) } if argsList.Len() > 5 { return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires at most 5 arguments", name)) } probability := argsList.Front().Value.(formulaArg).ToNumber() if probability.Type != ArgNumber { return probability } if probability.Number <= 0 || probability.Number >= 1 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } alpha := argsList.Front().Next().Value.(formulaArg).ToNumber() if alpha.Type != ArgNumber { return alpha } beta := argsList.Front().Next().Next().Value.(formulaArg).ToNumber() if beta.Type != ArgNumber { return beta } if alpha.Number <= 0 || beta.Number <= 0 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } a, b := newNumberFormulaArg(0), newNumberFormulaArg(1) if argsList.Len() > 3 { if a = argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber(); a.Type != ArgNumber { return a } } if argsList.Len() == 5 { if b = argsList.Back().Value.(formulaArg).ToNumber(); b.Type != ArgNumber { return b } } if a.Number == b.Number { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } return newNumberFormulaArg(calcBetainv(probability.Number, alpha.Number, beta.Number, a.Number, b.Number)) } // BETAINV function uses an iterative procedure to calculate the inverse of // the cumulative beta probability density function for a supplied // probability. The syntax of the function is: // // BETAINV(probability,alpha,beta,[A],[B]) // func (fn *formulaFuncs) BETAINV(argsList *list.List) formulaArg { return fn.betainv("BETAINV", argsList) } // BETAdotINV function uses an iterative procedure to calculate the inverse of // the cumulative beta probability density function for a supplied // probability. The syntax of the function is: // // BETA.INV(probability,alpha,beta,[A],[B]) // func (fn *formulaFuncs) BETAdotINV(argsList *list.List) formulaArg { return fn.betainv("BETA.INV", argsList) } // incompleteGamma is an implementation of the incomplete gamma function. func incompleteGamma(a, x float64) float64 { max := 32 summer := 0.0 for n := 0; n <= max; n++ { divisor := a for i := 1; i <= n; i++ { divisor *= (a + float64(i)) } summer += math.Pow(x, float64(n)) / divisor } return math.Pow(x, a) * math.Exp(0-x) * summer } // binomCoeff implement binomial coefficient calcuation. func binomCoeff(n, k float64) float64 { return fact(n) / (fact(k) * fact(n-k)) } // binomdist implement binomial distribution calcuation. func binomdist(x, n, p float64) float64 { return binomCoeff(n, x) * math.Pow(p, x) * math.Pow(1-p, n-x) } // BINOMdotDIST function returns the Binomial Distribution probability for a // given number of successes from a specified number of trials. The syntax of // the function is: // // BINOM.DIST(number_s,trials,probability_s,cumulative) // func (fn *formulaFuncs) BINOMdotDIST(argsList *list.List) formulaArg { if argsList.Len() != 4 { return newErrorFormulaArg(formulaErrorVALUE, "BINOM.DIST requires 4 arguments") } return fn.BINOMDIST(argsList) } // BINOMDIST function returns the Binomial Distribution probability of a // specified number of successes out of a specified number of trials. The // syntax of the function is: // // BINOMDIST(number_s,trials,probability_s,cumulative) // func (fn *formulaFuncs) BINOMDIST(argsList *list.List) formulaArg { if argsList.Len() != 4 { return newErrorFormulaArg(formulaErrorVALUE, "BINOMDIST requires 4 arguments") } var s, trials, probability, cumulative formulaArg if s = argsList.Front().Value.(formulaArg).ToNumber(); s.Type != ArgNumber { return s } if trials = argsList.Front().Next().Value.(formulaArg).ToNumber(); trials.Type != ArgNumber { return trials } if s.Number < 0 || s.Number > trials.Number { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } if probability = argsList.Back().Prev().Value.(formulaArg).ToNumber(); probability.Type != ArgNumber { return probability } if probability.Number < 0 || probability.Number > 1 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } if cumulative = argsList.Back().Value.(formulaArg).ToBool(); cumulative.Type == ArgError { return cumulative } if cumulative.Number == 1 { bm := 0.0 for i := 0; i <= int(s.Number); i++ { bm += binomdist(float64(i), trials.Number, probability.Number) } return newNumberFormulaArg(bm) } return newNumberFormulaArg(binomdist(s.Number, trials.Number, probability.Number)) } // BINOMdotDISTdotRANGE function returns the Binomial Distribution probability // for the number of successes from a specified number of trials falling into // a specified range. // // BINOM.DIST.RANGE(trials,probability_s,number_s,[number_s2]) // func (fn *formulaFuncs) BINOMdotDISTdotRANGE(argsList *list.List) formulaArg { if argsList.Len() < 3 { return newErrorFormulaArg(formulaErrorVALUE, "BINOM.DIST.RANGE requires at least 3 arguments") } if argsList.Len() > 4 { return newErrorFormulaArg(formulaErrorVALUE, "BINOM.DIST.RANGE requires at most 4 arguments") } trials := argsList.Front().Value.(formulaArg).ToNumber() if trials.Type != ArgNumber { return trials } probability := argsList.Front().Next().Value.(formulaArg).ToNumber() if probability.Type != ArgNumber { return probability } if probability.Number < 0 || probability.Number > 1 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } num1 := argsList.Front().Next().Next().Value.(formulaArg).ToNumber() if num1.Type != ArgNumber { return num1 } if num1.Number < 0 || num1.Number > trials.Number { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } num2 := num1 if argsList.Len() > 3 { if num2 = argsList.Back().Value.(formulaArg).ToNumber(); num2.Type != ArgNumber { return num2 } } if num2.Number < 0 || num2.Number > trials.Number { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } sumn := 0.0 for i := num1.Number; i <= num2.Number; i++ { sumn += binomdist(i, trials.Number, probability.Number) } return newNumberFormulaArg(sumn) } // binominv implement inverse of the binomial distribution calcuation. func binominv(n, p, alpha float64) float64 { q, i, sum, max := 1-p, 0.0, 0.0, 0.0 n = math.Floor(n) if q > p { factor := math.Pow(q, n) sum = factor for i = 0; i < n && sum < alpha; i++ { factor *= (n - i) / (i + 1) * p / q sum += factor } return i } factor := math.Pow(p, n) sum, max = 1-factor, n for i = 0; i < max && sum >= alpha; i++ { factor *= (n - i) / (i + 1) * q / p sum -= factor } return n - i } // BINOMdotINV function returns the inverse of the Cumulative Binomial // Distribution. The syntax of the function is: // // BINOM.INV(trials,probability_s,alpha) // func (fn *formulaFuncs) BINOMdotINV(argsList *list.List) formulaArg { if argsList.Len() != 3 { return newErrorFormulaArg(formulaErrorVALUE, "BINOM.INV requires 3 numeric arguments") } trials := argsList.Front().Value.(formulaArg).ToNumber() if trials.Type != ArgNumber { return trials } if trials.Number < 0 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } probability := argsList.Front().Next().Value.(formulaArg).ToNumber() if probability.Type != ArgNumber { return probability } if probability.Number <= 0 || probability.Number >= 1 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } alpha := argsList.Back().Value.(formulaArg).ToNumber() if alpha.Type != ArgNumber { return alpha } if alpha.Number <= 0 || alpha.Number >= 1 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } return newNumberFormulaArg(binominv(trials.Number, probability.Number, alpha.Number)) } // CHIDIST function calculates the right-tailed probability of the chi-square // distribution. The syntax of the function is: // // CHIDIST(x,degrees_freedom) // func (fn *formulaFuncs) CHIDIST(argsList *list.List) formulaArg { if argsList.Len() != 2 { return newErrorFormulaArg(formulaErrorVALUE, "CHIDIST requires 2 numeric arguments") } x := argsList.Front().Value.(formulaArg).ToNumber() if x.Type != ArgNumber { return x } degress := argsList.Back().Value.(formulaArg).ToNumber() if degress.Type != ArgNumber { return degress } return newNumberFormulaArg(1 - (incompleteGamma(degress.Number/2, x.Number/2) / math.Gamma(degress.Number/2))) } // CHIINV function calculates the inverse of the right-tailed probability of // the Chi-Square Distribution. The syntax of the function is: // // CHIINV(probability,degrees_freedom) // func (fn *formulaFuncs) CHIINV(argsList *list.List) formulaArg { if argsList.Len() != 2 { return newErrorFormulaArg(formulaErrorVALUE, "CHIINV requires 2 numeric arguments") } probability := argsList.Front().Value.(formulaArg).ToNumber() if probability.Type != ArgNumber { return probability } if probability.Number <= 0 || probability.Number > 1 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } degress := argsList.Back().Value.(formulaArg).ToNumber() if degress.Type != ArgNumber { return degress } if degress.Number < 1 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } return newNumberFormulaArg(gammainv(1-probability.Number, 0.5*degress.Number, 2.0)) } // confidence is an implementation of the formula functions CONFIDENCE and // CONFIDENCE.NORM. func (fn *formulaFuncs) confidence(name string, argsList *list.List) formulaArg { if argsList.Len() != 3 { return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires 3 numeric arguments", name)) } alpha := argsList.Front().Value.(formulaArg).ToNumber() if alpha.Type != ArgNumber { return alpha } if alpha.Number <= 0 || alpha.Number >= 1 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } stdDev := argsList.Front().Next().Value.(formulaArg).ToNumber() if stdDev.Type != ArgNumber { return stdDev } if stdDev.Number <= 0 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } size := argsList.Back().Value.(formulaArg).ToNumber() if size.Type != ArgNumber { return size } if size.Number < 1 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } args := list.New() args.Init() args.PushBack(newNumberFormulaArg(alpha.Number / 2)) args.PushBack(newNumberFormulaArg(0)) args.PushBack(newNumberFormulaArg(1)) return newNumberFormulaArg(-fn.NORMINV(args).Number * (stdDev.Number / math.Sqrt(size.Number))) } // CONFIDENCE function uses a Normal Distribution to calculate a confidence // value that can be used to construct the Confidence Interval for a // population mean, for a supplied probablity and sample size. It is assumed // that the standard deviation of the population is known. The syntax of the // function is: // // CONFIDENCE(alpha,standard_dev,size) // func (fn *formulaFuncs) CONFIDENCE(argsList *list.List) formulaArg { return fn.confidence("CONFIDENCE", argsList) } // CONFIDENCEdotNORM function uses a Normal Distribution to calculate a // confidence value that can be used to construct the confidence interval for // a population mean, for a supplied probablity and sample size. It is // assumed that the standard deviation of the population is known. The syntax // of the Confidence.Norm function is: // // CONFIDENCE.NORM(alpha,standard_dev,size) // func (fn *formulaFuncs) CONFIDENCEdotNORM(argsList *list.List) formulaArg { return fn.confidence("CONFIDENCE.NORM", argsList) } // COVAR function calculates the covariance of two supplied sets of values. The // syntax of the function is: // // COVAR(array1,array2) // func (fn *formulaFuncs) COVAR(argsList *list.List) formulaArg { if argsList.Len() != 2 { return newErrorFormulaArg(formulaErrorVALUE, "COVAR requires 2 arguments") } array1 := argsList.Front().Value.(formulaArg) array2 := argsList.Back().Value.(formulaArg) left, right := array1.ToList(), array2.ToList() n := len(left) if n != len(right) { return newErrorFormulaArg(formulaErrorNA, formulaErrorNA) } l1, l2 := list.New(), list.New() l1.PushBack(array1) l2.PushBack(array2) result, skip := 0.0, 0 mean1, mean2 := fn.AVERAGE(l1), fn.AVERAGE(l2) for i := 0; i < n; i++ { arg1 := left[i].ToNumber() arg2 := right[i].ToNumber() if arg1.Type == ArgError || arg2.Type == ArgError { skip++ continue } result += (arg1.Number - mean1.Number) * (arg2.Number - mean2.Number) } return newNumberFormulaArg(result / float64(n-skip)) } // COVARIANCEdotP function calculates the population covariance of two supplied // sets of values. The syntax of the function is: // // COVARIANCE.P(array1,array2) // func (fn *formulaFuncs) COVARIANCEdotP(argsList *list.List) formulaArg { if argsList.Len() != 2 { return newErrorFormulaArg(formulaErrorVALUE, "COVARIANCE.P requires 2 arguments") } return fn.COVAR(argsList) } // calcStringCountSum is part of the implementation countSum. func calcStringCountSum(countText bool, count, sum float64, num, arg formulaArg) (float64, float64) { if countText && num.Type == ArgError && arg.String != "" { count++ } if num.Type == ArgNumber { sum += num.Number count++ } return count, sum } // countSum get count and sum for a formula arguments array. func (fn *formulaFuncs) countSum(countText bool, args []formulaArg) (count, sum float64) { for _, arg := range args { switch arg.Type { case ArgNumber: if countText || !arg.Boolean { sum += arg.Number count++ } case ArgString: if !countText && (arg.Value() == "TRUE" || arg.Value() == "FALSE") { continue } else if countText && (arg.Value() == "TRUE" || arg.Value() == "FALSE") { num := arg.ToBool() if num.Type == ArgNumber { count++ sum += num.Number continue } } num := arg.ToNumber() count, sum = calcStringCountSum(countText, count, sum, num, arg) case ArgList, ArgMatrix: cnt, summary := fn.countSum(countText, arg.ToList()) sum += summary count += cnt } } return } // CORREL function calculates the Pearson Product-Moment Correlation // Coefficient for two sets of values. The syntax of the function is: // // CORREL(array1,array2) // func (fn *formulaFuncs) CORREL(argsList *list.List) formulaArg { if argsList.Len() != 2 { return newErrorFormulaArg(formulaErrorVALUE, "CORREL requires 2 arguments") } array1 := argsList.Front().Value.(formulaArg) array2 := argsList.Back().Value.(formulaArg) left, right := array1.ToList(), array2.ToList() n := len(left) if n != len(right) { return newErrorFormulaArg(formulaErrorNA, formulaErrorNA) } l1, l2, l3 := list.New(), list.New(), list.New() for i := 0; i < n; i++ { if lhs, rhs := left[i].ToNumber(), right[i].ToNumber(); lhs.Number != 0 && rhs.Number != 0 { l1.PushBack(lhs) l2.PushBack(rhs) } } stdev1, stdev2 := fn.STDEV(l1), fn.STDEV(l2) if stdev1.Number == 0 || stdev2.Number == 0 { return newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV) } mean1, mean2, skip := fn.AVERAGE(l1), fn.AVERAGE(l2), 0 for i := 0; i < n; i++ { lhs, rhs := left[i].ToNumber(), right[i].ToNumber() if lhs.Number == 0 || rhs.Number == 0 { skip++ continue } l3.PushBack(newNumberFormulaArg((lhs.Number - mean1.Number) * (rhs.Number - mean2.Number))) } return newNumberFormulaArg(fn.SUM(l3).Number / float64(n-skip-1) / stdev1.Number / stdev2.Number) } // COUNT function returns the count of numeric values in a supplied set of // cells or values. This count includes both numbers and dates. The syntax of // the function is: // // COUNT(value1,[value2],...) // func (fn *formulaFuncs) COUNT(argsList *list.List) formulaArg { var count int for token := argsList.Front(); token != nil; token = token.Next() { arg := token.Value.(formulaArg) switch arg.Type { case ArgString: if arg.ToNumber().Type != ArgError { count++ } case ArgNumber: count++ case ArgMatrix: for _, row := range arg.Matrix { for _, value := range row { if value.ToNumber().Type != ArgError { count++ } } } } } return newNumberFormulaArg(float64(count)) } // 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) formulaArg { 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 ArgNumber: count++ case ArgMatrix: for _, row := range arg.ToList() { switch row.Type { case ArgString: if row.String != "" { count++ } case ArgNumber: count++ } } } } return newNumberFormulaArg(float64(count)) } // COUNTBLANK function returns the number of blank cells in a supplied range. // The syntax of the function is: // // COUNTBLANK(range) // func (fn *formulaFuncs) COUNTBLANK(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "COUNTBLANK requires 1 argument") } var count float64 for _, cell := range argsList.Front().Value.(formulaArg).ToList() { if cell.Value() == "" { count++ } } return newNumberFormulaArg(count) } // COUNTIF function returns the number of cells within a supplied range, that // satisfy a given criteria. The syntax of the function is: // // COUNTIF(range,criteria) // func (fn *formulaFuncs) COUNTIF(argsList *list.List) formulaArg { if argsList.Len() != 2 { return newErrorFormulaArg(formulaErrorVALUE, "COUNTIF requires 2 arguments") } var ( criteria = formulaCriteriaParser(argsList.Front().Next().Value.(formulaArg).String) count float64 ) for _, cell := range argsList.Front().Value.(formulaArg).ToList() { if ok, _ := formulaCriteriaEval(cell.Value(), criteria); ok { count++ } } return newNumberFormulaArg(count) } // formulaIfsMatch function returns cells reference array which match criteria. func formulaIfsMatch(args []formulaArg) (cellRefs []cellRef) { for i := 0; i < len(args)-1; i += 2 { match := []cellRef{} matrix, criteria := args[i].Matrix, formulaCriteriaParser(args[i+1].Value()) if i == 0 { for rowIdx, row := range matrix { for colIdx, col := range row { if ok, _ := formulaCriteriaEval(col.Value(), criteria); ok { match = append(match, cellRef{Col: colIdx, Row: rowIdx}) } } } } else { for _, ref := range cellRefs { value := matrix[ref.Row][ref.Col] if ok, _ := formulaCriteriaEval(value.Value(), criteria); ok { match = append(match, ref) } } } if len(match) == 0 { return } cellRefs = match[:] } return } // COUNTIFS function returns the number of rows within a table, that satisfy a // set of given criteria. The syntax of the function is: // // COUNTIFS(criteria_range1,criteria1,[criteria_range2,criteria2],...) // func (fn *formulaFuncs) COUNTIFS(argsList *list.List) formulaArg { if argsList.Len() < 2 { return newErrorFormulaArg(formulaErrorVALUE, "COUNTIFS requires at least 2 arguments") } if argsList.Len()%2 != 0 { return newErrorFormulaArg(formulaErrorNA, formulaErrorNA) } args := []formulaArg{} for arg := argsList.Front(); arg != nil; arg = arg.Next() { args = append(args, arg.Value.(formulaArg)) } return newNumberFormulaArg(float64(len(formulaIfsMatch(args)))) } // CRITBINOM function returns the inverse of the Cumulative Binomial // Distribution. I.e. for a specific number of independent trials, the // function returns the smallest value (number of successes) for which the // cumulative binomial distribution is greater than or equal to a specified // value. The syntax of the function is: // // CRITBINOM(trials,probability_s,alpha) // func (fn *formulaFuncs) CRITBINOM(argsList *list.List) formulaArg { if argsList.Len() != 3 { return newErrorFormulaArg(formulaErrorVALUE, "CRITBINOM requires 3 numeric arguments") } return fn.BINOMdotINV(argsList) } // DEVSQ function calculates the sum of the squared deviations from the sample // mean. The syntax of the function is: // // DEVSQ(number1,[number2],...) // func (fn *formulaFuncs) DEVSQ(argsList *list.List) formulaArg { if argsList.Len() < 1 { return newErrorFormulaArg(formulaErrorVALUE, "DEVSQ requires at least 1 numeric argument") } avg, count, result := fn.AVERAGE(argsList), -1, 0.0 for arg := argsList.Front(); arg != nil; arg = arg.Next() { for _, number := range arg.Value.(formulaArg).ToList() { num := number.ToNumber() if num.Type != ArgNumber { continue } count++ if count == 0 { result = math.Pow(num.Number-avg.Number, 2) continue } result += math.Pow(num.Number-avg.Number, 2) } } if count == -1 { return newErrorFormulaArg(formulaErrorNA, formulaErrorNA) } return newNumberFormulaArg(result) } // FISHER function calculates the Fisher Transformation for a supplied value. // The syntax of the function is: // // FISHER(x) // func (fn *formulaFuncs) FISHER(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "FISHER requires 1 numeric argument") } token := argsList.Front().Value.(formulaArg) switch token.Type { case ArgString: arg := token.ToNumber() if arg.Type == ArgNumber { if arg.Number <= -1 || arg.Number >= 1 { return newErrorFormulaArg(formulaErrorNA, formulaErrorNA) } return newNumberFormulaArg(0.5 * math.Log((1+arg.Number)/(1-arg.Number))) } case ArgNumber: if token.Number <= -1 || token.Number >= 1 { return newErrorFormulaArg(formulaErrorNA, formulaErrorNA) } return newNumberFormulaArg(0.5 * math.Log((1+token.Number)/(1-token.Number))) } return newErrorFormulaArg(formulaErrorVALUE, "FISHER requires 1 numeric argument") } // FISHERINV function calculates the inverse of the Fisher Transformation and // returns a value between -1 and +1. The syntax of the function is: // // FISHERINV(y) // func (fn *formulaFuncs) FISHERINV(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "FISHERINV requires 1 numeric argument") } token := argsList.Front().Value.(formulaArg) switch token.Type { case ArgString: arg := token.ToNumber() if arg.Type == ArgNumber { return newNumberFormulaArg((math.Exp(2*arg.Number) - 1) / (math.Exp(2*arg.Number) + 1)) } case ArgNumber: return newNumberFormulaArg((math.Exp(2*token.Number) - 1) / (math.Exp(2*token.Number) + 1)) } return newErrorFormulaArg(formulaErrorVALUE, "FISHERINV requires 1 numeric argument") } // GAMMA function returns the value of the Gamma Function, Γ(n), for a // specified number, n. The syntax of the function is: // // GAMMA(number) // func (fn *formulaFuncs) GAMMA(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "GAMMA requires 1 numeric argument") } token := argsList.Front().Value.(formulaArg) switch token.Type { case ArgString: arg := token.ToNumber() if arg.Type == ArgNumber { if arg.Number <= 0 { return newErrorFormulaArg(formulaErrorNA, formulaErrorNA) } return newNumberFormulaArg(math.Gamma(arg.Number)) } case ArgNumber: if token.Number <= 0 { return newErrorFormulaArg(formulaErrorNA, formulaErrorNA) } return newNumberFormulaArg(math.Gamma(token.Number)) } return newErrorFormulaArg(formulaErrorVALUE, "GAMMA requires 1 numeric argument") } // GAMMAdotDIST function returns the Gamma Distribution, which is frequently // used to provide probabilities for values that may have a skewed // distribution, such as queuing analysis. // // GAMMA.DIST(x,alpha,beta,cumulative) // func (fn *formulaFuncs) GAMMAdotDIST(argsList *list.List) formulaArg { if argsList.Len() != 4 { return newErrorFormulaArg(formulaErrorVALUE, "GAMMA.DIST requires 4 arguments") } return fn.GAMMADIST(argsList) } // GAMMADIST function returns the Gamma Distribution, which is frequently used // to provide probabilities for values that may have a skewed distribution, // such as queuing analysis. // // GAMMADIST(x,alpha,beta,cumulative) // func (fn *formulaFuncs) GAMMADIST(argsList *list.List) formulaArg { if argsList.Len() != 4 { return newErrorFormulaArg(formulaErrorVALUE, "GAMMADIST requires 4 arguments") } var x, alpha, beta, cumulative formulaArg if x = argsList.Front().Value.(formulaArg).ToNumber(); x.Type != ArgNumber { return x } if x.Number < 0 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } if alpha = argsList.Front().Next().Value.(formulaArg).ToNumber(); alpha.Type != ArgNumber { return alpha } if beta = argsList.Back().Prev().Value.(formulaArg).ToNumber(); beta.Type != ArgNumber { return beta } if alpha.Number <= 0 || beta.Number <= 0 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } if cumulative = argsList.Back().Value.(formulaArg).ToBool(); cumulative.Type == ArgError { return cumulative } if cumulative.Number == 1 { return newNumberFormulaArg(incompleteGamma(alpha.Number, x.Number/beta.Number) / math.Gamma(alpha.Number)) } return newNumberFormulaArg((1 / (math.Pow(beta.Number, alpha.Number) * math.Gamma(alpha.Number))) * math.Pow(x.Number, (alpha.Number-1)) * math.Exp(0-(x.Number/beta.Number))) } // gammainv returns the inverse of the Gamma distribution for the specified // value. func gammainv(probability, alpha, beta float64) float64 { xLo, xHi := 0.0, alpha*beta*5 dx, x, xNew, result := 1024.0, 1.0, 1.0, 0.0 for i := 0; math.Abs(dx) > 8.88e-016 && i <= 256; i++ { result = incompleteGamma(alpha, x/beta) / math.Gamma(alpha) error := result - probability if error == 0 { dx = 0 } else if error < 0 { xLo = x } else { xHi = x } pdf := (1 / (math.Pow(beta, alpha) * math.Gamma(alpha))) * math.Pow(x, (alpha-1)) * math.Exp(0-(x/beta)) if pdf != 0 { dx = error / pdf xNew = x - dx } if xNew < xLo || xNew > xHi || pdf == 0 { xNew = (xLo + xHi) / 2 dx = xNew - x } x = xNew } return x } // GAMMAdotINV function returns the inverse of the Gamma Cumulative // Distribution. The syntax of the function is: // // GAMMA.INV(probability,alpha,beta) // func (fn *formulaFuncs) GAMMAdotINV(argsList *list.List) formulaArg { if argsList.Len() != 3 { return newErrorFormulaArg(formulaErrorVALUE, "GAMMA.INV requires 3 arguments") } return fn.GAMMAINV(argsList) } // GAMMAINV function returns the inverse of the Gamma Cumulative Distribution. // The syntax of the function is: // // GAMMAINV(probability,alpha,beta) // func (fn *formulaFuncs) GAMMAINV(argsList *list.List) formulaArg { if argsList.Len() != 3 { return newErrorFormulaArg(formulaErrorVALUE, "GAMMAINV requires 3 arguments") } var probability, alpha, beta formulaArg if probability = argsList.Front().Value.(formulaArg).ToNumber(); probability.Type != ArgNumber { return probability } if probability.Number < 0 || probability.Number >= 1 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } if alpha = argsList.Front().Next().Value.(formulaArg).ToNumber(); alpha.Type != ArgNumber { return alpha } if beta = argsList.Back().Value.(formulaArg).ToNumber(); beta.Type != ArgNumber { return beta } if alpha.Number <= 0 || beta.Number <= 0 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } return newNumberFormulaArg(gammainv(probability.Number, alpha.Number, beta.Number)) } // GAMMALN function returns the natural logarithm of the Gamma Function, Γ // (n). The syntax of the function is: // // GAMMALN(x) // func (fn *formulaFuncs) GAMMALN(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "GAMMALN requires 1 numeric argument") } token := argsList.Front().Value.(formulaArg) switch token.Type { case ArgString: arg := token.ToNumber() if arg.Type == ArgNumber { if arg.Number <= 0 { return newErrorFormulaArg(formulaErrorNA, formulaErrorNA) } return newNumberFormulaArg(math.Log(math.Gamma(arg.Number))) } case ArgNumber: if token.Number <= 0 { return newErrorFormulaArg(formulaErrorNA, formulaErrorNA) } return newNumberFormulaArg(math.Log(math.Gamma(token.Number))) } return newErrorFormulaArg(formulaErrorVALUE, "GAMMALN requires 1 numeric argument") } // GAUSS function returns the probability that a member of a standard normal // population will fall between the mean and a specified number of standard // deviations from the mean. The syntax of the function is: // // GAUSS(z) // func (fn *formulaFuncs) GAUSS(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "GAUSS requires 1 numeric argument") } args := list.New().Init() args.PushBack(argsList.Front().Value.(formulaArg)) args.PushBack(formulaArg{Type: ArgNumber, Number: 0}) args.PushBack(formulaArg{Type: ArgNumber, Number: 1}) args.PushBack(newBoolFormulaArg(true)) normdist := fn.NORMDIST(args) if normdist.Type != ArgNumber { return normdist } return newNumberFormulaArg(normdist.Number - 0.5) } // GEOMEAN function calculates the geometric mean of a supplied set of values. // The syntax of the function is: // // GEOMEAN(number1,[number2],...) // func (fn *formulaFuncs) GEOMEAN(argsList *list.List) formulaArg { if argsList.Len() < 1 { return newErrorFormulaArg(formulaErrorVALUE, "GEOMEAN requires at least 1 numeric argument") } product := fn.PRODUCT(argsList) if product.Type != ArgNumber { return product } count := fn.COUNT(argsList) min := fn.MIN(argsList) if product.Number > 0 && min.Number > 0 { return newNumberFormulaArg(math.Pow(product.Number, (1 / count.Number))) } return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } // HARMEAN function calculates the harmonic mean of a supplied set of values. // The syntax of the function is: // // HARMEAN(number1,[number2],...) // func (fn *formulaFuncs) HARMEAN(argsList *list.List) formulaArg { if argsList.Len() < 1 { return newErrorFormulaArg(formulaErrorVALUE, "HARMEAN requires at least 1 argument") } if min := fn.MIN(argsList); min.Number < 0 { return newErrorFormulaArg(formulaErrorNA, formulaErrorNA) } number, val, cnt := 0.0, 0.0, 0.0 for token := argsList.Front(); token != nil; token = token.Next() { arg := token.Value.(formulaArg) switch arg.Type { case ArgString: num := arg.ToNumber() if num.Type != ArgNumber { continue } number = num.Number case ArgNumber: number = arg.Number } if number <= 0 { return newErrorFormulaArg(formulaErrorNA, formulaErrorNA) } val += (1 / number) cnt++ } return newNumberFormulaArg(1 / (val / cnt)) } // KURT function calculates the kurtosis of a supplied set of values. The // syntax of the function is: // // KURT(number1,[number2],...) // func (fn *formulaFuncs) KURT(argsList *list.List) formulaArg { if argsList.Len() < 1 { return newErrorFormulaArg(formulaErrorVALUE, "KURT requires at least 1 argument") } mean, stdev := fn.AVERAGE(argsList), fn.STDEV(argsList) if stdev.Number > 0 { count, summer := 0.0, 0.0 for arg := argsList.Front(); arg != nil; arg = arg.Next() { token := arg.Value.(formulaArg) switch token.Type { case ArgString, ArgNumber: num := token.ToNumber() if num.Type == ArgError { continue } summer += math.Pow((num.Number-mean.Number)/stdev.Number, 4) count++ case ArgList, ArgMatrix: for _, row := range token.ToList() { if row.Type == ArgNumber || row.Type == ArgString { num := row.ToNumber() if num.Type == ArgError { continue } summer += math.Pow((num.Number-mean.Number)/stdev.Number, 4) count++ } } } } if count > 3 { return newNumberFormulaArg(summer*(count*(count+1)/((count-1)*(count-2)*(count-3))) - (3 * math.Pow(count-1, 2) / ((count - 2) * (count - 3)))) } } return newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV) } // EXPONdotDIST function returns the value of the exponential distribution for // a give value of x. The user can specify whether the probability density // function or the cumulative distribution function is used. The syntax of the // Expondist function is: // // EXPON.DIST(x,lambda,cumulative) // func (fn *formulaFuncs) EXPONdotDIST(argsList *list.List) formulaArg { if argsList.Len() != 3 { return newErrorFormulaArg(formulaErrorVALUE, "EXPON.DIST requires 3 arguments") } return fn.EXPONDIST(argsList) } // EXPONDIST function returns the value of the exponential distribution for a // give value of x. The user can specify whether the probability density // function or the cumulative distribution function is used. The syntax of the // Expondist function is: // // EXPONDIST(x,lambda,cumulative) // func (fn *formulaFuncs) EXPONDIST(argsList *list.List) formulaArg { if argsList.Len() != 3 { return newErrorFormulaArg(formulaErrorVALUE, "EXPONDIST requires 3 arguments") } var x, lambda, cumulative formulaArg if x = argsList.Front().Value.(formulaArg).ToNumber(); x.Type != ArgNumber { return x } if lambda = argsList.Front().Next().Value.(formulaArg).ToNumber(); lambda.Type != ArgNumber { return lambda } if cumulative = argsList.Back().Value.(formulaArg).ToBool(); cumulative.Type == ArgError { return cumulative } if x.Number < 0 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } if lambda.Number <= 0 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } if cumulative.Number == 1 { return newNumberFormulaArg(1 - math.Exp(-lambda.Number*x.Number)) } return newNumberFormulaArg(lambda.Number * math.Exp(-lambda.Number*x.Number)) } // prepareFinvArgs checking and prepare arguments for the formula function // F.INV, F.INV.RT and FINV. func (fn *formulaFuncs) prepareFinvArgs(name string, argsList *list.List) formulaArg { if argsList.Len() != 3 { return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires 3 arguments", name)) } var probability, d1, d2 formulaArg if probability = argsList.Front().Value.(formulaArg).ToNumber(); probability.Type != ArgNumber { return probability } if d1 = argsList.Front().Next().Value.(formulaArg).ToNumber(); d1.Type != ArgNumber { return d1 } if d2 = argsList.Back().Value.(formulaArg).ToNumber(); d2.Type != ArgNumber { return d2 } if probability.Number <= 0 || probability.Number > 1 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } if d1.Number < 1 || d1.Number >= math.Pow10(10) { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } if d2.Number < 1 || d2.Number >= math.Pow10(10) { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } return newListFormulaArg([]formulaArg{probability, d1, d2}) } // FdotINV function calculates the inverse of the Cumulative F Distribution // for a supplied probability. The syntax of the F.Inv function is: // // F.INV(probability,deg_freedom1,deg_freedom2) // func (fn *formulaFuncs) FdotINV(argsList *list.List) formulaArg { args := fn.prepareFinvArgs("F.INV", argsList) if args.Type != ArgList { return args } probability, d1, d2 := args.List[0], args.List[1], args.List[2] return newNumberFormulaArg((1/calcBetainv(1-probability.Number, d2.Number/2, d1.Number/2, 0, 1) - 1) * (d2.Number / d1.Number)) } // FdotINVdotRT function calculates the inverse of the (right-tailed) F // Probability Distribution for a supplied probability. The syntax of the // function is: // // F.INV.RT(probability,deg_freedom1,deg_freedom2) // func (fn *formulaFuncs) FdotINVdotRT(argsList *list.List) formulaArg { args := fn.prepareFinvArgs("F.INV.RT", argsList) if args.Type != ArgList { return args } probability, d1, d2 := args.List[0], args.List[1], args.List[2] return newNumberFormulaArg((1/calcBetainv(1-(1-probability.Number), d2.Number/2, d1.Number/2, 0, 1) - 1) * (d2.Number / d1.Number)) } // FINV function calculates the inverse of the (right-tailed) F Probability // Distribution for a supplied probability. The syntax of the function is: // // FINV(probability,deg_freedom1,deg_freedom2) // func (fn *formulaFuncs) FINV(argsList *list.List) formulaArg { args := fn.prepareFinvArgs("FINV", argsList) if args.Type != ArgList { return args } probability, d1, d2 := args.List[0], args.List[1], args.List[2] return newNumberFormulaArg((1/calcBetainv(1-(1-probability.Number), d2.Number/2, d1.Number/2, 0, 1) - 1) * (d2.Number / d1.Number)) } // LOGINV function calculates the inverse of the Cumulative Log-Normal // Distribution Function of x, for a supplied probability. The syntax of the // function is: // // LOGINV(probability,mean,standard_dev) // func (fn *formulaFuncs) LOGINV(argsList *list.List) formulaArg { if argsList.Len() != 3 { return newErrorFormulaArg(formulaErrorVALUE, "LOGINV requires 3 arguments") } var probability, mean, stdDev formulaArg if probability = argsList.Front().Value.(formulaArg).ToNumber(); probability.Type != ArgNumber { return probability } if mean = argsList.Front().Next().Value.(formulaArg).ToNumber(); mean.Type != ArgNumber { return mean } if stdDev = argsList.Back().Value.(formulaArg).ToNumber(); stdDev.Type != ArgNumber { return stdDev } if probability.Number <= 0 || probability.Number >= 1 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } if stdDev.Number <= 0 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } args := list.New() args.PushBack(probability) args.PushBack(newNumberFormulaArg(0)) args.PushBack(newNumberFormulaArg(1)) norminv := fn.NORMINV(args) return newNumberFormulaArg(math.Exp(mean.Number + stdDev.Number*norminv.Number)) } // LOGNORMdotINV function calculates the inverse of the Cumulative Log-Normal // Distribution Function of x, for a supplied probability. The syntax of the // function is: // // LOGNORM.INV(probability,mean,standard_dev) // func (fn *formulaFuncs) LOGNORMdotINV(argsList *list.List) formulaArg { if argsList.Len() != 3 { return newErrorFormulaArg(formulaErrorVALUE, "LOGNORM.INV requires 3 arguments") } return fn.LOGINV(argsList) } // LOGNORMdotDIST function calculates the Log-Normal Probability Density // Function or the Cumulative Log-Normal Distribution Function for a supplied // value of x. The syntax of the function is: // // LOGNORM.DIST(x,mean,standard_dev,cumulative) // func (fn *formulaFuncs) LOGNORMdotDIST(argsList *list.List) formulaArg { if argsList.Len() != 4 { return newErrorFormulaArg(formulaErrorVALUE, "LOGNORM.DIST requires 4 arguments") } var x, mean, stdDev, cumulative formulaArg if x = argsList.Front().Value.(formulaArg).ToNumber(); x.Type != ArgNumber { return x } if mean = argsList.Front().Next().Value.(formulaArg).ToNumber(); mean.Type != ArgNumber { return mean } if stdDev = argsList.Back().Prev().Value.(formulaArg).ToNumber(); stdDev.Type != ArgNumber { return stdDev } if cumulative = argsList.Back().Value.(formulaArg).ToBool(); cumulative.Type == ArgError { return cumulative } if x.Number <= 0 || stdDev.Number <= 0 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } if cumulative.Number == 1 { args := list.New() args.PushBack(newNumberFormulaArg((math.Log(x.Number) - mean.Number) / stdDev.Number)) args.PushBack(newNumberFormulaArg(0)) args.PushBack(newNumberFormulaArg(1)) args.PushBack(cumulative) return fn.NORMDIST(args) } return newNumberFormulaArg((1 / (math.Sqrt(2*math.Pi) * stdDev.Number * x.Number)) * math.Exp(0-(math.Pow((math.Log(x.Number)-mean.Number), 2)/(2*math.Pow(stdDev.Number, 2))))) } // LOGNORMDIST function calculates the Cumulative Log-Normal Distribution // Function at a supplied value of x. The syntax of the function is: // // LOGNORMDIST(x,mean,standard_dev) // func (fn *formulaFuncs) LOGNORMDIST(argsList *list.List) formulaArg { if argsList.Len() != 3 { return newErrorFormulaArg(formulaErrorVALUE, "LOGNORMDIST requires 3 arguments") } var x, mean, stdDev formulaArg if x = argsList.Front().Value.(formulaArg).ToNumber(); x.Type != ArgNumber { return x } if mean = argsList.Front().Next().Value.(formulaArg).ToNumber(); mean.Type != ArgNumber { return mean } if stdDev = argsList.Back().Value.(formulaArg).ToNumber(); stdDev.Type != ArgNumber { return stdDev } if x.Number <= 0 || stdDev.Number <= 0 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } args := list.New() args.PushBack(newNumberFormulaArg((math.Log(x.Number) - mean.Number) / stdDev.Number)) return fn.NORMSDIST(args) } // NORMdotDIST function calculates the Normal Probability Density Function or // the Cumulative Normal Distribution. Function for a supplied set of // parameters. The syntax of the function is: // // NORM.DIST(x,mean,standard_dev,cumulative) // func (fn *formulaFuncs) NORMdotDIST(argsList *list.List) formulaArg { if argsList.Len() != 4 { return newErrorFormulaArg(formulaErrorVALUE, "NORM.DIST requires 4 arguments") } return fn.NORMDIST(argsList) } // NORMDIST function calculates the Normal Probability Density Function or the // Cumulative Normal Distribution. Function for a supplied set of parameters. // The syntax of the function is: // // NORMDIST(x,mean,standard_dev,cumulative) // func (fn *formulaFuncs) NORMDIST(argsList *list.List) formulaArg { if argsList.Len() != 4 { return newErrorFormulaArg(formulaErrorVALUE, "NORMDIST requires 4 arguments") } var x, mean, stdDev, cumulative formulaArg if x = argsList.Front().Value.(formulaArg).ToNumber(); x.Type != ArgNumber { return x } if mean = argsList.Front().Next().Value.(formulaArg).ToNumber(); mean.Type != ArgNumber { return mean } if stdDev = argsList.Back().Prev().Value.(formulaArg).ToNumber(); stdDev.Type != ArgNumber { return stdDev } if cumulative = argsList.Back().Value.(formulaArg).ToBool(); cumulative.Type == ArgError { return cumulative } if stdDev.Number < 0 { return newErrorFormulaArg(formulaErrorNA, formulaErrorNA) } if cumulative.Number == 1 { return newNumberFormulaArg(0.5 * (1 + math.Erf((x.Number-mean.Number)/(stdDev.Number*math.Sqrt(2))))) } return newNumberFormulaArg((1 / (math.Sqrt(2*math.Pi) * stdDev.Number)) * math.Exp(0-(math.Pow(x.Number-mean.Number, 2)/(2*(stdDev.Number*stdDev.Number))))) } // NORMdotINV function calculates the inverse of the Cumulative Normal // Distribution Function for a supplied value of x, and a supplied // distribution mean & standard deviation. The syntax of the function is: // // NORM.INV(probability,mean,standard_dev) // func (fn *formulaFuncs) NORMdotINV(argsList *list.List) formulaArg { if argsList.Len() != 3 { return newErrorFormulaArg(formulaErrorVALUE, "NORM.INV requires 3 arguments") } return fn.NORMINV(argsList) } // NORMINV function calculates the inverse of the Cumulative Normal // Distribution Function for a supplied value of x, and a supplied // distribution mean & standard deviation. The syntax of the function is: // // NORMINV(probability,mean,standard_dev) // func (fn *formulaFuncs) NORMINV(argsList *list.List) formulaArg { if argsList.Len() != 3 { return newErrorFormulaArg(formulaErrorVALUE, "NORMINV requires 3 arguments") } var prob, mean, stdDev formulaArg if prob = argsList.Front().Value.(formulaArg).ToNumber(); prob.Type != ArgNumber { return prob } if mean = argsList.Front().Next().Value.(formulaArg).ToNumber(); mean.Type != ArgNumber { return mean } if stdDev = argsList.Back().Value.(formulaArg).ToNumber(); stdDev.Type != ArgNumber { return stdDev } if prob.Number < 0 || prob.Number > 1 { return newErrorFormulaArg(formulaErrorNA, formulaErrorNA) } if stdDev.Number < 0 { return newErrorFormulaArg(formulaErrorNA, formulaErrorNA) } inv, err := norminv(prob.Number) if err != nil { return newErrorFormulaArg(err.Error(), err.Error()) } return newNumberFormulaArg(inv*stdDev.Number + mean.Number) } // NORMdotSdotDIST function calculates the Standard Normal Cumulative // Distribution Function for a supplied value. The syntax of the function // is: // // NORM.S.DIST(z) // func (fn *formulaFuncs) NORMdotSdotDIST(argsList *list.List) formulaArg { if argsList.Len() != 2 { return newErrorFormulaArg(formulaErrorVALUE, "NORM.S.DIST requires 2 numeric arguments") } args := list.New().Init() args.PushBack(argsList.Front().Value.(formulaArg)) args.PushBack(formulaArg{Type: ArgNumber, Number: 0}) args.PushBack(formulaArg{Type: ArgNumber, Number: 1}) args.PushBack(argsList.Back().Value.(formulaArg)) return fn.NORMDIST(args) } // NORMSDIST function calculates the Standard Normal Cumulative Distribution // Function for a supplied value. The syntax of the function is: // // NORMSDIST(z) // func (fn *formulaFuncs) NORMSDIST(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "NORMSDIST requires 1 numeric argument") } args := list.New().Init() args.PushBack(argsList.Front().Value.(formulaArg)) args.PushBack(formulaArg{Type: ArgNumber, Number: 0}) args.PushBack(formulaArg{Type: ArgNumber, Number: 1}) args.PushBack(formulaArg{Type: ArgNumber, Number: 1, Boolean: true}) return fn.NORMDIST(args) } // NORMSINV function calculates the inverse of the Standard Normal Cumulative // Distribution Function for a supplied probability value. The syntax of the // function is: // // NORMSINV(probability) // func (fn *formulaFuncs) NORMSINV(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "NORMSINV requires 1 numeric argument") } args := list.New().Init() args.PushBack(argsList.Front().Value.(formulaArg)) args.PushBack(formulaArg{Type: ArgNumber, Number: 0}) args.PushBack(formulaArg{Type: ArgNumber, Number: 1}) return fn.NORMINV(args) } // NORMdotSdotINV function calculates the inverse of the Standard Normal // Cumulative Distribution Function for a supplied probability value. The // syntax of the function is: // // NORM.S.INV(probability) // func (fn *formulaFuncs) NORMdotSdotINV(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "NORM.S.INV requires 1 numeric argument") } args := list.New().Init() args.PushBack(argsList.Front().Value.(formulaArg)) args.PushBack(formulaArg{Type: ArgNumber, Number: 0}) args.PushBack(formulaArg{Type: ArgNumber, Number: 1}) return fn.NORMINV(args) } // norminv returns the inverse of the normal cumulative distribution for the // specified value. func norminv(p float64) (float64, error) { a := map[int]float64{ 1: -3.969683028665376e+01, 2: 2.209460984245205e+02, 3: -2.759285104469687e+02, 4: 1.383577518672690e+02, 5: -3.066479806614716e+01, 6: 2.506628277459239e+00, } b := map[int]float64{ 1: -5.447609879822406e+01, 2: 1.615858368580409e+02, 3: -1.556989798598866e+02, 4: 6.680131188771972e+01, 5: -1.328068155288572e+01, } c := map[int]float64{ 1: -7.784894002430293e-03, 2: -3.223964580411365e-01, 3: -2.400758277161838e+00, 4: -2.549732539343734e+00, 5: 4.374664141464968e+00, 6: 2.938163982698783e+00, } d := map[int]float64{ 1: 7.784695709041462e-03, 2: 3.224671290700398e-01, 3: 2.445134137142996e+00, 4: 3.754408661907416e+00, } pLow := 0.02425 // Use lower region approx. below this pHigh := 1 - pLow // Use upper region approx. above this if 0 < p && p < pLow { // Rational approximation for lower region. q := math.Sqrt(-2 * math.Log(p)) return (((((c[1]*q+c[2])*q+c[3])*q+c[4])*q+c[5])*q + c[6]) / ((((d[1]*q+d[2])*q+d[3])*q+d[4])*q + 1), nil } else if pLow <= p && p <= pHigh { // Rational approximation for central region. q := p - 0.5 r := q * q f1 := ((((a[1]*r+a[2])*r+a[3])*r+a[4])*r + a[5]) * r f2 := (b[1]*r + b[2]) * r f3 := ((math.Nextafter(f2, f2)+b[3])*r + b[4]) * r f4 := (math.Nextafter(f3, f3) + b[5]) * r return (math.Nextafter(f1, f1) + a[6]) * q / (math.Nextafter(f4, f4) + 1), nil } else if pHigh < p && p < 1 { // Rational approximation for upper region. q := math.Sqrt(-2 * math.Log(1-p)) return -(((((c[1]*q+c[2])*q+c[3])*q+c[4])*q+c[5])*q + c[6]) / ((((d[1]*q+d[2])*q+d[3])*q+d[4])*q + 1), nil } return 0, errors.New(formulaErrorNUM) } // kth is an implementation of the formula functions LARGE and SMALL. func (fn *formulaFuncs) kth(name string, argsList *list.List) formulaArg { if argsList.Len() != 2 { return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires 2 arguments", name)) } array := argsList.Front().Value.(formulaArg).ToList() argK := argsList.Back().Value.(formulaArg).ToNumber() if argK.Type != ArgNumber { return argK } k := int(argK.Number) if k < 1 { return newErrorFormulaArg(formulaErrorNUM, "k should be > 0") } data := []float64{} for _, arg := range array { if numArg := arg.ToNumber(); numArg.Type == ArgNumber { data = append(data, numArg.Number) } } if len(data) < k { return newErrorFormulaArg(formulaErrorNUM, "k should be <= length of array") } sort.Float64s(data) if name == "LARGE" { return newNumberFormulaArg(data[len(data)-k]) } return newNumberFormulaArg(data[k-1]) } // LARGE function returns the k'th largest value from an array of numeric // values. The syntax of the function is: // // LARGE(array,k) // func (fn *formulaFuncs) LARGE(argsList *list.List) formulaArg { return fn.kth("LARGE", argsList) } // MAX function returns the largest value from a supplied set of numeric // values. The syntax of the function is: // // MAX(number1,[number2],...) // func (fn *formulaFuncs) MAX(argsList *list.List) formulaArg { if argsList.Len() == 0 { return newErrorFormulaArg(formulaErrorVALUE, "MAX requires at least 1 argument") } return fn.max(false, argsList) } // MAXA function returns the largest value from a supplied set of numeric // values, while counting text and the logical value FALSE as the value 0 and // counting the logical value TRUE as the value 1. The syntax of the function // is: // // MAXA(number1,[number2],...) // func (fn *formulaFuncs) MAXA(argsList *list.List) formulaArg { if argsList.Len() == 0 { return newErrorFormulaArg(formulaErrorVALUE, "MAXA requires at least 1 argument") } return fn.max(true, argsList) } // MAXIFS function returns the maximum value from a subset of values that are // specified according to one or more criteria. The syntax of the function // is: // // MAXIFS(max_range,criteria_range1,criteria1,[criteria_range2,criteria2],...) // func (fn *formulaFuncs) MAXIFS(argsList *list.List) formulaArg { if argsList.Len() < 3 { return newErrorFormulaArg(formulaErrorVALUE, "MAXIFS requires at least 3 arguments") } if argsList.Len()%2 != 1 { return newErrorFormulaArg(formulaErrorNA, formulaErrorNA) } max, maxRange, args := -math.MaxFloat64, argsList.Front().Value.(formulaArg).Matrix, []formulaArg{} for arg := argsList.Front().Next(); arg != nil; arg = arg.Next() { args = append(args, arg.Value.(formulaArg)) } for _, ref := range formulaIfsMatch(args) { if num := maxRange[ref.Row][ref.Col].ToNumber(); num.Type == ArgNumber && max < num.Number { max = num.Number } } if max == -math.MaxFloat64 { max = 0 } return newNumberFormulaArg(max) } // calcListMatrixMax is part of the implementation max. func calcListMatrixMax(maxa bool, max float64, arg formulaArg) float64 { for _, row := range arg.ToList() { switch row.Type { case ArgString: if !maxa && (row.Value() == "TRUE" || row.Value() == "FALSE") { continue } else { num := row.ToBool() if num.Type == ArgNumber && num.Number > max { max = num.Number continue } } num := row.ToNumber() if num.Type != ArgError && num.Number > max { max = num.Number } case ArgNumber: if row.Number > max { max = row.Number } } } return max } // max is an implementation of the formula functions MAX and MAXA. func (fn *formulaFuncs) max(maxa bool, argsList *list.List) formulaArg { max := -math.MaxFloat64 for token := argsList.Front(); token != nil; token = token.Next() { arg := token.Value.(formulaArg) switch arg.Type { case ArgString: if !maxa && (arg.Value() == "TRUE" || arg.Value() == "FALSE") { continue } else { num := arg.ToBool() if num.Type == ArgNumber && num.Number > max { max = num.Number continue } } num := arg.ToNumber() if num.Type != ArgError && num.Number > max { max = num.Number } case ArgNumber: if arg.Number > max { max = arg.Number } case ArgList, ArgMatrix: max = calcListMatrixMax(maxa, max, arg) case ArgError: return arg } } if max == -math.MaxFloat64 { max = 0 } return newNumberFormulaArg(max) } // 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) formulaArg { if argsList.Len() == 0 { return newErrorFormulaArg(formulaErrorVALUE, "MEDIAN requires at least 1 argument") } values := []float64{} var median, digits float64 var err error for token := argsList.Front(); token != nil; token = token.Next() { arg := token.Value.(formulaArg) switch arg.Type { case ArgString: num := arg.ToNumber() if num.Type == ArgError { return newErrorFormulaArg(formulaErrorVALUE, num.Error) } values = append(values, num.Number) case ArgNumber: values = append(values, arg.Number) 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 { return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } 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] } return newNumberFormulaArg(median) } // MIN function returns the smallest value from a supplied set of numeric // values. The syntax of the function is: // // MIN(number1,[number2],...) // func (fn *formulaFuncs) MIN(argsList *list.List) formulaArg { if argsList.Len() == 0 { return newErrorFormulaArg(formulaErrorVALUE, "MIN requires at least 1 argument") } return fn.min(false, argsList) } // MINA function returns the smallest value from a supplied set of numeric // values, while counting text and the logical value FALSE as the value 0 and // counting the logical value TRUE as the value 1. The syntax of the function // is: // // MINA(number1,[number2],...) // func (fn *formulaFuncs) MINA(argsList *list.List) formulaArg { if argsList.Len() == 0 { return newErrorFormulaArg(formulaErrorVALUE, "MINA requires at least 1 argument") } return fn.min(true, argsList) } // MINIFS function returns the minimum value from a subset of values that are // specified according to one or more criteria. The syntax of the function // is: // // MINIFS(min_range,criteria_range1,criteria1,[criteria_range2,criteria2],...) // func (fn *formulaFuncs) MINIFS(argsList *list.List) formulaArg { if argsList.Len() < 3 { return newErrorFormulaArg(formulaErrorVALUE, "MINIFS requires at least 3 arguments") } if argsList.Len()%2 != 1 { return newErrorFormulaArg(formulaErrorNA, formulaErrorNA) } min, minRange, args := math.MaxFloat64, argsList.Front().Value.(formulaArg).Matrix, []formulaArg{} for arg := argsList.Front().Next(); arg != nil; arg = arg.Next() { args = append(args, arg.Value.(formulaArg)) } for _, ref := range formulaIfsMatch(args) { if num := minRange[ref.Row][ref.Col].ToNumber(); num.Type == ArgNumber && min > num.Number { min = num.Number } } if min == math.MaxFloat64 { min = 0 } return newNumberFormulaArg(min) } // calcListMatrixMin is part of the implementation min. func calcListMatrixMin(mina bool, min float64, arg formulaArg) float64 { for _, row := range arg.ToList() { switch row.Type { case ArgString: if !mina && (row.Value() == "TRUE" || row.Value() == "FALSE") { continue } else { num := row.ToBool() if num.Type == ArgNumber && num.Number < min { min = num.Number continue } } num := row.ToNumber() if num.Type != ArgError && num.Number < min { min = num.Number } case ArgNumber: if row.Number < min { min = row.Number } } } return min } // min is an implementation of the formula functions MIN and MINA. func (fn *formulaFuncs) min(mina bool, argsList *list.List) formulaArg { min := math.MaxFloat64 for token := argsList.Front(); token != nil; token = token.Next() { arg := token.Value.(formulaArg) switch arg.Type { case ArgString: if !mina && (arg.Value() == "TRUE" || arg.Value() == "FALSE") { continue } else { num := arg.ToBool() if num.Type == ArgNumber && num.Number < min { min = num.Number continue } } num := arg.ToNumber() if num.Type != ArgError && num.Number < min { min = num.Number } case ArgNumber: if arg.Number < min { min = arg.Number } case ArgList, ArgMatrix: min = calcListMatrixMin(mina, min, arg) case ArgError: return arg } } if min == math.MaxFloat64 { min = 0 } return newNumberFormulaArg(min) } // PERCENTILEdotEXC function returns the k'th percentile (i.e. the value below // which k% of the data values fall) for a supplied range of values and a // supplied k (between 0 & 1 exclusive).The syntax of the function is: // // PERCENTILE.EXC(array,k) // func (fn *formulaFuncs) PERCENTILEdotEXC(argsList *list.List) formulaArg { if argsList.Len() != 2 { return newErrorFormulaArg(formulaErrorVALUE, "PERCENTILE.EXC requires 2 arguments") } array := argsList.Front().Value.(formulaArg).ToList() k := argsList.Back().Value.(formulaArg).ToNumber() if k.Type != ArgNumber { return k } if k.Number <= 0 || k.Number >= 1 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } numbers := []float64{} for _, arg := range array { if arg.Type == ArgError { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } num := arg.ToNumber() if num.Type == ArgNumber { numbers = append(numbers, num.Number) } } cnt := len(numbers) sort.Float64s(numbers) idx := k.Number * (float64(cnt) + 1) base := math.Floor(idx) next := base - 1 proportion := math.Nextafter(idx, idx) - base return newNumberFormulaArg(numbers[int(next)] + ((numbers[int(base)] - numbers[int(next)]) * proportion)) } // PERCENTILEdotINC function returns the k'th percentile (i.e. the value below // which k% of the data values fall) for a supplied range of values and a // supplied k. The syntax of the function is: // // PERCENTILE.INC(array,k) // func (fn *formulaFuncs) PERCENTILEdotINC(argsList *list.List) formulaArg { if argsList.Len() != 2 { return newErrorFormulaArg(formulaErrorVALUE, "PERCENTILE.INC requires 2 arguments") } return fn.PERCENTILE(argsList) } // PERCENTILE function returns the k'th percentile (i.e. the value below which // k% of the data values fall) for a supplied range of values and a supplied // k. The syntax of the function is: // // PERCENTILE(array,k) // func (fn *formulaFuncs) PERCENTILE(argsList *list.List) formulaArg { if argsList.Len() != 2 { return newErrorFormulaArg(formulaErrorVALUE, "PERCENTILE requires 2 arguments") } array := argsList.Front().Value.(formulaArg).ToList() k := argsList.Back().Value.(formulaArg).ToNumber() if k.Type != ArgNumber { return k } if k.Number < 0 || k.Number > 1 { return newErrorFormulaArg(formulaErrorNA, formulaErrorNA) } numbers := []float64{} for _, arg := range array { if arg.Type == ArgError { return arg } num := arg.ToNumber() if num.Type == ArgNumber { numbers = append(numbers, num.Number) } } cnt := len(numbers) sort.Float64s(numbers) idx := k.Number * (float64(cnt) - 1) base := math.Floor(idx) if idx == base { return newNumberFormulaArg(numbers[int(idx)]) } next := base + 1 proportion := math.Nextafter(idx, idx) - base return newNumberFormulaArg(numbers[int(base)] + ((numbers[int(next)] - numbers[int(base)]) * proportion)) } // percentrank is an implementation of the formula functions PERCENTRANK and // PERCENTRANK.INC. func (fn *formulaFuncs) percentrank(name string, argsList *list.List) formulaArg { if argsList.Len() != 2 && argsList.Len() != 3 { return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires 2 or 3 arguments", name)) } array := argsList.Front().Value.(formulaArg).ToList() x := argsList.Front().Next().Value.(formulaArg).ToNumber() if x.Type != ArgNumber { return x } numbers := []float64{} for _, arg := range array { if arg.Type == ArgError { return arg } num := arg.ToNumber() if num.Type == ArgNumber { numbers = append(numbers, num.Number) } } cnt := len(numbers) sort.Float64s(numbers) if x.Number < numbers[0] || x.Number > numbers[cnt-1] { return newErrorFormulaArg(formulaErrorNA, formulaErrorNA) } pos, significance := float64(inFloat64Slice(numbers, x.Number)), newNumberFormulaArg(3) if argsList.Len() == 3 { if significance = argsList.Back().Value.(formulaArg).ToNumber(); significance.Type != ArgNumber { return significance } if significance.Number < 1 { return newErrorFormulaArg(formulaErrorNUM, fmt.Sprintf("%s arguments significance should be > 1", name)) } } if pos == -1 { pos = 0 cmp := numbers[0] for cmp < x.Number { pos++ cmp = numbers[int(pos)] } pos-- pos += (x.Number - numbers[int(pos)]) / (cmp - numbers[int(pos)]) } pow := math.Pow(10, float64(significance.Number)) digit := pow * float64(pos) / (float64(cnt) - 1) if name == "PERCENTRANK.EXC" { digit = pow * float64(pos+1) / (float64(cnt) + 1) } return newNumberFormulaArg(math.Floor(digit) / pow) } // PERCENTRANKdotEXC function calculates the relative position, between 0 and // 1 (exclusive), of a specified value within a supplied array. The syntax of // the function is: // // PERCENTRANK.EXC(array,x,[significance]) // func (fn *formulaFuncs) PERCENTRANKdotEXC(argsList *list.List) formulaArg { return fn.percentrank("PERCENTRANK.EXC", argsList) } // PERCENTRANKdotINC function calculates the relative position, between 0 and // 1 (inclusive), of a specified value within a supplied array.The syntax of // the function is: // // PERCENTRANK.INC(array,x,[significance]) // func (fn *formulaFuncs) PERCENTRANKdotINC(argsList *list.List) formulaArg { return fn.percentrank("PERCENTRANK.INC", argsList) } // PERCENTRANK function calculates the relative position of a specified value, // within a set of values, as a percentage. The syntax of the function is: // // PERCENTRANK(array,x,[significance]) // func (fn *formulaFuncs) PERCENTRANK(argsList *list.List) formulaArg { return fn.percentrank("PERCENTRANK", argsList) } // PERMUT function calculates the number of permutations of a specified number // of objects from a set of objects. The syntax of the function is: // // PERMUT(number,number_chosen) // func (fn *formulaFuncs) PERMUT(argsList *list.List) formulaArg { if argsList.Len() != 2 { return newErrorFormulaArg(formulaErrorVALUE, "PERMUT requires 2 numeric arguments") } number := argsList.Front().Value.(formulaArg).ToNumber() chosen := argsList.Back().Value.(formulaArg).ToNumber() if number.Type != ArgNumber { return number } if chosen.Type != ArgNumber { return chosen } if number.Number < chosen.Number { return newErrorFormulaArg(formulaErrorNA, formulaErrorNA) } return newNumberFormulaArg(math.Round(fact(number.Number) / fact(number.Number-chosen.Number))) } // PERMUTATIONA function calculates the number of permutations, with // repetitions, of a specified number of objects from a set. The syntax of // the function is: // // PERMUTATIONA(number,number_chosen) // func (fn *formulaFuncs) PERMUTATIONA(argsList *list.List) formulaArg { if argsList.Len() < 1 { return newErrorFormulaArg(formulaErrorVALUE, "PERMUTATIONA requires 2 numeric arguments") } number := argsList.Front().Value.(formulaArg).ToNumber() chosen := argsList.Back().Value.(formulaArg).ToNumber() if number.Type != ArgNumber { return number } if chosen.Type != ArgNumber { return chosen } num, numChosen := math.Floor(number.Number), math.Floor(chosen.Number) if num < 0 || numChosen < 0 { return newErrorFormulaArg(formulaErrorNA, formulaErrorNA) } return newNumberFormulaArg(math.Pow(num, numChosen)) } // QUARTILE function returns a requested quartile of a supplied range of // values. The syntax of the function is: // // QUARTILE(array,quart) // func (fn *formulaFuncs) QUARTILE(argsList *list.List) formulaArg { if argsList.Len() != 2 { return newErrorFormulaArg(formulaErrorVALUE, "QUARTILE requires 2 arguments") } quart := argsList.Back().Value.(formulaArg).ToNumber() if quart.Type != ArgNumber { return quart } if quart.Number < 0 || quart.Number > 4 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } args := list.New().Init() args.PushBack(argsList.Front().Value.(formulaArg)) args.PushBack(newNumberFormulaArg(quart.Number / 4)) return fn.PERCENTILE(args) } // QUARTILEdotEXC function returns a requested quartile of a supplied range of // values, based on a percentile range of 0 to 1 exclusive. The syntax of the // function is: // // QUARTILE.EXC(array,quart) // func (fn *formulaFuncs) QUARTILEdotEXC(argsList *list.List) formulaArg { if argsList.Len() != 2 { return newErrorFormulaArg(formulaErrorVALUE, "QUARTILE.EXC requires 2 arguments") } quart := argsList.Back().Value.(formulaArg).ToNumber() if quart.Type != ArgNumber { return quart } if quart.Number <= 0 || quart.Number >= 4 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } args := list.New().Init() args.PushBack(argsList.Front().Value.(formulaArg)) args.PushBack(newNumberFormulaArg(quart.Number / 4)) return fn.PERCENTILEdotEXC(args) } // QUARTILEdotINC function returns a requested quartile of a supplied range of // values. The syntax of the function is: // // QUARTILE.INC(array,quart) // func (fn *formulaFuncs) QUARTILEdotINC(argsList *list.List) formulaArg { if argsList.Len() != 2 { return newErrorFormulaArg(formulaErrorVALUE, "QUARTILE.INC requires 2 arguments") } return fn.QUARTILE(argsList) } // rank is an implementation of the formula functions RANK and RANK.EQ. func (fn *formulaFuncs) rank(name string, argsList *list.List) formulaArg { if argsList.Len() < 2 { return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires at least 2 arguments", name)) } if argsList.Len() > 3 { return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires at most 3 arguments", name)) } num := argsList.Front().Value.(formulaArg).ToNumber() if num.Type != ArgNumber { return num } arr := []float64{} for _, arg := range argsList.Front().Next().Value.(formulaArg).ToList() { n := arg.ToNumber() if n.Type == ArgNumber { arr = append(arr, n.Number) } } sort.Float64s(arr) order := newNumberFormulaArg(0) if argsList.Len() == 3 { if order = argsList.Back().Value.(formulaArg).ToNumber(); order.Type != ArgNumber { return order } } if order.Number == 0 { sort.Sort(sort.Reverse(sort.Float64Slice(arr))) } if idx := inFloat64Slice(arr, num.Number); idx != -1 { return newNumberFormulaArg(float64(idx + 1)) } return newErrorFormulaArg(formulaErrorNA, formulaErrorNA) } // RANK.EQ function returns the statistical rank of a given value, within a // supplied array of values. If there are duplicate values in the list, these // are given the same rank. The syntax of the function is: // // RANK.EQ(number,ref,[order]) // func (fn *formulaFuncs) RANKdotEQ(argsList *list.List) formulaArg { return fn.rank("RANK.EQ", argsList) } // RANK function returns the statistical rank of a given value, within a // supplied array of values. If there are duplicate values in the list, these // are given the same rank. The syntax of the function is: // // RANK(number,ref,[order]) // func (fn *formulaFuncs) RANK(argsList *list.List) formulaArg { return fn.rank("RANK", argsList) } // SKEW function calculates the skewness of the distribution of a supplied set // of values. The syntax of the function is: // // SKEW(number1,[number2],...) // func (fn *formulaFuncs) SKEW(argsList *list.List) formulaArg { if argsList.Len() < 1 { return newErrorFormulaArg(formulaErrorVALUE, "SKEW requires at least 1 argument") } mean, stdDev, count, summer := fn.AVERAGE(argsList), fn.STDEV(argsList), 0.0, 0.0 for arg := argsList.Front(); arg != nil; arg = arg.Next() { token := arg.Value.(formulaArg) switch token.Type { case ArgNumber, ArgString: num := token.ToNumber() if num.Type == ArgError { return num } summer += math.Pow((num.Number-mean.Number)/stdDev.Number, 3) count++ case ArgList, ArgMatrix: for _, row := range token.ToList() { numArg := row.ToNumber() if numArg.Type != ArgNumber { continue } summer += math.Pow((numArg.Number-mean.Number)/stdDev.Number, 3) count++ } } } if count > 2 { return newNumberFormulaArg(summer * (count / ((count - 1) * (count - 2)))) } return newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV) } // SMALL function returns the k'th smallest value from an array of numeric // values. The syntax of the function is: // // SMALL(array,k) // func (fn *formulaFuncs) SMALL(argsList *list.List) formulaArg { return fn.kth("SMALL", argsList) } // STANDARDIZE function returns a normalized value of a distribution that is // characterized by a supplied mean and standard deviation. The syntax of the // function is: // // STANDARDIZE(x,mean,standard_dev) // func (fn *formulaFuncs) STANDARDIZE(argsList *list.List) formulaArg { if argsList.Len() != 3 { return newErrorFormulaArg(formulaErrorVALUE, "STANDARDIZE requires 3 arguments") } x := argsList.Front().Value.(formulaArg).ToNumber() if x.Type != ArgNumber { return x } mean := argsList.Front().Next().Value.(formulaArg).ToNumber() if mean.Type != ArgNumber { return mean } stdDev := argsList.Back().Value.(formulaArg).ToNumber() if stdDev.Type != ArgNumber { return stdDev } if stdDev.Number <= 0 { return newErrorFormulaArg(formulaErrorNA, formulaErrorNA) } return newNumberFormulaArg((x.Number - mean.Number) / stdDev.Number) } // stdevp is an implementation of the formula functions STDEVP and STDEV.P. func (fn *formulaFuncs) stdevp(name string, argsList *list.List) formulaArg { if argsList.Len() < 1 { return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires at least 1 argument", name)) } varp := fn.VARP(argsList) if varp.Type != ArgNumber { return varp } return newNumberFormulaArg(math.Sqrt(varp.Number)) } // STDEVP function calculates the standard deviation of a supplied set of // values. The syntax of the function is: // // STDEVP(number1,[number2],...) // func (fn *formulaFuncs) STDEVP(argsList *list.List) formulaArg { return fn.stdevp("STDEVP", argsList) } // STDEVdotP function calculates the standard deviation of a supplied set of // values. // // STDEV.P( number1, [number2], ... ) // func (fn *formulaFuncs) STDEVdotP(argsList *list.List) formulaArg { return fn.stdevp("STDEV.P", argsList) } // TRIMMEAN function calculates the trimmed mean (or truncated mean) of a // supplied set of values. The syntax of the function is: // // TRIMMEAN(array,percent) // func (fn *formulaFuncs) TRIMMEAN(argsList *list.List) formulaArg { if argsList.Len() != 2 { return newErrorFormulaArg(formulaErrorVALUE, "TRIMMEAN requires 2 arguments") } percent := argsList.Back().Value.(formulaArg).ToNumber() if percent.Type != ArgNumber { return percent } if percent.Number < 0 || percent.Number >= 1 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } arr := []float64{} arrArg := argsList.Front().Value.(formulaArg).ToList() for _, cell := range arrArg { num := cell.ToNumber() if num.Type != ArgNumber { continue } arr = append(arr, num.Number) } discard := math.Floor(float64(len(arr)) * percent.Number / 2) sort.Float64s(arr) for i := 0; i < int(discard); i++ { if len(arr) > 0 { arr = arr[1:] } if len(arr) > 0 { arr = arr[:len(arr)-1] } } args := list.New().Init() for _, ele := range arr { args.PushBack(newNumberFormulaArg(ele)) } return fn.AVERAGE(args) } // vars is an implementation of the formula functions VAR, VARA, VARP, VAR.P // VAR.S and VARPA. func (fn *formulaFuncs) vars(name string, argsList *list.List) formulaArg { if argsList.Len() < 1 { return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires at least 1 argument", name)) } summerA, summerB, count := 0.0, 0.0, 0.0 minimum := 0.0 if name == "VAR" || name == "VAR.S" || name == "VARA" { minimum = 1.0 } for arg := argsList.Front(); arg != nil; arg = arg.Next() { for _, token := range arg.Value.(formulaArg).ToList() { num := token.ToNumber() if token.Value() != "TRUE" && num.Type == ArgNumber { summerA += (num.Number * num.Number) summerB += num.Number count++ continue } num = token.ToBool() if num.Type == ArgNumber { summerA += (num.Number * num.Number) summerB += num.Number count++ continue } if name == "VARA" || name == "VARPA" { count++ } } } if count > minimum { summerA *= count summerB *= summerB return newNumberFormulaArg((summerA - summerB) / (count * (count - minimum))) } return newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV) } // VAR function returns the sample variance of a supplied set of values. The // syntax of the function is: // // VAR(number1,[number2],...) // func (fn *formulaFuncs) VAR(argsList *list.List) formulaArg { return fn.vars("VAR", argsList) } // VARA function calculates the sample variance of a supplied set of values. // The syntax of the function is: // // VARA(number1,[number2],...) // func (fn *formulaFuncs) VARA(argsList *list.List) formulaArg { return fn.vars("VARA", argsList) } // VARP function returns the Variance of a given set of values. The syntax of // the function is: // // VARP(number1,[number2],...) // func (fn *formulaFuncs) VARP(argsList *list.List) formulaArg { return fn.vars("VARP", argsList) } // VARdotP function returns the Variance of a given set of values. The syntax // of the function is: // // VAR.P(number1,[number2],...) // func (fn *formulaFuncs) VARdotP(argsList *list.List) formulaArg { return fn.vars("VAR.P", argsList) } // VARdotS function calculates the sample variance of a supplied set of // values. The syntax of the function is: // // VAR.S(number1,[number2],...) // func (fn *formulaFuncs) VARdotS(argsList *list.List) formulaArg { return fn.vars("VAR.S", argsList) } // VARPA function returns the Variance of a given set of values. The syntax of // the function is: // // VARPA(number1,[number2],...) // func (fn *formulaFuncs) VARPA(argsList *list.List) formulaArg { return fn.vars("VARPA", argsList) } // WEIBULL function calculates the Weibull Probability Density Function or the // Weibull Cumulative Distribution Function for a supplied set of parameters. // The syntax of the function is: // // WEIBULL(x,alpha,beta,cumulative) // func (fn *formulaFuncs) WEIBULL(argsList *list.List) formulaArg { if argsList.Len() != 4 { return newErrorFormulaArg(formulaErrorVALUE, "WEIBULL requires 4 arguments") } x := argsList.Front().Value.(formulaArg).ToNumber() alpha := argsList.Front().Next().Value.(formulaArg).ToNumber() beta := argsList.Back().Prev().Value.(formulaArg).ToNumber() if alpha.Type == ArgNumber && beta.Type == ArgNumber && x.Type == ArgNumber { if alpha.Number < 0 || alpha.Number <= 0 || beta.Number <= 0 { return newErrorFormulaArg(formulaErrorNA, formulaErrorNA) } cumulative := argsList.Back().Value.(formulaArg).ToBool() if cumulative.Boolean && cumulative.Number == 1 { return newNumberFormulaArg(1 - math.Exp(0-math.Pow((x.Number/beta.Number), alpha.Number))) } return newNumberFormulaArg((alpha.Number / math.Pow(beta.Number, alpha.Number)) * math.Pow(x.Number, (alpha.Number-1)) * math.Exp(0-math.Pow((x.Number/beta.Number), alpha.Number))) } return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } // WEIBULLdotDIST function calculates the Weibull Probability Density Function // or the Weibull Cumulative Distribution Function for a supplied set of // parameters. The syntax of the function is: // // WEIBULL.DIST(x,alpha,beta,cumulative) // func (fn *formulaFuncs) WEIBULLdotDIST(argsList *list.List) formulaArg { if argsList.Len() != 4 { return newErrorFormulaArg(formulaErrorVALUE, "WEIBULL.DIST requires 4 arguments") } return fn.WEIBULL(argsList) } // ZdotTEST function calculates the one-tailed probability value of the // Z-Test. The syntax of the function is: // // Z.TEST(array,x,[sigma]) // func (fn *formulaFuncs) ZdotTEST(argsList *list.List) formulaArg { argsLen := argsList.Len() if argsLen < 2 { return newErrorFormulaArg(formulaErrorVALUE, "Z.TEST requires at least 2 arguments") } if argsLen > 3 { return newErrorFormulaArg(formulaErrorVALUE, "Z.TEST accepts at most 3 arguments") } return fn.ZTEST(argsList) } // ZTEST function calculates the one-tailed probability value of the Z-Test. // The syntax of the function is: // // ZTEST(array,x,[sigma]) // func (fn *formulaFuncs) ZTEST(argsList *list.List) formulaArg { argsLen := argsList.Len() if argsLen < 2 { return newErrorFormulaArg(formulaErrorVALUE, "ZTEST requires at least 2 arguments") } if argsLen > 3 { return newErrorFormulaArg(formulaErrorVALUE, "ZTEST accepts at most 3 arguments") } arrArg, arrArgs := argsList.Front().Value.(formulaArg), list.New() arrArgs.PushBack(arrArg) arr := fn.AVERAGE(arrArgs) if arr.Type == ArgError { return newErrorFormulaArg(formulaErrorNA, formulaErrorNA) } x := argsList.Front().Next().Value.(formulaArg).ToNumber() if x.Type == ArgError { return x } sigma := argsList.Back().Value.(formulaArg).ToNumber() if sigma.Type == ArgError { return sigma } if argsLen != 3 { sigma = fn.STDEV(arrArgs).ToNumber() } normsdistArg := list.New() div := sigma.Number / math.Sqrt(float64(len(arrArg.ToList()))) if div == 0 { return newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV) } normsdistArg.PushBack(newNumberFormulaArg((arr.Number - x.Number) / div)) return newNumberFormulaArg(1 - fn.NORMSDIST(normsdistArg).Number) } // Information Functions // ERRORdotTYPE function receives an error value and returns an integer, that // tells you the type of the supplied error. The syntax of the function is: // // ERROR.TYPE(error_val) // func (fn *formulaFuncs) ERRORdotTYPE(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "ERROR.TYPE requires 1 argument") } token := argsList.Front().Value.(formulaArg) if token.Type == ArgError { for i, errType := range []string{ formulaErrorNULL, formulaErrorDIV, formulaErrorVALUE, formulaErrorREF, formulaErrorNAME, formulaErrorNUM, formulaErrorNA, } { if errType == token.String { return newNumberFormulaArg(float64(i) + 1) } } } return newErrorFormulaArg(formulaErrorNA, formulaErrorNA) } // 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) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "ISBLANK requires 1 argument") } token := argsList.Front().Value.(formulaArg) result := "FALSE" switch token.Type { case ArgUnknown: result = "TRUE" case ArgString: if token.String == "" { result = "TRUE" } } return newStringFormulaArg(result) } // 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) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "ISERR requires 1 argument") } token := argsList.Front().Value.(formulaArg) result := false if token.Type == ArgError { for _, errType := range []string{ formulaErrorDIV, formulaErrorNAME, formulaErrorNUM, formulaErrorVALUE, formulaErrorREF, formulaErrorNULL, formulaErrorSPILL, formulaErrorCALC, formulaErrorGETTINGDATA, } { if errType == token.String { result = true } } } return newBoolFormulaArg(result) } // 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) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "ISERROR requires 1 argument") } token := argsList.Front().Value.(formulaArg) result := false if token.Type == ArgError { for _, errType := range []string{ formulaErrorDIV, formulaErrorNAME, formulaErrorNA, formulaErrorNUM, formulaErrorVALUE, formulaErrorREF, formulaErrorNULL, formulaErrorSPILL, formulaErrorCALC, formulaErrorGETTINGDATA, } { if errType == token.String { result = true } } } return newBoolFormulaArg(result) } // 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) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "ISEVEN requires 1 argument") } var ( token = argsList.Front().Value.(formulaArg) result = "FALSE" numeric int err error ) if token.Type == ArgString { if numeric, err = strconv.Atoi(token.String); err != nil { return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } if numeric == numeric/2*2 { return newStringFormulaArg("TRUE") } } return newStringFormulaArg(result) } // ISFORMULA function tests if a specified cell contains a formula, and if so, // returns TRUE; Otherwise, the function returns FALSE. The syntax of the // function is: // // ISFORMULA(reference) // func (fn *formulaFuncs) ISFORMULA(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "ISFORMULA requires 1 argument") } arg := argsList.Front().Value.(formulaArg) if arg.cellRefs != nil && arg.cellRefs.Len() == 1 { ref := arg.cellRefs.Front().Value.(cellRef) cell, _ := CoordinatesToCellName(ref.Col, ref.Row) if formula, _ := fn.f.GetCellFormula(ref.Sheet, cell); len(formula) > 0 { return newBoolFormulaArg(true) } } return newBoolFormulaArg(false) } // ISLOGICAL function tests if a supplied value (or expression) returns a // logical value (i.e. evaluates to True or False). If so, the function // returns TRUE; Otherwise, it returns FALSE. The syntax of the function is: // // ISLOGICAL(value) // func (fn *formulaFuncs) ISLOGICAL(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "ISLOGICAL requires 1 argument") } val := argsList.Front().Value.(formulaArg).Value() if strings.EqualFold("TRUE", val) || strings.EqualFold("FALSE", val) { return newBoolFormulaArg(true) } return newBoolFormulaArg(false) } // 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) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "ISNA requires 1 argument") } token := argsList.Front().Value.(formulaArg) result := "FALSE" if token.Type == ArgError && token.String == formulaErrorNA { result = "TRUE" } return newStringFormulaArg(result) } // 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) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "ISNONTEXT requires 1 argument") } token := argsList.Front().Value.(formulaArg) result := "TRUE" if token.Type == ArgString && token.String != "" { result = "FALSE" } return newStringFormulaArg(result) } // 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) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "ISNUMBER requires 1 argument") } token, result := argsList.Front().Value.(formulaArg), false if token.Type == ArgString && token.String != "" { if _, err := strconv.Atoi(token.String); err == nil { result = true } } return newBoolFormulaArg(result) } // 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) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "ISODD requires 1 argument") } var ( token = argsList.Front().Value.(formulaArg) result = "FALSE" numeric int err error ) if token.Type == ArgString { if numeric, err = strconv.Atoi(token.String); err != nil { return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } if numeric != numeric/2*2 { return newStringFormulaArg("TRUE") } } return newStringFormulaArg(result) } // ISREF function tests if a supplied value is a reference. If so, the // function returns TRUE; Otherwise it returns FALSE. The syntax of the // function is: // // ISREF(value) // func (fn *formulaFuncs) ISREF(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "ISREF requires 1 argument") } arg := argsList.Front().Value.(formulaArg) if arg.cellRanges != nil && arg.cellRanges.Len() > 0 || arg.cellRefs != nil && arg.cellRefs.Len() > 0 { return newBoolFormulaArg(true) } return newBoolFormulaArg(false) } // ISTEXT function tests if a supplied value is text, and if so, returns TRUE; // Otherwise, the function returns FALSE. The syntax of the function is: // // ISTEXT(value) // func (fn *formulaFuncs) ISTEXT(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "ISTEXT requires 1 argument") } token := argsList.Front().Value.(formulaArg) if token.ToNumber().Type != ArgError { return newBoolFormulaArg(false) } return newBoolFormulaArg(token.Type == ArgString) } // N function converts data into a numeric value. The syntax of the function // is: // // N(value) // func (fn *formulaFuncs) N(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "N requires 1 argument") } token, num := argsList.Front().Value.(formulaArg), 0.0 if token.Type == ArgError { return token } if arg := token.ToNumber(); arg.Type == ArgNumber { num = arg.Number } if token.Value() == "TRUE" { num = 1 } return newNumberFormulaArg(num) } // 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) formulaArg { if argsList.Len() != 0 { return newErrorFormulaArg(formulaErrorVALUE, "NA accepts no arguments") } return newErrorFormulaArg(formulaErrorNA, formulaErrorNA) } // SHEET function returns the Sheet number for a specified reference. The // syntax of the function is: // // SHEET([value]) // func (fn *formulaFuncs) SHEET(argsList *list.List) formulaArg { if argsList.Len() > 1 { return newErrorFormulaArg(formulaErrorVALUE, "SHEET accepts at most 1 argument") } if argsList.Len() == 0 { return newNumberFormulaArg(float64(fn.f.GetSheetIndex(fn.sheet) + 1)) } arg := argsList.Front().Value.(formulaArg) if sheetIdx := fn.f.GetSheetIndex(arg.Value()); sheetIdx != -1 { return newNumberFormulaArg(float64(sheetIdx + 1)) } if arg.cellRanges != nil && arg.cellRanges.Len() > 0 { if sheetIdx := fn.f.GetSheetIndex(arg.cellRanges.Front().Value.(cellRange).From.Sheet); sheetIdx != -1 { return newNumberFormulaArg(float64(sheetIdx + 1)) } } if arg.cellRefs != nil && arg.cellRefs.Len() > 0 { if sheetIdx := fn.f.GetSheetIndex(arg.cellRefs.Front().Value.(cellRef).Sheet); sheetIdx != -1 { return newNumberFormulaArg(float64(sheetIdx + 1)) } } return newErrorFormulaArg(formulaErrorNA, formulaErrorNA) } // SHEETS function returns the number of sheets in a supplied reference. The // result includes sheets that are Visible, Hidden or Very Hidden. The syntax // of the function is: // // SHEETS([reference]) // func (fn *formulaFuncs) SHEETS(argsList *list.List) formulaArg { if argsList.Len() > 1 { return newErrorFormulaArg(formulaErrorVALUE, "SHEETS accepts at most 1 argument") } if argsList.Len() == 0 { return newNumberFormulaArg(float64(len(fn.f.GetSheetList()))) } arg := argsList.Front().Value.(formulaArg) sheetMap := map[string]struct{}{} if arg.cellRanges != nil && arg.cellRanges.Len() > 0 { for rng := arg.cellRanges.Front(); rng != nil; rng = rng.Next() { sheetMap[rng.Value.(cellRange).From.Sheet] = struct{}{} } } if arg.cellRefs != nil && arg.cellRefs.Len() > 0 { for ref := arg.cellRefs.Front(); ref != nil; ref = ref.Next() { sheetMap[ref.Value.(cellRef).Sheet] = struct{}{} } } if len(sheetMap) > 0 { return newNumberFormulaArg(float64(len(sheetMap))) } return newErrorFormulaArg(formulaErrorNA, formulaErrorNA) } // TYPE function returns an integer that represents the value's data type. The // syntax of the function is: // // TYPE(value) // func (fn *formulaFuncs) TYPE(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "TYPE requires 1 argument") } token := argsList.Front().Value.(formulaArg) switch token.Type { case ArgError: return newNumberFormulaArg(16) case ArgMatrix: return newNumberFormulaArg(64) default: if arg := token.ToNumber(); arg.Type != ArgError || len(token.Value()) == 0 { return newNumberFormulaArg(1) } if arg := token.ToBool(); arg.Type != ArgError { return newNumberFormulaArg(4) } return newNumberFormulaArg(2) } } // T function tests if a supplied value is text and if so, returns the // supplied text; Otherwise, the function returns an empty text string. The // syntax of the function is: // // T(value) // func (fn *formulaFuncs) T(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "T requires 1 argument") } token := argsList.Front().Value.(formulaArg) if token.Type == ArgError { return token } if token.Type == ArgNumber { return newStringFormulaArg("") } return newStringFormulaArg(token.Value()) } // Logical Functions // AND function tests a number of supplied conditions and returns TRUE or // FALSE. The syntax of the function is: // // AND(logical_test1,[logical_test2],...) // func (fn *formulaFuncs) AND(argsList *list.List) formulaArg { if argsList.Len() == 0 { return newErrorFormulaArg(formulaErrorVALUE, "AND requires at least 1 argument") } if argsList.Len() > 30 { return newErrorFormulaArg(formulaErrorVALUE, "AND accepts at most 30 arguments") } var ( and = true val float64 err error ) for arg := argsList.Front(); arg != nil; arg = arg.Next() { token := arg.Value.(formulaArg) switch token.Type { case ArgUnknown: continue case ArgString: if token.String == "TRUE" { continue } if token.String == "FALSE" { return newStringFormulaArg(token.String) } if val, err = strconv.ParseFloat(token.String, 64); err != nil { return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } and = and && (val != 0) case ArgMatrix: // TODO return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } } return newBoolFormulaArg(and) } // FALSE function function returns the logical value FALSE. The syntax of the // function is: // // FALSE() // func (fn *formulaFuncs) FALSE(argsList *list.List) formulaArg { if argsList.Len() != 0 { return newErrorFormulaArg(formulaErrorVALUE, "FALSE takes no arguments") } return newBoolFormulaArg(false) } // IFERROR function receives two values (or expressions) and tests if the // first of these evaluates to an error. The syntax of the function is: // // IFERROR(value,value_if_error) // func (fn *formulaFuncs) IFERROR(argsList *list.List) formulaArg { if argsList.Len() != 2 { return newErrorFormulaArg(formulaErrorVALUE, "IFERROR requires 2 arguments") } value := argsList.Front().Value.(formulaArg) if value.Type != ArgError { if value.Type == ArgEmpty { return newNumberFormulaArg(0) } return value } return argsList.Back().Value.(formulaArg) } // IFNA function tests if an initial supplied value (or expression) evaluates // to the Excel #N/A error. If so, the function returns a second supplied // value; Otherwise the function returns the first supplied value. The syntax // of the function is: // // IFNA(value,value_if_na) // func (fn *formulaFuncs) IFNA(argsList *list.List) formulaArg { if argsList.Len() != 2 { return newErrorFormulaArg(formulaErrorVALUE, "IFNA requires 2 arguments") } arg := argsList.Front().Value.(formulaArg) if arg.Type == ArgError && arg.Value() == formulaErrorNA { return argsList.Back().Value.(formulaArg) } return arg } // IFS function tests a number of supplied conditions and returns the result // corresponding to the first condition that evaluates to TRUE. If none of // the supplied conditions evaluate to TRUE, the function returns the #N/A // error. // // IFS(logical_test1,value_if_true1,[logical_test2,value_if_true2],...) // func (fn *formulaFuncs) IFS(argsList *list.List) formulaArg { if argsList.Len() < 2 { return newErrorFormulaArg(formulaErrorVALUE, "IFS requires at least 2 arguments") } for arg := argsList.Front(); arg != nil; arg = arg.Next() { if arg.Value.(formulaArg).ToBool().Number == 1 { return arg.Next().Value.(formulaArg) } arg = arg.Next() } return newErrorFormulaArg(formulaErrorNA, formulaErrorNA) } // NOT function returns the opposite to a supplied logical value. The syntax // of the function is: // // NOT(logical) // func (fn *formulaFuncs) NOT(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "NOT requires 1 argument") } token := argsList.Front().Value.(formulaArg) switch token.Type { case ArgString, ArgList: if strings.ToUpper(token.String) == "TRUE" { return newBoolFormulaArg(false) } if strings.ToUpper(token.String) == "FALSE" { return newBoolFormulaArg(true) } case ArgNumber: return newBoolFormulaArg(!(token.Number != 0)) case ArgError: return token } return newErrorFormulaArg(formulaErrorVALUE, "NOT expects 1 boolean or numeric argument") } // OR function tests a number of supplied conditions and returns either TRUE // or FALSE. The syntax of the function is: // // OR(logical_test1,[logical_test2],...) // func (fn *formulaFuncs) OR(argsList *list.List) formulaArg { if argsList.Len() == 0 { return newErrorFormulaArg(formulaErrorVALUE, "OR requires at least 1 argument") } if argsList.Len() > 30 { return newErrorFormulaArg(formulaErrorVALUE, "OR accepts at most 30 arguments") } var ( or bool val float64 err error ) for arg := argsList.Front(); arg != nil; arg = arg.Next() { token := arg.Value.(formulaArg) switch token.Type { case ArgUnknown: continue case ArgString: if token.String == "FALSE" { continue } if token.String == "TRUE" { or = true continue } if val, err = strconv.ParseFloat(token.String, 64); err != nil { return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } or = val != 0 case ArgMatrix: // TODO return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } } return newStringFormulaArg(strings.ToUpper(strconv.FormatBool(or))) } // SWITCH function compares a number of supplied values to a supplied test // expression and returns a result corresponding to the first value that // matches the test expression. A default value can be supplied, to be // returned if none of the supplied values match the test expression. The // syntax of the function is: // // // SWITCH(expression,value1,result1,[value2,result2],[value3,result3],...,[default]) // func (fn *formulaFuncs) SWITCH(argsList *list.List) formulaArg { if argsList.Len() < 3 { return newErrorFormulaArg(formulaErrorVALUE, "SWITCH requires at least 3 arguments") } target := argsList.Front().Value.(formulaArg) argCount := argsList.Len() - 1 switchCount := int(math.Floor(float64(argCount) / 2)) hasDefaultClause := argCount%2 != 0 result := newErrorFormulaArg(formulaErrorNA, formulaErrorNA) if hasDefaultClause { result = argsList.Back().Value.(formulaArg) } if switchCount > 0 { arg := argsList.Front() for i := 0; i < switchCount; i++ { arg = arg.Next() if target.Value() == arg.Value.(formulaArg).Value() { result = arg.Next().Value.(formulaArg) break } arg = arg.Next() } } return result } // TRUE function returns the logical value TRUE. The syntax of the function // is: // // TRUE() // func (fn *formulaFuncs) TRUE(argsList *list.List) formulaArg { if argsList.Len() != 0 { return newErrorFormulaArg(formulaErrorVALUE, "TRUE takes no arguments") } return newBoolFormulaArg(true) } // calcXor checking if numeric cell exists and count it by given arguments // sequence for the formula function XOR. func calcXor(argsList *list.List) formulaArg { count, ok := 0, false for arg := argsList.Front(); arg != nil; arg = arg.Next() { token := arg.Value.(formulaArg) switch token.Type { case ArgError: return token case ArgString: if b := token.ToBool(); b.Type == ArgNumber { ok = true if b.Number == 1 { count++ } continue } if num := token.ToNumber(); num.Type == ArgNumber { ok = true if num.Number != 0 { count++ } } case ArgNumber: ok = true if token.Number != 0 { count++ } case ArgMatrix: for _, value := range token.ToList() { if num := value.ToNumber(); num.Type == ArgNumber { ok = true if num.Number != 0 { count++ } } } } } if !ok { return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } return newBoolFormulaArg(count%2 != 0) } // XOR function returns the Exclusive Or logical operation for one or more // supplied conditions. I.e. the Xor function returns TRUE if an odd number // of the supplied conditions evaluate to TRUE, and FALSE otherwise. The // syntax of the function is: // // XOR(logical_test1,[logical_test2],...) // func (fn *formulaFuncs) XOR(argsList *list.List) formulaArg { if argsList.Len() < 1 { return newErrorFormulaArg(formulaErrorVALUE, "XOR requires at least 1 argument") } return calcXor(argsList) } // Date and Time Functions // DATE returns a date, from a user-supplied year, month and day. The syntax // of the function is: // // DATE(year,month,day) // func (fn *formulaFuncs) DATE(argsList *list.List) formulaArg { if argsList.Len() != 3 { return newErrorFormulaArg(formulaErrorVALUE, "DATE requires 3 number arguments") } year := argsList.Front().Value.(formulaArg).ToNumber() month := argsList.Front().Next().Value.(formulaArg).ToNumber() day := argsList.Back().Value.(formulaArg).ToNumber() if year.Type != ArgNumber || month.Type != ArgNumber || day.Type != ArgNumber { return newErrorFormulaArg(formulaErrorVALUE, "DATE requires 3 number arguments") } d := makeDate(int(year.Number), time.Month(month.Number), int(day.Number)) return newStringFormulaArg(timeFromExcelTime(daysBetween(excelMinTime1900.Unix(), d)+1, false).String()) } // calcDateDif is an implementation of the formula function DATEDIF, // calculation difference between two dates. func calcDateDif(unit string, diff float64, seq []int, startArg, endArg formulaArg) float64 { ey, sy, em, sm, ed, sd := seq[0], seq[1], seq[2], seq[3], seq[4], seq[5] switch unit { case "d": diff = endArg.Number - startArg.Number case "md": smMD := em if ed < sd { smMD-- } diff = endArg.Number - daysBetween(excelMinTime1900.Unix(), makeDate(ey, time.Month(smMD), sd)) - 1 case "ym": diff = float64(em - sm) if ed < sd { diff-- } if diff < 0 { diff += 12 } case "yd": syYD := sy if em < sm || (em == sm && ed < sd) { syYD++ } s := daysBetween(excelMinTime1900.Unix(), makeDate(syYD, time.Month(em), ed)) e := daysBetween(excelMinTime1900.Unix(), makeDate(sy, time.Month(sm), sd)) diff = s - e } return diff } // DATEDIF function calculates the number of days, months, or years between // two dates. The syntax of the function is: // // DATEDIF(start_date,end_date,unit) // func (fn *formulaFuncs) DATEDIF(argsList *list.List) formulaArg { if argsList.Len() != 3 { return newErrorFormulaArg(formulaErrorVALUE, "DATEDIF requires 3 number arguments") } startArg, endArg := argsList.Front().Value.(formulaArg).ToNumber(), argsList.Front().Next().Value.(formulaArg).ToNumber() if startArg.Type != ArgNumber || endArg.Type != ArgNumber { return startArg } if startArg.Number > endArg.Number { return newErrorFormulaArg(formulaErrorNUM, "start_date > end_date") } if startArg.Number == endArg.Number { return newNumberFormulaArg(0) } unit := strings.ToLower(argsList.Back().Value.(formulaArg).Value()) startDate, endDate := timeFromExcelTime(startArg.Number, false), timeFromExcelTime(endArg.Number, false) sy, smm, sd := startDate.Date() ey, emm, ed := endDate.Date() sm, em, diff := int(smm), int(emm), 0.0 switch unit { case "y": diff = float64(ey - sy) if em < sm || (em == sm && ed < sd) { diff-- } case "m": ydiff := ey - sy mdiff := em - sm if ed < sd { mdiff-- } if mdiff < 0 { ydiff-- mdiff += 12 } diff = float64(ydiff*12 + mdiff) case "d", "md", "ym", "yd": diff = calcDateDif(unit, diff, []int{ey, sy, em, sm, ed, sd}, startArg, endArg) default: return newErrorFormulaArg(formulaErrorVALUE, "DATEDIF has invalid unit") } return newNumberFormulaArg(diff) } // isDateOnlyFmt check if the given string matches date-only format regular expressions. func isDateOnlyFmt(dateString string) bool { for _, df := range dateOnlyFormats { submatch := df.FindStringSubmatch(dateString) if len(submatch) > 1 { return true } } return false } // isTimeOnlyFmt check if the given string matches time-only format regular expressions. func isTimeOnlyFmt(timeString string) bool { for _, tf := range timeFormats { submatch := tf.FindStringSubmatch(timeString) if len(submatch) > 1 { return true } } return false } // strToTimePatternHandler1 parse and convert the given string in pattern // hh to the time. func strToTimePatternHandler1(submatch []string) (h, m int, s float64, err error) { h, err = strconv.Atoi(submatch[0]) return } // strToTimePatternHandler2 parse and convert the given string in pattern // hh:mm to the time. func strToTimePatternHandler2(submatch []string) (h, m int, s float64, err error) { if h, err = strconv.Atoi(submatch[0]); err != nil { return } m, err = strconv.Atoi(submatch[2]) return } // strToTimePatternHandler3 parse and convert the given string in pattern // mm:ss to the time. func strToTimePatternHandler3(submatch []string) (h, m int, s float64, err error) { if m, err = strconv.Atoi(submatch[0]); err != nil { return } s, err = strconv.ParseFloat(submatch[2], 64) return } // strToTimePatternHandler4 parse and convert the given string in pattern // hh:mm:ss to the time. func strToTimePatternHandler4(submatch []string) (h, m int, s float64, err error) { if h, err = strconv.Atoi(submatch[0]); err != nil { return } if m, err = strconv.Atoi(submatch[2]); err != nil { return } s, err = strconv.ParseFloat(submatch[4], 64) return } // strToTime parse and convert the given string to the time. func strToTime(str string) (int, int, float64, bool, bool, formulaArg) { pattern, submatch := "", []string{} for key, tf := range timeFormats { submatch = tf.FindStringSubmatch(str) if len(submatch) > 1 { pattern = key break } } if pattern == "" { return 0, 0, 0, false, false, newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } dateIsEmpty := submatch[1] == "" submatch = submatch[49:] var ( l = len(submatch) last = submatch[l-1] am = last == "am" pm = last == "pm" hours, minutes int seconds float64 err error ) if handler, ok := map[string]func(subsubmatch []string) (int, int, float64, error){ "hh": strToTimePatternHandler1, "hh:mm": strToTimePatternHandler2, "mm:ss": strToTimePatternHandler3, "hh:mm:ss": strToTimePatternHandler4, }[pattern]; ok { if hours, minutes, seconds, err = handler(submatch); err != nil { return 0, 0, 0, false, false, newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } } if minutes >= 60 { return 0, 0, 0, false, false, newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } if am || pm { if hours > 12 || seconds >= 60 { return 0, 0, 0, false, false, newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } else if hours == 12 { hours = 0 } } else if hours >= 24 || seconds >= 10000 { return 0, 0, 0, false, false, newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } return hours, minutes, seconds, pm, dateIsEmpty, newEmptyFormulaArg() } // strToDatePatternHandler1 parse and convert the given string in pattern // mm/dd/yy to the date. func strToDatePatternHandler1(submatch []string) (int, int, int, bool, error) { var year, month, day int var err error if month, err = strconv.Atoi(submatch[1]); err != nil { return 0, 0, 0, false, err } if day, err = strconv.Atoi(submatch[3]); err != nil { return 0, 0, 0, false, err } if year, err = strconv.Atoi(submatch[5]); err != nil { return 0, 0, 0, false, err } if year < 0 || year > 9999 || (year > 99 && year < 1900) { return 0, 0, 0, false, ErrParameterInvalid } return formatYear(year), month, day, submatch[8] == "", err } // strToDatePatternHandler2 parse and convert the given string in pattern mm // dd, yy to the date. func strToDatePatternHandler2(submatch []string) (int, int, int, bool, error) { var year, month, day int var err error month = month2num[submatch[1]] if day, err = strconv.Atoi(submatch[14]); err != nil { return 0, 0, 0, false, err } if year, err = strconv.Atoi(submatch[16]); err != nil { return 0, 0, 0, false, err } if year < 0 || year > 9999 || (year > 99 && year < 1900) { return 0, 0, 0, false, ErrParameterInvalid } return formatYear(year), month, day, submatch[19] == "", err } // strToDatePatternHandler3 parse and convert the given string in pattern // yy-mm-dd to the date. func strToDatePatternHandler3(submatch []string) (int, int, int, bool, error) { var year, month, day int v1, err := strconv.Atoi(submatch[1]) if err != nil { return 0, 0, 0, false, err } v2, err := strconv.Atoi(submatch[3]) if err != nil { return 0, 0, 0, false, err } v3, err := strconv.Atoi(submatch[5]) if err != nil { return 0, 0, 0, false, err } if v1 >= 1900 && v1 < 10000 { year = v1 month = v2 day = v3 } else if v1 > 0 && v1 < 13 { month = v1 day = v2 year = v3 } else { return 0, 0, 0, false, ErrParameterInvalid } return year, month, day, submatch[8] == "", err } // strToDatePatternHandler4 parse and convert the given string in pattern // yy-mmStr-dd, yy to the date. func strToDatePatternHandler4(submatch []string) (int, int, int, bool, error) { var year, month, day int var err error if year, err = strconv.Atoi(submatch[16]); err != nil { return 0, 0, 0, false, err } month = month2num[submatch[3]] if day, err = strconv.Atoi(submatch[1]); err != nil { return 0, 0, 0, false, err } return formatYear(year), month, day, submatch[19] == "", err } // strToDate parse and convert the given string to the date. func strToDate(str string) (int, int, int, bool, formulaArg) { pattern, submatch := "", []string{} for key, df := range dateFormats { submatch = df.FindStringSubmatch(str) if len(submatch) > 1 { pattern = key break } } if pattern == "" { return 0, 0, 0, false, newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } var ( timeIsEmpty bool year, month, day int err error ) if handler, ok := map[string]func(subsubmatch []string) (int, int, int, bool, error){ "mm/dd/yy": strToDatePatternHandler1, "mm dd, yy": strToDatePatternHandler2, "yy-mm-dd": strToDatePatternHandler3, "yy-mmStr-dd": strToDatePatternHandler4, }[pattern]; ok { if year, month, day, timeIsEmpty, err = handler(submatch); err != nil { return 0, 0, 0, false, newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } } if !validateDate(year, month, day) { return 0, 0, 0, false, newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } return year, month, day, timeIsEmpty, newEmptyFormulaArg() } // DATEVALUE function converts a text representation of a date into an Excel // date. For example, the function converts a text string representing a // date, into the serial number that represents the date in Excel's date-time // code. The syntax of the function is: // // DATEVALUE(date_text) // func (fn *formulaFuncs) DATEVALUE(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "DATEVALUE requires 1 argument") } dateText := argsList.Front().Value.(formulaArg).Value() if !isDateOnlyFmt(dateText) { if _, _, _, _, _, err := strToTime(dateText); err.Type == ArgError { return err } } y, m, d, _, err := strToDate(dateText) if err.Type == ArgError { return err } return newNumberFormulaArg(daysBetween(excelMinTime1900.Unix(), makeDate(y, time.Month(m), d)) + 1) } // DAY function returns the day of a date, represented by a serial number. The // day is given as an integer ranging from 1 to 31. The syntax of the // function is: // // DAY(serial_number) // func (fn *formulaFuncs) DAY(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "DAY requires exactly 1 argument") } arg := argsList.Front().Value.(formulaArg) num := arg.ToNumber() if num.Type != ArgNumber { dateString := strings.ToLower(arg.Value()) if !isDateOnlyFmt(dateString) { if _, _, _, _, _, err := strToTime(dateString); err.Type == ArgError { return err } } _, _, day, _, err := strToDate(dateString) if err.Type == ArgError { return err } return newNumberFormulaArg(float64(day)) } if num.Number < 0 { return newErrorFormulaArg(formulaErrorNUM, "DAY only accepts positive argument") } if num.Number <= 60 { return newNumberFormulaArg(math.Mod(num.Number, 31.0)) } return newNumberFormulaArg(float64(timeFromExcelTime(num.Number, false).Day())) } // DAYS function returns the number of days between two supplied dates. The // syntax of the function is: // // DAYS(end_date,start_date) // func (fn *formulaFuncs) DAYS(argsList *list.List) formulaArg { if argsList.Len() != 2 { return newErrorFormulaArg(formulaErrorVALUE, "DAYS requires 2 arguments") } args := fn.prepareDataValueArgs(2, argsList) if args.Type != ArgList { return args } end, start := args.List[0], args.List[1] return newNumberFormulaArg(end.Number - start.Number) } // ISOWEEKNUM function returns the ISO week number of a supplied date. The // syntax of the function is: // // ISOWEEKNUM(date) // func (fn *formulaFuncs) ISOWEEKNUM(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "ISOWEEKNUM requires 1 argument") } date := argsList.Front().Value.(formulaArg) num := date.ToNumber() weeknum := 0 if num.Type != ArgNumber { dateString := strings.ToLower(date.Value()) if !isDateOnlyFmt(dateString) { if _, _, _, _, _, err := strToTime(dateString); err.Type == ArgError { return err } } y, m, d, _, err := strToDate(dateString) if err.Type == ArgError { return err } _, weeknum = time.Date(y, time.Month(m), d, 0, 0, 0, 0, time.UTC).ISOWeek() } else { if num.Number < 0 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } _, weeknum = timeFromExcelTime(num.Number, false).ISOWeek() } return newNumberFormulaArg(float64(weeknum)) } // HOUR function returns an integer representing the hour component of a // supplied Excel time. The syntax of the function is: // // HOUR(serial_number) // func (fn *formulaFuncs) HOUR(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "HOUR requires exactly 1 argument") } date := argsList.Front().Value.(formulaArg) num := date.ToNumber() if num.Type != ArgNumber { timeString := strings.ToLower(date.Value()) if !isTimeOnlyFmt(timeString) { _, _, _, _, err := strToDate(timeString) if err.Type == ArgError { return err } } h, _, _, pm, _, err := strToTime(timeString) if err.Type == ArgError { return err } if pm { h += 12 } return newNumberFormulaArg(float64(h)) } if num.Number < 0 { return newErrorFormulaArg(formulaErrorNUM, "HOUR only accepts positive argument") } return newNumberFormulaArg(float64(timeFromExcelTime(num.Number, false).Hour())) } // MINUTE function returns an integer representing the minute component of a // supplied Excel time. The syntax of the function is: // // MINUTE(serial_number) // func (fn *formulaFuncs) MINUTE(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "MINUTE requires exactly 1 argument") } date := argsList.Front().Value.(formulaArg) num := date.ToNumber() if num.Type != ArgNumber { timeString := strings.ToLower(date.Value()) if !isTimeOnlyFmt(timeString) { _, _, _, _, err := strToDate(timeString) if err.Type == ArgError { return err } } _, m, _, _, _, err := strToTime(timeString) if err.Type == ArgError { return err } return newNumberFormulaArg(float64(m)) } if num.Number < 0 { return newErrorFormulaArg(formulaErrorNUM, "MINUTE only accepts positive argument") } return newNumberFormulaArg(float64(timeFromExcelTime(num.Number, false).Minute())) } // MONTH function returns the month of a date represented by a serial number. // The month is given as an integer, ranging from 1 (January) to 12 // (December). The syntax of the function is: // // MONTH(serial_number) // func (fn *formulaFuncs) MONTH(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "MONTH requires exactly 1 argument") } arg := argsList.Front().Value.(formulaArg) num := arg.ToNumber() if num.Type != ArgNumber { dateString := strings.ToLower(arg.Value()) if !isDateOnlyFmt(dateString) { if _, _, _, _, _, err := strToTime(dateString); err.Type == ArgError { return err } } _, month, _, _, err := strToDate(dateString) if err.Type == ArgError { return err } return newNumberFormulaArg(float64(month)) } if num.Number < 0 { return newErrorFormulaArg(formulaErrorNUM, "MONTH only accepts positive argument") } return newNumberFormulaArg(float64(timeFromExcelTime(num.Number, false).Month())) } // YEAR function returns an integer representing the year of a supplied date. // The syntax of the function is: // // YEAR(serial_number) // func (fn *formulaFuncs) YEAR(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "YEAR requires exactly 1 argument") } arg := argsList.Front().Value.(formulaArg) num := arg.ToNumber() if num.Type != ArgNumber { dateString := strings.ToLower(arg.Value()) if !isDateOnlyFmt(dateString) { if _, _, _, _, _, err := strToTime(dateString); err.Type == ArgError { return err } } year, _, _, _, err := strToDate(dateString) if err.Type == ArgError { return err } return newNumberFormulaArg(float64(year)) } if num.Number < 0 { return newErrorFormulaArg(formulaErrorNUM, "YEAR only accepts positive argument") } return newNumberFormulaArg(float64(timeFromExcelTime(num.Number, false).Year())) } // yearFracBasisCond is an implementation of the yearFracBasis1. func yearFracBasisCond(sy, sm, sd, ey, em, ed int) bool { return (isLeapYear(sy) && (sm < 2 || (sm == 2 && sd <= 29))) || (isLeapYear(ey) && (em > 2 || (em == 2 && ed == 29))) } // yearFracBasis0 function returns the fraction of a year that between two // supplied dates in US (NASD) 30/360 type of day. func yearFracBasis0(startDate, endDate float64) (dayDiff, daysInYear float64) { startTime, endTime := timeFromExcelTime(startDate, false), timeFromExcelTime(endDate, false) sy, smM, sd := startTime.Date() ey, emM, ed := endTime.Date() sm, em := int(smM), int(emM) if sd == 31 { sd-- } if sd == 30 && ed == 31 { ed-- } else if leap := isLeapYear(sy); sm == 2 && ((leap && sd == 29) || (!leap && sd == 28)) { sd = 30 if leap := isLeapYear(ey); em == 2 && ((leap && ed == 29) || (!leap && ed == 28)) { ed = 30 } } dayDiff = float64((ey-sy)*360 + (em-sm)*30 + (ed - sd)) daysInYear = 360 return } // yearFracBasis1 function returns the fraction of a year that between two // supplied dates in actual type of day. func yearFracBasis1(startDate, endDate float64) (dayDiff, daysInYear float64) { startTime, endTime := timeFromExcelTime(startDate, false), timeFromExcelTime(endDate, false) sy, smM, sd := startTime.Date() ey, emM, ed := endTime.Date() sm, em := int(smM), int(emM) dayDiff = endDate - startDate isYearDifferent := sy != ey if isYearDifferent && (ey != sy+1 || sm < em || (sm == em && sd < ed)) { dayCount := 0 for y := sy; y <= ey; y++ { dayCount += getYearDays(y, 1) } daysInYear = float64(dayCount) / float64(ey-sy+1) } else { if !isYearDifferent && isLeapYear(sy) { daysInYear = 366 } else { if isYearDifferent && yearFracBasisCond(sy, sm, sd, ey, em, ed) { daysInYear = 366 } else { daysInYear = 365 } } } return } // yearFracBasis4 function returns the fraction of a year that between two // supplied dates in European 30/360 type of day. func yearFracBasis4(startDate, endDate float64) (dayDiff, daysInYear float64) { startTime, endTime := timeFromExcelTime(startDate, false), timeFromExcelTime(endDate, false) sy, smM, sd := startTime.Date() ey, emM, ed := endTime.Date() sm, em := int(smM), int(emM) if sd == 31 { sd-- } if ed == 31 { ed-- } dayDiff = float64((ey-sy)*360 + (em-sm)*30 + (ed - sd)) daysInYear = 360 return } // yearFrac is an implementation of the formula function YEARFRAC. func yearFrac(startDate, endDate float64, basis int) formulaArg { startTime, endTime := timeFromExcelTime(startDate, false), timeFromExcelTime(endDate, false) if startTime == endTime { return newNumberFormulaArg(0) } var dayDiff, daysInYear float64 switch basis { case 0: dayDiff, daysInYear = yearFracBasis0(startDate, endDate) case 1: dayDiff, daysInYear = yearFracBasis1(startDate, endDate) case 2: dayDiff = endDate - startDate daysInYear = 360 case 3: dayDiff = endDate - startDate daysInYear = 365 case 4: dayDiff, daysInYear = yearFracBasis4(startDate, endDate) default: return newErrorFormulaArg(formulaErrorNUM, "invalid basis") } return newNumberFormulaArg(dayDiff / daysInYear) } // getYearDays return days of the year with specifying the type of day count // basis to be used. func getYearDays(year, basis int) int { switch basis { case 1: if isLeapYear(year) { return 366 } return 365 case 3: return 365 default: return 360 } } // YEARFRAC function returns the fraction of a year that is represented by the // number of whole days between two supplied dates. The syntax of the // function is: // // YEARFRAC(start_date,end_date,[basis]) // func (fn *formulaFuncs) YEARFRAC(argsList *list.List) formulaArg { if argsList.Len() != 2 && argsList.Len() != 3 { return newErrorFormulaArg(formulaErrorVALUE, "YEARFRAC requires 3 or 4 arguments") } args := fn.prepareDataValueArgs(2, argsList) if args.Type != ArgList { return args } start, end := args.List[0], args.List[1] basis := newNumberFormulaArg(0) if argsList.Len() == 3 { if basis = argsList.Back().Value.(formulaArg).ToNumber(); basis.Type != ArgNumber { return basis } } return yearFrac(start.Number, end.Number, int(basis.Number)) } // NOW function returns the current date and time. The function receives no // arguments and therefore. The syntax of the function is: // // NOW() // func (fn *formulaFuncs) NOW(argsList *list.List) formulaArg { if argsList.Len() != 0 { return newErrorFormulaArg(formulaErrorVALUE, "NOW accepts no arguments") } now := time.Now() _, offset := now.Zone() return newNumberFormulaArg(25569.0 + float64(now.Unix()+int64(offset))/86400) } // SECOND function returns an integer representing the second component of a // supplied Excel time. The syntax of the function is: // // SECOND(serial_number) // func (fn *formulaFuncs) SECOND(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "SECOND requires exactly 1 argument") } date := argsList.Front().Value.(formulaArg) num := date.ToNumber() if num.Type != ArgNumber { timeString := strings.ToLower(date.Value()) if !isTimeOnlyFmt(timeString) { _, _, _, _, err := strToDate(timeString) if err.Type == ArgError { return err } } _, _, s, _, _, err := strToTime(timeString) if err.Type == ArgError { return err } return newNumberFormulaArg(float64(int(s) % 60)) } if num.Number < 0 { return newErrorFormulaArg(formulaErrorNUM, "SECOND only accepts positive argument") } return newNumberFormulaArg(float64(timeFromExcelTime(num.Number, false).Second())) } // TIME function accepts three integer arguments representing hours, minutes // and seconds, and returns an Excel time. I.e. the function returns the // decimal value that represents the time in Excel. The syntax of the Time // function is: // // TIME(hour,minute,second) // func (fn *formulaFuncs) TIME(argsList *list.List) formulaArg { if argsList.Len() != 3 { return newErrorFormulaArg(formulaErrorVALUE, "TIME requires 3 number arguments") } h := argsList.Front().Value.(formulaArg).ToNumber() m := argsList.Front().Next().Value.(formulaArg).ToNumber() s := argsList.Back().Value.(formulaArg).ToNumber() if h.Type != ArgNumber || m.Type != ArgNumber || s.Type != ArgNumber { return newErrorFormulaArg(formulaErrorVALUE, "TIME requires 3 number arguments") } t := (h.Number*3600 + m.Number*60 + s.Number) / 86400 if t < 0 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } return newNumberFormulaArg(t) } // TIMEVALUE function converts a text representation of a time, into an Excel // time. The syntax of the Timevalue function is: // // TIMEVALUE(time_text) // func (fn *formulaFuncs) TIMEVALUE(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "TIMEVALUE requires exactly 1 argument") } date := argsList.Front().Value.(formulaArg) timeString := strings.ToLower(date.Value()) if !isTimeOnlyFmt(timeString) { _, _, _, _, err := strToDate(timeString) if err.Type == ArgError { return err } } h, m, s, pm, _, err := strToTime(timeString) if err.Type == ArgError { return err } if pm { h += 12 } args := list.New() args.PushBack(newNumberFormulaArg(float64(h))) args.PushBack(newNumberFormulaArg(float64(m))) args.PushBack(newNumberFormulaArg(s)) return fn.TIME(args) } // TODAY function returns the current date. The function has no arguments and // therefore. The syntax of the function is: // // TODAY() // func (fn *formulaFuncs) TODAY(argsList *list.List) formulaArg { if argsList.Len() != 0 { return newErrorFormulaArg(formulaErrorVALUE, "TODAY accepts no arguments") } now := time.Now() _, offset := now.Zone() return newNumberFormulaArg(daysBetween(excelMinTime1900.Unix(), now.Unix()+int64(offset)) + 1) } // makeDate return date as a Unix time, the number of seconds elapsed since // January 1, 1970 UTC. func makeDate(y int, m time.Month, d int) int64 { if y == 1900 && int(m) <= 2 { d-- } date := time.Date(y, m, d, 0, 0, 0, 0, time.UTC) return date.Unix() } // daysBetween return time interval of the given start timestamp and end // timestamp. func daysBetween(startDate, endDate int64) float64 { return float64(int(0.5 + float64((endDate-startDate)/86400))) } // WEEKDAY function returns an integer representing the day of the week for a // supplied date. The syntax of the function is: // // WEEKDAY(serial_number,[return_type]) // func (fn *formulaFuncs) WEEKDAY(argsList *list.List) formulaArg { if argsList.Len() < 1 { return newErrorFormulaArg(formulaErrorVALUE, "WEEKDAY requires at least 1 argument") } if argsList.Len() > 2 { return newErrorFormulaArg(formulaErrorVALUE, "WEEKDAY allows at most 2 arguments") } sn := argsList.Front().Value.(formulaArg) num := sn.ToNumber() weekday, returnType := 0, 1 if num.Type != ArgNumber { dateString := strings.ToLower(sn.Value()) if !isDateOnlyFmt(dateString) { if _, _, _, _, _, err := strToTime(dateString); err.Type == ArgError { return err } } y, m, d, _, err := strToDate(dateString) if err.Type == ArgError { return err } weekday = int(time.Date(y, time.Month(m), d, 0, 0, 0, 0, time.Now().Location()).Weekday()) } else { if num.Number < 0 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } weekday = int(timeFromExcelTime(num.Number, false).Weekday()) } if argsList.Len() == 2 { returnTypeArg := argsList.Back().Value.(formulaArg).ToNumber() if returnTypeArg.Type != ArgNumber { return returnTypeArg } returnType = int(returnTypeArg.Number) } if returnType == 2 { returnType = 11 } weekday++ if returnType == 1 { return newNumberFormulaArg(float64(weekday)) } if returnType == 3 { return newNumberFormulaArg(float64((weekday + 6 - 1) % 7)) } if returnType >= 11 && returnType <= 17 { return newNumberFormulaArg(float64((weekday+6-(returnType-10))%7 + 1)) } return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } // Text Functions // CHAR function returns the character relating to a supplied character set // number (from 1 to 255). syntax of the function is: // // CHAR(number) // func (fn *formulaFuncs) CHAR(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "CHAR requires 1 argument") } arg := argsList.Front().Value.(formulaArg).ToNumber() if arg.Type != ArgNumber { return arg } num := int(arg.Number) if num < 0 || num > MaxFieldLength { return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } return newStringFormulaArg(fmt.Sprintf("%c", num)) } // CLEAN removes all non-printable characters from a supplied text string. The // syntax of the function is: // // CLEAN(text) // func (fn *formulaFuncs) CLEAN(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "CLEAN requires 1 argument") } b := bytes.Buffer{} for _, c := range argsList.Front().Value.(formulaArg).String { if c > 31 { b.WriteRune(c) } } return newStringFormulaArg(b.String()) } // CODE function converts the first character of a supplied text string into // the associated numeric character set code used by your computer. The // syntax of the function is: // // CODE(text) // func (fn *formulaFuncs) CODE(argsList *list.List) formulaArg { return fn.code("CODE", argsList) } // code is an implementation of the formula functions CODE and UNICODE. func (fn *formulaFuncs) code(name string, argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires 1 argument", name)) } text := argsList.Front().Value.(formulaArg).Value() if len(text) == 0 { if name == "CODE" { return newNumberFormulaArg(0) } return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } return newNumberFormulaArg(float64(text[0])) } // CONCAT function joins together a series of supplied text strings into one // combined text string. // // CONCAT(text1,[text2],...) // func (fn *formulaFuncs) CONCAT(argsList *list.List) formulaArg { return fn.concat("CONCAT", argsList) } // CONCATENATE function joins together a series of supplied text strings into // one combined text string. // // CONCATENATE(text1,[text2],...) // func (fn *formulaFuncs) CONCATENATE(argsList *list.List) formulaArg { return fn.concat("CONCATENATE", argsList) } // concat is an implementation of the formula functions CONCAT and // CONCATENATE. func (fn *formulaFuncs) concat(name string, argsList *list.List) formulaArg { buf := bytes.Buffer{} for arg := argsList.Front(); arg != nil; arg = arg.Next() { token := arg.Value.(formulaArg) switch token.Type { case ArgString: buf.WriteString(token.String) case ArgNumber: if token.Boolean { if token.Number == 0 { buf.WriteString("FALSE") } else { buf.WriteString("TRUE") } } else { buf.WriteString(token.Value()) } default: return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires arguments to be strings", name)) } } return newStringFormulaArg(buf.String()) } // EXACT function tests if two supplied text strings or values are exactly // equal and if so, returns TRUE; Otherwise, the function returns FALSE. The // function is case-sensitive. The syntax of the function is: // // EXACT(text1,text2) // func (fn *formulaFuncs) EXACT(argsList *list.List) formulaArg { if argsList.Len() != 2 { return newErrorFormulaArg(formulaErrorVALUE, "EXACT requires 2 arguments") } text1 := argsList.Front().Value.(formulaArg).Value() text2 := argsList.Back().Value.(formulaArg).Value() return newBoolFormulaArg(text1 == text2) } // FIXED function rounds a supplied number to a specified number of decimal // places and then converts this into text. The syntax of the function is: // // FIXED(number,[decimals],[no_commas]) // func (fn *formulaFuncs) FIXED(argsList *list.List) formulaArg { if argsList.Len() < 1 { return newErrorFormulaArg(formulaErrorVALUE, "FIXED requires at least 1 argument") } if argsList.Len() > 3 { return newErrorFormulaArg(formulaErrorVALUE, "FIXED allows at most 3 arguments") } numArg := argsList.Front().Value.(formulaArg).ToNumber() if numArg.Type != ArgNumber { return numArg } precision, decimals, noCommas := 0, 0, false s := strings.Split(argsList.Front().Value.(formulaArg).Value(), ".") if argsList.Len() == 1 && len(s) == 2 { precision = len(s[1]) decimals = len(s[1]) } if argsList.Len() >= 2 { decimalsArg := argsList.Front().Next().Value.(formulaArg).ToNumber() if decimalsArg.Type != ArgNumber { return decimalsArg } decimals = int(decimalsArg.Number) } if argsList.Len() == 3 { noCommasArg := argsList.Back().Value.(formulaArg).ToBool() if noCommasArg.Type == ArgError { return noCommasArg } noCommas = noCommasArg.Boolean } n := math.Pow(10, float64(decimals)) r := numArg.Number * n fixed := float64(int(r+math.Copysign(0.5, r))) / n if decimals > 0 { precision = decimals } if noCommas { return newStringFormulaArg(fmt.Sprintf(fmt.Sprintf("%%.%df", precision), fixed)) } p := message.NewPrinter(language.English) return newStringFormulaArg(p.Sprintf(fmt.Sprintf("%%.%df", precision), fixed)) } // FIND function returns the position of a specified character or sub-string // within a supplied text string. The function is case-sensitive. The syntax // of the function is: // // FIND(find_text,within_text,[start_num]) // func (fn *formulaFuncs) FIND(argsList *list.List) formulaArg { return fn.find("FIND", argsList) } // FINDB counts each double-byte character as 2 when you have enabled the // editing of a language that supports DBCS and then set it as the default // language. Otherwise, FINDB counts each character as 1. The syntax of the // function is: // // FINDB(find_text,within_text,[start_num]) // func (fn *formulaFuncs) FINDB(argsList *list.List) formulaArg { return fn.find("FINDB", argsList) } // find is an implementation of the formula functions FIND and FINDB. func (fn *formulaFuncs) find(name string, argsList *list.List) formulaArg { if argsList.Len() < 2 { return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires at least 2 arguments", name)) } if argsList.Len() > 3 { return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s allows at most 3 arguments", name)) } findText := argsList.Front().Value.(formulaArg).Value() withinText := argsList.Front().Next().Value.(formulaArg).Value() startNum, result := 1, 1 if argsList.Len() == 3 { numArg := argsList.Back().Value.(formulaArg).ToNumber() if numArg.Type != ArgNumber { return numArg } if numArg.Number < 0 { return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } startNum = int(numArg.Number) } if findText == "" { return newNumberFormulaArg(float64(startNum)) } for idx := range withinText { if result < startNum { result++ } if strings.Index(withinText[idx:], findText) == 0 { return newNumberFormulaArg(float64(result)) } result++ } return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } // LEFT function returns a specified number of characters from the start of a // supplied text string. The syntax of the function is: // // LEFT(text,[num_chars]) // func (fn *formulaFuncs) LEFT(argsList *list.List) formulaArg { return fn.leftRight("LEFT", argsList) } // LEFTB returns the first character or characters in a text string, based on // the number of bytes you specify. The syntax of the function is: // // LEFTB(text,[num_bytes]) // func (fn *formulaFuncs) LEFTB(argsList *list.List) formulaArg { return fn.leftRight("LEFTB", argsList) } // leftRight is an implementation of the formula functions LEFT, LEFTB, RIGHT, // RIGHTB. TODO: support DBCS include Japanese, Chinese (Simplified), Chinese // (Traditional), and Korean. func (fn *formulaFuncs) leftRight(name string, argsList *list.List) formulaArg { if argsList.Len() < 1 { return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires at least 1 argument", name)) } if argsList.Len() > 2 { return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s allows at most 2 arguments", name)) } text, numChars := argsList.Front().Value.(formulaArg).Value(), 1 if argsList.Len() == 2 { numArg := argsList.Back().Value.(formulaArg).ToNumber() if numArg.Type != ArgNumber { return numArg } if numArg.Number < 0 { return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } numChars = int(numArg.Number) } if len(text) > numChars { if name == "LEFT" || name == "LEFTB" { return newStringFormulaArg(text[:numChars]) } return newStringFormulaArg(text[len(text)-numChars:]) } return newStringFormulaArg(text) } // LEN returns the length of a supplied text string. The syntax of the // function is: // // LEN(text) // func (fn *formulaFuncs) LEN(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "LEN requires 1 string argument") } return newStringFormulaArg(strconv.Itoa(len(argsList.Front().Value.(formulaArg).String))) } // LENB returns the number of bytes used to represent the characters in a text // string. LENB counts 2 bytes per character only when a DBCS language is set // as the default language. Otherwise LENB behaves the same as LEN, counting // 1 byte per character. The syntax of the function is: // // LENB(text) // // TODO: the languages that support DBCS include Japanese, Chinese // (Simplified), Chinese (Traditional), and Korean. func (fn *formulaFuncs) LENB(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "LENB requires 1 string argument") } return newStringFormulaArg(strconv.Itoa(len(argsList.Front().Value.(formulaArg).String))) } // LOWER converts all characters in a supplied text string to lower case. The // syntax of the function is: // // LOWER(text) // func (fn *formulaFuncs) LOWER(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "LOWER requires 1 argument") } return newStringFormulaArg(strings.ToLower(argsList.Front().Value.(formulaArg).String)) } // MID function returns a specified number of characters from the middle of a // supplied text string. The syntax of the function is: // // MID(text,start_num,num_chars) // func (fn *formulaFuncs) MID(argsList *list.List) formulaArg { return fn.mid("MID", argsList) } // MIDB returns a specific number of characters from a text string, starting // at the position you specify, based on the number of bytes you specify. The // syntax of the function is: // // MID(text,start_num,num_chars) // func (fn *formulaFuncs) MIDB(argsList *list.List) formulaArg { return fn.mid("MIDB", argsList) } // mid is an implementation of the formula functions MID and MIDB. TODO: // support DBCS include Japanese, Chinese (Simplified), Chinese // (Traditional), and Korean. func (fn *formulaFuncs) mid(name string, argsList *list.List) formulaArg { if argsList.Len() != 3 { return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires 3 arguments", name)) } text := argsList.Front().Value.(formulaArg).Value() startNumArg, numCharsArg := argsList.Front().Next().Value.(formulaArg).ToNumber(), argsList.Front().Next().Next().Value.(formulaArg).ToNumber() if startNumArg.Type != ArgNumber { return startNumArg } if numCharsArg.Type != ArgNumber { return numCharsArg } startNum := int(startNumArg.Number) if startNum < 0 { return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } textLen := len(text) if startNum > textLen { return newStringFormulaArg("") } startNum-- endNum := startNum + int(numCharsArg.Number) if endNum > textLen+1 { return newStringFormulaArg(text[startNum:]) } return newStringFormulaArg(text[startNum:endNum]) } // PROPER converts all characters in a supplied text string to proper case // (i.e. all letters that do not immediately follow another letter are set to // upper case and all other characters are lower case). The syntax of the // function is: // // PROPER(text) // func (fn *formulaFuncs) PROPER(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "PROPER requires 1 argument") } buf := bytes.Buffer{} isLetter := false for _, char := range argsList.Front().Value.(formulaArg).String { if !isLetter && unicode.IsLetter(char) { buf.WriteRune(unicode.ToUpper(char)) } else { buf.WriteRune(unicode.ToLower(char)) } isLetter = unicode.IsLetter(char) } return newStringFormulaArg(buf.String()) } // REPLACE function replaces all or part of a text string with another string. // The syntax of the function is: // // REPLACE(old_text,start_num,num_chars,new_text) // func (fn *formulaFuncs) REPLACE(argsList *list.List) formulaArg { return fn.replace("REPLACE", argsList) } // REPLACEB replaces part of a text string, based on the number of bytes you // specify, with a different text string. // // REPLACEB(old_text,start_num,num_chars,new_text) // func (fn *formulaFuncs) REPLACEB(argsList *list.List) formulaArg { return fn.replace("REPLACEB", argsList) } // replace is an implementation of the formula functions REPLACE and REPLACEB. // TODO: support DBCS include Japanese, Chinese (Simplified), Chinese // (Traditional), and Korean. func (fn *formulaFuncs) replace(name string, argsList *list.List) formulaArg { if argsList.Len() != 4 { return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires 4 arguments", name)) } oldText, newText := argsList.Front().Value.(formulaArg).Value(), argsList.Back().Value.(formulaArg).Value() startNumArg, numCharsArg := argsList.Front().Next().Value.(formulaArg).ToNumber(), argsList.Front().Next().Next().Value.(formulaArg).ToNumber() if startNumArg.Type != ArgNumber { return startNumArg } if numCharsArg.Type != ArgNumber { return numCharsArg } oldTextLen, startIdx := len(oldText), int(startNumArg.Number) if startIdx > oldTextLen { startIdx = oldTextLen + 1 } endIdx := startIdx + int(numCharsArg.Number) if endIdx > oldTextLen { endIdx = oldTextLen + 1 } if startIdx < 1 || endIdx < 1 { return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } result := oldText[:startIdx-1] + newText + oldText[endIdx-1:] return newStringFormulaArg(result) } // REPT function returns a supplied text string, repeated a specified number // of times. The syntax of the function is: // // REPT(text,number_times) // func (fn *formulaFuncs) REPT(argsList *list.List) formulaArg { if argsList.Len() != 2 { return newErrorFormulaArg(formulaErrorVALUE, "REPT requires 2 arguments") } text := argsList.Front().Value.(formulaArg) if text.Type != ArgString { return newErrorFormulaArg(formulaErrorVALUE, "REPT requires first argument to be a string") } times := argsList.Back().Value.(formulaArg).ToNumber() if times.Type != ArgNumber { return newErrorFormulaArg(formulaErrorVALUE, "REPT requires second argument to be a number") } if times.Number < 0 { return newErrorFormulaArg(formulaErrorVALUE, "REPT requires second argument to be >= 0") } if times.Number == 0 { return newStringFormulaArg("") } buf := bytes.Buffer{} for i := 0; i < int(times.Number); i++ { buf.WriteString(text.String) } return newStringFormulaArg(buf.String()) } // RIGHT function returns a specified number of characters from the end of a // supplied text string. The syntax of the function is: // // RIGHT(text,[num_chars]) // func (fn *formulaFuncs) RIGHT(argsList *list.List) formulaArg { return fn.leftRight("RIGHT", argsList) } // RIGHTB returns the last character or characters in a text string, based on // the number of bytes you specify. The syntax of the function is: // // RIGHTB(text,[num_bytes]) // func (fn *formulaFuncs) RIGHTB(argsList *list.List) formulaArg { return fn.leftRight("RIGHTB", argsList) } // SUBSTITUTE function replaces one or more instances of a given text string, // within an original text string. The syntax of the function is: // // SUBSTITUTE(text,old_text,new_text,[instance_num]) // func (fn *formulaFuncs) SUBSTITUTE(argsList *list.List) formulaArg { if argsList.Len() != 3 && argsList.Len() != 4 { return newErrorFormulaArg(formulaErrorVALUE, "SUBSTITUTE requires 3 or 4 arguments") } text, oldText := argsList.Front().Value.(formulaArg), argsList.Front().Next().Value.(formulaArg) newText, instanceNum := argsList.Front().Next().Next().Value.(formulaArg), 0 if argsList.Len() == 3 { return newStringFormulaArg(strings.Replace(text.Value(), oldText.Value(), newText.Value(), -1)) } instanceNumArg := argsList.Back().Value.(formulaArg).ToNumber() if instanceNumArg.Type != ArgNumber { return instanceNumArg } instanceNum = int(instanceNumArg.Number) if instanceNum < 1 { return newErrorFormulaArg(formulaErrorVALUE, "instance_num should be > 0") } str, oldTextLen, count, chars, pos := text.Value(), len(oldText.Value()), instanceNum, 0, -1 for { count-- index := strings.Index(str, oldText.Value()) if index == -1 { pos = -1 break } else { pos = index + chars if count == 0 { break } idx := oldTextLen + index chars += idx str = str[idx:] } } if pos == -1 { return newStringFormulaArg(text.Value()) } pre, post := text.Value()[:pos], text.Value()[pos+oldTextLen:] return newStringFormulaArg(pre + newText.Value() + post) } // TEXTJOIN function joins together a series of supplied text strings into one // combined text string. The user can specify a delimiter to add between the // individual text items, if required. The syntax of the function is: // // TEXTJOIN([delimiter],[ignore_empty],text1,[text2],...) // func (fn *formulaFuncs) TEXTJOIN(argsList *list.List) formulaArg { if argsList.Len() < 3 { return newErrorFormulaArg(formulaErrorVALUE, "TEXTJOIN requires at least 3 arguments") } if argsList.Len() > 252 { return newErrorFormulaArg(formulaErrorVALUE, "TEXTJOIN accepts at most 252 arguments") } delimiter := argsList.Front().Value.(formulaArg) ignoreEmpty := argsList.Front().Next().Value.(formulaArg).ToBool() if ignoreEmpty.Type != ArgNumber { return ignoreEmpty } args, ok := textJoin(argsList.Front().Next().Next(), []string{}, ignoreEmpty.Number != 0) if ok.Type != ArgNumber { return ok } result := strings.Join(args, delimiter.Value()) if len(result) > TotalCellChars { return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("TEXTJOIN function exceeds %d characters", TotalCellChars)) } return newStringFormulaArg(result) } // textJoin is an implementation of the formula function TEXTJOIN. func textJoin(arg *list.Element, arr []string, ignoreEmpty bool) ([]string, formulaArg) { for arg.Next(); arg != nil; arg = arg.Next() { switch arg.Value.(formulaArg).Type { case ArgError: return arr, arg.Value.(formulaArg) case ArgString: val := arg.Value.(formulaArg).Value() if val != "" || !ignoreEmpty { arr = append(arr, val) } case ArgNumber: arr = append(arr, arg.Value.(formulaArg).Value()) case ArgMatrix: for _, row := range arg.Value.(formulaArg).Matrix { argList := list.New().Init() for _, ele := range row { argList.PushBack(ele) } if argList.Len() > 0 { args, _ := textJoin(argList.Front(), []string{}, ignoreEmpty) arr = append(arr, args...) } } } } return arr, newBoolFormulaArg(true) } // TRIM removes extra spaces (i.e. all spaces except for single spaces between // words or characters) from a supplied text string. The syntax of the // function is: // // TRIM(text) // func (fn *formulaFuncs) TRIM(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "TRIM requires 1 argument") } return newStringFormulaArg(strings.TrimSpace(argsList.Front().Value.(formulaArg).String)) } // UNICHAR returns the Unicode character that is referenced by the given // numeric value. The syntax of the function is: // // UNICHAR(number) // func (fn *formulaFuncs) UNICHAR(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "UNICHAR requires 1 argument") } numArg := argsList.Front().Value.(formulaArg).ToNumber() if numArg.Type != ArgNumber { return numArg } if numArg.Number <= 0 || numArg.Number > 55295 { return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } return newStringFormulaArg(string(rune(numArg.Number))) } // UNICODE function returns the code point for the first character of a // supplied text string. The syntax of the function is: // // UNICODE(text) // func (fn *formulaFuncs) UNICODE(argsList *list.List) formulaArg { return fn.code("UNICODE", argsList) } // UPPER converts all characters in a supplied text string to upper case. The // syntax of the function is: // // UPPER(text) // func (fn *formulaFuncs) UPPER(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "UPPER requires 1 argument") } return newStringFormulaArg(strings.ToUpper(argsList.Front().Value.(formulaArg).String)) } // VALUE function converts a text string into a numeric value. The syntax of // the function is: // // VALUE(text) // func (fn *formulaFuncs) VALUE(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "VALUE requires 1 argument") } text := strings.ReplaceAll(argsList.Front().Value.(formulaArg).Value(), ",", "") percent := 1.0 if strings.HasSuffix(text, "%") { percent, text = 0.01, strings.TrimSuffix(text, "%") } decimal := big.Float{} if _, ok := decimal.SetString(text); ok { value, _ := decimal.Float64() return newNumberFormulaArg(value * percent) } dateValue, timeValue, errTime, errDate := 0.0, 0.0, false, false if !isDateOnlyFmt(text) { h, m, s, _, _, err := strToTime(text) errTime = err.Type == ArgError if !errTime { timeValue = (float64(h)*3600 + float64(m)*60 + s) / 86400 } } y, m, d, _, err := strToDate(text) errDate = err.Type == ArgError if !errDate { dateValue = daysBetween(excelMinTime1900.Unix(), makeDate(y, time.Month(m), d)) + 1 } if errTime && errDate { return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } return newNumberFormulaArg(dateValue + timeValue) } // Conditional Functions // IF function tests a supplied condition and returns one result if the // condition evaluates to TRUE, and another result if the condition evaluates // to FALSE. The syntax of the function is: // // IF(logical_test,value_if_true,value_if_false) // func (fn *formulaFuncs) IF(argsList *list.List) formulaArg { if argsList.Len() == 0 { return newErrorFormulaArg(formulaErrorVALUE, "IF requires at least 1 argument") } if argsList.Len() > 3 { return newErrorFormulaArg(formulaErrorVALUE, "IF accepts at most 3 arguments") } token := argsList.Front().Value.(formulaArg) var ( cond bool err error result formulaArg ) switch token.Type { case ArgString: if cond, err = strconv.ParseBool(token.String); err != nil { return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } if argsList.Len() == 1 { return newBoolFormulaArg(cond) } if cond { value := argsList.Front().Next().Value.(formulaArg) switch value.Type { case ArgNumber: result = value.ToNumber() default: result = newStringFormulaArg(value.String) } return result } if argsList.Len() == 3 { value := argsList.Back().Value.(formulaArg) switch value.Type { case ArgNumber: result = value.ToNumber() default: result = newStringFormulaArg(value.String) } } } return result } // Lookup and Reference Functions // ADDRESS function takes a row and a column number and returns a cell // reference as a text string. The syntax of the function is: // // ADDRESS(row_num,column_num,[abs_num],[a1],[sheet_text]) // func (fn *formulaFuncs) ADDRESS(argsList *list.List) formulaArg { if argsList.Len() < 2 { return newErrorFormulaArg(formulaErrorVALUE, "ADDRESS requires at least 2 arguments") } if argsList.Len() > 5 { return newErrorFormulaArg(formulaErrorVALUE, "ADDRESS requires at most 5 arguments") } rowNum := argsList.Front().Value.(formulaArg).ToNumber() if rowNum.Type != ArgNumber { return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } if rowNum.Number >= TotalRows { return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } colNum := argsList.Front().Next().Value.(formulaArg).ToNumber() if colNum.Type != ArgNumber { return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } absNum := newNumberFormulaArg(1) if argsList.Len() >= 3 { absNum = argsList.Front().Next().Next().Value.(formulaArg).ToNumber() if absNum.Type != ArgNumber { return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } } if absNum.Number < 1 || absNum.Number > 4 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } a1 := newBoolFormulaArg(true) if argsList.Len() >= 4 { a1 = argsList.Front().Next().Next().Next().Value.(formulaArg).ToBool() if a1.Type != ArgNumber { return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } } var sheetText string if argsList.Len() == 5 { sheetText = trimSheetName(argsList.Back().Value.(formulaArg).Value()) } if len(sheetText) > 0 { sheetText = fmt.Sprintf("%s!", sheetText) } formatter := addressFmtMaps[fmt.Sprintf("%d_%s", int(absNum.Number), a1.Value())] addr, err := formatter(int(colNum.Number), int(colNum.Number)) if err != nil { return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } return newStringFormulaArg(fmt.Sprintf("%s%s", sheetText, addr)) } // CHOOSE function returns a value from an array, that corresponds to a // supplied index number (position). The syntax of the function is: // // CHOOSE(index_num,value1,[value2],...) // func (fn *formulaFuncs) CHOOSE(argsList *list.List) formulaArg { if argsList.Len() < 2 { return newErrorFormulaArg(formulaErrorVALUE, "CHOOSE requires 2 arguments") } idx, err := strconv.Atoi(argsList.Front().Value.(formulaArg).String) if err != nil { return newErrorFormulaArg(formulaErrorVALUE, "CHOOSE requires first argument of type number") } if argsList.Len() <= idx { return newErrorFormulaArg(formulaErrorVALUE, "index_num should be <= to the number of values") } arg := argsList.Front() for i := 0; i < idx; i++ { arg = arg.Next() } return arg.Value.(formulaArg) } // deepMatchRune finds whether the text deep matches/satisfies the pattern // string. func deepMatchRune(str, pattern []rune, simple bool) bool { for len(pattern) > 0 { switch pattern[0] { default: if len(str) == 0 || str[0] != pattern[0] { return false } case '?': if len(str) == 0 && !simple { return false } case '*': return deepMatchRune(str, pattern[1:], simple) || (len(str) > 0 && deepMatchRune(str[1:], pattern, simple)) } str = str[1:] pattern = pattern[1:] } return len(str) == 0 && len(pattern) == 0 } // matchPattern finds whether the text matches or satisfies the pattern // string. The pattern supports '*' and '?' wildcards in the pattern string. func matchPattern(pattern, name string) (matched bool) { if pattern == "" { return name == pattern } if pattern == "*" { return true } rname, rpattern := make([]rune, 0, len(name)), make([]rune, 0, len(pattern)) for _, r := range name { rname = append(rname, r) } for _, r := range pattern { rpattern = append(rpattern, r) } simple := false // Does extended wildcard '*' and '?' match. return deepMatchRune(rname, rpattern, simple) } // compareFormulaArg compares the left-hand sides and the right-hand sides // formula arguments by given conditions such as case sensitive, if exact // match, and make compare result as formula criteria condition type. func compareFormulaArg(lhs, rhs, matchMode formulaArg, caseSensitive bool) byte { if lhs.Type != rhs.Type { return criteriaErr } switch lhs.Type { case ArgNumber: if lhs.Number == rhs.Number { return criteriaEq } if lhs.Number < rhs.Number { return criteriaL } return criteriaG case ArgString: ls, rs := lhs.String, rhs.String if !caseSensitive { ls, rs = strings.ToLower(ls), strings.ToLower(rs) } if matchMode.Number == matchModeWildcard { if matchPattern(rs, ls) { return criteriaEq } } return map[int]byte{1: criteriaG, -1: criteriaL, 0: criteriaEq}[strings.Compare(ls, rs)] case ArgEmpty: return criteriaEq case ArgList: return compareFormulaArgList(lhs, rhs, matchMode, caseSensitive) case ArgMatrix: return compareFormulaArgMatrix(lhs, rhs, matchMode, caseSensitive) } return criteriaErr } // compareFormulaArgList compares the left-hand sides and the right-hand sides // list type formula arguments. func compareFormulaArgList(lhs, rhs, matchMode formulaArg, caseSensitive bool) byte { if len(lhs.List) < len(rhs.List) { return criteriaL } if len(lhs.List) > len(rhs.List) { return criteriaG } for arg := range lhs.List { criteria := compareFormulaArg(lhs.List[arg], rhs.List[arg], matchMode, caseSensitive) if criteria != criteriaEq { return criteria } } return criteriaEq } // compareFormulaArgMatrix compares the left-hand sides and the right-hand sides // matrix type formula arguments. func compareFormulaArgMatrix(lhs, rhs, matchMode formulaArg, caseSensitive bool) byte { if len(lhs.Matrix) < len(rhs.Matrix) { return criteriaL } if len(lhs.Matrix) > len(rhs.Matrix) { return criteriaG } for i := range lhs.Matrix { left := lhs.Matrix[i] right := lhs.Matrix[i] if len(left) < len(right) { return criteriaL } if len(left) > len(right) { return criteriaG } for arg := range left { criteria := compareFormulaArg(left[arg], right[arg], matchMode, caseSensitive) if criteria != criteriaEq { return criteria } } } return criteriaEq } // COLUMN function returns the first column number within a supplied reference // or the number of the current column. The syntax of the function is: // // COLUMN([reference]) // func (fn *formulaFuncs) COLUMN(argsList *list.List) formulaArg { if argsList.Len() > 1 { return newErrorFormulaArg(formulaErrorVALUE, "COLUMN requires at most 1 argument") } if argsList.Len() == 1 { if argsList.Front().Value.(formulaArg).cellRanges != nil && argsList.Front().Value.(formulaArg).cellRanges.Len() > 0 { return newNumberFormulaArg(float64(argsList.Front().Value.(formulaArg).cellRanges.Front().Value.(cellRange).From.Col)) } if argsList.Front().Value.(formulaArg).cellRefs != nil && argsList.Front().Value.(formulaArg).cellRefs.Len() > 0 { return newNumberFormulaArg(float64(argsList.Front().Value.(formulaArg).cellRefs.Front().Value.(cellRef).Col)) } return newErrorFormulaArg(formulaErrorVALUE, "invalid reference") } col, _, _ := CellNameToCoordinates(fn.cell) return newNumberFormulaArg(float64(col)) } // calcColumnsMinMax calculation min and max value for given formula arguments // sequence of the formula function COLUMNS. func calcColumnsMinMax(argsList *list.List) (min, max int) { if argsList.Front().Value.(formulaArg).cellRanges != nil && argsList.Front().Value.(formulaArg).cellRanges.Len() > 0 { crs := argsList.Front().Value.(formulaArg).cellRanges for cr := crs.Front(); cr != nil; cr = cr.Next() { if min == 0 { min = cr.Value.(cellRange).From.Col } if min > cr.Value.(cellRange).From.Col { min = cr.Value.(cellRange).From.Col } if min > cr.Value.(cellRange).To.Col { min = cr.Value.(cellRange).To.Col } if max < cr.Value.(cellRange).To.Col { max = cr.Value.(cellRange).To.Col } if max < cr.Value.(cellRange).From.Col { max = cr.Value.(cellRange).From.Col } } } if argsList.Front().Value.(formulaArg).cellRefs != nil && argsList.Front().Value.(formulaArg).cellRefs.Len() > 0 { cr := argsList.Front().Value.(formulaArg).cellRefs for refs := cr.Front(); refs != nil; refs = refs.Next() { if min == 0 { min = refs.Value.(cellRef).Col } if min > refs.Value.(cellRef).Col { min = refs.Value.(cellRef).Col } if max < refs.Value.(cellRef).Col { max = refs.Value.(cellRef).Col } } } return } // COLUMNS function receives an Excel range and returns the number of columns // that are contained within the range. The syntax of the function is: // // COLUMNS(array) // func (fn *formulaFuncs) COLUMNS(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "COLUMNS requires 1 argument") } min, max := calcColumnsMinMax(argsList) if max == TotalColumns { return newNumberFormulaArg(float64(TotalColumns)) } result := max - min + 1 if max == min { if min == 0 { return newErrorFormulaArg(formulaErrorVALUE, "invalid reference") } return newNumberFormulaArg(float64(1)) } return newNumberFormulaArg(float64(result)) } // FORMULATEXT function returns a formula as a text string. The syntax of the // function is: // // FORMULATEXT(reference) // func (fn *formulaFuncs) FORMULATEXT(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "FORMULATEXT requires 1 argument") } refs := argsList.Front().Value.(formulaArg).cellRefs col, row := 0, 0 if refs != nil && refs.Len() > 0 { col, row = refs.Front().Value.(cellRef).Col, refs.Front().Value.(cellRef).Row } ranges := argsList.Front().Value.(formulaArg).cellRanges if ranges != nil && ranges.Len() > 0 { col, row = ranges.Front().Value.(cellRange).From.Col, ranges.Front().Value.(cellRange).From.Row } cell, err := CoordinatesToCellName(col, row) if err != nil { return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } formula, _ := fn.f.GetCellFormula(fn.sheet, cell) return newStringFormulaArg(formula) } // checkHVLookupArgs checking arguments, prepare extract mode, lookup value, // and data for the formula functions HLOOKUP and VLOOKUP. func checkHVLookupArgs(name string, argsList *list.List) (idx int, lookupValue, tableArray, matchMode, errArg formulaArg) { unit := map[string]string{ "HLOOKUP": "row", "VLOOKUP": "col", }[name] if argsList.Len() < 3 { errArg = newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires at least 3 arguments", name)) return } if argsList.Len() > 4 { errArg = newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires at most 4 arguments", name)) return } lookupValue = argsList.Front().Value.(formulaArg) tableArray = argsList.Front().Next().Value.(formulaArg) if tableArray.Type != ArgMatrix { errArg = newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires second argument of table array", name)) return } arg := argsList.Front().Next().Next().Value.(formulaArg).ToNumber() if arg.Type != ArgNumber { errArg = newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires numeric %s argument", name, unit)) return } idx, matchMode = int(arg.Number)-1, newNumberFormulaArg(matchModeMaxLess) if argsList.Len() == 4 { rangeLookup := argsList.Back().Value.(formulaArg).ToBool() if rangeLookup.Type == ArgError { errArg = newErrorFormulaArg(formulaErrorVALUE, rangeLookup.Error) return } if rangeLookup.Number == 0 { matchMode = newNumberFormulaArg(matchModeWildcard) } } return } // HLOOKUP function 'looks up' a given value in the top row of a data array // (or table), and returns the corresponding value from another row of the // array. The syntax of the function is: // // HLOOKUP(lookup_value,table_array,row_index_num,[range_lookup]) // func (fn *formulaFuncs) HLOOKUP(argsList *list.List) formulaArg { rowIdx, lookupValue, tableArray, matchMode, errArg := checkHVLookupArgs("HLOOKUP", argsList) if errArg.Type == ArgError { return errArg } var matchIdx int var wasExact bool if matchMode.Number == matchModeWildcard || len(tableArray.Matrix) == TotalRows { matchIdx, wasExact = lookupLinearSearch(false, lookupValue, tableArray, matchMode, newNumberFormulaArg(searchModeLinear)) } else { matchIdx, wasExact = lookupBinarySearch(false, lookupValue, tableArray, matchMode, newNumberFormulaArg(searchModeAscBinary)) } if matchIdx == -1 { return newErrorFormulaArg(formulaErrorNA, "HLOOKUP no result found") } if rowIdx < 0 || rowIdx >= len(tableArray.Matrix) { return newErrorFormulaArg(formulaErrorNA, "HLOOKUP has invalid row index") } row := tableArray.Matrix[rowIdx] if wasExact || matchMode.Number == matchModeWildcard { return row[matchIdx] } return newErrorFormulaArg(formulaErrorNA, "HLOOKUP no result found") } // calcMatch returns the position of the value by given match type, criteria // and lookup array for the formula function MATCH. func calcMatch(matchType int, criteria *formulaCriteria, lookupArray []formulaArg) formulaArg { switch matchType { case 0: for i, arg := range lookupArray { if ok, _ := formulaCriteriaEval(arg.Value(), criteria); ok { return newNumberFormulaArg(float64(i + 1)) } } case -1: for i, arg := range lookupArray { if ok, _ := formulaCriteriaEval(arg.Value(), criteria); ok { return newNumberFormulaArg(float64(i + 1)) } if ok, _ := formulaCriteriaEval(arg.Value(), &formulaCriteria{ Type: criteriaL, Condition: criteria.Condition, }); ok { if i == 0 { return newErrorFormulaArg(formulaErrorNA, formulaErrorNA) } return newNumberFormulaArg(float64(i)) } } case 1: for i, arg := range lookupArray { if ok, _ := formulaCriteriaEval(arg.Value(), criteria); ok { return newNumberFormulaArg(float64(i + 1)) } if ok, _ := formulaCriteriaEval(arg.Value(), &formulaCriteria{ Type: criteriaG, Condition: criteria.Condition, }); ok { if i == 0 { return newErrorFormulaArg(formulaErrorNA, formulaErrorNA) } return newNumberFormulaArg(float64(i)) } } } return newErrorFormulaArg(formulaErrorNA, formulaErrorNA) } // MATCH function looks up a value in an array, and returns the position of // the value within the array. The user can specify that the function should // only return a result if an exact match is found, or that the function // should return the position of the closest match (above or below), if an // exact match is not found. The syntax of the Match function is: // // MATCH(lookup_value,lookup_array,[match_type]) // func (fn *formulaFuncs) MATCH(argsList *list.List) formulaArg { if argsList.Len() != 2 && argsList.Len() != 3 { return newErrorFormulaArg(formulaErrorVALUE, "MATCH requires 1 or 2 arguments") } var ( matchType = 1 lookupArray []formulaArg lookupArrayArg = argsList.Front().Next().Value.(formulaArg) lookupArrayErr = "MATCH arguments lookup_array should be one-dimensional array" ) if argsList.Len() == 3 { matchTypeArg := argsList.Back().Value.(formulaArg).ToNumber() if matchTypeArg.Type != ArgNumber { return newErrorFormulaArg(formulaErrorVALUE, "MATCH requires numeric match_type argument") } if matchTypeArg.Number == -1 || matchTypeArg.Number == 0 { matchType = int(matchTypeArg.Number) } } switch lookupArrayArg.Type { case ArgMatrix: if len(lookupArrayArg.Matrix[0]) != 1 { return newErrorFormulaArg(formulaErrorNA, lookupArrayErr) } lookupArray = lookupArrayArg.ToList() default: return newErrorFormulaArg(formulaErrorNA, lookupArrayErr) } return calcMatch(matchType, formulaCriteriaParser(argsList.Front().Value.(formulaArg).String), lookupArray) } // TRANSPOSE function 'transposes' an array of cells (i.e. the function copies // a horizontal range of cells into a vertical range and vice versa). The // syntax of the function is: // // TRANSPOSE(array) // func (fn *formulaFuncs) TRANSPOSE(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "TRANSPOSE requires 1 argument") } args := argsList.Back().Value.(formulaArg).ToList() rmin, rmax := calcRowsMinMax(argsList) cmin, cmax := calcColumnsMinMax(argsList) cols, rows := cmax-cmin+1, rmax-rmin+1 src := make([][]formulaArg, 0) for i := 0; i < len(args); i += cols { src = append(src, args[i:i+cols]) } mtx := make([][]formulaArg, cols) for r, row := range src { colIdx := r % rows for c, cell := range row { rowIdx := c % cols if len(mtx[rowIdx]) == 0 { mtx[rowIdx] = make([]formulaArg, rows) } mtx[rowIdx][colIdx] = cell } } return newMatrixFormulaArg(mtx) } // lookupLinearSearch sequentially checks each look value of the lookup array until // a match is found or the whole list has been searched. func lookupLinearSearch(vertical bool, lookupValue, lookupArray, matchMode, searchMode formulaArg) (int, bool) { tableArray := []formulaArg{} if vertical { for _, row := range lookupArray.Matrix { tableArray = append(tableArray, row[0]) } } else { tableArray = lookupArray.Matrix[0] } matchIdx, wasExact := -1, false start: for i, cell := range tableArray { lhs := cell if lookupValue.Type == ArgNumber { if lhs = cell.ToNumber(); lhs.Type == ArgError { lhs = cell } } else if lookupValue.Type == ArgMatrix { lhs = lookupArray } if compareFormulaArg(lhs, lookupValue, matchMode, false) == criteriaEq { matchIdx = i wasExact = true if searchMode.Number == searchModeLinear { break start } } if matchMode.Number == matchModeMinGreater || matchMode.Number == matchModeMaxLess { matchIdx = int(calcMatch(int(matchMode.Number), formulaCriteriaParser(lookupValue.Value()), tableArray).Number) continue } } return matchIdx, wasExact } // VLOOKUP function 'looks up' a given value in the left-hand column of a // data array (or table), and returns the corresponding value from another // column of the array. The syntax of the function is: // // VLOOKUP(lookup_value,table_array,col_index_num,[range_lookup]) // func (fn *formulaFuncs) VLOOKUP(argsList *list.List) formulaArg { colIdx, lookupValue, tableArray, matchMode, errArg := checkHVLookupArgs("VLOOKUP", argsList) if errArg.Type == ArgError { return errArg } var matchIdx int var wasExact bool if matchMode.Number == matchModeWildcard || len(tableArray.Matrix) == TotalRows { matchIdx, wasExact = lookupLinearSearch(true, lookupValue, tableArray, matchMode, newNumberFormulaArg(searchModeLinear)) } else { matchIdx, wasExact = lookupBinarySearch(true, lookupValue, tableArray, matchMode, newNumberFormulaArg(searchModeAscBinary)) } if matchIdx == -1 { return newErrorFormulaArg(formulaErrorNA, "VLOOKUP no result found") } mtx := tableArray.Matrix[matchIdx] if colIdx < 0 || colIdx >= len(mtx) { return newErrorFormulaArg(formulaErrorNA, "VLOOKUP has invalid column index") } if wasExact || matchMode.Number == matchModeWildcard { return mtx[colIdx] } return newErrorFormulaArg(formulaErrorNA, "VLOOKUP no result found") } // lookupBinarySearch finds the position of a target value when range lookup // is TRUE, if the data of table array can't guarantee be sorted, it will // return wrong result. func lookupBinarySearch(vertical bool, lookupValue, lookupArray, matchMode, searchMode formulaArg) (matchIdx int, wasExact bool) { tableArray := []formulaArg{} if vertical { for _, row := range lookupArray.Matrix { tableArray = append(tableArray, row[0]) } } else { tableArray = lookupArray.Matrix[0] } var low, high, lastMatchIdx int = 0, len(tableArray) - 1, -1 count := high for low <= high { mid := low + (high-low)/2 cell := tableArray[mid] lhs := cell if lookupValue.Type == ArgNumber { if lhs = cell.ToNumber(); lhs.Type == ArgError { lhs = cell } } else if lookupValue.Type == ArgMatrix && vertical { lhs = lookupArray } result := compareFormulaArg(lhs, lookupValue, matchMode, false) if result == criteriaEq { matchIdx, wasExact = mid, true if searchMode.Number == searchModeDescBinary { matchIdx = count - matchIdx } return } else if result == criteriaG { high = mid - 1 } else if result == criteriaL { matchIdx = mid if lhs.Value() != "" { lastMatchIdx = matchIdx } low = mid + 1 } else { return -1, false } } matchIdx, wasExact = lastMatchIdx, true return } // checkLookupArgs checking arguments, prepare lookup value, and data for the // formula function LOOKUP. func checkLookupArgs(argsList *list.List) (arrayForm bool, lookupValue, lookupVector, errArg formulaArg) { if argsList.Len() < 2 { errArg = newErrorFormulaArg(formulaErrorVALUE, "LOOKUP requires at least 2 arguments") return } if argsList.Len() > 3 { errArg = newErrorFormulaArg(formulaErrorVALUE, "LOOKUP requires at most 3 arguments") return } lookupValue = argsList.Front().Value.(formulaArg) lookupVector = argsList.Front().Next().Value.(formulaArg) if lookupVector.Type != ArgMatrix && lookupVector.Type != ArgList { errArg = newErrorFormulaArg(formulaErrorVALUE, "LOOKUP requires second argument of table array") return } arrayForm = lookupVector.Type == ArgMatrix if arrayForm && len(lookupVector.Matrix) == 0 { errArg = newErrorFormulaArg(formulaErrorVALUE, "LOOKUP requires not empty range as second argument") } return } // iterateLookupArgs iterate arguments to extract columns and calculate match // index for the formula function LOOKUP. func iterateLookupArgs(lookupValue, lookupVector formulaArg) ([]formulaArg, int, bool) { cols, matchIdx, ok := lookupCol(lookupVector, 0), -1, false for idx, col := range cols { lhs := lookupValue switch col.Type { case ArgNumber: lhs = lhs.ToNumber() if !col.Boolean { if lhs.Type == ArgError { lhs = lookupValue } } } compare := compareFormulaArg(lhs, col, newNumberFormulaArg(matchModeMaxLess), false) // Find exact match if compare == criteriaEq { matchIdx = idx break } // Find nearest match if lookup value is more than or equal to the first value in lookup vector if idx == 0 { ok = compare == criteriaG } else if ok && compare == criteriaL && matchIdx == -1 { matchIdx = idx - 1 } } return cols, matchIdx, ok } // index is an implementation of the formula function INDEX. func (fn *formulaFuncs) index(array formulaArg, rowIdx, colIdx int) formulaArg { var cells []formulaArg if array.Type == ArgMatrix { cellMatrix := array.Matrix if rowIdx < -1 || rowIdx >= len(cellMatrix) { return newErrorFormulaArg(formulaErrorREF, "INDEX row_num out of range") } if rowIdx == -1 { if colIdx >= len(cellMatrix[0]) { return newErrorFormulaArg(formulaErrorREF, "INDEX col_num out of range") } column := [][]formulaArg{} for _, cells = range cellMatrix { column = append(column, []formulaArg{cells[colIdx]}) } return newMatrixFormulaArg(column) } cells = cellMatrix[rowIdx] } if colIdx < -1 || colIdx >= len(cells) { return newErrorFormulaArg(formulaErrorREF, "INDEX col_num out of range") } return newListFormulaArg(cells) } // validateMatchMode check the number of match mode if be equal to 0, 1, -1 or // 2. func validateMatchMode(mode float64) bool { return mode == matchModeExact || mode == matchModeMinGreater || mode == matchModeMaxLess || mode == matchModeWildcard } // validateSearchMode check the number of search mode if be equal to 1, -1, 2 // or -2. func validateSearchMode(mode float64) bool { return mode == searchModeLinear || mode == searchModeReverseLinear || mode == searchModeAscBinary || mode == searchModeDescBinary } // prepareXlookupArgs checking and prepare arguments for the formula function // XLOOKUP. func (fn *formulaFuncs) prepareXlookupArgs(argsList *list.List) formulaArg { if argsList.Len() < 3 { return newErrorFormulaArg(formulaErrorVALUE, "XLOOKUP requires at least 3 arguments") } if argsList.Len() > 6 { return newErrorFormulaArg(formulaErrorVALUE, "XLOOKUP allows at most 6 arguments") } lookupValue := argsList.Front().Value.(formulaArg) lookupArray := argsList.Front().Next().Value.(formulaArg) returnArray := argsList.Front().Next().Next().Value.(formulaArg) ifNotFond := newErrorFormulaArg(formulaErrorNA, formulaErrorNA) matchMode, searchMode := newNumberFormulaArg(matchModeExact), newNumberFormulaArg(searchModeLinear) if argsList.Len() > 3 { ifNotFond = argsList.Front().Next().Next().Next().Value.(formulaArg) } if argsList.Len() > 4 { if matchMode = argsList.Front().Next().Next().Next().Next().Value.(formulaArg).ToNumber(); matchMode.Type != ArgNumber { return matchMode } } if argsList.Len() > 5 { if searchMode = argsList.Back().Value.(formulaArg).ToNumber(); searchMode.Type != ArgNumber { return searchMode } } if lookupArray.Type != ArgMatrix || returnArray.Type != ArgMatrix { return newErrorFormulaArg(formulaErrorNA, formulaErrorNA) } if !validateMatchMode(matchMode.Number) || !validateSearchMode(searchMode.Number) { return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } return newListFormulaArg([]formulaArg{lookupValue, lookupArray, returnArray, ifNotFond, matchMode, searchMode}) } // xlookup is an implementation of the formula function XLOOKUP. func (fn *formulaFuncs) xlookup(lookupRows, lookupCols, returnArrayRows, returnArrayCols, matchIdx int, condition1, condition2, condition3, condition4 bool, returnArray formulaArg, ) formulaArg { result := [][]formulaArg{} for rowIdx, row := range returnArray.Matrix { for colIdx, cell := range row { if condition1 { if condition2 { result = append(result, []formulaArg{cell}) continue } if returnArrayRows > 1 && returnArrayCols > 1 { return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } } if condition3 { if returnArrayCols != lookupCols { return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } if colIdx == matchIdx { result = append(result, []formulaArg{cell}) continue } } if condition4 { if returnArrayRows != lookupRows { return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } if rowIdx == matchIdx { if len(result) == 0 { result = append(result, []formulaArg{cell}) continue } result[0] = append(result[0], cell) } } } } array := newMatrixFormulaArg(result) cells := array.ToList() if len(cells) == 1 { return cells[0] } return array } // XLOOKUP function searches a range or an array, and then returns the item // corresponding to the first match it finds. If no match exists, then // XLOOKUP can return the closest (approximate) match. The syntax of the // function is: // // XLOOKUP(lookup_value,lookup_array,return_array,[if_not_found],[match_mode],[search_mode]) // func (fn *formulaFuncs) XLOOKUP(argsList *list.List) formulaArg { args := fn.prepareXlookupArgs(argsList) if args.Type != ArgList { return args } lookupValue, lookupArray, returnArray, ifNotFond, matchMode, searchMode := args.List[0], args.List[1], args.List[2], args.List[3], args.List[4], args.List[5] lookupRows, lookupCols := len(lookupArray.Matrix), 0 if lookupRows > 0 { lookupCols = len(lookupArray.Matrix[0]) } if lookupRows != 1 && lookupCols != 1 { return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } verticalLookup := lookupRows >= lookupCols var matchIdx int switch searchMode.Number { case searchModeLinear, searchModeReverseLinear: matchIdx, _ = lookupLinearSearch(verticalLookup, lookupValue, lookupArray, matchMode, searchMode) default: matchIdx, _ = lookupBinarySearch(verticalLookup, lookupValue, lookupArray, matchMode, searchMode) } if matchIdx == -1 { return ifNotFond } returnArrayRows, returnArrayCols := len(returnArray.Matrix), len(returnArray.Matrix[0]) condition1 := lookupRows == 1 && lookupCols == 1 condition2 := returnArrayRows == 1 || returnArrayCols == 1 condition3 := lookupRows == 1 && lookupCols > 1 condition4 := lookupRows > 1 && lookupCols == 1 return fn.xlookup(lookupRows, lookupCols, returnArrayRows, returnArrayCols, matchIdx, condition1, condition2, condition3, condition4, returnArray) } // INDEX function returns a reference to a cell that lies in a specified row // and column of a range of cells. The syntax of the function is: // // INDEX(array,row_num,[col_num]) // func (fn *formulaFuncs) INDEX(argsList *list.List) formulaArg { if argsList.Len() < 2 || argsList.Len() > 3 { return newErrorFormulaArg(formulaErrorVALUE, "INDEX requires 2 or 3 arguments") } array := argsList.Front().Value.(formulaArg) if array.Type != ArgMatrix && array.Type != ArgList { array = newMatrixFormulaArg([][]formulaArg{{array}}) } rowArg := argsList.Front().Next().Value.(formulaArg).ToNumber() if rowArg.Type != ArgNumber { return rowArg } rowIdx, colIdx := int(rowArg.Number)-1, -1 if argsList.Len() == 3 { colArg := argsList.Back().Value.(formulaArg).ToNumber() if colArg.Type != ArgNumber { return colArg } colIdx = int(colArg.Number) - 1 } if rowIdx == -1 && colIdx == -1 { if len(array.ToList()) != 1 { return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } return array.ToList()[0] } cells := fn.index(array, rowIdx, colIdx) if cells.Type != ArgList { return cells } if colIdx == -1 { return newMatrixFormulaArg([][]formulaArg{cells.List}) } return cells.List[colIdx] } // INDIRECT function converts a text string into a cell reference. The syntax // of the Indirect function is: // // INDIRECT(ref_text,[a1]) // func (fn *formulaFuncs) INDIRECT(argsList *list.List) formulaArg { if argsList.Len() != 1 && argsList.Len() != 2 { return newErrorFormulaArg(formulaErrorVALUE, "INDIRECT requires 1 or 2 arguments") } refText := argsList.Front().Value.(formulaArg).Value() a1 := newBoolFormulaArg(true) if argsList.Len() == 2 { if a1 = argsList.Back().Value.(formulaArg).ToBool(); a1.Type != ArgNumber { return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } } R1C1ToA1 := func(ref string) (cell string, err error) { parts := strings.Split(strings.TrimLeft(ref, "R"), "C") if len(parts) != 2 { return } row, err := strconv.Atoi(parts[0]) if err != nil { return } col, err := strconv.Atoi(parts[1]) if err != nil { return } cell, err = CoordinatesToCellName(col, row) return } refs := strings.Split(refText, ":") fromRef, toRef := refs[0], "" if len(refs) == 2 { toRef = refs[1] } if a1.Number == 0 { from, err := R1C1ToA1(refs[0]) if err != nil { return newErrorFormulaArg(formulaErrorREF, formulaErrorREF) } fromRef = from if len(refs) == 2 { to, err := R1C1ToA1(refs[1]) if err != nil { return newErrorFormulaArg(formulaErrorREF, formulaErrorREF) } toRef = to } } if len(refs) == 1 { value, err := fn.f.GetCellValue(fn.sheet, fromRef) if err != nil { return newErrorFormulaArg(formulaErrorREF, formulaErrorREF) } return newStringFormulaArg(value) } arg, _ := fn.f.parseReference(fn.sheet, fromRef+":"+toRef) return arg } // LOOKUP function performs an approximate match lookup in a one-column or // one-row range, and returns the corresponding value from another one-column // or one-row range. The syntax of the function is: // // LOOKUP(lookup_value,lookup_vector,[result_vector]) // func (fn *formulaFuncs) LOOKUP(argsList *list.List) formulaArg { arrayForm, lookupValue, lookupVector, errArg := checkLookupArgs(argsList) if errArg.Type == ArgError { return errArg } cols, matchIdx, ok := iterateLookupArgs(lookupValue, lookupVector) if ok && matchIdx == -1 { matchIdx = len(cols) - 1 } var column []formulaArg if argsList.Len() == 3 { column = lookupCol(argsList.Back().Value.(formulaArg), 0) } else if arrayForm && len(lookupVector.Matrix[0]) > 1 { column = lookupCol(lookupVector, 1) } else { column = cols } if matchIdx < 0 || matchIdx >= len(column) { return newErrorFormulaArg(formulaErrorNA, "LOOKUP no result found") } return column[matchIdx] } // lookupCol extract columns for LOOKUP. func lookupCol(arr formulaArg, idx int) []formulaArg { col := arr.List if arr.Type == ArgMatrix { col = nil for _, r := range arr.Matrix { if len(r) > 0 { col = append(col, r[idx]) continue } col = append(col, newEmptyFormulaArg()) } } return col } // ROW function returns the first row number within a supplied reference or // the number of the current row. The syntax of the function is: // // ROW([reference]) // func (fn *formulaFuncs) ROW(argsList *list.List) formulaArg { if argsList.Len() > 1 { return newErrorFormulaArg(formulaErrorVALUE, "ROW requires at most 1 argument") } if argsList.Len() == 1 { if argsList.Front().Value.(formulaArg).cellRanges != nil && argsList.Front().Value.(formulaArg).cellRanges.Len() > 0 { return newNumberFormulaArg(float64(argsList.Front().Value.(formulaArg).cellRanges.Front().Value.(cellRange).From.Row)) } if argsList.Front().Value.(formulaArg).cellRefs != nil && argsList.Front().Value.(formulaArg).cellRefs.Len() > 0 { return newNumberFormulaArg(float64(argsList.Front().Value.(formulaArg).cellRefs.Front().Value.(cellRef).Row)) } return newErrorFormulaArg(formulaErrorVALUE, "invalid reference") } _, row, _ := CellNameToCoordinates(fn.cell) return newNumberFormulaArg(float64(row)) } // calcRowsMinMax calculation min and max value for given formula arguments // sequence of the formula function ROWS. func calcRowsMinMax(argsList *list.List) (min, max int) { if argsList.Front().Value.(formulaArg).cellRanges != nil && argsList.Front().Value.(formulaArg).cellRanges.Len() > 0 { crs := argsList.Front().Value.(formulaArg).cellRanges for cr := crs.Front(); cr != nil; cr = cr.Next() { if min == 0 { min = cr.Value.(cellRange).From.Row } if min > cr.Value.(cellRange).From.Row { min = cr.Value.(cellRange).From.Row } if min > cr.Value.(cellRange).To.Row { min = cr.Value.(cellRange).To.Row } if max < cr.Value.(cellRange).To.Row { max = cr.Value.(cellRange).To.Row } if max < cr.Value.(cellRange).From.Row { max = cr.Value.(cellRange).From.Row } } } if argsList.Front().Value.(formulaArg).cellRefs != nil && argsList.Front().Value.(formulaArg).cellRefs.Len() > 0 { cr := argsList.Front().Value.(formulaArg).cellRefs for refs := cr.Front(); refs != nil; refs = refs.Next() { if min == 0 { min = refs.Value.(cellRef).Row } if min > refs.Value.(cellRef).Row { min = refs.Value.(cellRef).Row } if max < refs.Value.(cellRef).Row { max = refs.Value.(cellRef).Row } } } return } // ROWS function takes an Excel range and returns the number of rows that are // contained within the range. The syntax of the function is: // // ROWS(array) // func (fn *formulaFuncs) ROWS(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "ROWS requires 1 argument") } min, max := calcRowsMinMax(argsList) if max == TotalRows { return newStringFormulaArg(strconv.Itoa(TotalRows)) } result := max - min + 1 if max == min { if min == 0 { return newErrorFormulaArg(formulaErrorVALUE, "invalid reference") } return newNumberFormulaArg(float64(1)) } return newStringFormulaArg(strconv.Itoa(result)) } // Web Functions // ENCODEURL function returns a URL-encoded string, replacing certain // non-alphanumeric characters with the percentage symbol (%) and a // hexadecimal number. The syntax of the function is: // // ENCODEURL(url) // func (fn *formulaFuncs) ENCODEURL(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "ENCODEURL requires 1 argument") } token := argsList.Front().Value.(formulaArg).Value() return newStringFormulaArg(strings.Replace(url.QueryEscape(token), "+", "%20", -1)) } // Financial Functions // validateFrequency check the number of coupon payments per year if be equal to 1, 2 or 4. func validateFrequency(freq float64) bool { return freq == 1 || freq == 2 || freq == 4 } // ACCRINT function returns the accrued interest for a security that pays // periodic interest. The syntax of the function is: // // ACCRINT(issue,first_interest,settlement,rate,par,frequency,[basis],[calc_method]) // func (fn *formulaFuncs) ACCRINT(argsList *list.List) formulaArg { if argsList.Len() < 6 { return newErrorFormulaArg(formulaErrorVALUE, "ACCRINT requires at least 6 arguments") } if argsList.Len() > 8 { return newErrorFormulaArg(formulaErrorVALUE, "ACCRINT allows at most 8 arguments") } args := fn.prepareDataValueArgs(3, argsList) if args.Type != ArgList { return args } issue, settlement := args.List[0], args.List[2] rate := argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber() par := argsList.Front().Next().Next().Next().Next().Value.(formulaArg).ToNumber() frequency := argsList.Front().Next().Next().Next().Next().Next().Value.(formulaArg).ToNumber() if rate.Type != ArgNumber || par.Type != ArgNumber || frequency.Type != ArgNumber { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } if !validateFrequency(frequency.Number) { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } basis := newNumberFormulaArg(0) if argsList.Len() >= 7 { if basis = argsList.Front().Next().Next().Next().Next().Next().Next().Value.(formulaArg).ToNumber(); basis.Type != ArgNumber { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } } if argsList.Len() == 8 { if cm := argsList.Back().Value.(formulaArg).ToBool(); cm.Type != ArgNumber { return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } } frac1 := yearFrac(issue.Number, settlement.Number, int(basis.Number)) if frac1.Type != ArgNumber { return frac1 } return newNumberFormulaArg(par.Number * rate.Number * frac1.Number) } // ACCRINTM function returns the accrued interest for a security that pays // interest at maturity. The syntax of the function is: // // ACCRINTM(issue,settlement,rate,[par],[basis]) // func (fn *formulaFuncs) ACCRINTM(argsList *list.List) formulaArg { if argsList.Len() != 4 && argsList.Len() != 5 { return newErrorFormulaArg(formulaErrorVALUE, "ACCRINTM requires 4 or 5 arguments") } args := fn.prepareDataValueArgs(2, argsList) if args.Type != ArgList { return args } issue, settlement := args.List[0], args.List[1] if settlement.Number < issue.Number { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } rate := argsList.Front().Next().Next().Value.(formulaArg).ToNumber() par := argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber() if rate.Type != ArgNumber || par.Type != ArgNumber { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } if par.Number <= 0 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } basis := newNumberFormulaArg(0) if argsList.Len() == 5 { if basis = argsList.Back().Value.(formulaArg).ToNumber(); basis.Type != ArgNumber { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } } frac := yearFrac(issue.Number, settlement.Number, int(basis.Number)) if frac.Type != ArgNumber { return frac } return newNumberFormulaArg(frac.Number * rate.Number * par.Number) } // prepareAmorArgs checking and prepare arguments for the formula functions // AMORDEGRC and AMORLINC. func (fn *formulaFuncs) prepareAmorArgs(name string, argsList *list.List) formulaArg { cost := argsList.Front().Value.(formulaArg).ToNumber() if cost.Type != ArgNumber { return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires cost to be number argument", name)) } if cost.Number < 0 { return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires cost >= 0", name)) } args := list.New().Init() args.PushBack(argsList.Front().Next().Value.(formulaArg)) datePurchased := fn.DATEVALUE(args) if datePurchased.Type != ArgNumber { return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } args.Init() args.PushBack(argsList.Front().Next().Next().Value.(formulaArg)) firstPeriod := fn.DATEVALUE(args) if firstPeriod.Type != ArgNumber { return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } if firstPeriod.Number < datePurchased.Number { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } salvage := argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber() if salvage.Type != ArgNumber { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } if salvage.Number < 0 || salvage.Number > cost.Number { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } period := argsList.Front().Next().Next().Next().Next().Value.(formulaArg).ToNumber() if period.Type != ArgNumber || period.Number < 0 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } rate := argsList.Front().Next().Next().Next().Next().Next().Value.(formulaArg).ToNumber() if rate.Type != ArgNumber || rate.Number < 0 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } basis := newNumberFormulaArg(0) if argsList.Len() == 7 { if basis = argsList.Back().Value.(formulaArg).ToNumber(); basis.Type != ArgNumber { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } } return newListFormulaArg([]formulaArg{cost, datePurchased, firstPeriod, salvage, period, rate, basis}) } // AMORDEGRC function is provided for users of the French accounting system. // The function calculates the prorated linear depreciation of an asset for a // specified accounting period. The syntax of the function is: // // AMORDEGRC(cost,date_purchased,first_period,salvage,period,rate,[basis]) // func (fn *formulaFuncs) AMORDEGRC(argsList *list.List) formulaArg { if argsList.Len() != 6 && argsList.Len() != 7 { return newErrorFormulaArg(formulaErrorVALUE, "AMORDEGRC requires 6 or 7 arguments") } args := fn.prepareAmorArgs("AMORDEGRC", argsList) if args.Type != ArgList { return args } cost, datePurchased, firstPeriod, salvage, period, rate, basis := args.List[0], args.List[1], args.List[2], args.List[3], args.List[4], args.List[5], args.List[6] if rate.Number >= 0.5 { return newErrorFormulaArg(formulaErrorNUM, "AMORDEGRC requires rate to be < 0.5") } assetsLife, amorCoeff := 1/rate.Number, 2.5 if assetsLife < 3 { amorCoeff = 1 } else if assetsLife < 5 { amorCoeff = 1.5 } else if assetsLife <= 6 { amorCoeff = 2 } rate.Number *= amorCoeff frac := yearFrac(datePurchased.Number, firstPeriod.Number, int(basis.Number)) if frac.Type != ArgNumber { return frac } nRate := float64(int((frac.Number * cost.Number * rate.Number) + 0.5)) cost.Number -= nRate rest := cost.Number - salvage.Number for n := 0; n < int(period.Number); n++ { nRate = float64(int((cost.Number * rate.Number) + 0.5)) rest -= nRate if rest < 0 { switch int(period.Number) - n { case 0: case 1: return newNumberFormulaArg(float64(int((cost.Number * 0.5) + 0.5))) default: return newNumberFormulaArg(0) } } cost.Number -= nRate } return newNumberFormulaArg(nRate) } // AMORLINC function is provided for users of the French accounting system. // The function calculates the prorated linear depreciation of an asset for a // specified accounting period. The syntax of the function is: // // AMORLINC(cost,date_purchased,first_period,salvage,period,rate,[basis]) // func (fn *formulaFuncs) AMORLINC(argsList *list.List) formulaArg { if argsList.Len() != 6 && argsList.Len() != 7 { return newErrorFormulaArg(formulaErrorVALUE, "AMORLINC requires 6 or 7 arguments") } args := fn.prepareAmorArgs("AMORLINC", argsList) if args.Type != ArgList { return args } cost, datePurchased, firstPeriod, salvage, period, rate, basis := args.List[0], args.List[1], args.List[2], args.List[3], args.List[4], args.List[5], args.List[6] frac := yearFrac(datePurchased.Number, firstPeriod.Number, int(basis.Number)) if frac.Type != ArgNumber { return frac } rate1 := frac.Number * cost.Number * rate.Number if period.Number == 0 { return newNumberFormulaArg(rate1) } rate2 := cost.Number * rate.Number delta := cost.Number - salvage.Number periods := int((delta - rate1) / rate2) if int(period.Number) <= periods { return newNumberFormulaArg(rate2) } else if int(period.Number)-1 == periods { return newNumberFormulaArg(delta - rate2*float64(periods) - math.Nextafter(rate1, rate1)) } return newNumberFormulaArg(0) } // prepareCouponArgs checking and prepare arguments for the formula functions // COUPDAYBS, COUPDAYS, COUPDAYSNC, COUPPCD, COUPNUM and COUPNCD. func (fn *formulaFuncs) prepareCouponArgs(name string, argsList *list.List) formulaArg { if argsList.Len() != 3 && argsList.Len() != 4 { return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires 3 or 4 arguments", name)) } args := fn.prepareDataValueArgs(2, argsList) if args.Type != ArgList { return args } settlement, maturity := args.List[0], args.List[1] if settlement.Number >= maturity.Number { return newErrorFormulaArg(formulaErrorNUM, fmt.Sprintf("%s requires maturity > settlement", name)) } frequency := argsList.Front().Next().Next().Value.(formulaArg).ToNumber() if frequency.Type != ArgNumber { return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } if !validateFrequency(frequency.Number) { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } basis := newNumberFormulaArg(0) if argsList.Len() == 4 { if basis = argsList.Back().Value.(formulaArg).ToNumber(); basis.Type != ArgNumber { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } } return newListFormulaArg([]formulaArg{settlement, maturity, frequency, basis}) } // is30BasisMethod determine if the financial day count basis rules is 30/360 // methods. func is30BasisMethod(basis int) bool { return basis == 0 || basis == 4 } // getDaysInMonthRange return the day by given year, month range and day count // basis. func getDaysInMonthRange(year, fromMonth, toMonth, basis int) int { if fromMonth > toMonth { return 0 } return (toMonth - fromMonth + 1) * 30 } // getDayOnBasis returns the day by given date and day count basis. func getDayOnBasis(y, m, d, basis int) int { if !is30BasisMethod(basis) { return d } day := d dim := getDaysInMonth(y, m) if day > 30 || d >= dim || day >= dim { day = 30 } return day } // coupdays returns the number of days that base on date range and the day // count basis to be used. func coupdays(from, to time.Time, basis int) float64 { days := 0 fromY, fromM, fromD := from.Date() toY, toM, toD := to.Date() fromDay, toDay := getDayOnBasis(fromY, int(fromM), fromD, basis), getDayOnBasis(toY, int(toM), toD, basis) if !is30BasisMethod(basis) { return (daysBetween(excelMinTime1900.Unix(), makeDate(toY, toM, toDay)) + 1) - (daysBetween(excelMinTime1900.Unix(), makeDate(fromY, fromM, fromDay)) + 1) } if basis == 0 { if (int(fromM) == 2 || fromDay < 30) && toD == 31 { toDay = 31 } } else { if int(fromM) == 2 && fromDay == 30 { fromDay = getDaysInMonth(fromY, 2) } if int(toM) == 2 && toDay == 30 { toDay = getDaysInMonth(toY, 2) } } if fromY < toY || (fromY == toY && int(fromM) < int(toM)) { days = 30 - fromDay + 1 fromD = 1 fromDay = 1 date := time.Date(fromY, fromM, fromD, 0, 0, 0, 0, time.UTC).AddDate(0, 1, 0) if date.Year() < toY { days += getDaysInMonthRange(date.Year(), int(date.Month()), 12, basis) date = date.AddDate(0, 13-int(date.Month()), 0) } days += getDaysInMonthRange(toY, int(date.Month()), int(toM)-1, basis) } if days += toDay - fromDay; days > 0 { return float64(days) } return 0 } // COUPDAYBS function calculates the number of days from the beginning of a // coupon's period to the settlement date. The syntax of the function is: // // COUPDAYBS(settlement,maturity,frequency,[basis]) // func (fn *formulaFuncs) COUPDAYBS(argsList *list.List) formulaArg { args := fn.prepareCouponArgs("COUPDAYBS", argsList) if args.Type != ArgList { return args } settlement := timeFromExcelTime(args.List[0].Number, false) pcd := timeFromExcelTime(fn.COUPPCD(argsList).Number, false) return newNumberFormulaArg(coupdays(pcd, settlement, int(args.List[3].Number))) } // COUPDAYS function calculates the number of days in a coupon period that // contains the settlement date. The syntax of the function is: // // COUPDAYS(settlement,maturity,frequency,[basis]) // func (fn *formulaFuncs) COUPDAYS(argsList *list.List) formulaArg { args := fn.prepareCouponArgs("COUPDAYS", argsList) if args.Type != ArgList { return args } freq := args.List[2].Number basis := int(args.List[3].Number) if basis == 1 { pcd := timeFromExcelTime(fn.COUPPCD(argsList).Number, false) next := pcd.AddDate(0, 12/int(freq), 0) return newNumberFormulaArg(coupdays(pcd, next, basis)) } return newNumberFormulaArg(float64(getYearDays(0, basis)) / freq) } // COUPDAYSNC function calculates the number of days from the settlement date // to the next coupon date. The syntax of the function is: // // COUPDAYSNC(settlement,maturity,frequency,[basis]) // func (fn *formulaFuncs) COUPDAYSNC(argsList *list.List) formulaArg { args := fn.prepareCouponArgs("COUPDAYSNC", argsList) if args.Type != ArgList { return args } settlement := timeFromExcelTime(args.List[0].Number, false) basis := int(args.List[3].Number) ncd := timeFromExcelTime(fn.COUPNCD(argsList).Number, false) return newNumberFormulaArg(coupdays(settlement, ncd, basis)) } // coupons is an implementation of the formula functions COUPNCD and COUPPCD. func (fn *formulaFuncs) coupons(name string, arg formulaArg) formulaArg { settlement := timeFromExcelTime(arg.List[0].Number, false) maturity := timeFromExcelTime(arg.List[1].Number, false) maturityDays := (maturity.Year()-settlement.Year())*12 + (int(maturity.Month()) - int(settlement.Month())) coupon := 12 / int(arg.List[2].Number) mod := maturityDays % coupon year := settlement.Year() month := int(settlement.Month()) if mod == 0 && settlement.Day() >= maturity.Day() { month += coupon } else { month += mod } if name != "COUPNCD" { month -= coupon } if month > 11 { year += 1 month -= 12 } else if month < 0 { year -= 1 month += 12 } day, lastDay := maturity.Day(), time.Date(year, time.Month(month), 1, 0, 0, 0, 0, time.UTC) days := getDaysInMonth(lastDay.Year(), int(lastDay.Month())) if getDaysInMonth(maturity.Year(), int(maturity.Month())) == maturity.Day() { day = days } else if day > 27 && day > days { day = days } return newNumberFormulaArg(daysBetween(excelMinTime1900.Unix(), makeDate(year, time.Month(month), day)) + 1) } // COUPNCD function calculates the number of coupons payable, between a // security's settlement date and maturity date, rounded up to the nearest // whole coupon. The syntax of the function is: // // COUPNCD(settlement,maturity,frequency,[basis]) // func (fn *formulaFuncs) COUPNCD(argsList *list.List) formulaArg { args := fn.prepareCouponArgs("COUPNCD", argsList) if args.Type != ArgList { return args } return fn.coupons("COUPNCD", args) } // COUPNUM function calculates the number of coupons payable, between a // security's settlement date and maturity date, rounded up to the nearest // whole coupon. The syntax of the function is: // // COUPNUM(settlement,maturity,frequency,[basis]) // func (fn *formulaFuncs) COUPNUM(argsList *list.List) formulaArg { args := fn.prepareCouponArgs("COUPNUM", argsList) if args.Type != ArgList { return args } frac := yearFrac(args.List[0].Number, args.List[1].Number, 0) return newNumberFormulaArg(math.Ceil(frac.Number * args.List[2].Number)) } // COUPPCD function returns the previous coupon date, before the settlement // date for a security. The syntax of the function is: // // COUPPCD(settlement,maturity,frequency,[basis]) // func (fn *formulaFuncs) COUPPCD(argsList *list.List) formulaArg { args := fn.prepareCouponArgs("COUPPCD", argsList) if args.Type != ArgList { return args } return fn.coupons("COUPPCD", args) } // CUMIPMT function calculates the cumulative interest paid on a loan or // investment, between two specified periods. The syntax of the function is: // // CUMIPMT(rate,nper,pv,start_period,end_period,type) // func (fn *formulaFuncs) CUMIPMT(argsList *list.List) formulaArg { return fn.cumip("CUMIPMT", argsList) } // CUMPRINC function calculates the cumulative payment on the principal of a // loan or investment, between two specified periods. The syntax of the // function is: // // CUMPRINC(rate,nper,pv,start_period,end_period,type) // func (fn *formulaFuncs) CUMPRINC(argsList *list.List) formulaArg { return fn.cumip("CUMPRINC", argsList) } // cumip is an implementation of the formula functions CUMIPMT and CUMPRINC. func (fn *formulaFuncs) cumip(name string, argsList *list.List) formulaArg { if argsList.Len() != 6 { return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires 6 arguments", name)) } rate := argsList.Front().Value.(formulaArg).ToNumber() if rate.Type != ArgNumber { return rate } nper := argsList.Front().Next().Value.(formulaArg).ToNumber() if nper.Type != ArgNumber { return nper } pv := argsList.Front().Next().Next().Value.(formulaArg).ToNumber() if pv.Type != ArgNumber { return pv } start := argsList.Back().Prev().Prev().Value.(formulaArg).ToNumber() if start.Type != ArgNumber { return start } end := argsList.Back().Prev().Value.(formulaArg).ToNumber() if end.Type != ArgNumber { return end } typ := argsList.Back().Value.(formulaArg).ToNumber() if typ.Type != ArgNumber { return typ } if typ.Number != 0 && typ.Number != 1 { return newErrorFormulaArg(formulaErrorNA, formulaErrorNA) } if start.Number < 1 || start.Number > end.Number { return newErrorFormulaArg(formulaErrorNA, formulaErrorNA) } num := 0.0 for per := start.Number; per <= end.Number; per++ { args := list.New().Init() args.PushBack(rate) args.PushBack(newNumberFormulaArg(per)) args.PushBack(nper) args.PushBack(pv) args.PushBack(newNumberFormulaArg(0)) args.PushBack(typ) if name == "CUMIPMT" { num += fn.IPMT(args).Number continue } num += fn.PPMT(args).Number } return newNumberFormulaArg(num) } // calcDbArgsCompare implements common arguments comparison for DB and DDB. func calcDbArgsCompare(cost, salvage, life, period formulaArg) bool { return (cost.Number <= 0) || ((salvage.Number / cost.Number) < 0) || (life.Number <= 0) || (period.Number < 1) } // DB function calculates the depreciation of an asset, using the Fixed // Declining Balance Method, for each period of the asset's lifetime. The // syntax of the function is: // // DB(cost,salvage,life,period,[month]) // func (fn *formulaFuncs) DB(argsList *list.List) formulaArg { if argsList.Len() < 4 { return newErrorFormulaArg(formulaErrorVALUE, "DB requires at least 4 arguments") } if argsList.Len() > 5 { return newErrorFormulaArg(formulaErrorVALUE, "DB allows at most 5 arguments") } cost := argsList.Front().Value.(formulaArg).ToNumber() if cost.Type != ArgNumber { return cost } salvage := argsList.Front().Next().Value.(formulaArg).ToNumber() if salvage.Type != ArgNumber { return salvage } life := argsList.Front().Next().Next().Value.(formulaArg).ToNumber() if life.Type != ArgNumber { return life } period := argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber() if period.Type != ArgNumber { return period } month := newNumberFormulaArg(12) if argsList.Len() == 5 { if month = argsList.Back().Value.(formulaArg).ToNumber(); month.Type != ArgNumber { return month } } if cost.Number == 0 { return newNumberFormulaArg(0) } if calcDbArgsCompare(cost, salvage, life, period) || (month.Number < 1) { return newErrorFormulaArg(formulaErrorNA, formulaErrorNA) } dr := 1 - math.Pow(salvage.Number/cost.Number, 1/life.Number) dr = math.Round(dr*1000) / 1000 pd, depreciation := 0.0, 0.0 for per := 1; per <= int(period.Number); per++ { if per == 1 { depreciation = cost.Number * dr * month.Number / 12 } else if per == int(life.Number+1) { depreciation = (cost.Number - pd) * dr * (12 - month.Number) / 12 } else { depreciation = (cost.Number - pd) * dr } pd += depreciation } return newNumberFormulaArg(depreciation) } // DDB function calculates the depreciation of an asset, using the Double // Declining Balance Method, or another specified depreciation rate. The // syntax of the function is: // // DDB(cost,salvage,life,period,[factor]) // func (fn *formulaFuncs) DDB(argsList *list.List) formulaArg { if argsList.Len() < 4 { return newErrorFormulaArg(formulaErrorVALUE, "DDB requires at least 4 arguments") } if argsList.Len() > 5 { return newErrorFormulaArg(formulaErrorVALUE, "DDB allows at most 5 arguments") } cost := argsList.Front().Value.(formulaArg).ToNumber() if cost.Type != ArgNumber { return cost } salvage := argsList.Front().Next().Value.(formulaArg).ToNumber() if salvage.Type != ArgNumber { return salvage } life := argsList.Front().Next().Next().Value.(formulaArg).ToNumber() if life.Type != ArgNumber { return life } period := argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber() if period.Type != ArgNumber { return period } factor := newNumberFormulaArg(2) if argsList.Len() == 5 { if factor = argsList.Back().Value.(formulaArg).ToNumber(); factor.Type != ArgNumber { return factor } } if cost.Number == 0 { return newNumberFormulaArg(0) } if calcDbArgsCompare(cost, salvage, life, period) || (factor.Number <= 0.0) || (period.Number > life.Number) { return newErrorFormulaArg(formulaErrorNA, formulaErrorNA) } pd, depreciation := 0.0, 0.0 for per := 1; per <= int(period.Number); per++ { depreciation = math.Min((cost.Number-pd)*(factor.Number/life.Number), (cost.Number - salvage.Number - pd)) pd += depreciation } return newNumberFormulaArg(depreciation) } // prepareDataValueArgs convert first N arguments to data value for the // formula functions. func (fn *formulaFuncs) prepareDataValueArgs(n int, argsList *list.List) formulaArg { l := list.New() dataValues := []formulaArg{} getDateValue := func(arg formulaArg, l *list.List) formulaArg { switch arg.Type { case ArgNumber: break case ArgString: num := arg.ToNumber() if num.Type == ArgNumber { arg = num break } l.Init() l.PushBack(arg) arg = fn.DATEVALUE(l) if arg.Type == ArgError { return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } default: return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } return arg } for i, arg := 0, argsList.Front(); i < n; arg = arg.Next() { dataValue := getDateValue(arg.Value.(formulaArg), l) if dataValue.Type != ArgNumber { return dataValue } dataValues = append(dataValues, dataValue) i++ } return newListFormulaArg(dataValues) } // DISC function calculates the Discount Rate for a security. The syntax of // the function is: // // DISC(settlement,maturity,pr,redemption,[basis]) // func (fn *formulaFuncs) DISC(argsList *list.List) formulaArg { if argsList.Len() != 4 && argsList.Len() != 5 { return newErrorFormulaArg(formulaErrorVALUE, "DISC requires 4 or 5 arguments") } args := fn.prepareDataValueArgs(2, argsList) if args.Type != ArgList { return args } settlement, maturity := args.List[0], args.List[1] if maturity.Number <= settlement.Number { return newErrorFormulaArg(formulaErrorNUM, "DISC requires maturity > settlement") } pr := argsList.Front().Next().Next().Value.(formulaArg).ToNumber() if pr.Type != ArgNumber { return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } if pr.Number <= 0 { return newErrorFormulaArg(formulaErrorNUM, "DISC requires pr > 0") } redemption := argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber() if redemption.Type != ArgNumber { return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } if redemption.Number <= 0 { return newErrorFormulaArg(formulaErrorNUM, "DISC requires redemption > 0") } basis := newNumberFormulaArg(0) if argsList.Len() == 5 { if basis = argsList.Back().Value.(formulaArg).ToNumber(); basis.Type != ArgNumber { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } } frac := yearFrac(settlement.Number, maturity.Number, int(basis.Number)) if frac.Type != ArgNumber { return frac } return newNumberFormulaArg((redemption.Number - pr.Number) / redemption.Number / frac.Number) } // DOLLARDE function converts a dollar value in fractional notation, into a // dollar value expressed as a decimal. The syntax of the function is: // // DOLLARDE(fractional_dollar,fraction) // func (fn *formulaFuncs) DOLLARDE(argsList *list.List) formulaArg { return fn.dollar("DOLLARDE", argsList) } // DOLLARFR function converts a dollar value in decimal notation, into a // dollar value that is expressed in fractional notation. The syntax of the // function is: // // DOLLARFR(decimal_dollar,fraction) // func (fn *formulaFuncs) DOLLARFR(argsList *list.List) formulaArg { return fn.dollar("DOLLARFR", argsList) } // dollar is an implementation of the formula functions DOLLARDE and DOLLARFR. func (fn *formulaFuncs) dollar(name string, argsList *list.List) formulaArg { if argsList.Len() != 2 { return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires 2 arguments", name)) } dollar := argsList.Front().Value.(formulaArg).ToNumber() if dollar.Type != ArgNumber { return dollar } frac := argsList.Back().Value.(formulaArg).ToNumber() if frac.Type != ArgNumber { return frac } if frac.Number < 0 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } if frac.Number == 0 { return newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV) } cents := math.Mod(dollar.Number, 1) if name == "DOLLARDE" { cents /= frac.Number cents *= math.Pow(10, math.Ceil(math.Log10(frac.Number))) } else { cents *= frac.Number cents *= math.Pow(10, -math.Ceil(math.Log10(frac.Number))) } return newNumberFormulaArg(math.Floor(dollar.Number) + cents) } // prepareDurationArgs checking and prepare arguments for the formula // functions DURATION and MDURATION. func (fn *formulaFuncs) prepareDurationArgs(name string, argsList *list.List) formulaArg { if argsList.Len() != 5 && argsList.Len() != 6 { return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires 5 or 6 arguments", name)) } args := fn.prepareDataValueArgs(2, argsList) if args.Type != ArgList { return args } settlement, maturity := args.List[0], args.List[1] if settlement.Number >= maturity.Number { return newErrorFormulaArg(formulaErrorNUM, fmt.Sprintf("%s requires maturity > settlement", name)) } coupon := argsList.Front().Next().Next().Value.(formulaArg).ToNumber() if coupon.Type != ArgNumber { return coupon } if coupon.Number < 0 { return newErrorFormulaArg(formulaErrorNUM, fmt.Sprintf("%s requires coupon >= 0", name)) } yld := argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber() if yld.Type != ArgNumber { return yld } if yld.Number < 0 { return newErrorFormulaArg(formulaErrorNUM, fmt.Sprintf("%s requires yld >= 0", name)) } frequency := argsList.Front().Next().Next().Next().Next().Value.(formulaArg).ToNumber() if frequency.Type != ArgNumber { return frequency } if !validateFrequency(frequency.Number) { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } basis := newNumberFormulaArg(0) if argsList.Len() == 6 { if basis = argsList.Back().Value.(formulaArg).ToNumber(); basis.Type != ArgNumber { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } } return newListFormulaArg([]formulaArg{settlement, maturity, coupon, yld, frequency, basis}) } // duration is an implementation of the formula function DURATION. func (fn *formulaFuncs) duration(settlement, maturity, coupon, yld, frequency, basis formulaArg) formulaArg { frac := yearFrac(settlement.Number, maturity.Number, int(basis.Number)) if frac.Type != ArgNumber { return frac } argumments := list.New().Init() argumments.PushBack(settlement) argumments.PushBack(maturity) argumments.PushBack(frequency) argumments.PushBack(basis) coups := fn.COUPNUM(argumments) duration := 0.0 p := 0.0 coupon.Number *= 100 / frequency.Number yld.Number /= frequency.Number yld.Number++ diff := frac.Number*frequency.Number - coups.Number for t := 1.0; t < coups.Number; t++ { tDiff := t + diff add := coupon.Number / math.Pow(yld.Number, tDiff) p += add duration += tDiff * add } add := (coupon.Number + 100) / math.Pow(yld.Number, coups.Number+diff) p += add duration += (coups.Number + diff) * add duration /= p duration /= frequency.Number return newNumberFormulaArg(duration) } // DURATION function calculates the Duration (specifically, the Macaulay // Duration) of a security that pays periodic interest, assuming a par value // of $100. The syntax of the function is: // // DURATION(settlement,maturity,coupon,yld,frequency,[basis]) // func (fn *formulaFuncs) DURATION(argsList *list.List) formulaArg { args := fn.prepareDurationArgs("DURATION", argsList) if args.Type != ArgList { return args } return fn.duration(args.List[0], args.List[1], args.List[2], args.List[3], args.List[4], args.List[5]) } // EFFECT function returns the effective annual interest rate for a given // nominal interest rate and number of compounding periods per year. The // syntax of the function is: // // EFFECT(nominal_rate,npery) // func (fn *formulaFuncs) EFFECT(argsList *list.List) formulaArg { if argsList.Len() != 2 { return newErrorFormulaArg(formulaErrorVALUE, "EFFECT requires 2 arguments") } rate := argsList.Front().Value.(formulaArg).ToNumber() if rate.Type != ArgNumber { return rate } npery := argsList.Back().Value.(formulaArg).ToNumber() if npery.Type != ArgNumber { return npery } if rate.Number <= 0 || npery.Number < 1 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } return newNumberFormulaArg(math.Pow((1+rate.Number/npery.Number), npery.Number) - 1) } // FV function calculates the Future Value of an investment with periodic // constant payments and a constant interest rate. The syntax of the function // is: // // FV(rate,nper,[pmt],[pv],[type]) // func (fn *formulaFuncs) FV(argsList *list.List) formulaArg { if argsList.Len() < 3 { return newErrorFormulaArg(formulaErrorVALUE, "FV requires at least 3 arguments") } if argsList.Len() > 5 { return newErrorFormulaArg(formulaErrorVALUE, "FV allows at most 5 arguments") } rate := argsList.Front().Value.(formulaArg).ToNumber() if rate.Type != ArgNumber { return rate } nper := argsList.Front().Next().Value.(formulaArg).ToNumber() if nper.Type != ArgNumber { return nper } pmt := argsList.Front().Next().Next().Value.(formulaArg).ToNumber() if pmt.Type != ArgNumber { return pmt } pv, typ := newNumberFormulaArg(0), newNumberFormulaArg(0) if argsList.Len() >= 4 { if pv = argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber(); pv.Type != ArgNumber { return pv } } if argsList.Len() == 5 { if typ = argsList.Back().Value.(formulaArg).ToNumber(); typ.Type != ArgNumber { return typ } } if typ.Number != 0 && typ.Number != 1 { return newErrorFormulaArg(formulaErrorNA, formulaErrorNA) } if rate.Number != 0 { return newNumberFormulaArg(-pv.Number*math.Pow(1+rate.Number, nper.Number) - pmt.Number*(1+rate.Number*typ.Number)*(math.Pow(1+rate.Number, nper.Number)-1)/rate.Number) } return newNumberFormulaArg(-pv.Number - pmt.Number*nper.Number) } // FVSCHEDULE function calculates the Future Value of an investment with a // variable interest rate. The syntax of the function is: // // FVSCHEDULE(principal,schedule) // func (fn *formulaFuncs) FVSCHEDULE(argsList *list.List) formulaArg { if argsList.Len() != 2 { return newErrorFormulaArg(formulaErrorVALUE, "FVSCHEDULE requires 2 arguments") } pri := argsList.Front().Value.(formulaArg).ToNumber() if pri.Type != ArgNumber { return pri } principal := pri.Number for _, arg := range argsList.Back().Value.(formulaArg).ToList() { if arg.Value() == "" { continue } rate := arg.ToNumber() if rate.Type != ArgNumber { return rate } principal *= (1 + rate.Number) } return newNumberFormulaArg(principal) } // INTRATE function calculates the interest rate for a fully invested // security. The syntax of the function is: // // INTRATE(settlement,maturity,investment,redemption,[basis]) // func (fn *formulaFuncs) INTRATE(argsList *list.List) formulaArg { if argsList.Len() != 4 && argsList.Len() != 5 { return newErrorFormulaArg(formulaErrorVALUE, "INTRATE requires 4 or 5 arguments") } args := fn.prepareDataValueArgs(2, argsList) if args.Type != ArgList { return args } settlement, maturity := args.List[0], args.List[1] if maturity.Number <= settlement.Number { return newErrorFormulaArg(formulaErrorNUM, "INTRATE requires maturity > settlement") } investment := argsList.Front().Next().Next().Value.(formulaArg).ToNumber() if investment.Type != ArgNumber { return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } if investment.Number <= 0 { return newErrorFormulaArg(formulaErrorNUM, "INTRATE requires investment > 0") } redemption := argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber() if redemption.Type != ArgNumber { return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } if redemption.Number <= 0 { return newErrorFormulaArg(formulaErrorNUM, "INTRATE requires redemption > 0") } basis := newNumberFormulaArg(0) if argsList.Len() == 5 { if basis = argsList.Back().Value.(formulaArg).ToNumber(); basis.Type != ArgNumber { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } } frac := yearFrac(settlement.Number, maturity.Number, int(basis.Number)) if frac.Type != ArgNumber { return frac } return newNumberFormulaArg((redemption.Number - investment.Number) / investment.Number / frac.Number) } // IPMT function calculates the interest payment, during a specific period of a // loan or investment that is paid in constant periodic payments, with a // constant interest rate. The syntax of the function is: // // IPMT(rate,per,nper,pv,[fv],[type]) // func (fn *formulaFuncs) IPMT(argsList *list.List) formulaArg { return fn.ipmt("IPMT", argsList) } // calcIpmt is part of the implementation ipmt. func calcIpmt(name string, typ, per, pmt, pv, rate formulaArg) formulaArg { capital, interest, principal := pv.Number, 0.0, 0.0 for i := 1; i <= int(per.Number); i++ { if typ.Number != 0 && i == 1 { interest = 0 } else { interest = -capital * rate.Number } principal = pmt.Number - interest capital += principal } if name == "IPMT" { return newNumberFormulaArg(interest) } return newNumberFormulaArg(principal) } // ipmt is an implementation of the formula functions IPMT and PPMT. func (fn *formulaFuncs) ipmt(name string, argsList *list.List) formulaArg { if argsList.Len() < 4 { return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires at least 4 arguments", name)) } if argsList.Len() > 6 { return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s allows at most 6 arguments", name)) } rate := argsList.Front().Value.(formulaArg).ToNumber() if rate.Type != ArgNumber { return rate } per := argsList.Front().Next().Value.(formulaArg).ToNumber() if per.Type != ArgNumber { return per } nper := argsList.Front().Next().Next().Value.(formulaArg).ToNumber() if nper.Type != ArgNumber { return nper } pv := argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber() if pv.Type != ArgNumber { return pv } fv, typ := newNumberFormulaArg(0), newNumberFormulaArg(0) if argsList.Len() >= 5 { if fv = argsList.Front().Next().Next().Next().Next().Value.(formulaArg).ToNumber(); fv.Type != ArgNumber { return fv } } if argsList.Len() == 6 { if typ = argsList.Back().Value.(formulaArg).ToNumber(); typ.Type != ArgNumber { return typ } } if typ.Number != 0 && typ.Number != 1 { return newErrorFormulaArg(formulaErrorNA, formulaErrorNA) } if per.Number <= 0 || per.Number > nper.Number { return newErrorFormulaArg(formulaErrorNA, formulaErrorNA) } args := list.New().Init() args.PushBack(rate) args.PushBack(nper) args.PushBack(pv) args.PushBack(fv) args.PushBack(typ) pmt := fn.PMT(args) return calcIpmt(name, typ, per, pmt, pv, rate) } // IRR function returns the Internal Rate of Return for a supplied series of // periodic cash flows (i.e. an initial investment value and a series of net // income values). The syntax of the function is: // // IRR(values,[guess]) // func (fn *formulaFuncs) IRR(argsList *list.List) formulaArg { if argsList.Len() < 1 { return newErrorFormulaArg(formulaErrorVALUE, "IRR requires at least 1 argument") } if argsList.Len() > 2 { return newErrorFormulaArg(formulaErrorVALUE, "IRR allows at most 2 arguments") } values, guess := argsList.Front().Value.(formulaArg).ToList(), newNumberFormulaArg(0.1) if argsList.Len() > 1 { if guess = argsList.Back().Value.(formulaArg).ToNumber(); guess.Type != ArgNumber { return guess } } x1, x2 := newNumberFormulaArg(0), guess args := list.New().Init() args.PushBack(x1) for _, v := range values { args.PushBack(v) } f1 := fn.NPV(args) args.Front().Value = x2 f2 := fn.NPV(args) for i := 0; i < maxFinancialIterations; i++ { if f1.Number*f2.Number < 0 { break } if math.Abs(f1.Number) < math.Abs((f2.Number)) { x1.Number += 1.6 * (x1.Number - x2.Number) args.Front().Value = x1 f1 = fn.NPV(args) continue } x2.Number += 1.6 * (x2.Number - x1.Number) args.Front().Value = x2 f2 = fn.NPV(args) } if f1.Number*f2.Number > 0 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } args.Front().Value = x1 f := fn.NPV(args) var rtb, dx, xMid, fMid float64 if f.Number < 0 { rtb = x1.Number dx = x2.Number - x1.Number } else { rtb = x2.Number dx = x1.Number - x2.Number } for i := 0; i < maxFinancialIterations; i++ { dx *= 0.5 xMid = rtb + dx args.Front().Value = newNumberFormulaArg(xMid) fMid = fn.NPV(args).Number if fMid <= 0 { rtb = xMid } if math.Abs(fMid) < financialPercision || math.Abs(dx) < financialPercision { break } } return newNumberFormulaArg(xMid) } // ISPMT function calculates the interest paid during a specific period of a // loan or investment. The syntax of the function is: // // ISPMT(rate,per,nper,pv) // func (fn *formulaFuncs) ISPMT(argsList *list.List) formulaArg { if argsList.Len() != 4 { return newErrorFormulaArg(formulaErrorVALUE, "ISPMT requires 4 arguments") } rate := argsList.Front().Value.(formulaArg).ToNumber() if rate.Type != ArgNumber { return rate } per := argsList.Front().Next().Value.(formulaArg).ToNumber() if per.Type != ArgNumber { return per } nper := argsList.Back().Prev().Value.(formulaArg).ToNumber() if nper.Type != ArgNumber { return nper } pv := argsList.Back().Value.(formulaArg).ToNumber() if pv.Type != ArgNumber { return pv } pr, payment, num := pv.Number, pv.Number/nper.Number, 0.0 for i := 0; i <= int(per.Number); i++ { num = rate.Number * pr * -1 pr -= payment if i == int(nper.Number) { num = 0 } } return newNumberFormulaArg(num) } // MDURATION function calculates the Modified Macaulay Duration of a security // that pays periodic interest, assuming a par value of $100. The syntax of // the function is: // // MDURATION(settlement,maturity,coupon,yld,frequency,[basis]) // func (fn *formulaFuncs) MDURATION(argsList *list.List) formulaArg { args := fn.prepareDurationArgs("MDURATION", argsList) if args.Type != ArgList { return args } duration := fn.duration(args.List[0], args.List[1], args.List[2], args.List[3], args.List[4], args.List[5]) if duration.Type != ArgNumber { return duration } return newNumberFormulaArg(duration.Number / (1 + args.List[3].Number/args.List[4].Number)) } // MIRR function returns the Modified Internal Rate of Return for a supplied // series of periodic cash flows (i.e. a set of values, which includes an // initial investment value and a series of net income values). The syntax of // the function is: // // MIRR(values,finance_rate,reinvest_rate) // func (fn *formulaFuncs) MIRR(argsList *list.List) formulaArg { if argsList.Len() != 3 { return newErrorFormulaArg(formulaErrorVALUE, "MIRR requires 3 arguments") } values := argsList.Front().Value.(formulaArg).ToList() financeRate := argsList.Front().Next().Value.(formulaArg).ToNumber() if financeRate.Type != ArgNumber { return financeRate } reinvestRate := argsList.Back().Value.(formulaArg).ToNumber() if reinvestRate.Type != ArgNumber { return reinvestRate } n, fr, rr, npvPos, npvNeg := len(values), 1+financeRate.Number, 1+reinvestRate.Number, 0.0, 0.0 for i, v := range values { val := v.ToNumber() if val.Number >= 0 { npvPos += val.Number / math.Pow(float64(rr), float64(i)) continue } npvNeg += val.Number / math.Pow(float64(fr), float64(i)) } if npvNeg == 0 || npvPos == 0 || reinvestRate.Number <= -1 { return newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV) } return newNumberFormulaArg(math.Pow(-npvPos*math.Pow(rr, float64(n))/(npvNeg*rr), 1/(float64(n)-1)) - 1) } // NOMINAL function returns the nominal interest rate for a given effective // interest rate and number of compounding periods per year. The syntax of // the function is: // // NOMINAL(effect_rate,npery) // func (fn *formulaFuncs) NOMINAL(argsList *list.List) formulaArg { if argsList.Len() != 2 { return newErrorFormulaArg(formulaErrorVALUE, "NOMINAL requires 2 arguments") } rate := argsList.Front().Value.(formulaArg).ToNumber() if rate.Type != ArgNumber { return rate } npery := argsList.Back().Value.(formulaArg).ToNumber() if npery.Type != ArgNumber { return npery } if rate.Number <= 0 || npery.Number < 1 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } return newNumberFormulaArg(npery.Number * (math.Pow(rate.Number+1, 1/npery.Number) - 1)) } // NPER function calculates the number of periods required to pay off a loan, // for a constant periodic payment and a constant interest rate. The syntax // of the function is: // // NPER(rate,pmt,pv,[fv],[type]) // func (fn *formulaFuncs) NPER(argsList *list.List) formulaArg { if argsList.Len() < 3 { return newErrorFormulaArg(formulaErrorVALUE, "NPER requires at least 3 arguments") } if argsList.Len() > 5 { return newErrorFormulaArg(formulaErrorVALUE, "NPER allows at most 5 arguments") } rate := argsList.Front().Value.(formulaArg).ToNumber() if rate.Type != ArgNumber { return rate } pmt := argsList.Front().Next().Value.(formulaArg).ToNumber() if pmt.Type != ArgNumber { return pmt } pv := argsList.Front().Next().Next().Value.(formulaArg).ToNumber() if pv.Type != ArgNumber { return pv } fv, typ := newNumberFormulaArg(0), newNumberFormulaArg(0) if argsList.Len() >= 4 { if fv = argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber(); fv.Type != ArgNumber { return fv } } if argsList.Len() == 5 { if typ = argsList.Back().Value.(formulaArg).ToNumber(); typ.Type != ArgNumber { return typ } } if typ.Number != 0 && typ.Number != 1 { return newErrorFormulaArg(formulaErrorNA, formulaErrorNA) } if pmt.Number == 0 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } if rate.Number != 0 { p := math.Log((pmt.Number*(1+rate.Number*typ.Number)/rate.Number-fv.Number)/(pv.Number+pmt.Number*(1+rate.Number*typ.Number)/rate.Number)) / math.Log(1+rate.Number) return newNumberFormulaArg(p) } return newNumberFormulaArg((-pv.Number - fv.Number) / pmt.Number) } // NPV function calculates the Net Present Value of an investment, based on a // supplied discount rate, and a series of future payments and income. The // syntax of the function is: // // NPV(rate,value1,[value2],[value3],...) // func (fn *formulaFuncs) NPV(argsList *list.List) formulaArg { if argsList.Len() < 2 { return newErrorFormulaArg(formulaErrorVALUE, "NPV requires at least 2 arguments") } rate := argsList.Front().Value.(formulaArg).ToNumber() if rate.Type != ArgNumber { return rate } val, i := 0.0, 1 for arg := argsList.Front().Next(); arg != nil; arg = arg.Next() { num := arg.Value.(formulaArg).ToNumber() if num.Type != ArgNumber { continue } val += num.Number / math.Pow(1+rate.Number, float64(i)) i++ } return newNumberFormulaArg(val) } // aggrBetween is a part of implementation of the formula function ODDFPRICE. func aggrBetween(startPeriod, endPeriod float64, initialValue []float64, f func(acc []float64, index float64) []float64) []float64 { s := []float64{} if startPeriod <= endPeriod { for i := startPeriod; i <= endPeriod; i++ { s = append(s, i) } } else { for i := startPeriod; i >= endPeriod; i-- { s = append(s, i) } } return fold(f, initialValue, s) } // fold is a part of implementation of the formula function ODDFPRICE. func fold(f func(acc []float64, index float64) []float64, state []float64, source []float64) []float64 { length, value := len(source), state for index := 0; length > index; index++ { value = f(value, source[index]) } return value } // changeMonth is a part of implementation of the formula function ODDFPRICE. func changeMonth(date time.Time, numMonths float64, returnLastMonth bool) time.Time { offsetDay := 0 if returnLastMonth && date.Day() == getDaysInMonth(date.Year(), int(date.Month())) { offsetDay-- } newDate := date.AddDate(0, int(numMonths), offsetDay) if returnLastMonth { lastDay := getDaysInMonth(newDate.Year(), int(newDate.Month())) return timeFromExcelTime(daysBetween(excelMinTime1900.Unix(), makeDate(newDate.Year(), newDate.Month(), lastDay))+1, false) } return newDate } // datesAggregate is a part of implementation of the formula function // ODDFPRICE. func datesAggregate(startDate, endDate time.Time, numMonths, basis float64, f func(pcd, ncd time.Time) float64, acc float64, returnLastMonth bool) (time.Time, time.Time, float64) { frontDate, trailingDate := startDate, endDate s1 := frontDate.After(endDate) || frontDate.Equal(endDate) s2 := endDate.After(frontDate) || endDate.Equal(frontDate) stop := s2 if numMonths > 0 { stop = s1 } for !stop { trailingDate = frontDate frontDate = changeMonth(frontDate, numMonths, returnLastMonth) fn := f(frontDate, trailingDate) acc += fn s1 = frontDate.After(endDate) || frontDate.Equal(endDate) s2 = endDate.After(frontDate) || endDate.Equal(frontDate) stop = s2 if numMonths > 0 { stop = s1 } } return frontDate, trailingDate, acc } // coupNumber is a part of implementation of the formula function ODDFPRICE. func coupNumber(maturity, settlement, numMonths, basis float64) float64 { maturityTime, settlementTime := timeFromExcelTime(maturity, false), timeFromExcelTime(settlement, false) my, mm, md := maturityTime.Year(), maturityTime.Month(), maturityTime.Day() sy, sm, sd := settlementTime.Year(), settlementTime.Month(), settlementTime.Day() couponsTemp, endOfMonthTemp := 0.0, getDaysInMonth(my, int(mm)) == md endOfMonth := endOfMonthTemp if !endOfMonthTemp && mm != 2 && md > 28 && md < getDaysInMonth(my, int(mm)) { endOfMonth = getDaysInMonth(sy, int(sm)) == sd } startDate := changeMonth(settlementTime, 0, endOfMonth) coupons := couponsTemp if startDate.After(settlementTime) { coupons++ } date := changeMonth(startDate, numMonths, endOfMonth) f := func(pcd, ncd time.Time) float64 { return 1 } _, _, result := datesAggregate(date, maturityTime, numMonths, basis, f, coupons, endOfMonth) return result } // prepareOddfpriceArgs checking and prepare arguments for the formula // function ODDFPRICE. func (fn *formulaFuncs) prepareOddfpriceArgs(argsList *list.List) formulaArg { dateValues := fn.prepareDataValueArgs(4, argsList) if dateValues.Type != ArgList { return dateValues } settlement, maturity, issue, firstCoupon := dateValues.List[0], dateValues.List[1], dateValues.List[2], dateValues.List[3] if issue.Number >= settlement.Number { return newErrorFormulaArg(formulaErrorNUM, "ODDFPRICE requires settlement > issue") } if settlement.Number >= firstCoupon.Number { return newErrorFormulaArg(formulaErrorNUM, "ODDFPRICE requires first_coupon > settlement") } if firstCoupon.Number >= maturity.Number { return newErrorFormulaArg(formulaErrorNUM, "ODDFPRICE requires maturity > first_coupon") } rate := argsList.Front().Next().Next().Next().Next().Value.(formulaArg).ToNumber() if rate.Type != ArgNumber { return rate } if rate.Number < 0 { return newErrorFormulaArg(formulaErrorNUM, "ODDFPRICE requires rate >= 0") } yld := argsList.Front().Next().Next().Next().Next().Next().Value.(formulaArg).ToNumber() if yld.Type != ArgNumber { return yld } if yld.Number < 0 { return newErrorFormulaArg(formulaErrorNUM, "ODDFPRICE requires yld >= 0") } redemption := argsList.Front().Next().Next().Next().Next().Next().Next().Value.(formulaArg).ToNumber() if redemption.Type != ArgNumber { return redemption } if redemption.Number <= 0 { return newErrorFormulaArg(formulaErrorNUM, "ODDFPRICE requires redemption > 0") } frequency := argsList.Front().Next().Next().Next().Next().Next().Next().Next().Value.(formulaArg).ToNumber() if frequency.Type != ArgNumber { return frequency } if !validateFrequency(frequency.Number) { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } basis := newNumberFormulaArg(0) if argsList.Len() == 9 { if basis = argsList.Back().Value.(formulaArg).ToNumber(); basis.Type != ArgNumber { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } } return newListFormulaArg([]formulaArg{settlement, maturity, issue, firstCoupon, rate, yld, redemption, frequency, basis}) } // ODDFPRICE function calculates the price per $100 face value of a security // with an odd (short or long) first period. The syntax of the function is: // // ODDFPRICE(settlement,maturity,issue,first_coupon,rate,yld,redemption,frequency,[basis]) // func (fn *formulaFuncs) ODDFPRICE(argsList *list.List) formulaArg { if argsList.Len() != 8 && argsList.Len() != 9 { return newErrorFormulaArg(formulaErrorVALUE, "ODDFPRICE requires 8 or 9 arguments") } args := fn.prepareOddfpriceArgs(argsList) if args.Type != ArgList { return args } settlement, maturity, issue, firstCoupon, rate, yld, redemption, frequency, basisArg := args.List[0], args.List[1], args.List[2], args.List[3], args.List[4], args.List[5], args.List[6], args.List[7], args.List[8] if basisArg.Number < 0 || basisArg.Number > 4 { return newErrorFormulaArg(formulaErrorNUM, "invalid basis") } issueTime := timeFromExcelTime(issue.Number, false) settlementTime := timeFromExcelTime(settlement.Number, false) maturityTime := timeFromExcelTime(maturity.Number, false) firstCouponTime := timeFromExcelTime(firstCoupon.Number, false) basis := int(basisArg.Number) monthDays := getDaysInMonth(maturityTime.Year(), int(maturityTime.Month())) returnLastMonth := monthDays == maturityTime.Day() numMonths := 12 / frequency.Number numMonthsNeg := -numMonths mat := changeMonth(maturityTime, numMonthsNeg, returnLastMonth) pcd, _, _ := datesAggregate(mat, firstCouponTime, numMonthsNeg, basisArg.Number, func(d1, d2 time.Time) float64 { return 0 }, 0, returnLastMonth) if !pcd.Equal(firstCouponTime) { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } fnArgs := list.New().Init() fnArgs.PushBack(settlement) fnArgs.PushBack(maturity) fnArgs.PushBack(frequency) fnArgs.PushBack(basisArg) e := fn.COUPDAYS(fnArgs) n := fn.COUPNUM(fnArgs) m := frequency.Number dfc := coupdays(issueTime, firstCouponTime, basis) if dfc < e.Number { dsc := coupdays(settlementTime, firstCouponTime, basis) a := coupdays(issueTime, settlementTime, basis) x := yld.Number/m + 1 y := dsc / e.Number p1 := x p3 := math.Pow(p1, n.Number-1+y) term1 := redemption.Number / p3 term2 := 100 * rate.Number / m * dfc / e.Number / math.Pow(p1, y) f := func(acc []float64, index float64) []float64 { return []float64{acc[0] + 100*rate.Number/m/math.Pow(p1, index-1+y)} } term3 := aggrBetween(2, math.Floor(n.Number), []float64{0}, f) p2 := rate.Number / m term4 := a / e.Number * p2 * 100 return newNumberFormulaArg(term1 + term2 + term3[0] - term4) } fnArgs.Init() fnArgs.PushBack(issue) fnArgs.PushBack(firstCoupon) fnArgs.PushBack(frequency) nc := fn.COUPNUM(fnArgs) lastCoupon := firstCoupon.Number aggrFunc := func(acc []float64, index float64) []float64 { lastCouponTime := timeFromExcelTime(lastCoupon, false) earlyCoupon := daysBetween(excelMinTime1900.Unix(), makeDate(lastCouponTime.Year(), time.Month(float64(lastCouponTime.Month())+numMonthsNeg), lastCouponTime.Day())) + 1 earlyCouponTime := timeFromExcelTime(earlyCoupon, false) nl := e.Number if basis == 1 { nl = coupdays(earlyCouponTime, lastCouponTime, basis) } dci := coupdays(issueTime, lastCouponTime, basis) if index > 1 { dci = nl } startDate := earlyCoupon if issue.Number > earlyCoupon { startDate = issue.Number } endDate := lastCoupon if settlement.Number < lastCoupon { endDate = settlement.Number } startDateTime := timeFromExcelTime(startDate, false) endDateTime := timeFromExcelTime(endDate, false) a := coupdays(startDateTime, endDateTime, basis) lastCoupon = earlyCoupon dcnl := acc[0] anl := acc[1] return []float64{dcnl + dci/nl, anl + a/nl} } ag := aggrBetween(math.Floor(nc.Number), 1, []float64{0, 0}, aggrFunc) dcnl, anl := ag[0], ag[1] dsc := 0.0 fnArgs.Init() fnArgs.PushBack(settlement) fnArgs.PushBack(firstCoupon) fnArgs.PushBack(frequency) if basis == 2 || basis == 3 { d := timeFromExcelTime(fn.COUPNCD(fnArgs).Number, false) dsc = coupdays(settlementTime, d, basis) } else { d := timeFromExcelTime(fn.COUPPCD(fnArgs).Number, false) a := coupdays(d, settlementTime, basis) dsc = e.Number - a } nq := coupNumber(firstCoupon.Number, settlement.Number, numMonths, basisArg.Number) fnArgs.Init() fnArgs.PushBack(firstCoupon) fnArgs.PushBack(maturity) fnArgs.PushBack(frequency) fnArgs.PushBack(basisArg) n = fn.COUPNUM(fnArgs) x := yld.Number/m + 1 y := dsc / e.Number p1 := x p3 := math.Pow(p1, y+nq+n.Number) term1 := redemption.Number / p3 term2 := 100 * rate.Number / m * dcnl / math.Pow(p1, nq+y) f := func(acc []float64, index float64) []float64 { return []float64{acc[0] + 100*rate.Number/m/math.Pow(p1, index+nq+y)} } term3 := aggrBetween(1, math.Floor(n.Number), []float64{0}, f) term4 := 100 * rate.Number / m * anl return newNumberFormulaArg(term1 + term2 + term3[0] - term4) } // PDURATION function calculates the number of periods required for an // investment to reach a specified future value. The syntax of the function // is: // // PDURATION(rate,pv,fv) // func (fn *formulaFuncs) PDURATION(argsList *list.List) formulaArg { if argsList.Len() != 3 { return newErrorFormulaArg(formulaErrorVALUE, "PDURATION requires 3 arguments") } rate := argsList.Front().Value.(formulaArg).ToNumber() if rate.Type != ArgNumber { return rate } pv := argsList.Front().Next().Value.(formulaArg).ToNumber() if pv.Type != ArgNumber { return pv } fv := argsList.Back().Value.(formulaArg).ToNumber() if fv.Type != ArgNumber { return fv } if rate.Number <= 0 || pv.Number <= 0 || fv.Number <= 0 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } return newNumberFormulaArg((math.Log(fv.Number) - math.Log(pv.Number)) / math.Log(1+rate.Number)) } // PMT function calculates the constant periodic payment required to pay off // (or partially pay off) a loan or investment, with a constant interest // rate, over a specified period. The syntax of the function is: // // PMT(rate,nper,pv,[fv],[type]) // func (fn *formulaFuncs) PMT(argsList *list.List) formulaArg { if argsList.Len() < 3 { return newErrorFormulaArg(formulaErrorVALUE, "PMT requires at least 3 arguments") } if argsList.Len() > 5 { return newErrorFormulaArg(formulaErrorVALUE, "PMT allows at most 5 arguments") } rate := argsList.Front().Value.(formulaArg).ToNumber() if rate.Type != ArgNumber { return rate } nper := argsList.Front().Next().Value.(formulaArg).ToNumber() if nper.Type != ArgNumber { return nper } pv := argsList.Front().Next().Next().Value.(formulaArg).ToNumber() if pv.Type != ArgNumber { return pv } fv, typ := newNumberFormulaArg(0), newNumberFormulaArg(0) if argsList.Len() >= 4 { if fv = argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber(); fv.Type != ArgNumber { return fv } } if argsList.Len() == 5 { if typ = argsList.Back().Value.(formulaArg).ToNumber(); typ.Type != ArgNumber { return typ } } if typ.Number != 0 && typ.Number != 1 { return newErrorFormulaArg(formulaErrorNA, formulaErrorNA) } if rate.Number != 0 { p := (-fv.Number - pv.Number*math.Pow((1+rate.Number), nper.Number)) / (1 + rate.Number*typ.Number) / ((math.Pow((1+rate.Number), nper.Number) - 1) / rate.Number) return newNumberFormulaArg(p) } return newNumberFormulaArg((-pv.Number - fv.Number) / nper.Number) } // PPMT function calculates the payment on the principal, during a specific // period of a loan or investment that is paid in constant periodic payments, // with a constant interest rate. The syntax of the function is: // // PPMT(rate,per,nper,pv,[fv],[type]) // func (fn *formulaFuncs) PPMT(argsList *list.List) formulaArg { return fn.ipmt("PPMT", argsList) } // price is an implementation of the formula function PRICE. func (fn *formulaFuncs) price(settlement, maturity, rate, yld, redemption, frequency, basis formulaArg) formulaArg { if basis.Number < 0 || basis.Number > 4 { return newErrorFormulaArg(formulaErrorNUM, "invalid basis") } argsList := list.New().Init() argsList.PushBack(settlement) argsList.PushBack(maturity) argsList.PushBack(frequency) argsList.PushBack(basis) e := fn.COUPDAYS(argsList) dsc := fn.COUPDAYSNC(argsList).Number / e.Number n := fn.COUPNUM(argsList) a := fn.COUPDAYBS(argsList) ret := 0.0 if n.Number > 1 { ret = redemption.Number / math.Pow(1+yld.Number/frequency.Number, n.Number-1+dsc) ret -= 100 * rate.Number / frequency.Number * a.Number / e.Number t1 := 100 * rate.Number / frequency.Number t2 := 1 + yld.Number/frequency.Number for k := 0.0; k < n.Number; k++ { ret += t1 / math.Pow(t2, k+dsc) } } else { dsc = e.Number - a.Number t1 := 100*(rate.Number/frequency.Number) + redemption.Number t2 := (yld.Number/frequency.Number)*(dsc/e.Number) + 1 t3 := 100 * (rate.Number / frequency.Number) * (a.Number / e.Number) ret = t1/t2 - t3 } return newNumberFormulaArg(ret) } // PRICE function calculates the price, per $100 face value of a security that // pays periodic interest. The syntax of the function is: // // PRICE(settlement,maturity,rate,yld,redemption,frequency,[basis]) // func (fn *formulaFuncs) PRICE(argsList *list.List) formulaArg { if argsList.Len() != 6 && argsList.Len() != 7 { return newErrorFormulaArg(formulaErrorVALUE, "PRICE requires 6 or 7 arguments") } args := fn.prepareDataValueArgs(2, argsList) if args.Type != ArgList { return args } settlement, maturity := args.List[0], args.List[1] rate := argsList.Front().Next().Next().Value.(formulaArg).ToNumber() if rate.Type != ArgNumber { return rate } if rate.Number < 0 { return newErrorFormulaArg(formulaErrorNUM, "PRICE requires rate >= 0") } yld := argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber() if yld.Type != ArgNumber { return yld } if yld.Number < 0 { return newErrorFormulaArg(formulaErrorNUM, "PRICE requires yld >= 0") } redemption := argsList.Front().Next().Next().Next().Next().Value.(formulaArg).ToNumber() if redemption.Type != ArgNumber { return redemption } if redemption.Number <= 0 { return newErrorFormulaArg(formulaErrorNUM, "PRICE requires redemption > 0") } frequency := argsList.Front().Next().Next().Next().Next().Next().Value.(formulaArg).ToNumber() if frequency.Type != ArgNumber { return frequency } if !validateFrequency(frequency.Number) { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } basis := newNumberFormulaArg(0) if argsList.Len() == 7 { if basis = argsList.Back().Value.(formulaArg).ToNumber(); basis.Type != ArgNumber { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } } return fn.price(settlement, maturity, rate, yld, redemption, frequency, basis) } // PRICEDISC function calculates the price, per $100 face value of a // discounted security. The syntax of the function is: // // PRICEDISC(settlement,maturity,discount,redemption,[basis]) // func (fn *formulaFuncs) PRICEDISC(argsList *list.List) formulaArg { if argsList.Len() != 4 && argsList.Len() != 5 { return newErrorFormulaArg(formulaErrorVALUE, "PRICEDISC requires 4 or 5 arguments") } args := fn.prepareDataValueArgs(2, argsList) if args.Type != ArgList { return args } settlement, maturity := args.List[0], args.List[1] if maturity.Number <= settlement.Number { return newErrorFormulaArg(formulaErrorNUM, "PRICEDISC requires maturity > settlement") } discount := argsList.Front().Next().Next().Value.(formulaArg).ToNumber() if discount.Type != ArgNumber { return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } if discount.Number <= 0 { return newErrorFormulaArg(formulaErrorNUM, "PRICEDISC requires discount > 0") } redemption := argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber() if redemption.Type != ArgNumber { return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } if redemption.Number <= 0 { return newErrorFormulaArg(formulaErrorNUM, "PRICEDISC requires redemption > 0") } basis := newNumberFormulaArg(0) if argsList.Len() == 5 { if basis = argsList.Back().Value.(formulaArg).ToNumber(); basis.Type != ArgNumber { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } } frac := yearFrac(settlement.Number, maturity.Number, int(basis.Number)) if frac.Type != ArgNumber { return frac } return newNumberFormulaArg(redemption.Number * (1 - discount.Number*frac.Number)) } // PRICEMAT function calculates the price, per $100 face value of a security // that pays interest at maturity. The syntax of the function is: // // PRICEMAT(settlement,maturity,issue,rate,yld,[basis]) // func (fn *formulaFuncs) PRICEMAT(argsList *list.List) formulaArg { if argsList.Len() != 5 && argsList.Len() != 6 { return newErrorFormulaArg(formulaErrorVALUE, "PRICEMAT requires 5 or 6 arguments") } args := fn.prepareDataValueArgs(3, argsList) if args.Type != ArgList { return args } settlement, maturity, issue := args.List[0], args.List[1], args.List[2] if settlement.Number >= maturity.Number { return newErrorFormulaArg(formulaErrorNUM, "PRICEMAT requires maturity > settlement") } if issue.Number >= settlement.Number { return newErrorFormulaArg(formulaErrorNUM, "PRICEMAT requires settlement > issue") } rate := argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber() if rate.Type != ArgNumber { return rate } if rate.Number < 0 { return newErrorFormulaArg(formulaErrorNUM, "PRICEMAT requires rate >= 0") } yld := argsList.Front().Next().Next().Next().Next().Value.(formulaArg).ToNumber() if yld.Type != ArgNumber { return yld } if yld.Number < 0 { return newErrorFormulaArg(formulaErrorNUM, "PRICEMAT requires yld >= 0") } basis := newNumberFormulaArg(0) if argsList.Len() == 6 { if basis = argsList.Back().Value.(formulaArg).ToNumber(); basis.Type != ArgNumber { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } } dsm := yearFrac(settlement.Number, maturity.Number, int(basis.Number)) if dsm.Type != ArgNumber { return dsm } dis := yearFrac(issue.Number, settlement.Number, int(basis.Number)) dim := yearFrac(issue.Number, maturity.Number, int(basis.Number)) return newNumberFormulaArg(((1+dim.Number*rate.Number)/(1+dsm.Number*yld.Number) - dis.Number*rate.Number) * 100) } // PV function calculates the Present Value of an investment, based on a // series of future payments. The syntax of the function is: // // PV(rate,nper,pmt,[fv],[type]) // func (fn *formulaFuncs) PV(argsList *list.List) formulaArg { if argsList.Len() < 3 { return newErrorFormulaArg(formulaErrorVALUE, "PV requires at least 3 arguments") } if argsList.Len() > 5 { return newErrorFormulaArg(formulaErrorVALUE, "PV allows at most 5 arguments") } rate := argsList.Front().Value.(formulaArg).ToNumber() if rate.Type != ArgNumber { return rate } nper := argsList.Front().Next().Value.(formulaArg).ToNumber() if nper.Type != ArgNumber { return nper } pmt := argsList.Front().Next().Next().Value.(formulaArg).ToNumber() if pmt.Type != ArgNumber { return pmt } fv := newNumberFormulaArg(0) if argsList.Len() >= 4 { if fv = argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber(); fv.Type != ArgNumber { return fv } } t := newNumberFormulaArg(0) if argsList.Len() == 5 { if t = argsList.Back().Value.(formulaArg).ToNumber(); t.Type != ArgNumber { return t } if t.Number != 0 { t.Number = 1 } } if rate.Number == 0 { return newNumberFormulaArg(-pmt.Number*nper.Number - fv.Number) } return newNumberFormulaArg((((1-math.Pow(1+rate.Number, nper.Number))/rate.Number)*pmt.Number*(1+rate.Number*t.Number) - fv.Number) / math.Pow(1+rate.Number, nper.Number)) } // rate is an implementation of the formula function RATE. func (fn *formulaFuncs) rate(nper, pmt, pv, fv, t, guess formulaArg, argsList *list.List) formulaArg { maxIter, iter, close, epsMax, rate := 100, 0, false, 1e-6, guess.Number for iter < maxIter && !close { t1 := math.Pow(rate+1, nper.Number) t2 := math.Pow(rate+1, nper.Number-1) rt := rate*t.Number + 1 p0 := pmt.Number * (t1 - 1) f1 := fv.Number + t1*pv.Number + p0*rt/rate n1 := nper.Number * t2 * pv.Number n2 := p0 * rt / math.Pow(rate, 2) f2 := math.Nextafter(n1, n1) - math.Nextafter(n2, n2) f3 := (nper.Number*pmt.Number*t2*rt + p0*t.Number) / rate delta := f1 / (f2 + f3) if math.Abs(delta) < epsMax { close = true } iter++ rate -= delta } return newNumberFormulaArg(rate) } // RATE function calculates the interest rate required to pay off a specified // amount of a loan, or to reach a target amount on an investment, over a // given period. The syntax of the function is: // // RATE(nper,pmt,pv,[fv],[type],[guess]) // func (fn *formulaFuncs) RATE(argsList *list.List) formulaArg { if argsList.Len() < 3 { return newErrorFormulaArg(formulaErrorVALUE, "RATE requires at least 3 arguments") } if argsList.Len() > 6 { return newErrorFormulaArg(formulaErrorVALUE, "RATE allows at most 6 arguments") } nper := argsList.Front().Value.(formulaArg).ToNumber() if nper.Type != ArgNumber { return nper } pmt := argsList.Front().Next().Value.(formulaArg).ToNumber() if pmt.Type != ArgNumber { return pmt } pv := argsList.Front().Next().Next().Value.(formulaArg).ToNumber() if pv.Type != ArgNumber { return pv } fv := newNumberFormulaArg(0) if argsList.Len() >= 4 { if fv = argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber(); fv.Type != ArgNumber { return fv } } t := newNumberFormulaArg(0) if argsList.Len() >= 5 { if t = argsList.Front().Next().Next().Next().Next().Value.(formulaArg).ToNumber(); t.Type != ArgNumber { return t } if t.Number != 0 { t.Number = 1 } } guess := newNumberFormulaArg(0.1) if argsList.Len() == 6 { if guess = argsList.Back().Value.(formulaArg).ToNumber(); guess.Type != ArgNumber { return guess } } return fn.rate(nper, pmt, pv, fv, t, guess, argsList) } // RECEIVED function calculates the amount received at maturity for a fully // invested security. The syntax of the function is: // // RECEIVED(settlement,maturity,investment,discount,[basis]) // func (fn *formulaFuncs) RECEIVED(argsList *list.List) formulaArg { if argsList.Len() < 4 { return newErrorFormulaArg(formulaErrorVALUE, "RECEIVED requires at least 4 arguments") } if argsList.Len() > 5 { return newErrorFormulaArg(formulaErrorVALUE, "RECEIVED allows at most 5 arguments") } args := fn.prepareDataValueArgs(2, argsList) if args.Type != ArgList { return args } settlement, maturity := args.List[0], args.List[1] investment := argsList.Front().Next().Next().Value.(formulaArg).ToNumber() if investment.Type != ArgNumber { return investment } discount := argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber() if discount.Type != ArgNumber { return discount } if discount.Number <= 0 { return newErrorFormulaArg(formulaErrorNUM, "RECEIVED requires discount > 0") } basis := newNumberFormulaArg(0) if argsList.Len() == 5 { if basis = argsList.Back().Value.(formulaArg).ToNumber(); basis.Type != ArgNumber { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } } frac := yearFrac(settlement.Number, maturity.Number, int(basis.Number)) if frac.Type != ArgNumber { return frac } return newNumberFormulaArg(investment.Number / (1 - discount.Number*frac.Number)) } // RRI function calculates the equivalent interest rate for an investment with // specified present value, future value and duration. The syntax of the // function is: // // RRI(nper,pv,fv) // func (fn *formulaFuncs) RRI(argsList *list.List) formulaArg { if argsList.Len() != 3 { return newErrorFormulaArg(formulaErrorVALUE, "RRI requires 3 arguments") } nper := argsList.Front().Value.(formulaArg).ToNumber() pv := argsList.Front().Next().Value.(formulaArg).ToNumber() fv := argsList.Back().Value.(formulaArg).ToNumber() if nper.Type != ArgNumber || pv.Type != ArgNumber || fv.Type != ArgNumber { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } if nper.Number <= 0 { return newErrorFormulaArg(formulaErrorNUM, "RRI requires nper argument to be > 0") } if pv.Number <= 0 { return newErrorFormulaArg(formulaErrorNUM, "RRI requires pv argument to be > 0") } if fv.Number < 0 { return newErrorFormulaArg(formulaErrorNUM, "RRI requires fv argument to be >= 0") } return newNumberFormulaArg(math.Pow(fv.Number/pv.Number, 1/nper.Number) - 1) } // SLN function calculates the straight line depreciation of an asset for one // period. The syntax of the function is: // // SLN(cost,salvage,life) // func (fn *formulaFuncs) SLN(argsList *list.List) formulaArg { if argsList.Len() != 3 { return newErrorFormulaArg(formulaErrorVALUE, "SLN requires 3 arguments") } cost := argsList.Front().Value.(formulaArg).ToNumber() salvage := argsList.Front().Next().Value.(formulaArg).ToNumber() life := argsList.Back().Value.(formulaArg).ToNumber() if cost.Type != ArgNumber || salvage.Type != ArgNumber || life.Type != ArgNumber { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } if life.Number == 0 { return newErrorFormulaArg(formulaErrorNUM, "SLN requires life argument to be > 0") } return newNumberFormulaArg((cost.Number - salvage.Number) / life.Number) } // SYD function calculates the sum-of-years' digits depreciation for a // specified period in the lifetime of an asset. The syntax of the function // is: // // SYD(cost,salvage,life,per) // func (fn *formulaFuncs) SYD(argsList *list.List) formulaArg { if argsList.Len() != 4 { return newErrorFormulaArg(formulaErrorVALUE, "SYD requires 4 arguments") } cost := argsList.Front().Value.(formulaArg).ToNumber() salvage := argsList.Front().Next().Value.(formulaArg).ToNumber() life := argsList.Back().Prev().Value.(formulaArg).ToNumber() per := argsList.Back().Value.(formulaArg).ToNumber() if cost.Type != ArgNumber || salvage.Type != ArgNumber || life.Type != ArgNumber || per.Type != ArgNumber { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } if life.Number <= 0 { return newErrorFormulaArg(formulaErrorNUM, "SYD requires life argument to be > 0") } if per.Number <= 0 { return newErrorFormulaArg(formulaErrorNUM, "SYD requires per argument to be > 0") } if per.Number > life.Number { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } return newNumberFormulaArg(((cost.Number - salvage.Number) * (life.Number - per.Number + 1) * 2) / (life.Number * (life.Number + 1))) } // TBILLEQ function calculates the bond-equivalent yield for a Treasury Bill. // The syntax of the function is: // // TBILLEQ(settlement,maturity,discount) // func (fn *formulaFuncs) TBILLEQ(argsList *list.List) formulaArg { if argsList.Len() != 3 { return newErrorFormulaArg(formulaErrorVALUE, "TBILLEQ requires 3 arguments") } args := fn.prepareDataValueArgs(2, argsList) if args.Type != ArgList { return args } settlement, maturity := args.List[0], args.List[1] dsm := maturity.Number - settlement.Number if dsm > 365 || maturity.Number <= settlement.Number { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } discount := argsList.Back().Value.(formulaArg).ToNumber() if discount.Type != ArgNumber { return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } if discount.Number <= 0 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } return newNumberFormulaArg((365 * discount.Number) / (360 - discount.Number*dsm)) } // TBILLPRICE function returns the price, per $100 face value, of a Treasury // Bill. The syntax of the function is: // // TBILLPRICE(settlement,maturity,discount) // func (fn *formulaFuncs) TBILLPRICE(argsList *list.List) formulaArg { if argsList.Len() != 3 { return newErrorFormulaArg(formulaErrorVALUE, "TBILLPRICE requires 3 arguments") } args := fn.prepareDataValueArgs(2, argsList) if args.Type != ArgList { return args } settlement, maturity := args.List[0], args.List[1] dsm := maturity.Number - settlement.Number if dsm > 365 || maturity.Number <= settlement.Number { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } discount := argsList.Back().Value.(formulaArg).ToNumber() if discount.Type != ArgNumber { return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } if discount.Number <= 0 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } return newNumberFormulaArg(100 * (1 - discount.Number*dsm/360)) } // TBILLYIELD function calculates the yield of a Treasury Bill. The syntax of // the function is: // // TBILLYIELD(settlement,maturity,pr) // func (fn *formulaFuncs) TBILLYIELD(argsList *list.List) formulaArg { if argsList.Len() != 3 { return newErrorFormulaArg(formulaErrorVALUE, "TBILLYIELD requires 3 arguments") } args := fn.prepareDataValueArgs(2, argsList) if args.Type != ArgList { return args } settlement, maturity := args.List[0], args.List[1] dsm := maturity.Number - settlement.Number if dsm > 365 || maturity.Number <= settlement.Number { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } pr := argsList.Back().Value.(formulaArg).ToNumber() if pr.Type != ArgNumber { return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } if pr.Number <= 0 { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } return newNumberFormulaArg(((100 - pr.Number) / pr.Number) * (360 / dsm)) } // prepareVdbArgs checking and prepare arguments for the formula function // VDB. func (fn *formulaFuncs) prepareVdbArgs(argsList *list.List) formulaArg { cost := argsList.Front().Value.(formulaArg).ToNumber() if cost.Type != ArgNumber { return cost } if cost.Number < 0 { return newErrorFormulaArg(formulaErrorNUM, "VDB requires cost >= 0") } salvage := argsList.Front().Next().Value.(formulaArg).ToNumber() if salvage.Type != ArgNumber { return salvage } if salvage.Number < 0 { return newErrorFormulaArg(formulaErrorNUM, "VDB requires salvage >= 0") } life := argsList.Front().Next().Next().Value.(formulaArg).ToNumber() if life.Type != ArgNumber { return life } if life.Number <= 0 { return newErrorFormulaArg(formulaErrorNUM, "VDB requires life > 0") } startPeriod := argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber() if startPeriod.Type != ArgNumber { return startPeriod } if startPeriod.Number < 0 { return newErrorFormulaArg(formulaErrorNUM, "VDB requires start_period > 0") } endPeriod := argsList.Front().Next().Next().Next().Next().Value.(formulaArg).ToNumber() if endPeriod.Type != ArgNumber { return endPeriod } if startPeriod.Number > endPeriod.Number { return newErrorFormulaArg(formulaErrorNUM, "VDB requires start_period <= end_period") } if endPeriod.Number > life.Number { return newErrorFormulaArg(formulaErrorNUM, "VDB requires end_period <= life") } factor := newNumberFormulaArg(2) if argsList.Len() > 5 { if factor = argsList.Front().Next().Next().Next().Next().Next().Value.(formulaArg).ToNumber(); factor.Type != ArgNumber { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } if factor.Number < 0 { return newErrorFormulaArg(formulaErrorVALUE, "VDB requires factor >= 0") } } return newListFormulaArg([]formulaArg{cost, salvage, life, startPeriod, endPeriod, factor}) } // vdb is a part of implementation of the formula function VDB. func (fn *formulaFuncs) vdb(cost, salvage, life, life1, period, factor formulaArg) formulaArg { var ddb, vdb, sln, term float64 endInt, cs, nowSln := math.Ceil(period.Number), cost.Number-salvage.Number, false ddbArgs := list.New() for i := 1.0; i <= endInt; i++ { if !nowSln { ddbArgs.Init() ddbArgs.PushBack(cost) ddbArgs.PushBack(salvage) ddbArgs.PushBack(life) ddbArgs.PushBack(newNumberFormulaArg(i)) ddbArgs.PushBack(factor) ddb = fn.DDB(ddbArgs).Number sln = cs / (life1.Number - i + 1) if sln > ddb { term = sln nowSln = true } else { term = ddb cs -= ddb } } else { term = sln } if i == endInt { term *= period.Number + 1 - endInt } vdb += term } return newNumberFormulaArg(vdb) } // VDB function calculates the depreciation of an asset, using the Double // Declining Balance Method, or another specified depreciation rate, for a // specified period (including partial periods). The syntax of the function // is: // // VDB(cost,salvage,life,start_period,end_period,[factor],[no_switch]) // func (fn *formulaFuncs) VDB(argsList *list.List) formulaArg { if argsList.Len() < 5 || argsList.Len() > 7 { return newErrorFormulaArg(formulaErrorVALUE, "VDB requires 5 or 7 arguments") } args := fn.prepareVdbArgs(argsList) if args.Type != ArgList { return args } cost, salvage, life, startPeriod, endPeriod, factor := args.List[0], args.List[1], args.List[2], args.List[3], args.List[4], args.List[5] noSwitch := newBoolFormulaArg(false) if argsList.Len() > 6 { if noSwitch = argsList.Back().Value.(formulaArg).ToBool(); noSwitch.Type != ArgNumber { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } } startInt, endInt, vdb, ddbArgs := math.Floor(startPeriod.Number), math.Ceil(endPeriod.Number), newNumberFormulaArg(0), list.New() if noSwitch.Number == 1 { for i := startInt + 1; i <= endInt; i++ { ddbArgs.Init() ddbArgs.PushBack(cost) ddbArgs.PushBack(salvage) ddbArgs.PushBack(life) ddbArgs.PushBack(newNumberFormulaArg(i)) ddbArgs.PushBack(factor) term := fn.DDB(ddbArgs) if i == startInt+1 { term.Number *= math.Min(endPeriod.Number, startInt+1) - startPeriod.Number } else if i == endInt { term.Number *= endPeriod.Number + 1 - endInt } vdb.Number += term.Number } return vdb } life1, part := life, 0.0 if startPeriod.Number != math.Floor(startPeriod.Number) && factor.Number > 1.0 && startPeriod.Number >= life.Number/2.0 { part = startPeriod.Number - life.Number/2.0 startPeriod.Number = life.Number / 2.0 endPeriod.Number -= part } cost.Number -= fn.vdb(cost, salvage, life, life1, startPeriod, factor).Number return fn.vdb(cost, salvage, life, newNumberFormulaArg(life.Number-startPeriod.Number), newNumberFormulaArg(endPeriod.Number-startPeriod.Number), factor) } // prepareXArgs prepare arguments for the formula function XIRR and XNPV. func (fn *formulaFuncs) prepareXArgs(name string, values, dates formulaArg) (valuesArg, datesArg []float64, err formulaArg) { for _, arg := range values.ToList() { if numArg := arg.ToNumber(); numArg.Type == ArgNumber { valuesArg = append(valuesArg, numArg.Number) continue } err = newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) return } if len(valuesArg) < 2 { err = newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) return } args, date := list.New(), 0.0 for _, arg := range dates.ToList() { args.Init() args.PushBack(arg) dateValue := fn.DATEVALUE(args) if dateValue.Type != ArgNumber { err = newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) return } if dateValue.Number < date { err = newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) return } datesArg = append(datesArg, dateValue.Number) date = dateValue.Number } if len(valuesArg) != len(datesArg) { err = newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) return } err = newEmptyFormulaArg() return } // xirr is an implementation of the formula function XIRR. func (fn *formulaFuncs) xirr(values, dates []float64, guess float64) formulaArg { positive, negative := false, false for i := 0; i < len(values); i++ { if values[i] > 0 { positive = true } if values[i] < 0 { negative = true } } if !positive || !negative { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } result, epsMax, count, maxIterate, err := guess, 1e-10, 0, 50, false for { resultValue := xirrPart1(values, dates, result) newRate := result - resultValue/xirrPart2(values, dates, result) epsRate := math.Abs(newRate - result) result = newRate count++ if epsRate <= epsMax || math.Abs(resultValue) <= epsMax { break } if count > maxIterate { err = true break } } if err || math.IsNaN(result) || math.IsInf(result, 0) { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } return newNumberFormulaArg(result) } // xirrPart1 is a part of implementation of the formula function XIRR. func xirrPart1(values, dates []float64, rate float64) float64 { r := rate + 1 result := values[0] vlen := len(values) firstDate := dates[0] for i := 1; i < vlen; i++ { result += values[i] / math.Pow(r, (dates[i]-firstDate)/365) } return result } // xirrPart2 is a part of implementation of the formula function XIRR. func xirrPart2(values, dates []float64, rate float64) float64 { r := rate + 1 result := 0.0 vlen := len(values) firstDate := dates[0] for i := 1; i < vlen; i++ { frac := (dates[i] - firstDate) / 365 result -= frac * values[i] / math.Pow(r, frac+1) } return result } // XIRR function returns the Internal Rate of Return for a supplied series of // cash flows (i.e. a set of values, which includes an initial investment // value and a series of net income values) occurring at a series of supplied // dates. The syntax of the function is: // // XIRR(values,dates,[guess]) // func (fn *formulaFuncs) XIRR(argsList *list.List) formulaArg { if argsList.Len() != 2 && argsList.Len() != 3 { return newErrorFormulaArg(formulaErrorVALUE, "XIRR requires 2 or 3 arguments") } values, dates, err := fn.prepareXArgs("XIRR", argsList.Front().Value.(formulaArg), argsList.Front().Next().Value.(formulaArg)) if err.Type != ArgEmpty { return err } guess := newNumberFormulaArg(0) if argsList.Len() == 3 { if guess = argsList.Back().Value.(formulaArg).ToNumber(); guess.Type != ArgNumber { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } if guess.Number <= -1 { return newErrorFormulaArg(formulaErrorVALUE, "XIRR requires guess > -1") } } return fn.xirr(values, dates, guess.Number) } // XNPV function calculates the Net Present Value for a schedule of cash flows // that is not necessarily periodic. The syntax of the function is: // // XNPV(rate,values,dates) // func (fn *formulaFuncs) XNPV(argsList *list.List) formulaArg { if argsList.Len() != 3 { return newErrorFormulaArg(formulaErrorVALUE, "XNPV requires 3 arguments") } rate := argsList.Front().Value.(formulaArg).ToNumber() if rate.Type != ArgNumber { return rate } if rate.Number <= 0 { return newErrorFormulaArg(formulaErrorVALUE, "XNPV requires rate > 0") } values, dates, err := fn.prepareXArgs("XNPV", argsList.Front().Next().Value.(formulaArg), argsList.Back().Value.(formulaArg)) if err.Type != ArgEmpty { return err } date1, xnpv := dates[0], 0.0 for idx, value := range values { xnpv += value / math.Pow(1+rate.Number, (dates[idx]-date1)/365) } return newNumberFormulaArg(xnpv) } // yield is an implementation of the formula function YIELD. func (fn *formulaFuncs) yield(settlement, maturity, rate, pr, redemption, frequency, basis formulaArg) formulaArg { priceN, yield1, yield2 := newNumberFormulaArg(0), newNumberFormulaArg(0), newNumberFormulaArg(1) price1 := fn.price(settlement, maturity, rate, yield1, redemption, frequency, basis) if price1.Type != ArgNumber { return price1 } price2 := fn.price(settlement, maturity, rate, yield2, redemption, frequency, basis) yieldN := newNumberFormulaArg((yield2.Number - yield1.Number) * 0.5) for iter := 0; iter < 100 && priceN.Number != pr.Number; iter++ { priceN = fn.price(settlement, maturity, rate, yieldN, redemption, frequency, basis) if pr.Number == price1.Number { return yield1 } else if pr.Number == price2.Number { return yield2 } else if pr.Number == priceN.Number { return yieldN } else if pr.Number < price2.Number { yield2.Number *= 2.0 price2 = fn.price(settlement, maturity, rate, yield2, redemption, frequency, basis) yieldN.Number = (yield2.Number - yield1.Number) * 0.5 } else { if pr.Number < priceN.Number { yield1 = yieldN price1 = priceN } else { yield2 = yieldN price2 = priceN } f1 := (yield2.Number - yield1.Number) * ((pr.Number - price2.Number) / (price1.Number - price2.Number)) yieldN.Number = yield2.Number - math.Nextafter(f1, f1) } } return yieldN } // YIELD function calculates the Yield of a security that pays periodic // interest. The syntax of the function is: // // YIELD(settlement,maturity,rate,pr,redemption,frequency,[basis]) // func (fn *formulaFuncs) YIELD(argsList *list.List) formulaArg { if argsList.Len() != 6 && argsList.Len() != 7 { return newErrorFormulaArg(formulaErrorVALUE, "YIELD requires 6 or 7 arguments") } args := fn.prepareDataValueArgs(2, argsList) if args.Type != ArgList { return args } settlement, maturity := args.List[0], args.List[1] rate := argsList.Front().Next().Next().Value.(formulaArg).ToNumber() if rate.Type != ArgNumber { return rate } if rate.Number < 0 { return newErrorFormulaArg(formulaErrorNUM, "PRICE requires rate >= 0") } pr := argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber() if pr.Type != ArgNumber { return pr } if pr.Number <= 0 { return newErrorFormulaArg(formulaErrorNUM, "PRICE requires pr > 0") } redemption := argsList.Front().Next().Next().Next().Next().Value.(formulaArg).ToNumber() if redemption.Type != ArgNumber { return redemption } if redemption.Number < 0 { return newErrorFormulaArg(formulaErrorNUM, "PRICE requires redemption >= 0") } frequency := argsList.Front().Next().Next().Next().Next().Next().Value.(formulaArg).ToNumber() if frequency.Type != ArgNumber { return frequency } if !validateFrequency(frequency.Number) { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } basis := newNumberFormulaArg(0) if argsList.Len() == 7 { if basis = argsList.Back().Value.(formulaArg).ToNumber(); basis.Type != ArgNumber { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } } return fn.yield(settlement, maturity, rate, pr, redemption, frequency, basis) } // YIELDDISC function calculates the annual yield of a discounted security. // The syntax of the function is: // // YIELDDISC(settlement,maturity,pr,redemption,[basis]) // func (fn *formulaFuncs) YIELDDISC(argsList *list.List) formulaArg { if argsList.Len() != 4 && argsList.Len() != 5 { return newErrorFormulaArg(formulaErrorVALUE, "YIELDDISC requires 4 or 5 arguments") } args := fn.prepareDataValueArgs(2, argsList) if args.Type != ArgList { return args } settlement, maturity := args.List[0], args.List[1] pr := argsList.Front().Next().Next().Value.(formulaArg).ToNumber() if pr.Type != ArgNumber { return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } if pr.Number <= 0 { return newErrorFormulaArg(formulaErrorNUM, "YIELDDISC requires pr > 0") } redemption := argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber() if redemption.Type != ArgNumber { return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } if redemption.Number <= 0 { return newErrorFormulaArg(formulaErrorNUM, "YIELDDISC requires redemption > 0") } basis := newNumberFormulaArg(0) if argsList.Len() == 5 { if basis = argsList.Back().Value.(formulaArg).ToNumber(); basis.Type != ArgNumber { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } } frac := yearFrac(settlement.Number, maturity.Number, int(basis.Number)) if frac.Type != ArgNumber { return frac } return newNumberFormulaArg((redemption.Number/pr.Number - 1) / frac.Number) } // YIELDMAT function calculates the annual yield of a security that pays // interest at maturity. The syntax of the function is: // // YIELDMAT(settlement,maturity,issue,rate,pr,[basis]) // func (fn *formulaFuncs) YIELDMAT(argsList *list.List) formulaArg { if argsList.Len() != 5 && argsList.Len() != 6 { return newErrorFormulaArg(formulaErrorVALUE, "YIELDMAT requires 5 or 6 arguments") } args := fn.prepareDataValueArgs(2, argsList) if args.Type != ArgList { return args } settlement, maturity := args.List[0], args.List[1] arg := list.New().Init() issue := argsList.Front().Next().Next().Value.(formulaArg).ToNumber() if issue.Type != ArgNumber { arg.PushBack(argsList.Front().Next().Next().Value.(formulaArg)) issue = fn.DATEVALUE(arg) if issue.Type != ArgNumber { return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } } if issue.Number >= settlement.Number { return newErrorFormulaArg(formulaErrorNUM, "YIELDMAT requires settlement > issue") } rate := argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber() if rate.Type != ArgNumber { return rate } if rate.Number < 0 { return newErrorFormulaArg(formulaErrorNUM, "YIELDMAT requires rate >= 0") } pr := argsList.Front().Next().Next().Next().Next().Value.(formulaArg).ToNumber() if pr.Type != ArgNumber { return pr } if pr.Number <= 0 { return newErrorFormulaArg(formulaErrorNUM, "YIELDMAT requires pr > 0") } basis := newNumberFormulaArg(0) if argsList.Len() == 6 { if basis = argsList.Back().Value.(formulaArg).ToNumber(); basis.Type != ArgNumber { return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } } dim := yearFrac(issue.Number, maturity.Number, int(basis.Number)) if dim.Type != ArgNumber { return dim } dis := yearFrac(issue.Number, settlement.Number, int(basis.Number)) dsm := yearFrac(settlement.Number, maturity.Number, int(basis.Number)) f1 := dim.Number * rate.Number result := 1 + math.Nextafter(f1, f1) result /= pr.Number/100 + dis.Number*rate.Number result-- result /= dsm.Number return newNumberFormulaArg(result) }