Financial functions: part 3 (#361)

* PMT, PPMT
* PRICEDISC, fixed YEARFRAC
* PV and handling empty arguments
* RATE
* RECEIVED
* RRI
* ODDLPRICE, ODDLYIELD
* PRICE, PRICEMAT
* SLN
* SYD
* TBILLEQ, TBILLPRICE, TBILLYIELD
* VDB
This commit is contained in:
Vyacheslav Zgordan 2019-12-27 15:24:38 +03:00 committed by Gunnsteinn Hall
parent dc34147325
commit f4e59e2275
7 changed files with 1689 additions and 339 deletions

View File

@ -31,7 +31,7 @@ func init() {
RegisterFunction("TIMEVALUE", TimeValue)
RegisterFunction("TODAY", Today)
RegisterFunctionComplex("YEAR", Year)
RegisterFunctionComplex("YEARFRAC", YearFrac)
RegisterFunction("YEARFRAC", YearFrac)
}
var date1900 int64 = makeDateS(1900, time.January, 1)
@ -107,6 +107,8 @@ func initRegexpTime() {
}
}
var empty Result = MakeEmptyResult()
// Day is an implementation of the Excel DAY() function.
func Day(args []Result) Result {
if len(args) != 1 {
@ -406,7 +408,7 @@ func dateValue(dateString string) (int, int, int, bool, Result) {
if !validateDate(year, month, day) {
return 0, 0, 0, false, MakeErrorResultType(ErrorTypeValue, dvErrMsg)
}
return year, month, day, timeIsEmpty, MakeEmptyResult()
return year, month, day, timeIsEmpty, empty
}
func validateDate(year, month, day int) bool {
@ -743,7 +745,7 @@ func timeValue(timeString string) (int, int, float64, bool, bool, Result) {
} else if hours >= 24 || seconds >= 10000 {
return 0, 0, 0, false, false, MakeErrorResultType(ErrorTypeValue, tvErrMsg)
}
return hours, minutes, seconds, pm, dateIsEmpty, MakeEmptyResult()
return hours, minutes, seconds, pm, dateIsEmpty, empty
}
// Year is an implementation of the Excel YEAR() function.
@ -760,73 +762,104 @@ func Year(ctx Context, ev Evaluator, args []Result) Result {
}
// YearFrac is an implementation of the Excel YEARFRAC() function.
func YearFrac(ctx Context, ev Evaluator, args []Result) Result {
func YearFrac(args []Result) Result {
argsNum := len(args)
if (argsNum != 2 && argsNum != 3) || args[0].Type != ResultTypeNumber || args[1].Type != ResultTypeNumber {
return MakeErrorResult("YEARFRAC requires two or three number arguments")
}
basis := 0
if argsNum == 3 {
if argsNum == 3 && args[2].Type != ResultTypeEmpty {
if args[2].Type != ResultTypeNumber {
return MakeErrorResult("YEARFRAC requires two or three number arguments")
}
basis = int(args[2].ValueNumber)
if !checkBasis(basis) {
return MakeErrorResultType(ErrorTypeNum, "Incorrect basis argument for YEARFRAC")
}
}
epoch := ctx.GetEpoch()
startDate, err := getValueAsTime(args[0].Value(), epoch)
if err != nil {
return MakeErrorResult("incorrect start date")
if args[0].Type != ResultTypeNumber {
return MakeErrorResult("YEARFRAC requires start date to be number argument")
}
endDate, err := getValueAsTime(args[1].Value(), epoch)
if err != nil {
return MakeErrorResult("incorrect end date")
startDate := args[0].ValueNumber
if args[1].Type != ResultTypeNumber {
return MakeErrorResult("YEARFRAC requires end date to be number argument")
}
return yearFracFromTime(startDate, endDate, basis)
endDate := args[1].ValueNumber
yf, errResult := yearFrac(startDate, endDate, basis)
if errResult.Type == ResultTypeError {
return errResult
}
return MakeNumberResult(yf)
}
func yearFrac(startDate, endDate float64, basis int) Result {
return yearFracFromTime(dateFromDays(startDate), dateFromDays(endDate), basis)
}
func yearFracFromTime(startDate, endDate time.Time, basis int) Result {
// yearFrac returns float64 fraction of the year and Result value which can be of ResultTypeError type if an error occurs or ResultTypeEmpty if doesn't.
func yearFrac(startDateF, endDateF float64, basis int) (float64, Result) {
startDate, endDate := dateFromDays(startDateF), dateFromDays(endDateF)
startDateS := startDate.Unix()
endDateS := endDate.Unix()
sy, sm, sd := startDate.Date()
ey, em, ed := endDate.Date()
if startDateS == endDateS {
return 0, empty
}
sy, smM, sd := startDate.Date()
ey, emM, ed := endDate.Date()
sm, em := int(smM), int(emM)
var dayDiff, daysInYear float64
switch basis {
case 0:
if sd == 31 && ed == 31 {
sd = 30
ed = 30
} else if sd == 31 {
sd = 30
} else if sd == 30 && ed == 31 {
ed = 30
if sd == 31 {
sd--
}
return MakeNumberResult(float64(((ed + int(em) * 30 + ey * 360) - (sd + int(sm) * 30 + sy * 360))) / 360)
case 1:
var ylength = 365.0
if (sy == ey || ((sy + 1) == ey) && ((sm > em) || ((sm == em) && (sd >= ed)))) {
if ((sy == ey && isLeapYear(sy)) || feb29Between(startDate, endDate) || (em == 1 && ed == 29)) {
ylength = 366.0
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
case 1:
dayDiff = endDateF - startDateF
isYearDifferent := sy != ey
if isYearDifferent && (ey != sy + 1 || sm < em || (sm == em && sd < ed)) {
dayCount := 0
for y := sy; y <= ey; y++ {
dayCount += getDaysInYear(y, 1)
}
daysInYear = float64(dayCount) / float64(ey - sy + 1)
} else {
if !isYearDifferent && isLeapYear(sy) {
daysInYear = 366
} else {
if isYearDifferent && ((isLeapYear(sy) && (sm < 2 || (sm == 2 && sd <= 29))) || (isLeapYear(ey) && (em > 2 || (em == 2 && ed == 29)))) {
daysInYear = 366
} else {
daysInYear = 365
}
}
return MakeNumberResult(daysBetween(startDateS, endDateS) / ylength)
}
var years = float64((ey - sy) + 1)
var days = float64((makeDateS(ey + 1, time.January, 1) - makeDateS(sy, time.January, 1)) / 86400)
var average = days / years
return MakeNumberResult(daysBetween(startDateS, endDateS) / average)
case 2:
return MakeNumberResult(daysBetween(startDateS, endDateS) / 360.0)
dayDiff = endDateF - startDateF
daysInYear = 360
case 3:
return MakeNumberResult(daysBetween(startDateS, endDateS) / 365.0)
case 4:
return MakeNumberResult(float64(((ed + int(em) * 30 + ey * 360) - (sd + int(sm) * 30 + sy * 360))) / 360.0)
dayDiff = endDateF - startDateF
daysInYear = 365
case 4:
if sd == 31 {
sd--
}
if ed == 31 {
ed--
}
dayDiff = float64((ey - sy) * 360 + (em - sm) * 30 + (ed - sd))
daysInYear = 360
default:
return 0, MakeErrorResultType(ErrorTypeNum, "Incorrect basis for YearFrac")
}
return MakeErrorResultType(ErrorTypeValue, "")
return dayDiff / daysInYear, empty
}
func getDaysInYear(year, basis int) int {
@ -979,3 +1012,23 @@ func getDaysInYearRange(from, to, basis int) int {
func basis30(basis int) bool {
return basis == 0 || basis == 4
}
func parseDate(arg Result, dateName, funcName string) (float64, Result) {
var date float64
switch arg.Type {
case ResultTypeNumber:
date = float64(int(arg.ValueNumber))
case ResultTypeString:
dateFromString := DateValue([]Result{arg})
if dateFromString.Type == ResultTypeError {
return 0, MakeErrorResult("Incorrect " + dateName + " date for " + funcName)
}
date = dateFromString.ValueNumber
default:
return 0, MakeErrorResult("Incorrect argument for " + funcName)
}
if date < 0 {
return 0, MakeErrorResultType(ErrorTypeNum, dateName + " should be non negative")
}
return date, empty
}

File diff suppressed because it is too large Load Diff

View File

@ -98,7 +98,7 @@ func Index(args []Result) Result {
}
row := int(rowArg.ValueNumber) - 1
col := -1
if argsNum == 3 {
if argsNum == 3 && args[2].Type != ResultTypeEmpty {
colArg := args[2].AsNumber()
if colArg.Type != ResultTypeNumber {
return MakeErrorResult("INDEX requires numeric col argument")
@ -192,7 +192,7 @@ func Match(args []Result) Result {
}
matchType := 1
if argsNum == 3 {
if argsNum == 3 && args[2].Type != ResultTypeEmpty {
if args[2].Type != ResultTypeNumber {
return MakeErrorResult("MATCH requires the third argument to be a number")
}
@ -354,10 +354,11 @@ func Offset(ctx Context, ev Evaluator, args []Result) Result {
// VLookup implements the VLOOKUP function that returns a matching value from a
// column in an array.
func VLookup(args []Result) Result {
if len(args) < 3 {
argsNum := len(args)
if argsNum < 3 {
return MakeErrorResult("VLOOKUP requires at least three arguments")
}
if len(args) > 4 {
if argsNum > 4 {
return MakeErrorResult("VLOOKUP requires at most four arguments")
}
lookupValue := args[0]
@ -370,7 +371,7 @@ func VLookup(args []Result) Result {
return MakeErrorResult("VLOOKUP requires numeric col argument")
}
exactMatch := false
if len(args) == 4 {
if argsNum == 4 && args[3].Type != ResultTypeEmpty {
em := args[3].AsNumber()
if em.Type != ResultTypeNumber {
return MakeErrorResult("VLOOKUP requires numeric match argument")
@ -547,7 +548,7 @@ func extractCol(arr Result) []Result {
if len(r) > 0 {
col = append(col, r[0])
} else {
col = append(col, MakeEmptyResult())
col = append(col, empty)
}
}
}

View File

@ -1022,7 +1022,7 @@ func multinomial(args []Result) (float64, float64, Result) {
return 0, 0, arg
}
}
return num, denom, MakeEmptyResult()
return num, denom, empty
}
// Multinomial implements the excel MULTINOMIAL function.

View File

@ -284,7 +284,7 @@ func checkIfsRanges(args []Result, sumRange bool, fnName string) Result {
i-- // after sumRange should go column 1, not 2
}
}
return MakeEmptyResult()
return empty
}
//getIfsMatch returns an array of indexes of cells which meets all *IFS criterias

View File

@ -167,7 +167,8 @@ type parsedSearchObject struct {
}
func parseSearchResults(fname string, args []Result) (*parsedSearchObject, Result) {
if len(args) != 2 && len(args) != 3 {
argsNum := len(args)
if argsNum != 2 && argsNum != 3 {
return nil, MakeErrorResult(fname + " requires two or three arguments")
}
findTextResult := args[0]
@ -181,7 +182,7 @@ func parseSearchResults(fname string, args []Result) (*parsedSearchObject, Resul
text := textResult.ValueString
findText := findTextResult.ValueString
position := 1
if len(args) == 3 {
if argsNum == 3 && args[2].Type != ResultTypeEmpty {
positionResult := args[2]
if positionResult.Type != ResultTypeNumber {
return nil, MakeErrorResult("Position should be a number")
@ -198,7 +199,7 @@ func parseSearchResults(fname string, args []Result) (*parsedSearchObject, Resul
findText,
text,
position,
}, MakeEmptyResult()
}, empty
}
// Find is an implementation of the Excel FIND().
@ -543,7 +544,7 @@ func T(args []Result) Result {
if s.Type == ResultTypeError || s.Type == ResultTypeString {
return s
}
return MakeEmptyResult()
return empty
}
// Trim is an implementation of the Excel TRIM function that removes leading,
@ -648,7 +649,7 @@ func parseReplaceResults(fname string, args []Result) (*parsedReplaceObject, Res
startPos,
length,
textToReplace,
}, MakeEmptyResult()
}, empty
}
// Replace is an implementation of the Excel REPLACE().

View File

@ -533,6 +533,7 @@ func TestMatch(t *testing.T) {
td := []testStruct{
{`MATCH("??ny",A1:A5)`, `2 ResultTypeNumber`},
{`MATCH("*nny",A1:A5)`, `4 ResultTypeNumber`},
{`MATCH("*nny",A1:A5,)`, `4 ResultTypeNumber`},
{`=MATCH(5,B1:B5,1)`, `2 ResultTypeNumber`},
{`=MATCH(5,C1:C5,-1)`, `3 ResultTypeNumber`},
}
@ -947,6 +948,7 @@ func TestFind(t *testing.T) {
td := []testStruct{
{`FIND("",A1)`, `1 ResultTypeNumber`},
{`FIND("",A1,)`, `1 ResultTypeNumber`},
{`FIND(B1,A1)`, `2 ResultTypeNumber`},
{`FIND(B2,A1,3)`, `3 ResultTypeNumber`},
{`FIND(B2,A1,4)`, `#VALUE! ResultTypeError`},
@ -975,6 +977,7 @@ func TestFindb(t *testing.T) {
td := []testStruct{
{`FINDB("",A1)`, `1 ResultTypeNumber`},
{`FINDB(B1,A1)`, `2 ResultTypeNumber`},
{`FINDB(B1,A1,)`, `2 ResultTypeNumber`},
{`FINDB(B2,A1,3)`, `3 ResultTypeNumber`},
{`FINDB(B2,A1,4)`, `#VALUE! ResultTypeError`},
{`FINDB(D1,C1)`, `1 ResultTypeNumber`},
@ -1002,6 +1005,7 @@ func TestSearch(t *testing.T) {
td := []testStruct{
{`SEARCH("",A1)`, `1 ResultTypeNumber`},
{`SEARCH(B1,A1)`, `2 ResultTypeNumber`},
{`SEARCH(B1,A1,)`, `2 ResultTypeNumber`},
{`SEARCH(B2,A1,3)`, `3 ResultTypeNumber`},
{`SEARCH(B2,A1,4)`, `#VALUE! ResultTypeError`},
{`SEARCH(B3,A1,2)`, `2 ResultTypeNumber`},
@ -1031,6 +1035,7 @@ func TestSearchb(t *testing.T) {
td := []testStruct{
{`SEARCHB("",A1)`, `1 ResultTypeNumber`},
{`SEARCHB(B1,A1)`, `2 ResultTypeNumber`},
{`SEARCHB(B1,A1,)`, `2 ResultTypeNumber`},
{`SEARCHB(B2,A1,3)`, `3 ResultTypeNumber`},
{`SEARCHB(B2,A1,4)`, `#VALUE! ResultTypeError`},
{`SEARCHB(D1,C1)`, `1 ResultTypeNumber`},
@ -1096,6 +1101,7 @@ func TestYearFrac(t *testing.T) {
{`=YEARFRAC(A1,A2)`, `0.00277777777 ResultTypeNumber`},
{`=YEARFRAC(A3,A4)`, `0.16666666666 ResultTypeNumber`},
{`=YEARFRAC(A3,A5)`, `0.00277777777 ResultTypeNumber`},
{`=YEARFRAC(A3,A5,)`, `0.00277777777 ResultTypeNumber`},
{`=YEARFRAC(A1,A2,1)`, `0.00273972602 ResultTypeNumber`},
{`=YEARFRAC(A6,A7,1)`, `0.00273224043 ResultTypeNumber`},
{`=YEARFRAC(A6,A8,1)`, `1 ResultTypeNumber`},
@ -1307,6 +1313,8 @@ func TestDuration(t *testing.T) {
sheet.Cell("A8").SetString("01/01/2048") // maturity date in string format
td := []testStruct{
{`=DURATION(A1,A2,A3,A4,A5)`, `10.9191452815 ResultTypeNumber`},
{`=DURATION(A1,A2,A3,A4,A5,)`, `10.9191452815 ResultTypeNumber`},
{`=DURATION(A1,A2,A3,A4,A5,A6)`, `10.9191452815 ResultTypeNumber`},
{`=DURATION(A7,A8,A3,A4,A5,A6)`, `10.9191452815 ResultTypeNumber`},
{`=DURATION(A1,A2,A3,A4,A5,5)`, `#NUM! ResultTypeError`},
@ -1331,6 +1339,8 @@ func TestMduration(t *testing.T) {
sheet.Cell("A8").SetString("01/01/2016") // maturity date in string format
td := []testStruct{
{`=MDURATION(A1,A2,A3,A4,A5)`, `5.73566981391 ResultTypeNumber`},
{`=MDURATION(A1,A2,A3,A4,A5,)`, `5.73566981391 ResultTypeNumber`},
{`=MDURATION(A1,A2,A3,A4,A5,A6)`, `5.73566981391 ResultTypeNumber`},
{`=MDURATION(A7,A8,A3,A4,A5,A6)`, `5.73566981391 ResultTypeNumber`},
{`=MDURATION(A1,A2,A3,A4,A5,5)`, `#NUM! ResultTypeError`},
@ -1424,6 +1434,7 @@ func TestVlookup(t *testing.T) {
td := []testStruct{
{`=VLOOKUP(150,A1:B4,2)`, `value1 ResultTypeString`},
{`=VLOOKUP(250,A1:B4,2)`, `value2 ResultTypeString`},
{`=VLOOKUP(250,A1:B4,2,)`, `value2 ResultTypeString`},
{`=VLOOKUP(250,A1:B4,2,FALSE)`, `#N/A ResultTypeError`},
{`=VLOOKUP(300,A1:B4,2,FALSE)`, `value3 ResultTypeString`},
}
@ -1667,11 +1678,13 @@ func TestAccrintm(t *testing.T) {
ctx := sheet.FormulaContext()
td := []testStruct{
{`=ACCRINTM(39539,39614,0.1,1000,0))`, `20.5555555555 ResultTypeNumber`},
{`=ACCRINTM(39539,39614,0.1,1000,1))`, `20.4918032786 ResultTypeNumber`},
{`=ACCRINTM(39539,39614,0.1,1000,2))`, `20.8333333333 ResultTypeNumber`},
{`=ACCRINTM(39539,39614,0.1,1000,3))`, `20.5479452054 ResultTypeNumber`},
{`=ACCRINTM(39539,39614,0.1,1000,4))`, `20.5555555555 ResultTypeNumber`},
{`=ACCRINTM(39539,39614,0.1,1000)`, `20.5555555555 ResultTypeNumber`},
{`=ACCRINTM(39539,39614,0.1,1000,)`, `20.5555555555 ResultTypeNumber`},
{`=ACCRINTM(39539,39614,0.1,1000,0)`, `20.5555555555 ResultTypeNumber`},
{`=ACCRINTM(39539,39614,0.1,1000,1)`, `20.4918032786 ResultTypeNumber`},
{`=ACCRINTM(39539,39614,0.1,1000,2)`, `20.8333333333 ResultTypeNumber`},
{`=ACCRINTM(39539,39614,0.1,1000,3)`, `20.5479452054 ResultTypeNumber`},
{`=ACCRINTM(39539,39614,0.1,1000,4)`, `20.5555555555 ResultTypeNumber`},
}
runTests(t, ctx, td)
@ -1684,6 +1697,9 @@ func TestAmordegrc(t *testing.T) {
ctx := sheet.FormulaContext()
td := []testStruct{
{`=AMORDEGRC(2400,39679,39813,300,1,0.15)`, `776 ResultTypeNumber`},
{`=AMORDEGRC(2400,39679,39813,300,1,0.15,)`, `776 ResultTypeNumber`},
{`=AMORDEGRC(2400,39679,39813,300,1,0.15,0)`, `776 ResultTypeNumber`},
{`=AMORDEGRC(2400,39679,39813,300,1,0.15,1)`, `776 ResultTypeNumber`},
{`=AMORDEGRC(2400,39679,39813,300,1,0.15,2)`, `#NUM! ResultTypeError`},
}
@ -1698,6 +1714,9 @@ func TestAmorlinc(t *testing.T) {
ctx := sheet.FormulaContext()
td := []testStruct{
{`=AMORLINC(2400,39679,39813,300,1,0.15)`, `360 ResultTypeNumber`},
{`=AMORLINC(2400,39679,39813,300,1,0.15,)`, `360 ResultTypeNumber`},
{`=AMORLINC(2400,39679,39813,300,1,0.15,0)`, `360 ResultTypeNumber`},
{`=AMORLINC(2400,39679,39813,300,1,0.15,1)`, `360 ResultTypeNumber`},
{`=AMORLINC(2400,39679,39813,300,1,0.15,2)`, `#NUM! ResultTypeError`},
}
@ -1712,6 +1731,8 @@ func TestCoupdaybs(t *testing.T) {
ctx := sheet.FormulaContext()
td := []testStruct{
{`=COUPDAYBS(40568,40862,2)`, `70 ResultTypeNumber`},
{`=COUPDAYBS(40568,40862,2,)`, `70 ResultTypeNumber`},
{`=COUPDAYBS(40568,40862,2,0)`, `70 ResultTypeNumber`},
{`=COUPDAYBS(40568,40862,2,1)`, `71 ResultTypeNumber`},
{`=COUPDAYBS(40568,40862,2,2)`, `71 ResultTypeNumber`},
@ -1740,6 +1761,8 @@ func TestCoupdays(t *testing.T) {
ctx := sheet.FormulaContext()
td := []testStruct{
{`=COUPDAYS(40964,41228,1)`, `360 ResultTypeNumber`},
{`=COUPDAYS(40964,41228,1,)`, `360 ResultTypeNumber`},
{`=COUPDAYS(40964,41228,1,0)`, `360 ResultTypeNumber`},
{`=COUPDAYS(40964,41228,1,1)`, `366 ResultTypeNumber`},
{`=COUPDAYS(40964,41228,1,2)`, `360 ResultTypeNumber`},
@ -1768,6 +1791,8 @@ func TestCoupdaysnc(t *testing.T) {
ctx := sheet.FormulaContext()
td := []testStruct{
{`=COUPDAYSNC(40933,41228,1)`, `290 ResultTypeNumber`},
{`=COUPDAYSNC(40933,41228,1,)`, `290 ResultTypeNumber`},
{`=COUPDAYSNC(40933,41228,1,0)`, `290 ResultTypeNumber`},
{`=COUPDAYSNC(40933,41228,1,1)`, `295 ResultTypeNumber`},
{`=COUPDAYSNC(40933,41228,1,2)`, `295 ResultTypeNumber`},
@ -1796,6 +1821,9 @@ func TestCoupncd(t *testing.T) {
ctx := sheet.FormulaContext()
td := []testStruct{
{`=COUPNCD(40568,40862,1)`, `40862 ResultTypeNumber`},
{`=COUPNCD(40568,40862,1,)`, `40862 ResultTypeNumber`},
{`=COUPNCD(40568,40862,1,0)`, `40862 ResultTypeNumber`},
{`=COUPNCD(40568,40862,1,1)`, `40862 ResultTypeNumber`},
{`=COUPNCD(40568,40862,2,1)`, `40678 ResultTypeNumber`},
{`=COUPNCD(40568,40862,4,1)`, `40589 ResultTypeNumber`},
@ -1812,6 +1840,9 @@ func TestCouppcd(t *testing.T) {
ctx := sheet.FormulaContext()
td := []testStruct{
{`=COUPPCD(40568,40862,2)`, `40497 ResultTypeNumber`},
{`=COUPPCD(40568,40862,2,)`, `40497 ResultTypeNumber`},
{`=COUPPCD(40568,40862,2,0)`, `40497 ResultTypeNumber`},
{`=COUPPCD(40568,40862,2,1)`, `40497 ResultTypeNumber`},
{`=COUPPCD(40872,40568,2,1)`, `#NUM! ResultTypeError`},
}
@ -1826,6 +1857,9 @@ func TestCoupnum(t *testing.T) {
ctx := sheet.FormulaContext()
td := []testStruct{
{`=COUPNUM(39107,39767,2)`, `4 ResultTypeNumber`},
{`=COUPNUM(39107,39767,2,)`, `4 ResultTypeNumber`},
{`=COUPNUM(39107,39767,2,0)`, `4 ResultTypeNumber`},
{`=COUPNUM(39107,39767,2,1)`, `4 ResultTypeNumber`},
{`=COUPNUM(39767,39107,2,1)`, `#NUM! ResultTypeError`},
}
@ -1925,6 +1959,8 @@ func TestDisc(t *testing.T) {
ctx := sheet.FormulaContext()
td := []testStruct{
{`=DISC(A1,A2,A3,A4)`, `0.00068644067 ResultTypeNumber`},
{`=DISC(A1,A2,A3,A4,)`, `0.00068644067 ResultTypeNumber`},
{`=DISC(A1,A2,A3,A4,0)`, `0.00068644067 ResultTypeNumber`},
{`=DISC(A1,A2,A3,A4,1)`, `0.00068638416 ResultTypeNumber`},
{`=DISC(A1,A2,A3,A4,2)`, `0.00067650334 ResultTypeNumber`},
@ -2028,6 +2064,8 @@ func TestIntrate(t *testing.T) {
ctx := sheet.FormulaContext()
td := []testStruct{
{`=INTRATE(A1,A2,A3,A4)`, `0.05768 ResultTypeNumber`},
{`=INTRATE(A1,A2,A3,A4,)`, `0.05768 ResultTypeNumber`},
{`=INTRATE(A1,A2,A3,A4,0)`, `0.05768 ResultTypeNumber`},
{`=INTRATE(A1,A2,A3,A4,1)`, `0.05864133333 ResultTypeNumber`},
{`=INTRATE(A1,A2,A3,A4,2)`, `0.05768 ResultTypeNumber`},
@ -2165,7 +2203,12 @@ func TestNper(t *testing.T) {
td := []testStruct{
{`=NPER(A1/12,A2,A3,A4,1)`, `59.6738656742 ResultTypeNumber`},
{`=NPER(A1/12,A2,A3,A4)`, `60.0821228537 ResultTypeNumber`},
{`=NPER(A1/12,A2,A3,A4,)`, `60.0821228537 ResultTypeNumber`},
{`=NPER(A1/12,A2,A3,A4,0)`, `60.0821228537 ResultTypeNumber`},
{`=NPER(A1/12,A2,A3)`, `-9.5785940398 ResultTypeNumber`},
{`=NPER(A1/12,A2,A3,,)`, `-9.5785940398 ResultTypeNumber`},
{`=NPER(A1/12,A2,A3,0,)`, `-9.5785940398 ResultTypeNumber`},
{`=NPER(A1/12,A2,A3,,0)`, `-9.5785940398 ResultTypeNumber`},
}
runTests(t, ctx, td)
@ -2192,3 +2235,377 @@ func TestNpv(t *testing.T) {
runTests(t, ctx, td)
}
func TestPmt(t *testing.T) {
ss := spreadsheet.New()
sheet := ss.AddSheet()
sheet.Cell("A1").SetNumber(0.08)
sheet.Cell("A2").SetNumber(10)
sheet.Cell("A3").SetNumber(10000)
sheet.Cell("A4").SetNumber(0.06)
sheet.Cell("A5").SetNumber(18)
sheet.Cell("A6").SetNumber(50000)
ctx := sheet.FormulaContext()
td := []testStruct{
{`=PMT(A1/12,A2,A3)`, `-1037.0320893 ResultTypeNumber`},
{`=PMT(A1/12,A2,A3,)`, `-1037.0320893 ResultTypeNumber`},
{`=PMT(A1/12,A2,A3,0)`, `-1037.0320893 ResultTypeNumber`},
{`=PMT(A1/12,A2,A3,1)`, `-1037.1291259 ResultTypeNumber`},
{`=PMT(A4/12,A5*12,0,A6)`, `-129.08116086 ResultTypeNumber`},
{`=PMT("A4/12",A5*12,0,A6)`, `#VALUE! ResultTypeError`},
}
runTests(t, ctx, td)
}
func TestPpmt(t *testing.T) {
ss := spreadsheet.New()
sheet := ss.AddSheet()
ctx := sheet.FormulaContext()
td := []testStruct{
{`=PPMT(0.1/12,1,2*12,2000)`, `-75.623186008 ResultTypeNumber`},
{`=PPMT(0.08,10,10,200000)`, `-27598.053462 ResultTypeNumber`},
{`=PPMT(0.08,11,10,200000)`, `#NUM! ResultTypeError`},
{`=PPMT("0.08%",10,10,200000)`, `#VALUE! ResultTypeError`},
}
runTests(t, ctx, td)
}
func TestPricedisc(t *testing.T) {
ss := spreadsheet.New()
sheet := ss.AddSheet()
sheet.Cell("A1").SetDate(time.Date(2008, 2, 16, 0, 0, 0, 0, time.UTC))
sheet.Cell("A2").SetDate(time.Date(2008, 3, 1, 0, 0, 0, 0, time.UTC))
sheet.Cell("A3").SetNumber(0.0525)
sheet.Cell("A4").SetNumber(100)
ctx := sheet.FormulaContext()
td := []testStruct{
{`=PRICEDISC(A1,A2,A3,A4)`, `99.78125 ResultTypeNumber`},
{`=PRICEDISC(A1,A2,A3,A4,)`, `99.78125 ResultTypeNumber`},
{`=PRICEDISC(A1,A2,A3,A4,0)`, `99.78125 ResultTypeNumber`},
{`=PRICEDISC(A1,A2,A3,A4,1)`, `99.7991803278 ResultTypeNumber`},
{`=PRICEDISC(A1,A2,A3,A4,2)`, `99.7958333333 ResultTypeNumber`},
{`=PRICEDISC(A1,A2,A3,A4,3)`, `99.7986301369 ResultTypeNumber`},
{`=PRICEDISC(A1,A2,A3,A4,4)`, `99.78125 ResultTypeNumber`},
}
runTests(t, ctx, td)
}
func TestPv(t *testing.T) {
ss := spreadsheet.New()
sheet := ss.AddSheet()
ctx := sheet.FormulaContext()
td := []testStruct{
{`=PV(0.08/12,20*12,500)`, `-59777.145851 ResultTypeNumber`},
{`=PV(0.08/12,20*12,500,,)`, `-59777.145851 ResultTypeNumber`},
{`=PV(0.08/12,20*12,500,0,)`, `-59777.145851 ResultTypeNumber`},
{`=PV(0.08/12,20*12,500,,0)`, `-59777.145851 ResultTypeNumber`},
{`=PV(0.08/12,20*12,500,0,0)`, `-59777.145851 ResultTypeNumber`},
{`=PV(0.1/12,2*12,1000,10000)`, `-29864.950264 ResultTypeNumber`},
{`=PV(0.1/12,2*12,1000,10000,)`, `-29864.950264 ResultTypeNumber`},
{`=PV(0.1/12,2*12,1000,10000,0)`, `-29864.950264 ResultTypeNumber`},
{`=PV(0.1/12,2*12,1000,10000,1)`, `-30045.540721 ResultTypeNumber`},
{`=PV(0,2*12,1000,10000,1)`, `-34000 ResultTypeNumber`},
{`=PV("hello world",2*12,1000,10000,1)`, `#VALUE! ResultTypeError`},
}
runTests(t, ctx, td)
}
func TestRate(t *testing.T) {
ss := spreadsheet.New()
sheet := ss.AddSheet()
ctx := sheet.FormulaContext()
td := []testStruct{
{`=RATE(2*12,-1000,-10000,100000)`, `0.06517891177 ResultTypeNumber`},
{`=RATE(2*12,-1000,-10000,100000,)`, `0.06517891177 ResultTypeNumber`},
{`=RATE(2*12,-1000,-10000,100000,,)`, `0.06517891177 ResultTypeNumber`},
{`=RATE(2*12,-1000,-10000,100000,0,0.1)`, `0.06517891177 ResultTypeNumber`},
{`=RATE(2*12,-1000,-10000,100000,0,0.75)`, `0.06517891177 ResultTypeNumber`},
{`=RATE(2*12,-1000,-10000,100000,0,0.065)`, `0.06517891177 ResultTypeNumber`},
{`=RATE(2*12,-1000,-10000,100000,1,0.1)`, `0.06323958 ResultTypeNumber`},
}
runTests(t, ctx, td)
}
func TestReceived(t *testing.T) {
ss := spreadsheet.New()
sheet := ss.AddSheet()
sheet.Cell("A1").SetDate(time.Date(2008, 2, 15, 0, 0, 0, 0, time.UTC))
sheet.Cell("A2").SetDate(time.Date(2008, 5, 15, 0, 0, 0, 0, time.UTC))
sheet.Cell("A3").SetNumber(1000000)
sheet.Cell("A4").SetNumber(0.0575)
ctx := sheet.FormulaContext()
td := []testStruct{
{`=RECEIVED(A1,A2,A3,A4)`, `1014584.6544 ResultTypeNumber`},
{`=RECEIVED(A1,A2,A3,A4,)`, `1014584.6544 ResultTypeNumber`},
{`=RECEIVED(A1,A2,A3,A4,0)`, `1014584.6544 ResultTypeNumber`},
{`=RECEIVED(A1,A2,A3,A4,1)`, `1014342.13261 ResultTypeNumber`},
{`=RECEIVED(A1,A2,A3,A4,2)`, `1014584.6544 ResultTypeNumber`},
{`=RECEIVED(A1,A2,A3,A4,3)`, `1014381.99124 ResultTypeNumber`},
{`=RECEIVED(A1,A2,A3,A4,4)`, `1014584.6544 ResultTypeNumber`},
}
runTests(t, ctx, td)
}
func TestRri(t *testing.T) {
ss := spreadsheet.New()
sheet := ss.AddSheet()
ctx := sheet.FormulaContext()
td := []testStruct{
{`=RRI(96,10000,11000)`, `0.00099330737 ResultTypeNumber`},
{`=RRI(8,10000,11000)`, `0.01198502414 ResultTypeNumber`},
{`=RRI(0,10000,11000)`, `#NUM! ResultTypeError`},
{`=RRI(8,0,11000)`, `#NUM! ResultTypeError`},
{`=RRI(8,10000,-0.000001)`, `#NUM! ResultTypeError`},
}
runTests(t, ctx, td)
}
func TestOddlprice(t *testing.T) {
ss := spreadsheet.New()
sheet := ss.AddSheet()
sheet.Cell("A1").SetDate(time.Date(2008, 2, 7, 0, 0, 0, 0, time.UTC))
sheet.Cell("A2").SetDate(time.Date(2008, 6, 15, 0, 0, 0, 0, time.UTC))
sheet.Cell("A3").SetDate(time.Date(2007, 10, 15, 0, 0, 0, 0, time.UTC))
sheet.Cell("A4").SetNumber(0.0375)
sheet.Cell("A5").SetNumber(0.0405)
sheet.Cell("A6").SetNumber(100)
sheet.Cell("A7").SetNumber(2)
ctx := sheet.FormulaContext()
td := []testStruct{
{`=ODDLPRICE(A1,A2,A3,A4,A5,A6,A7,0)`, `99.8782860147 ResultTypeNumber`},
{`=ODDLPRICE(A1,A2,A3,A4,A5,A6,A7,1)`, `99.8759395207 ResultTypeNumber`},
{`=ODDLPRICE(A1,A2,A3,A4,A5,A6,A7,2)`, `99.8769016984 ResultTypeNumber`},
{`=ODDLPRICE(A1,A2,A3,A4,A5,A6,A7,3)`, `99.8787957508 ResultTypeNumber`},
{`=ODDLPRICE(A1,A2,A3,A4,A5,A6,A7,4)`, `99.8782860147 ResultTypeNumber`},
{`=ODDLPRICE(A2,A1,A3,A4,A5,A6,A7,4)`, `#NUM! ResultTypeError`},
{`=ODDLPRICE(A1,A3,A2,A4,A5,A6,A7,4)`, `#NUM! ResultTypeError`},
}
runTests(t, ctx, td)
}
func TestOddyield(t *testing.T) {
ss := spreadsheet.New()
sheet := ss.AddSheet()
sheet.Cell("A1").SetDate(time.Date(2008, 4, 20, 0, 0, 0, 0, time.UTC))
sheet.Cell("A2").SetDate(time.Date(2008, 6, 15, 0, 0, 0, 0, time.UTC))
sheet.Cell("A3").SetDate(time.Date(2007, 12, 24, 0, 0, 0, 0, time.UTC))
sheet.Cell("A4").SetNumber(0.0375)
sheet.Cell("A5").SetNumber(99.875)
sheet.Cell("A6").SetNumber(100)
sheet.Cell("A7").SetNumber(2)
ctx := sheet.FormulaContext()
td := []testStruct{
{`=ODDLYIELD(A1,A2,A3,A4,A5,A6,A7,0)`, `0.04519223562 ResultTypeNumber`},
{`=ODDLYIELD(A1,A2,A3,A4,A5,A6,A7,1)`, `0.04517988549 ResultTypeNumber`},
{`=ODDLYIELD(A1,A2,A3,A4,A5,A6,A7,2)`, `0.04503841511 ResultTypeNumber`},
{`=ODDLYIELD(A1,A2,A3,A4,A5,A6,A7,3)`, `0.04515632373 ResultTypeNumber`},
{`=ODDLYIELD(A1,A2,A3,A4,A5,A6,A7,4)`, `0.04519223562 ResultTypeNumber`},
{`=ODDLYIELD(A2,A1,A3,A4,A5,A6,A7,4)`, `#NUM! ResultTypeError`},
{`=ODDLYIELD(A1,A3,A2,A4,A5,A6,A7,4)`, `#NUM! ResultTypeError`},
}
runTests(t, ctx, td)
}
func TestPrice(t *testing.T) {
ss := spreadsheet.New()
sheet := ss.AddSheet()
sheet.Cell("A1").SetDate(time.Date(2008, 2, 15, 0, 0, 0, 0, time.UTC))
sheet.Cell("A2").SetDate(time.Date(2017, 11, 15, 0, 0, 0, 0, time.UTC))
sheet.Cell("A3").SetNumber(0.0575)
sheet.Cell("A4").SetNumber(0.065)
sheet.Cell("A5").SetNumber(100)
sheet.Cell("A6").SetNumber(2)
ctx := sheet.FormulaContext()
td := []testStruct{
{`=PRICE(A1,A2,A3,A4,A5,A6,0)`, `94.6343616213 ResultTypeNumber`},
{`=PRICE(A1,A2,A3,A4,A5,A6,1)`, `94.6354492078 ResultTypeNumber`},
{`=PRICE(A1,A2,A3,A4,A5,A6,2)`, `94.6024171768 ResultTypeNumber`},
{`=PRICE(A1,A2,A3,A4,A5,A6,3)`, `94.6435945482 ResultTypeNumber`},
{`=PRICE(A1,A2,A3,A4,A5,A6,4)`, `94.6343616213 ResultTypeNumber`},
}
runTests(t, ctx, td)
}
func TestPricemat(t *testing.T) {
ss := spreadsheet.New()
sheet := ss.AddSheet()
sheet.Cell("A1").SetDate(time.Date(2008, 2, 15, 0, 0, 0, 0, time.UTC))
sheet.Cell("A2").SetDate(time.Date(2008, 4, 13, 0, 0, 0, 0, time.UTC))
sheet.Cell("A3").SetDate(time.Date(2007, 11, 11, 0, 0, 0, 0, time.UTC))
sheet.Cell("A4").SetNumber(0.061)
sheet.Cell("A5").SetNumber(0.061)
ctx := sheet.FormulaContext()
td := []testStruct{
{`=PRICEMAT(A1,A2,A3,A4,A5,0)`, `99.9844988755 ResultTypeNumber`},
{`=PRICEMAT(A1,A2,A3,A4,A5,1)`, `99.9802978513 ResultTypeNumber`},
{`=PRICEMAT(A1,A2,A3,A4,A5,2)`, `99.9841690643 ResultTypeNumber`},
{`=PRICEMAT(A1,A2,A3,A4,A5,3)`, `99.9845977645 ResultTypeNumber`},
{`=PRICEMAT(A1,A2,A3,A4,A5,4)`, `99.9844988755 ResultTypeNumber`},
}
runTests(t, ctx, td)
}
func TestSln(t *testing.T) {
ss := spreadsheet.New()
sheet := ss.AddSheet()
ctx := sheet.FormulaContext()
td := []testStruct{
{`=SLN(30000,7500,10)`, `2250 ResultTypeNumber`},
{`=SLN(30000,7500,0)`, `#DIV/0! ResultTypeError`},
{`=SLN("hello world",7500,10)`, `#VALUE! ResultTypeError`},
}
runTests(t, ctx, td)
}
func TestSyd(t *testing.T) {
ss := spreadsheet.New()
sheet := ss.AddSheet()
ctx := sheet.FormulaContext()
td := []testStruct{
{`=SYD(30000,7500,10,1)`, `4090.9090909 ResultTypeNumber`},
{`=SYD(30000,7500,10,10)`, `409.09090909 ResultTypeNumber`},
{`=SYD(30000,7500,10,11)`, `#NUM! ResultTypeError`},
{`=SYD(30000,7500,0,0)`, `#NUM! ResultTypeError`},
{`=SYD(30000,7500,10,0)`, `#NUM! ResultTypeError`},
{`=SLN("hello world",7500,10,1)`, `#VALUE! ResultTypeError`},
}
runTests(t, ctx, td)
}
func TestTbilleq(t *testing.T) {
ss := spreadsheet.New()
sheet := ss.AddSheet()
sheet.Cell("A1").SetDate(time.Date(2008, 3, 31, 0, 0, 0, 0, time.UTC))
sheet.Cell("A2").SetDate(time.Date(2008, 6, 1, 0, 0, 0, 0, time.UTC))
sheet.Cell("A3").SetNumber(0.0914)
sheet.Cell("A4").SetDate(time.Date(2009, 4, 1, 0, 0, 0, 0, time.UTC))
ctx := sheet.FormulaContext()
td := []testStruct{
{`=TBILLEQ(A1,A2,A3)`, `0.09415149356 ResultTypeNumber`},
{`=TBILLEQ("A1",A2,A3)`, `#VALUE! ResultTypeError`},
{`=TBILLEQ(A1,A2,0)`, `#NUM! ResultTypeError`},
{`=TBILLEQ(A2,A1,A3)`, `#NUM! ResultTypeError`},
{`=TBILLEQ(A1,A4,A3)`, `#NUM! ResultTypeError`},
}
runTests(t, ctx, td)
}
func TestTbillprice(t *testing.T) {
ss := spreadsheet.New()
sheet := ss.AddSheet()
sheet.Cell("A1").SetDate(time.Date(2008, 3, 31, 0, 0, 0, 0, time.UTC))
sheet.Cell("A2").SetDate(time.Date(2008, 6, 1, 0, 0, 0, 0, time.UTC))
sheet.Cell("A3").SetNumber(0.09)
sheet.Cell("A4").SetDate(time.Date(2009, 4, 1, 0, 0, 0, 0, time.UTC))
ctx := sheet.FormulaContext()
td := []testStruct{
{`=TBILLPRICE(A1,A2,A3)`, `98.45 ResultTypeNumber`},
{`=TBILLPRICE("A1",A2,A3)`, `#VALUE! ResultTypeError`},
{`=TBILLPRICE(A1,A2,0)`, `#NUM! ResultTypeError`},
{`=TBILLPRICE(A2,A1,A3)`, `#NUM! ResultTypeError`},
{`=TBILLPRICE(A1,A4,A3)`, `#NUM! ResultTypeError`},
}
runTests(t, ctx, td)
}
func TestTbillyield(t *testing.T) {
ss := spreadsheet.New()
sheet := ss.AddSheet()
sheet.Cell("A1").SetDate(time.Date(2008, 3, 31, 0, 0, 0, 0, time.UTC))
sheet.Cell("A2").SetDate(time.Date(2008, 6, 1, 0, 0, 0, 0, time.UTC))
sheet.Cell("A3").SetNumber(98.45)
sheet.Cell("A4").SetDate(time.Date(2009, 4, 1, 0, 0, 0, 0, time.UTC))
ctx := sheet.FormulaContext()
td := []testStruct{
{`=TBILLYIELD(A1,A2,A3)`, `0.09141696292 ResultTypeNumber`},
{`=TBILLYIELD("A1",A2,A3)`, `#VALUE! ResultTypeError`},
{`=TBILLYIELD(A1,A2,0)`, `#NUM! ResultTypeError`},
{`=TBILLYIELD(A2,A1,A3)`, `#NUM! ResultTypeError`},
{`=TBILLYIELD(A1,A4,A3)`, `#NUM! ResultTypeError`},
}
runTests(t, ctx, td)
}
func TestVdb(t *testing.T) {
ss := spreadsheet.New()
sheet := ss.AddSheet()
sheet.Cell("A1").SetNumber(2400)
sheet.Cell("A2").SetNumber(300)
sheet.Cell("A3").SetNumber(10)
ctx := sheet.FormulaContext()
td := []testStruct{
{`=VDB(A1,A2,A3*365,0,1)`, `1.31506849315 ResultTypeNumber`},
{`=VDB(A1,A2,A3*12,0,1)`, `40 ResultTypeNumber`},
{`=VDB(A1,A2,A3,0,1)`, `480 ResultTypeNumber`},
{`=VDB(A1,A2,A3*12,6,18)`, `396.306053264 ResultTypeNumber`},
{`=VDB(A1,A2,A3*12,6,18,)`, `210 ResultTypeNumber`},
{`=VDB(A1,A2,A3*12,6,18,,1)`, `0 ResultTypeNumber`},
{`=VDB(A1,A2,A3*12,6,18,1.5)`, `311.808936658 ResultTypeNumber`},
{`=VDB(A1,A2,A3,0,0.875,1.5)`, `315 ResultTypeNumber`},
{`=VDB(A1,A2,A3,0,0.875,,)`, `183.75 ResultTypeNumber`},
{`=VDB(A1,A2,A3,0,0.875,,1)`, `0 ResultTypeNumber`},
}
runTests(t, ctx, td)
}