mirror of
https://github.com/unidoc/unioffice.git
synced 2025-05-04 22:17:38 +08:00
formula: more implementations of math/trig functins
- MDETERM - MOD - MROUND - MULTINOMIAL - MUNIT - ODD - POWER - PRODUCT - QUOTIENT - RADIANS - RAND - RANDBETWEEN - ROMAN - ROUND - ROUNDDOWN - ROUNDUP - SERIESSUM
This commit is contained in:
parent
23e428e770
commit
e46a7165b4
@ -8,13 +8,19 @@
|
||||
package formula
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var rnd *rand.Rand
|
||||
|
||||
func init() {
|
||||
rnd = rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
RegisterFunction("ABS", makeMathWrapper("ASIN", math.Abs))
|
||||
RegisterFunction("ACOS", makeMathWrapper("ASIN", math.Acos))
|
||||
RegisterFunction("ACOSH", makeMathWrapper("ASIN", math.Acosh))
|
||||
@ -55,7 +61,28 @@ func init() {
|
||||
RegisterFunction("LN", makeMathWrapper("LN", math.Log))
|
||||
RegisterFunction("LOG", Log)
|
||||
RegisterFunction("LOG10", makeMathWrapper("LOG10", math.Log10))
|
||||
RegisterFunction("MDETERM", MDeterm)
|
||||
// RegisterFunction("MINVERSE", MInverse) // TODO: skipping the other matrix functins, not sure how common they are
|
||||
// RegisterFunction("MMULT"
|
||||
RegisterFunction("MOD", Mod)
|
||||
RegisterFunction("MROUND", Mround)
|
||||
RegisterFunction("MULTINOMIAL", Multinomial)
|
||||
RegisterFunction("_xlfn.MUNIT", Munit)
|
||||
RegisterFunction("ODD", Odd)
|
||||
RegisterFunction("PI", Pi)
|
||||
RegisterFunction("POWER", Power)
|
||||
RegisterFunction("PRODUCT", Product)
|
||||
RegisterFunction("QUOTIENT", Quotient)
|
||||
RegisterFunction("RADIANS", Radians)
|
||||
RegisterFunction("RAND", Rand)
|
||||
RegisterFunction("RANDBETWEEN", RandBetween)
|
||||
RegisterFunction("ROMAN", Roman)
|
||||
RegisterFunction("ROUND", Round)
|
||||
RegisterFunction("ROUNDDOWN", RoundDown)
|
||||
RegisterFunction("ROUNDUP", RoundUp)
|
||||
// RegisterFunction("SEC"
|
||||
// RegisterFunction("SECH"
|
||||
RegisterFunction("SERIESSUM", SeriesSum)
|
||||
}
|
||||
|
||||
// makeMathWrapper is used to wrap single argument math functions from the Go
|
||||
@ -738,6 +765,211 @@ func Log(args []Result) Result {
|
||||
|
||||
}
|
||||
|
||||
func minor(sqMtx [][]Result, idx int) [][]Result {
|
||||
ret := [][]Result{}
|
||||
for i := range sqMtx {
|
||||
if i == 0 {
|
||||
continue
|
||||
}
|
||||
row := []Result{}
|
||||
for j := range sqMtx {
|
||||
if j == idx {
|
||||
continue
|
||||
}
|
||||
row = append(row, sqMtx[i][j])
|
||||
}
|
||||
ret = append(ret, row)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
func det(sqMtx [][]Result) float64 {
|
||||
// two by two
|
||||
if len(sqMtx) == 2 {
|
||||
m00 := sqMtx[0][0].AsNumber()
|
||||
m01 := sqMtx[0][1].AsNumber()
|
||||
m10 := sqMtx[1][0].AsNumber()
|
||||
m11 := sqMtx[1][1].AsNumber()
|
||||
if m00.Type != ResultTypeNumber || m01.Type != ResultTypeNumber ||
|
||||
m10.Type != ResultTypeNumber || m11.Type != ResultTypeNumber {
|
||||
return math.NaN()
|
||||
}
|
||||
return m00.ValueNumber*m11.ValueNumber -
|
||||
m10.ValueNumber*m01.ValueNumber
|
||||
}
|
||||
|
||||
res := float64(0)
|
||||
sgn := float64(1)
|
||||
for j := range sqMtx {
|
||||
res += sgn * sqMtx[0][j].ValueNumber * det(minor(sqMtx, j))
|
||||
sgn *= -1
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// MDeterm is an implementation of the Excel MDETERM which finds the determinant
|
||||
// of a matrix.
|
||||
func MDeterm(args []Result) Result {
|
||||
if len(args) != 1 {
|
||||
return MakeErrorResult("MDETERM() requires a single array argument")
|
||||
}
|
||||
|
||||
mtx := args[0]
|
||||
if mtx.Type != ResultTypeArray {
|
||||
return MakeErrorResult("MDETERM() requires a single array argument")
|
||||
}
|
||||
|
||||
numRows := len(mtx.ValueArray)
|
||||
for _, row := range mtx.ValueArray {
|
||||
if len(row) != numRows {
|
||||
return MakeErrorResult("MDETERM() requires a square matrix")
|
||||
}
|
||||
}
|
||||
return MakeNumberResult(det(mtx.ValueArray))
|
||||
}
|
||||
|
||||
// Mod is an implementation of the Excel MOD function which returns the
|
||||
// remainder after division. It requires two numeric argumnts.
|
||||
func Mod(args []Result) Result {
|
||||
if len(args) != 2 {
|
||||
return MakeErrorResult("MOD() requires two numeric arguments")
|
||||
}
|
||||
n := args[0].AsNumber()
|
||||
d := args[1].AsNumber()
|
||||
if n.Type != ResultTypeNumber || d.Type != ResultTypeNumber {
|
||||
return MakeErrorResult("MOD() requires two numeric arguments")
|
||||
}
|
||||
if d.ValueNumber == 0 {
|
||||
return MakeErrorResultType(ErrorTypeDivideByZero, "MOD() divide by zero")
|
||||
}
|
||||
|
||||
// Per MS page, MOD(n, d) = n - d*INT(n/d)
|
||||
// where INT is trunc in:
|
||||
trunc, rem := math.Modf(n.ValueNumber / d.ValueNumber)
|
||||
if rem < 0 {
|
||||
trunc--
|
||||
}
|
||||
return MakeNumberResult(n.ValueNumber - d.ValueNumber*trunc)
|
||||
}
|
||||
|
||||
// Mround is an implementation of the Excel MROUND function. It is not a
|
||||
// generic rounding function and has some oddities to match Excel's behavior.
|
||||
func Mround(args []Result) Result {
|
||||
if len(args) != 2 {
|
||||
return MakeErrorResult("MROUND() requires two numeric arguments")
|
||||
}
|
||||
// number to round
|
||||
number := args[0].AsNumber()
|
||||
if number.Type != ResultTypeNumber {
|
||||
return MakeErrorResult("first argument to MROUND() must be a number")
|
||||
}
|
||||
|
||||
// significance
|
||||
significance := float64(1)
|
||||
sigArg := args[1].AsNumber()
|
||||
if sigArg.Type != ResultTypeNumber {
|
||||
return MakeErrorResult("second argument to MROUND() must be a number")
|
||||
}
|
||||
significance = sigArg.ValueNumber
|
||||
|
||||
if significance < 0 && number.ValueNumber > 0 ||
|
||||
significance > 0 && number.ValueNumber < 0 {
|
||||
return MakeErrorResult("MROUND() argument signs must match")
|
||||
}
|
||||
|
||||
v := number.ValueNumber
|
||||
v, res := math.Modf(v / significance)
|
||||
if math.Trunc(res+0.5) > 0 {
|
||||
v++
|
||||
}
|
||||
return MakeNumberResult(v * significance)
|
||||
}
|
||||
|
||||
func multinomial(args []Result) (float64, float64, Result) {
|
||||
num := 0.0
|
||||
denom := 1.0
|
||||
for _, arg := range args {
|
||||
switch arg.Type {
|
||||
case ResultTypeNumber:
|
||||
num += arg.ValueNumber
|
||||
denom *= fact(arg.ValueNumber)
|
||||
case ResultTypeList, ResultTypeArray:
|
||||
n, d, e := multinomial(arg.ListValues())
|
||||
num += n
|
||||
denom *= fact(d)
|
||||
if e.Type == ResultTypeError {
|
||||
return 0, 0, e
|
||||
}
|
||||
case ResultTypeString:
|
||||
return 0, 0, MakeErrorResult("MULTINOMIAL() requires numeric arguments")
|
||||
case ResultTypeError:
|
||||
return 0, 0, arg
|
||||
}
|
||||
}
|
||||
return num, denom, MakeEmptyResult()
|
||||
}
|
||||
|
||||
// Multinomial implements the excel MULTINOMIAL function.
|
||||
func Multinomial(args []Result) Result {
|
||||
if len(args) < 1 {
|
||||
return MakeErrorResult("MULTINOMIAL() requires at least one numeric input")
|
||||
}
|
||||
num, denom, err := multinomial(args)
|
||||
if err.Type == ResultTypeError {
|
||||
return err
|
||||
}
|
||||
return MakeNumberResult(fact(num) / denom)
|
||||
}
|
||||
|
||||
// Munit is an implementation of the Excel MUNIT function that returns an
|
||||
// identity matrix.
|
||||
func Munit(args []Result) Result {
|
||||
if len(args) != 1 {
|
||||
return MakeErrorResult("MUNIT() requires one numeric input")
|
||||
}
|
||||
dim := args[0].AsNumber()
|
||||
if dim.Type != ResultTypeNumber {
|
||||
return MakeErrorResult("MUNIT() requires one numeric input")
|
||||
}
|
||||
dimInt := int(dim.ValueNumber)
|
||||
mtx := make([][]Result, 0, dimInt)
|
||||
for i := 0; i < dimInt; i++ {
|
||||
row := make([]Result, dimInt)
|
||||
for j := 0; j < dimInt; j++ {
|
||||
if i == j {
|
||||
row[j] = MakeNumberResult(1.0)
|
||||
} else {
|
||||
row[j] = MakeNumberResult(0.0)
|
||||
}
|
||||
}
|
||||
mtx = append(mtx, row)
|
||||
}
|
||||
return MakeArrayResult(mtx)
|
||||
}
|
||||
|
||||
// Odd is an implementation of the Excel ODD() that rounds a number to the
|
||||
// nearest odd integer.
|
||||
func Odd(args []Result) Result {
|
||||
if len(args) != 1 {
|
||||
return MakeErrorResult("ODD() requires one argument")
|
||||
}
|
||||
vArg := args[0].AsNumber()
|
||||
if vArg.Type != ResultTypeNumber {
|
||||
return MakeErrorResult("ODD() requires number argument")
|
||||
}
|
||||
|
||||
sign := math.Signbit(vArg.ValueNumber)
|
||||
m, r := math.Modf((vArg.ValueNumber - 1) / 2)
|
||||
v := m*2 + 1
|
||||
if r != 0 {
|
||||
if !sign {
|
||||
v += 2
|
||||
} else {
|
||||
v -= 2
|
||||
}
|
||||
}
|
||||
return MakeNumberResult(v)
|
||||
}
|
||||
|
||||
// Pi is an implementation of the Excel Pi() function that just returns the Pi
|
||||
// constant.
|
||||
func Pi(args []Result) Result {
|
||||
@ -746,3 +978,369 @@ func Pi(args []Result) Result {
|
||||
}
|
||||
return MakeNumberResult(math.Pi)
|
||||
}
|
||||
|
||||
// Power is an implementation of the Excel POWER function that raises a number
|
||||
// to a power. It requires two numeric arguments.
|
||||
func Power(args []Result) Result {
|
||||
if len(args) != 2 {
|
||||
return MakeErrorResult("POWER() requires two numeric arguments")
|
||||
}
|
||||
|
||||
number := args[0].AsNumber()
|
||||
if number.Type != ResultTypeNumber {
|
||||
return MakeErrorResult("first argument to POWER() must be a number")
|
||||
}
|
||||
|
||||
exp := args[1].AsNumber()
|
||||
if exp.Type != ResultTypeNumber {
|
||||
return MakeErrorResult("second argument to POWER() must be a number")
|
||||
}
|
||||
|
||||
return MakeNumberResult(math.Pow(number.ValueNumber, exp.ValueNumber))
|
||||
}
|
||||
|
||||
// Product is an implementation of the Excel PRODUCT() function.
|
||||
func Product(args []Result) Result {
|
||||
res := 1.0
|
||||
for _, a := range args {
|
||||
a = a.AsNumber()
|
||||
switch a.Type {
|
||||
case ResultTypeNumber:
|
||||
res *= a.ValueNumber
|
||||
case ResultTypeList, ResultTypeArray:
|
||||
subSum := Product(a.ListValues())
|
||||
if subSum.Type != ResultTypeNumber {
|
||||
return subSum
|
||||
}
|
||||
res *= subSum.ValueNumber
|
||||
case ResultTypeString:
|
||||
// treated as zero by Excel
|
||||
case ResultTypeError:
|
||||
return a
|
||||
case ResultTypeEmpty:
|
||||
// skip
|
||||
default:
|
||||
return MakeErrorResult(fmt.Sprintf("unhandled PRODUCT() argument type %s", a.Type))
|
||||
}
|
||||
}
|
||||
return MakeNumberResult(res)
|
||||
}
|
||||
|
||||
// Quotient is an implementation of the Excel QUOTIENT function that returns the
|
||||
// integer portion of division.
|
||||
func Quotient(args []Result) Result {
|
||||
if len(args) != 2 {
|
||||
return MakeErrorResult("QUOTIENT() requires two numeric arguments")
|
||||
}
|
||||
arg1 := args[0].AsNumber()
|
||||
arg2 := args[1].AsNumber()
|
||||
if arg1.Type != ResultTypeNumber || arg2.Type != ResultTypeNumber {
|
||||
return MakeErrorResult("QUOTIENT() requires two numeric arguments")
|
||||
}
|
||||
if arg2.ValueNumber == 0 {
|
||||
return MakeErrorResultType(ErrorTypeDivideByZero, "QUOTIENT() divide by zero")
|
||||
}
|
||||
|
||||
return MakeNumberResult(math.Trunc(arg1.ValueNumber / arg2.ValueNumber))
|
||||
}
|
||||
|
||||
// Radians is an implementation of the Excel function RADIANS() that converts
|
||||
// degrees to radians.
|
||||
func Radians(args []Result) Result {
|
||||
if len(args) != 1 {
|
||||
return MakeErrorResult("RADIANS() requires one argument")
|
||||
}
|
||||
vArg := args[0].AsNumber()
|
||||
if vArg.Type != ResultTypeNumber {
|
||||
return MakeErrorResult("RADIANS() requires number argument")
|
||||
}
|
||||
|
||||
return MakeNumberResult(math.Pi / 180.0 * vArg.ValueNumber)
|
||||
}
|
||||
|
||||
// Rand is an implementation of the Excel RAND() function that returns random
|
||||
// numbers in the range [0,1).
|
||||
func Rand(args []Result) Result {
|
||||
if len(args) != 0 {
|
||||
return MakeErrorResult("RAND() accepts no arguments")
|
||||
}
|
||||
return MakeNumberResult(rnd.Float64())
|
||||
}
|
||||
|
||||
// RandBetween is an implementation of the Excel RANDBETWEEN() function that returns a random
|
||||
// integer in the range specified.
|
||||
func RandBetween(args []Result) Result {
|
||||
if len(args) != 2 {
|
||||
return MakeErrorResult("RANDBETWEEN() requires two numeric arguments")
|
||||
}
|
||||
arg1 := args[0].AsNumber()
|
||||
arg2 := args[1].AsNumber()
|
||||
if arg1.Type != ResultTypeNumber || arg2.Type != ResultTypeNumber {
|
||||
return MakeErrorResult("RANDBETWEEN() requires two numeric arguments")
|
||||
}
|
||||
if arg2.ValueNumber < arg1.ValueNumber {
|
||||
return MakeErrorResult("RANDBETWEEN() requires second argument to be larger")
|
||||
}
|
||||
bottom := int64(arg1.ValueNumber)
|
||||
top := int64(arg2.ValueNumber)
|
||||
return MakeNumberResult(float64(rnd.Int63n(top-bottom+1) + bottom))
|
||||
}
|
||||
|
||||
type ri struct {
|
||||
n float64
|
||||
s string
|
||||
}
|
||||
|
||||
var r1tables = []ri{
|
||||
{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"},
|
||||
}
|
||||
|
||||
var r2tables = []ri{
|
||||
{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"},
|
||||
}
|
||||
|
||||
var r3tables = []ri{
|
||||
{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"},
|
||||
}
|
||||
|
||||
var r4tables = []ri{
|
||||
{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"},
|
||||
}
|
||||
|
||||
var r5tables = []ri{
|
||||
{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 is an implementation of the Excel ROMAN function that convers numbers
|
||||
// to roman numerals in one of 5 formats.
|
||||
func Roman(args []Result) Result {
|
||||
if len(args) == 0 {
|
||||
return MakeErrorResult("ROMAN() requires at least one numeric argument")
|
||||
}
|
||||
if len(args) > 2 {
|
||||
return MakeErrorResult("ROMAN() requires at most two numeric arguments")
|
||||
}
|
||||
|
||||
nArg := args[0].AsNumber()
|
||||
if nArg.Type != ResultTypeNumber {
|
||||
return MakeErrorResult("ROMAN() requires at least one numeric argument")
|
||||
}
|
||||
format := 0
|
||||
if len(args) > 1 {
|
||||
fmtArg := args[1]
|
||||
if fmtArg.Type != ResultTypeNumber {
|
||||
return MakeErrorResult("ROMAN() requires second argument to be numeric")
|
||||
}
|
||||
format = int(fmtArg.ValueNumber)
|
||||
if format < 0 {
|
||||
format = 0
|
||||
} else if format > 4 {
|
||||
format = 4
|
||||
}
|
||||
}
|
||||
dt := r1tables
|
||||
switch format {
|
||||
case 1:
|
||||
dt = r2tables
|
||||
case 2:
|
||||
dt = r3tables
|
||||
case 3:
|
||||
dt = r4tables
|
||||
case 4:
|
||||
dt = r5tables
|
||||
}
|
||||
v := math.Trunc(nArg.ValueNumber)
|
||||
buf := bytes.Buffer{}
|
||||
for _, r := range dt {
|
||||
for v >= r.n {
|
||||
buf.WriteString(r.s)
|
||||
v -= r.n
|
||||
}
|
||||
}
|
||||
return MakeStringResult(buf.String())
|
||||
}
|
||||
|
||||
type rmode byte
|
||||
|
||||
const (
|
||||
closest rmode = iota
|
||||
down
|
||||
up
|
||||
)
|
||||
|
||||
// Round is an implementation of the Excel ROUND function that rounds a number
|
||||
// to a specified number of digits.
|
||||
func round(args []Result, mode rmode) Result {
|
||||
if len(args) == 0 {
|
||||
return MakeErrorResult("ROUND() requires at least one numeric arguments")
|
||||
}
|
||||
// number to round
|
||||
number := args[0].AsNumber()
|
||||
if number.Type != ResultTypeNumber {
|
||||
return MakeErrorResult("first argument to ROUND() must be a number")
|
||||
}
|
||||
|
||||
digits := float64(0)
|
||||
if len(args) > 1 {
|
||||
digitArg := args[1].AsNumber()
|
||||
if digitArg.Type != ResultTypeNumber {
|
||||
return MakeErrorResult("second argument to ROUND() must be a number")
|
||||
}
|
||||
digits = digitArg.ValueNumber
|
||||
}
|
||||
|
||||
v := number.ValueNumber
|
||||
|
||||
significance := 1.0
|
||||
if digits > 0 {
|
||||
significance = math.Pow(1/10.0, digits)
|
||||
} else {
|
||||
significance = math.Pow(10.0, -digits)
|
||||
}
|
||||
|
||||
v, res := math.Modf(v / significance)
|
||||
switch mode {
|
||||
case closest:
|
||||
const eps = 0.499999999
|
||||
if res >= eps {
|
||||
v++
|
||||
} else if res <= -eps {
|
||||
v--
|
||||
}
|
||||
case down:
|
||||
// do nothing, truncates
|
||||
case up:
|
||||
if res > 0 {
|
||||
v++
|
||||
} else if res < 0 {
|
||||
v--
|
||||
}
|
||||
}
|
||||
|
||||
return MakeNumberResult(v * significance)
|
||||
}
|
||||
|
||||
// Round is an implementation of the Excel ROUND function that rounds a number
|
||||
// to a specified number of digits.
|
||||
func Round(args []Result) Result {
|
||||
return round(args, closest)
|
||||
}
|
||||
|
||||
// RoundDown is an implementation of the Excel ROUNDDOWN function that rounds a number
|
||||
// down to a specified number of digits.
|
||||
func RoundDown(args []Result) Result {
|
||||
return round(args, down)
|
||||
}
|
||||
|
||||
// RoundUp is an implementation of the Excel ROUNDUP function that rounds a number
|
||||
// up to a specified number of digits.
|
||||
func RoundUp(args []Result) Result {
|
||||
return round(args, up)
|
||||
}
|
||||
|
||||
// SeriesSum implements the Excel SERIESSUM function.
|
||||
func SeriesSum(args []Result) Result {
|
||||
if len(args) != 4 {
|
||||
return MakeErrorResult("SERIESSUM() requires 4 arguments")
|
||||
}
|
||||
x := args[0].AsNumber()
|
||||
n := args[1].AsNumber()
|
||||
m := args[2].AsNumber()
|
||||
coeffs := args[3].ListValues()
|
||||
if x.Type != ResultTypeNumber || n.Type != ResultTypeNumber || m.Type != ResultTypeNumber {
|
||||
return MakeErrorResult("SERIESSUM() requires first three arguments to be numeric")
|
||||
}
|
||||
res := float64(0)
|
||||
for i, c := range coeffs {
|
||||
res += c.ValueNumber * math.Pow(x.ValueNumber, n.ValueNumber+float64(i)*m.ValueNumber)
|
||||
}
|
||||
return MakeNumberResult(res)
|
||||
}
|
||||
|
@ -101,40 +101,40 @@ const yyLast = 175
|
||||
|
||||
var yyAct = [...]int{
|
||||
|
||||
3, 41, 18, 9, 70, 37, 29, 43, 44, 42,
|
||||
27, 28, 29, 45, 46, 36, 67, 71, 29, 23,
|
||||
49, 36, 47, 51, 65, 40, 52, 53, 54, 55,
|
||||
56, 57, 58, 59, 60, 61, 62, 63, 13, 19,
|
||||
64, 66, 42, 14, 15, 16, 17, 72, 21, 69,
|
||||
25, 26, 27, 28, 29, 34, 30, 31, 32, 33,
|
||||
35, 50, 75, 36, 11, 1, 20, 10, 73, 2,
|
||||
42, 74, 76, 68, 25, 26, 27, 28, 29, 34,
|
||||
30, 31, 32, 33, 35, 8, 0, 36, 25, 26,
|
||||
42, 3, 41, 29, 18, 37, 67, 43, 44, 27,
|
||||
28, 29, 36, 45, 46, 29, 23, 49, 40, 13,
|
||||
36, 65, 19, 51, 47, 21, 52, 53, 54, 55,
|
||||
56, 57, 58, 59, 60, 61, 62, 63, 66, 50,
|
||||
64, 75, 11, 9, 1, 20, 10, 2, 8, 72,
|
||||
70, 69, 25, 26, 27, 28, 29, 34, 30, 31,
|
||||
32, 33, 35, 71, 0, 36, 0, 0, 74, 73,
|
||||
0, 0, 76, 68, 25, 26, 27, 28, 29, 34,
|
||||
30, 31, 32, 33, 35, 0, 0, 36, 25, 26,
|
||||
27, 28, 29, 34, 30, 31, 32, 33, 35, 0,
|
||||
0, 36, 25, 26, 27, 28, 29, 0, 0, 0,
|
||||
0, 14, 15, 16, 17, 36, 24, 23, 22, 5,
|
||||
0, 12, 0, 6, 7, 0, 0, 0, 4, 14,
|
||||
15, 16, 17, 0, 24, 23, 22, 38, 0, 12,
|
||||
48, 6, 7, 14, 15, 16, 17, 0, 24, 23,
|
||||
22, 38, 0, 12, 0, 6, 7, 14, 15, 16,
|
||||
17, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 39,
|
||||
0, 14, 15, 16, 17, 36, 24, 23, 22, 38,
|
||||
0, 12, 0, 6, 7, 0, 0, 0, 39, 14,
|
||||
15, 16, 17, 0, 24, 23, 22, 5, 0, 12,
|
||||
0, 6, 7, 0, 0, 0, 4, 14, 15, 16,
|
||||
17, 0, 24, 23, 22, 38, 0, 12, 48, 6,
|
||||
7, 14, 15, 16, 17, 0, 24, 23, 22, 38,
|
||||
0, 12, 0, 6, 7,
|
||||
}
|
||||
var yyPact = [...]int{
|
||||
|
||||
104, -1000, -1000, 69, 136, 150, 136, 136, -1000, -1000,
|
||||
-1000, -1000, 136, -1000, -1000, -1000, -1000, -1000, -16, 6,
|
||||
-1000, -1000, 122, -1000, -1000, 136, 136, 136, 136, 136,
|
||||
136, 136, 136, 136, 136, 136, 136, 69, 36, 136,
|
||||
8, -15, -1000, -11, -11, 55, 6, -1000, -1000, -14,
|
||||
-1000, 69, -11, -11, -17, -17, -1000, 83, 83, 83,
|
||||
83, 83, 83, -5, 31, -1000, 36, 36, -1000, -1000,
|
||||
-1000, 136, -1000, -15, -1000, -1000, 69,
|
||||
122, -1000, -1000, 69, 154, 104, 154, 154, -1000, -1000,
|
||||
-1000, -1000, 154, -1000, -1000, -1000, -1000, -1000, -16, 3,
|
||||
-1000, -1000, 140, -1000, -1000, 154, 154, 154, 154, 154,
|
||||
154, 154, 154, 154, 154, 154, 154, 69, 154, 154,
|
||||
5, -25, 69, -12, -12, 55, 3, -1000, -1000, 32,
|
||||
-1000, 69, -12, -12, -20, -20, -1000, 83, 83, 83,
|
||||
83, 83, 83, -8, 33, -1000, 154, 154, -1000, -1000,
|
||||
-1000, 154, -1000, -25, 69, -1000, 69,
|
||||
}
|
||||
var yyPgo = [...]int{
|
||||
|
||||
0, 0, 85, 69, 67, 2, 66, 65, 3, 64,
|
||||
62, 61, 48, 39, 38, 25, 20, 1,
|
||||
0, 0, 48, 47, 46, 4, 45, 44, 43, 42,
|
||||
41, 39, 25, 22, 19, 18, 17, 2,
|
||||
}
|
||||
var yyR1 = [...]int{
|
||||
|
||||
@ -158,10 +158,10 @@ var yyChk = [...]int{
|
||||
-4, -9, 17, -14, 7, 8, 9, 10, -5, -13,
|
||||
-6, -12, 14, 13, 12, 19, 20, 21, 22, 23,
|
||||
25, 26, 27, 28, 24, 29, 32, -1, 15, 24,
|
||||
-15, -17, -8, -1, -1, -1, 30, -5, 18, -16,
|
||||
-15, -17, -1, -1, -1, -1, 30, -5, 18, -16,
|
||||
-11, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, 16, 33, 31, 18, -5,
|
||||
18, 31, 16, -17, -8, -10, -1,
|
||||
18, 31, 16, -17, -1, -10, -1,
|
||||
}
|
||||
var yyDef = [...]int{
|
||||
|
||||
|
@ -72,8 +72,8 @@ constArrayRows:
|
||||
| constArrayRows tokenSemi constArrayCols { $$ = append($1, $3)};
|
||||
|
||||
constArrayCols:
|
||||
constant { $$ = append($$,$1) }
|
||||
| constArrayCols tokenComma constant { $$ = append($1,$3)}
|
||||
formula { $$ = append($$,$1) }
|
||||
| constArrayCols tokenComma formula { $$ = append($1,$3)}
|
||||
|
||||
|
||||
reference:
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -21,6 +21,7 @@ type plex struct {
|
||||
|
||||
func (f *plex) Lex(lval *yySymType) int {
|
||||
//yyDebug = 3
|
||||
yyErrorVerbose = true
|
||||
n := <-f.nodes
|
||||
if n != nil {
|
||||
lval.node = n
|
||||
|
@ -8,7 +8,6 @@
|
||||
package formula
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
@ -37,11 +36,25 @@ type Result struct {
|
||||
Type ResultType
|
||||
}
|
||||
|
||||
func (r Result) String() string {
|
||||
return r.Value()
|
||||
}
|
||||
|
||||
// Value returns a string version of the result.
|
||||
func (r Result) Value() string {
|
||||
switch r.Type {
|
||||
case ResultTypeNumber:
|
||||
return fmt.Sprintf("%g", r.ValueNumber)
|
||||
n := strconv.FormatFloat(r.ValueNumber, 'f', -1, 64)
|
||||
// HACK: currently only used for testing, need to write a better general
|
||||
// number format function
|
||||
if len(n) > 12 {
|
||||
end := 12
|
||||
for i := end; i > 0 && n[i] == '0'; i-- {
|
||||
end--
|
||||
}
|
||||
n = n[0 : end+1]
|
||||
}
|
||||
return n
|
||||
case ResultTypeError:
|
||||
return r.ValueString
|
||||
case ResultTypeString:
|
||||
|
BIN
spreadsheet/formula/testdata/formulareference.xlsx
vendored
BIN
spreadsheet/formula/testdata/formulareference.xlsx
vendored
Binary file not shown.
@ -390,7 +390,7 @@ func (wb *Workbook) onNewRelationship(decMap *zippkg.DecodeMap, target, typ stri
|
||||
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, len(wb.charts))
|
||||
|
||||
default:
|
||||
log.Printf("unsupported relationship", target, typ)
|
||||
log.Printf("unsupported relationship %s %s", target, typ)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user