From de34ecaacee83633977298d0424e8eb5eaacb876 Mon Sep 17 00:00:00 2001 From: xuri Date: Thu, 7 May 2020 00:15:54 +0800 Subject: #65 fn: MOD, MROUND, MULTINOMIAL, MUNIT, ODD, PI, RADIANS, RAND, RANDBETWEEN, ROMAN --- calc.go | 338 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 313 insertions(+), 25 deletions(-) (limited to 'calc.go') diff --git a/calc.go b/calc.go index 2ab3d61..fb94e27 100644 --- a/calc.go +++ b/calc.go @@ -12,13 +12,16 @@ package excelize import ( + "bytes" "container/list" "errors" "fmt" "math" + "math/rand" "reflect" "strconv" "strings" + "time" "github.com/xuri/efp" ) @@ -1715,6 +1718,168 @@ func (fn *formulaFuncs) MDETERM(argsList *list.List) (result string, err error) return } +// MOD function returns the remainder of a division between two supplied +// numbers. The syntax of the function is: +// +// MOD(number,divisor) +// +func (fn *formulaFuncs) MOD(argsList *list.List) (result string, err error) { + if argsList.Len() != 2 { + err = errors.New("MOD requires 2 numeric arguments") + return + } + var number, divisor float64 + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + return + } + if divisor, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).Value, 64); err != nil { + return + } + if divisor == 0 { + err = errors.New(formulaErrorDIV) + return + } + trunc, rem := math.Modf(number / divisor) + if rem < 0 { + trunc-- + } + result = fmt.Sprintf("%g", number-divisor*trunc) + return +} + +// MROUND function rounds a supplied number up or down to the nearest multiple +// of a given number. The syntax of the function is: +// +// MOD(number,multiple) +// +func (fn *formulaFuncs) MROUND(argsList *list.List) (result string, err error) { + if argsList.Len() != 2 { + err = errors.New("MROUND requires 2 numeric arguments") + return + } + var number, multiple float64 = 0, 1 + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + return + } + if multiple, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).Value, 64); err != nil { + return + } + if multiple == 0 { + err = errors.New(formulaErrorNUM) + return + } + if multiple < 0 && number > 0 || + multiple > 0 && number < 0 { + err = errors.New(formulaErrorNUM) + return + } + number, res := math.Modf(number / multiple) + if math.Trunc(res+0.5) > 0 { + number++ + } + result = fmt.Sprintf("%g", number*multiple) + return +} + +// MULTINOMIAL function calculates the ratio of the factorial of a sum of +// supplied values to the product of factorials of those values. The syntax of +// the function is: +// +// MULTINOMIAL(number1,[number2],...) +// +func (fn *formulaFuncs) MULTINOMIAL(argsList *list.List) (result string, err error) { + var val, num, denom float64 = 0, 0, 1 + for arg := argsList.Front(); arg != nil; arg = arg.Next() { + token := arg.Value.(formulaArg) + if token.Value == "" { + continue + } + if val, err = strconv.ParseFloat(token.Value, 64); err != nil { + return + } + num += val + denom *= fact(val) + } + result = fmt.Sprintf("%g", fact(num)/denom) + return +} + +// MUNIT function returns the unit matrix for a specified dimension. The +// syntax of the function is: +// +// MUNIT(dimension) +// +func (fn *formulaFuncs) MUNIT(argsList *list.List) (result string, err error) { + if argsList.Len() != 1 { + err = errors.New("MUNIT requires 1 numeric argument") + return + } + var dimension int + if dimension, err = strconv.Atoi(argsList.Front().Value.(formulaArg).Value); err != nil { + return + } + matrix := make([][]float64, 0, dimension) + for i := 0; i < dimension; i++ { + row := make([]float64, dimension) + for j := 0; j < dimension; j++ { + if i == j { + row[j] = float64(1.0) + } else { + row[j] = float64(0.0) + } + } + matrix = append(matrix, row) + } + return +} + +// ODD function ounds a supplied number away from zero (i.e. rounds a positive +// number up and a negative number down), to the next odd number. The syntax +// of the function is: +// +// ODD(number) +// +func (fn *formulaFuncs) ODD(argsList *list.List) (result string, err error) { + if argsList.Len() != 1 { + err = errors.New("ODD requires 1 numeric argument") + return + } + var number float64 + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + return + } + if number == 0 { + result = "1" + return + } + sign := math.Signbit(number) + m, frac := math.Modf((number - 1) / 2) + val := m*2 + 1 + if frac != 0 { + if !sign { + val += 2 + } else { + val -= 2 + } + } + result = fmt.Sprintf("%g", val) + return +} + +// PI function returns the value of the mathematical constant π (pi), accurate +// to 15 digits (14 decimal places). The syntax of the function is: +// +// PI() +// +func (fn *formulaFuncs) PI(argsList *list.List) (result string, err error) { + if argsList.Len() != 0 { + err = errors.New("PI accepts no arguments") + return + } + result = fmt.Sprintf("%g", math.Pi) + return +} + // POWER function calculates a given number, raised to a supplied power. // The syntax of the function is: // @@ -1765,6 +1930,154 @@ func (fn *formulaFuncs) PRODUCT(argsList *list.List) (result string, err error) return } +// QUOTIENT function returns the integer portion of a division between two +// supplied numbers. The syntax of the function is: +// +// QUOTIENT(numerator,denominator) +// +func (fn *formulaFuncs) QUOTIENT(argsList *list.List) (result string, err error) { + if argsList.Len() != 2 { + err = errors.New("QUOTIENT requires 2 numeric arguments") + return + } + var x, y float64 + if x, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + return + } + if y, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).Value, 64); err != nil { + return + } + if y == 0 { + err = errors.New(formulaErrorDIV) + return + } + result = fmt.Sprintf("%g", math.Trunc(x/y)) + return +} + +// RADIANS function converts radians into degrees. The syntax of the function is: +// +// RADIANS(angle) +// +func (fn *formulaFuncs) RADIANS(argsList *list.List) (result string, err error) { + if argsList.Len() != 1 { + err = errors.New("RADIANS requires 1 numeric argument") + return + } + var angle float64 + if angle, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + return + } + result = fmt.Sprintf("%g", math.Pi/180.0*angle) + return +} + +// RAND function generates a random real number between 0 and 1. The syntax of +// the function is: +// +// RAND() +// +func (fn *formulaFuncs) RAND(argsList *list.List) (result string, err error) { + if argsList.Len() != 0 { + err = errors.New("RAND accepts no arguments") + return + } + result = fmt.Sprintf("%g", rand.New(rand.NewSource(time.Now().UnixNano())).Float64()) + return +} + +// RANDBETWEEN function generates a random integer between two supplied +// integers. The syntax of the function is: +// +// RANDBETWEEN(bottom,top) +// +func (fn *formulaFuncs) RANDBETWEEN(argsList *list.List) (result string, err error) { + if argsList.Len() != 2 { + err = errors.New("RANDBETWEEN requires 2 numeric arguments") + return + } + var bottom, top int64 + if bottom, err = strconv.ParseInt(argsList.Front().Value.(formulaArg).Value, 10, 64); err != nil { + return + } + if top, err = strconv.ParseInt(argsList.Back().Value.(formulaArg).Value, 10, 64); err != nil { + return + } + if top < bottom { + err = errors.New(formulaErrorNUM) + return + } + result = fmt.Sprintf("%g", float64(rand.New(rand.NewSource(time.Now().UnixNano())).Int63n(top-bottom+1)+bottom)) + return +} + +// romanNumerals defined a numeral system that originated in ancient Rome and +// remained the usual way of writing numbers throughout Europe well into the +// Late Middle Ages. +type romanNumerals struct { + n float64 + s string +} + +var romanTable = [][]romanNumerals{{{1000, "M"}, {900, "CM"}, {500, "D"}, {400, "CD"}, {100, "C"}, {90, "XC"}, {50, "L"}, {40, "XL"}, {10, "X"}, {9, "IX"}, {5, "V"}, {4, "IV"}, {1, "I"}}, + {{1000, "M"}, {950, "LM"}, {900, "CM"}, {500, "D"}, {450, "LD"}, {400, "CD"}, {100, "C"}, {95, "VC"}, {90, "XC"}, {50, "L"}, {45, "VL"}, {40, "XL"}, {10, "X"}, {9, "IX"}, {5, "V"}, {4, "IV"}, {1, "I"}}, + {{1000, "M"}, {990, "XM"}, {950, "LM"}, {900, "CM"}, {500, "D"}, {490, "XD"}, {450, "LD"}, {400, "CD"}, {100, "C"}, {99, "IC"}, {90, "XC"}, {50, "L"}, {45, "VL"}, {40, "XL"}, {10, "X"}, {9, "IX"}, {5, "V"}, {4, "IV"}, {1, "I"}}, + {{1000, "M"}, {995, "VM"}, {990, "XM"}, {950, "LM"}, {900, "CM"}, {500, "D"}, {495, "VD"}, {490, "XD"}, {450, "LD"}, {400, "CD"}, {100, "C"}, {99, "IC"}, {90, "XC"}, {50, "L"}, {45, "VL"}, {40, "XL"}, {10, "X"}, {9, "IX"}, {5, "V"}, {4, "IV"}, {1, "I"}}, + {{1000, "M"}, {999, "IM"}, {995, "VM"}, {990, "XM"}, {950, "LM"}, {900, "CM"}, {500, "D"}, {499, "ID"}, {495, "VD"}, {490, "XD"}, {450, "LD"}, {400, "CD"}, {100, "C"}, {99, "IC"}, {90, "XC"}, {50, "L"}, {45, "VL"}, {40, "XL"}, {10, "X"}, {9, "IX"}, {5, "V"}, {4, "IV"}, {1, "I"}}} + +// ROMAN function converts an arabic number to Roman. I.e. for a supplied +// integer, the function returns a text string depicting the roman numeral +// form of the number. The syntax of the function is: +// +// ROMAN(number,[form]) +// +func (fn *formulaFuncs) ROMAN(argsList *list.List) (result string, err error) { + if argsList.Len() == 0 { + err = errors.New("ROMAN requires at least 1 argument") + return + } + if argsList.Len() > 2 { + err = errors.New("ROMAN allows at most 2 arguments") + return + } + var number float64 + var form int + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { + return + } + if argsList.Len() > 1 { + if form, err = strconv.Atoi(argsList.Back().Value.(formulaArg).Value); err != nil { + return + } + if form < 0 { + form = 0 + } else if form > 4 { + form = 4 + } + } + decimalTable := romanTable[0] + switch form { + case 1: + decimalTable = romanTable[1] + case 2: + decimalTable = romanTable[2] + case 3: + decimalTable = romanTable[3] + case 4: + decimalTable = romanTable[4] + } + val := math.Trunc(number) + buf := bytes.Buffer{} + for _, r := range decimalTable { + for val >= r.n { + buf.WriteString(r.s) + val -= r.n + } + } + result = buf.String() + return +} + // 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 @@ -1840,28 +2153,3 @@ func (fn *formulaFuncs) SUM(argsList *list.List) (result string, err error) { result = fmt.Sprintf("%g", sum) return } - -// QUOTIENT function returns the integer portion of a division between two -// supplied numbers. The syntax of the function is: -// -// QUOTIENT(numerator,denominator) -// -func (fn *formulaFuncs) QUOTIENT(argsList *list.List) (result string, err error) { - if argsList.Len() != 2 { - err = errors.New("QUOTIENT requires 2 numeric arguments") - return - } - var x, y float64 - if x, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil { - return - } - if y, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).Value, 64); err != nil { - return - } - if y == 0 { - err = errors.New(formulaErrorDIV) - return - } - result = fmt.Sprintf("%g", math.Trunc(x/y)) - return -} -- cgit v1.2.1