fn: ABS, GCD, LCM, POWER, PRODUCT, SIGN, SQRT, SUM, QUOTIENT

formula
xuri 5 years ago
parent 48fc4c08a2
commit bdf0538640
No known key found for this signature in database
GPG Key ID: BA5E5BB1C948EDF7

@ -412,7 +412,7 @@ func (f *File) parseReference(sheet, reference string) (result []string, err err
// rangeResolver extract value as string from given reference and range list.
// This function will not ignore the empty cell. Note that the result of 3D
// range references may be different from Excel in some cases, for example,
// A1:A2:A2:B3 in Excel will include B2, but we wont.
// A1:A2:A2:B3 in Excel will include B1, but we wont.
func (f *File) rangeResolver(cellRefs, cellRanges *list.List) (result []string, err error) {
filter := map[string]string{}
// extract value from ranges
@ -475,14 +475,58 @@ func callFuncByName(receiver interface{}, name string, params []reflect.Value) (
// Math and Trigonometric functions
// SUM function adds together a supplied set of numbers and returns the sum of
// these values. The syntax of the function is:
// ABS function returns the absolute value of any supplied number. The syntax
// of the function is:
//
// SUM(number1,[number2],...)
// ABS(number)
//
func (fn *formulaFuncs) SUM(argsStack *Stack) (result string, err error) {
func (fn *formulaFuncs) ABS(argsStack *Stack) (result string, err error) {
if argsStack.Len() != 1 {
err = errors.New("ABS requires 1 numeric arguments")
return
}
var val float64
var sum float64
val, err = strconv.ParseFloat(argsStack.Pop().(efp.Token).TValue, 64)
if err != nil {
return
}
result = fmt.Sprintf("%g", math.Abs(val))
return
}
// gcd returns the greatest common divisor of two supplied integers.
func gcd(x, y float64) float64 {
x, y = math.Trunc(x), math.Trunc(y)
if x == 0 {
return y
}
if y == 0 {
return x
}
for x != y {
if x > y {
x = x - y
} else {
y = y - x
}
}
return x
}
// GCD function returns the greatest common divisor of two or more supplied
// integers.The syntax of the function is:
//
// GCD(number1,[number2],...)
//
func (fn *formulaFuncs) GCD(argsStack *Stack) (result string, err error) {
if argsStack.Len() == 0 {
err = errors.New("GCD requires at least 1 argument")
return
}
var (
val float64
nums = []float64{}
)
for !argsStack.Empty() {
token := argsStack.Pop().(efp.Token)
if token.TValue == "" {
@ -492,21 +536,51 @@ func (fn *formulaFuncs) SUM(argsStack *Stack) (result string, err error) {
if err != nil {
return
}
sum += val
nums = append(nums, val)
}
result = fmt.Sprintf("%g", sum)
if nums[0] < 0 {
err = errors.New("GCD only accepts positive arguments")
return
}
if len(nums) == 1 {
result = fmt.Sprintf("%g", nums[0])
return
}
cd := nums[0]
for i := 1; i < len(nums); i++ {
if nums[i] < 0 {
err = errors.New("GCD only accepts positive arguments")
return
}
cd = gcd(cd, nums[i])
}
result = fmt.Sprintf("%g", cd)
return
}
// PRODUCT function returns the product (multiplication) of a supplied set of numerical values.
// The syntax of the function is:
// 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:
//
// PRODUCT(number1,[number2],...)
// LCM(number1,[number2],...)
//
func (fn *formulaFuncs) PRODUCT(argsStack *Stack) (result string, err error) {
func (fn *formulaFuncs) LCM(argsStack *Stack) (result string, err error) {
if argsStack.Len() == 0 {
err = errors.New("LCM requires at least 1 argument")
return
}
var (
val float64
product float64 = 1
nums = []float64{}
)
for !argsStack.Empty() {
token := argsStack.Pop().(efp.Token)
@ -517,13 +591,29 @@ func (fn *formulaFuncs) PRODUCT(argsStack *Stack) (result string, err error) {
if err != nil {
return
}
product = product * val
nums = append(nums, val)
}
result = fmt.Sprintf("%g", product)
if nums[0] < 0 {
err = errors.New("LCM only accepts positive arguments")
return
}
if len(nums) == 1 {
result = fmt.Sprintf("%g", nums[0])
return
}
cm := nums[0]
for i := 1; i < len(nums); i++ {
if nums[i] < 0 {
err = errors.New("LCM only accepts positive arguments")
return
}
cm = lcm(cm, nums[i])
}
result = fmt.Sprintf("%g", cm)
return
}
// PRODUCT function calculates a given number, raised to a supplied power.
// POWER function calculates a given number, raised to a supplied power.
// The syntax of the function is:
//
// POWER(number,power)
@ -554,8 +644,62 @@ func (fn *formulaFuncs) POWER(argsStack *Stack) (result string, err error) {
return
}
// SQRT function calculates the positive square root of a supplied number.
// The syntax of the function is:
// 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(argsStack *Stack) (result string, err error) {
var (
val float64
product float64 = 1
)
for !argsStack.Empty() {
token := argsStack.Pop().(efp.Token)
if token.TValue == "" {
continue
}
val, err = strconv.ParseFloat(token.TValue, 64)
if err != nil {
return
}
product = product * val
}
result = fmt.Sprintf("%g", product)
return
}
// SIGN function returns the arithmetic sign (+1, -1 or 0) of a supplied
// number. I.e. if the number is positive, the Sign function returns +1, if
// the number is negative, the function returns -1 and if the number is 0
// (zero), the function returns 0. The syntax of the function is:
//
// SIGN(number)
//
func (fn *formulaFuncs) SIGN(argsStack *Stack) (result string, err error) {
if argsStack.Len() != 1 {
err = errors.New("SIGN requires 1 numeric arguments")
return
}
var val float64
val, err = strconv.ParseFloat(argsStack.Pop().(efp.Token).TValue, 64)
if err != nil {
return
}
if val < 0 {
result = "-1"
return
}
if val > 0 {
result = "1"
return
}
result = "0"
return
}
// SQRT function calculates the positive square root of a supplied number. The
// syntax of the function is:
//
// SQRT(number)
//
@ -577,8 +721,31 @@ func (fn *formulaFuncs) SQRT(argsStack *Stack) (result string, err error) {
return
}
// QUOTIENT function returns the integer portion of a division between two supplied numbers.
// The syntax of the function is:
// 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(argsStack *Stack) (result string, err error) {
var val float64
var sum float64
for !argsStack.Empty() {
token := argsStack.Pop().(efp.Token)
if token.TValue == "" {
continue
}
val, err = strconv.ParseFloat(token.TValue, 64)
if err != nil {
return
}
sum += val
}
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)
//

@ -18,6 +18,35 @@ func TestCalcCellValue(t *testing.T) {
}
mathCalc := map[string]string{
// ABS
"=ABS(-1)": "1",
"=ABS(-6.5)": "6.5",
"=ABS(6.5)": "6.5",
"=ABS(0)": "0",
"=ABS(2-4.5)": "2.5",
// GCD
"=GCD(1,5)": "1",
"=GCD(15,10,25)": "5",
"=GCD(0,8,12)": "4",
"=GCD(7,2)": "1",
// LCM
"=LCM(1,5)": "5",
"=LCM(15,10,25)": "150",
"=LCM(1,8,12)": "24",
"=LCM(7,2)": "14",
// POWER
"=POWER(4,2)": "16",
// PRODUCT
"=PRODUCT(3,6)": "18",
// SIGN
"=SIGN(9.5)": "1",
"=SIGN(-9.5)": "-1",
"=SIGN(0)": "0",
"=SIGN(0.00000001)": "1",
"=SIGN(6-7)": "-1",
// SQRT
"=SQRT(4)": "2",
// SUM
"=SUM(1,2)": "3",
"=SUM(1,2+3)": "6",
"=SUM(SUM(1,2),2)": "5",
@ -31,10 +60,6 @@ func TestCalcCellValue(t *testing.T) {
"=((3+5*2)+3)/5+(-6)/4*2+3": "3.2",
"=1+SUM(SUM(1,2*3),4)*-4/2+5+(4+2)*3": "2",
"=1+SUM(SUM(1,2*3),4)*4/3+5+(4+2)*3": "38.666666666666664",
// POWER
"=POWER(4,2)": "16",
// SQRT
"=SQRT(4)": "2",
// QUOTIENT
"=QUOTIENT(5, 2)": "2",
"=QUOTIENT(4.5, 3.1)": "1",
@ -48,10 +73,23 @@ func TestCalcCellValue(t *testing.T) {
assert.Equal(t, expected, result)
}
mathCalcError := map[string]string{
// ABS
"=ABS(1,2)": "ABS requires 1 numeric arguments",
"=ABS(~)": `cannot convert cell "~" to coordinates: invalid cell name "~"`,
// GCD
"=GCD()": "GCD requires at least 1 argument",
"=GCD(-1)": "GCD only accepts positive arguments",
"=GCD(1,-1)": "GCD only accepts positive arguments",
// LCM
"=LCM()": "LCM requires at least 1 argument",
"=LCM(-1)": "LCM only accepts positive arguments",
"=LCM(1,-1)": "LCM only accepts positive arguments",
// POWER
"=POWER(0,0)": "#NUM!",
"=POWER(0,-1)": "#DIV/0!",
"=POWER(1)": "POWER requires 2 numeric arguments",
// SIGN
"=SIGN()": "SIGN requires 1 numeric arguments",
// SQRT
"=SQRT(-1)": "#NUM!",
"=SQRT(1,2)": "SQRT requires 1 numeric arguments",
@ -68,6 +106,9 @@ func TestCalcCellValue(t *testing.T) {
}
referenceCalc := map[string]string{
// PRODUCT
"=PRODUCT(Sheet1!A1:Sheet1!A1:A2,A2)": "4",
// SUM
"=A1/A3": "0.3333333333333333",
"=SUM(A1:A2)": "3",
"=SUM(Sheet1!A1,A2)": "3",
@ -77,8 +118,6 @@ func TestCalcCellValue(t *testing.T) {
"=1+SUM(SUM(A1+A2/A3)*(2-3),2)": "1.3333333333333335",
"=A1/A2/SUM(A1:A2:B1)": "0.07142857142857142",
"=A1/A2/SUM(A1:A2:B1)*A3": "0.21428571428571427",
// PRODUCT
"=PRODUCT(Sheet1!A1:Sheet1!A1:A2,A2)": "4",
}
for formula, expected := range referenceCalc {
f := prepareData()

Loading…
Cancel
Save