Skip to content

Commit ecbc6e2

Browse files
committed
ref #65, new formula functions: T.INV and T.INV.2T
- Typo fixed
1 parent be8fc0a commit ecbc6e2

File tree

5 files changed

+140
-51
lines changed

5 files changed

+140
-51
lines changed

calc.go

Lines changed: 80 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -664,6 +664,8 @@ type formulaFuncs struct {
664664
// TEXTJOIN
665665
// TIME
666666
// TIMEVALUE
667+
// T.INV
668+
// T.INV.2T
667669
// TODAY
668670
// TRANSPOSE
669671
// TRIM
@@ -1265,27 +1267,6 @@ func isOperand(token efp.Token) bool {
12651267
return token.TType == efp.TokenTypeOperand && (token.TSubType == efp.TokenSubTypeNumber || token.TSubType == efp.TokenSubTypeText)
12661268
}
12671269

1268-
// getDefinedNameRefTo convert defined name to reference range.
1269-
func (f *File) getDefinedNameRefTo(definedNameName string, currentSheet string) (refTo string) {
1270-
var workbookRefTo, worksheetRefTo string
1271-
for _, definedName := range f.GetDefinedName() {
1272-
if definedName.Name == definedNameName {
1273-
// worksheet scope takes precedence over scope workbook when both definedNames exist
1274-
if definedName.Scope == "Workbook" {
1275-
workbookRefTo = definedName.RefersTo
1276-
}
1277-
if definedName.Scope == currentSheet {
1278-
worksheetRefTo = definedName.RefersTo
1279-
}
1280-
}
1281-
}
1282-
refTo = workbookRefTo
1283-
if worksheetRefTo != "" {
1284-
refTo = worksheetRefTo
1285-
}
1286-
return
1287-
}
1288-
12891270
// parseToken parse basic arithmetic operator priority and evaluate based on
12901271
// operators and operands.
12911272
func (f *File) parseToken(sheet string, token efp.Token, opdStack, optStack *Stack) error {
@@ -6647,14 +6628,16 @@ func hasChangeOfSign(u, w float64) bool {
66476628
// calcInverseIterator directly maps the required parameters for inverse
66486629
// distribution functions.
66496630
type calcInverseIterator struct {
6650-
name string
6651-
fp, fDF float64
6631+
name string
6632+
fp, fDF, nT float64
66526633
}
66536634

6654-
// chiSqDist implements inverse distribution with left tail for the Chi-Square
6655-
// distribution.
6656-
func (iterator *calcInverseIterator) chiSqDist(x float64) float64 {
6657-
return iterator.fp - getChiSqDistCDF(x, iterator.fDF)
6635+
// callBack implements the callback function for the inverse iterator.
6636+
func (iterator *calcInverseIterator) callBack(x float64) float64 {
6637+
if iterator.name == "CHISQ.INV" {
6638+
return iterator.fp - getChiSqDistCDF(x, iterator.fDF)
6639+
}
6640+
return iterator.fp - getTDist(x, iterator.fDF, iterator.nT)
66586641
}
66596642

66606643
// inverseQuadraticInterpolation inverse quadratic interpolation with
@@ -6682,7 +6665,7 @@ func inverseQuadraticInterpolation(iterator calcInverseIterator, fAx, fAy, fBx,
66826665
bHasToInterpolate = true
66836666
}
66846667
fPx, fQx, fRx, fPy, fQy = fQx, fRx, fSx, fQy, fRy
6685-
fRy = iterator.chiSqDist(fSx)
6668+
fRy = iterator.callBack(fSx)
66866669
if hasChangeOfSign(fAy, fRy) {
66876670
fBx, fBy = fRx, fRy
66886671
} else {
@@ -6697,7 +6680,7 @@ func inverseQuadraticInterpolation(iterator calcInverseIterator, fAx, fAy, fBx,
66976680
// calcIterateInverse function calculates the iteration for inverse
66986681
// distributions.
66996682
func calcIterateInverse(iterator calcInverseIterator, fAx, fBx float64) float64 {
6700-
fAy, fBy := iterator.chiSqDist(fAx), iterator.chiSqDist(fBx)
6683+
fAy, fBy := iterator.callBack(fAx), iterator.callBack(fBx)
67016684
var fTemp float64
67026685
var nCount int
67036686
for nCount = 0; nCount < 1000 && !hasChangeOfSign(fAy, fBy); nCount++ {
@@ -6709,13 +6692,13 @@ func calcIterateInverse(iterator calcInverseIterator, fAx, fBx float64) float64
67096692
}
67106693
fBx = fTemp
67116694
fBy = fAy
6712-
fAy = iterator.chiSqDist(fAx)
6695+
fAy = iterator.callBack(fAx)
67136696
} else {
67146697
fTemp = fBx
67156698
fBx += 2 * (fBx - fAx)
67166699
fAx = fTemp
67176700
fAy = fBy
6718-
fBy = iterator.chiSqDist(fBx)
6701+
fBy = iterator.callBack(fBx)
67196702
}
67206703
}
67216704
if fAy == 0 || fBy == 0 {
@@ -9152,6 +9135,72 @@ func (fn *formulaFuncs) TDIST(argsList *list.List) formulaArg {
91529135
return newNumberFormulaArg(getTDist(x.Number, degrees.Number, tails.Number))
91539136
}
91549137

9138+
// TdotINV function calculates the left-tailed inverse of the Student's T
9139+
// Distribution, which is a continuous probability distribution that is
9140+
// frequently used for testing hypotheses on small sample data sets. The
9141+
// syntax of the function is:
9142+
//
9143+
// T.INV(probability,degrees_freedom)
9144+
//
9145+
func (fn *formulaFuncs) TdotINV(argsList *list.List) formulaArg {
9146+
if argsList.Len() != 2 {
9147+
return newErrorFormulaArg(formulaErrorVALUE, "T.INV requires 2 arguments")
9148+
}
9149+
var probability, degrees formulaArg
9150+
if probability = argsList.Front().Value.(formulaArg).ToNumber(); probability.Type != ArgNumber {
9151+
return probability
9152+
}
9153+
if degrees = argsList.Back().Value.(formulaArg).ToNumber(); degrees.Type != ArgNumber {
9154+
return degrees
9155+
}
9156+
if probability.Number <= 0 || probability.Number >= 1 || degrees.Number < 1 {
9157+
return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)
9158+
}
9159+
if probability.Number < 0.5 {
9160+
return newNumberFormulaArg(-calcIterateInverse(calcInverseIterator{
9161+
name: "T.INV",
9162+
fp: 1 - probability.Number,
9163+
fDF: degrees.Number,
9164+
nT: 4,
9165+
}, degrees.Number/2, degrees.Number))
9166+
}
9167+
return newNumberFormulaArg(calcIterateInverse(calcInverseIterator{
9168+
name: "T.INV",
9169+
fp: probability.Number,
9170+
fDF: degrees.Number,
9171+
nT: 4,
9172+
}, degrees.Number/2, degrees.Number))
9173+
}
9174+
9175+
// TdotINVdot2T function calculates the inverse of the two-tailed Student's T
9176+
// Distribution, which is a continuous probability distribution that is
9177+
// frequently used for testing hypotheses on small sample data sets. The
9178+
// syntax of the function is:
9179+
//
9180+
// T.INV.2T(probability,degrees_freedom)
9181+
//
9182+
func (fn *formulaFuncs) TdotINVdot2T(argsList *list.List) formulaArg {
9183+
if argsList.Len() != 2 {
9184+
return newErrorFormulaArg(formulaErrorVALUE, "T.INV.2T requires 2 arguments")
9185+
}
9186+
var probability, degrees formulaArg
9187+
if probability = argsList.Front().Value.(formulaArg).ToNumber(); probability.Type != ArgNumber {
9188+
return probability
9189+
}
9190+
if degrees = argsList.Back().Value.(formulaArg).ToNumber(); degrees.Type != ArgNumber {
9191+
return degrees
9192+
}
9193+
if probability.Number <= 0 || probability.Number > 1 || degrees.Number < 1 {
9194+
return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)
9195+
}
9196+
return newNumberFormulaArg(calcIterateInverse(calcInverseIterator{
9197+
name: "T.INV.2T",
9198+
fp: probability.Number,
9199+
fDF: degrees.Number,
9200+
nT: 2,
9201+
}, degrees.Number/2, degrees.Number))
9202+
}
9203+
91559204
// TRIMMEAN function calculates the trimmed mean (or truncated mean) of a
91569205
// supplied set of values. The syntax of the function is:
91579206
//

calc_test.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1167,6 +1167,12 @@ func TestCalcCellValue(t *testing.T) {
11671167
// TDIST
11681168
"=TDIST(1,10,1)": "0.17044656615103",
11691169
"=TDIST(1,10,2)": "0.34089313230206",
1170+
// T.INV
1171+
"=T.INV(0.25,10)": "-0.699812061312432",
1172+
"=T.INV(0.75,10)": "0.699812061312432",
1173+
// T.INV.2T
1174+
"=T.INV.2T(1,10)": "0",
1175+
"=T.INV.2T(0.5,10)": "0.699812061312432",
11701176
// TRIMMEAN
11711177
"=TRIMMEAN(A1:B4,10%)": "2.5",
11721178
"=TRIMMEAN(A1:B4,70%)": "2.5",
@@ -3048,6 +3054,19 @@ func TestCalcCellValue(t *testing.T) {
30483054
"=TDIST(-1,10,1)": "#NUM!",
30493055
"=TDIST(1,0,1)": "#NUM!",
30503056
"=TDIST(1,10,0)": "#NUM!",
3057+
// T.INV
3058+
"=T.INV()": "T.INV requires 2 arguments",
3059+
"=T.INV(\"\",10)": "strconv.ParseFloat: parsing \"\": invalid syntax",
3060+
"=T.INV(0.25,\"\")": "strconv.ParseFloat: parsing \"\": invalid syntax",
3061+
"=T.INV(0,10)": "#NUM!",
3062+
"=T.INV(1,10)": "#NUM!",
3063+
"=T.INV(0.25,0.5)": "#NUM!",
3064+
// T.INV.2T
3065+
"=T.INV.2T()": "T.INV.2T requires 2 arguments",
3066+
"=T.INV.2T(\"\",10)": "strconv.ParseFloat: parsing \"\": invalid syntax",
3067+
"=T.INV.2T(0.25,\"\")": "strconv.ParseFloat: parsing \"\": invalid syntax",
3068+
"=T.INV.2T(0,10)": "#NUM!",
3069+
"=T.INV.2T(0.25,0.5)": "#NUM!",
30513070
// TRIMMEAN
30523071
"=TRIMMEAN()": "TRIMMEAN requires 2 arguments",
30533072
"=TRIMMEAN(A1,\"\")": "strconv.ParseFloat: parsing \"\": invalid syntax",

file.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ func (f *File) Save() error {
6363
return f.SaveAs(f.Path)
6464
}
6565

66-
// SaveAs provides a function to create or update to an spreadsheet at the
66+
// SaveAs provides a function to create or update to a spreadsheet at the
6767
// provided path.
6868
func (f *File) SaveAs(name string, opt ...Options) error {
6969
if len(name) > MaxFileNameLength {
@@ -81,7 +81,7 @@ func (f *File) SaveAs(name string, opt ...Options) error {
8181
return ErrWorkbookExt
8282
}
8383
f.setContentTypePartProjectExtensions(contentType)
84-
file, err := os.OpenFile(filepath.Clean(name), os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0o600)
84+
file, err := os.OpenFile(filepath.Clean(name), os.O_WRONLY|os.O_TRUNC|os.O_CREATE, os.ModePerm)
8585
if err != nil {
8686
return err
8787
}

lib.go

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ func (f *File) saveFileList(name string, content []byte) {
132132
f.Pkg.Store(name, append([]byte(xml.Header), content...))
133133
}
134134

135-
// Read file content as string in a archive file.
135+
// Read file content as string in an archive file.
136136
func readFile(file *zip.File) ([]byte, error) {
137137
rc, err := file.Open()
138138
if err != nil {
@@ -157,8 +157,8 @@ func SplitCellName(cell string) (string, int, error) {
157157
if strings.IndexFunc(cell, alpha) == 0 {
158158
i := strings.LastIndexFunc(cell, alpha)
159159
if i >= 0 && i < len(cell)-1 {
160-
col, rowstr := strings.ReplaceAll(cell[:i+1], "$", ""), cell[i+1:]
161-
if row, err := strconv.Atoi(rowstr); err == nil && row > 0 {
160+
col, rowStr := strings.ReplaceAll(cell[:i+1], "$", ""), cell[i+1:]
161+
if row, err := strconv.Atoi(rowStr); err == nil && row > 0 {
162162
return col, row, nil
163163
}
164164
}
@@ -187,7 +187,7 @@ func JoinCellName(col string, row int) (string, error) {
187187
}
188188

189189
// ColumnNameToNumber provides a function to convert Excel sheet column name
190-
// to int. Column name case insensitive. The function returns an error if
190+
// to int. Column name case-insensitive. The function returns an error if
191191
// column name incorrect.
192192
//
193193
// Example:
@@ -248,14 +248,14 @@ func ColumnNumberToName(num int) (string, error) {
248248
// excelize.CellNameToCoordinates("Z3") // returns 26, 3, nil
249249
//
250250
func CellNameToCoordinates(cell string) (int, int, error) {
251-
colname, row, err := SplitCellName(cell)
251+
colName, row, err := SplitCellName(cell)
252252
if err != nil {
253253
return -1, -1, newCellNameToCoordinatesError(cell, err)
254254
}
255255
if row > TotalRows {
256256
return -1, -1, ErrMaxRows
257257
}
258-
col, err := ColumnNameToNumber(colname)
258+
col, err := ColumnNameToNumber(colName)
259259
return col, row, err
260260
}
261261

@@ -277,8 +277,8 @@ func CoordinatesToCellName(col, row int, abs ...bool) (string, error) {
277277
sign = "$"
278278
}
279279
}
280-
colname, err := ColumnNumberToName(col)
281-
return sign + colname + sign + strconv.Itoa(row), err
280+
colName, err := ColumnNumberToName(col)
281+
return sign + colName + sign + strconv.Itoa(row), err
282282
}
283283

284284
// areaRefToCoordinates provides a function to convert area reference to a
@@ -336,6 +336,27 @@ func (f *File) coordinatesToAreaRef(coordinates []int) (string, error) {
336336
return firstCell + ":" + lastCell, err
337337
}
338338

339+
// getDefinedNameRefTo convert defined name to reference range.
340+
func (f *File) getDefinedNameRefTo(definedNameName string, currentSheet string) (refTo string) {
341+
var workbookRefTo, worksheetRefTo string
342+
for _, definedName := range f.GetDefinedName() {
343+
if definedName.Name == definedNameName {
344+
// worksheet scope takes precedence over scope workbook when both definedNames exist
345+
if definedName.Scope == "Workbook" {
346+
workbookRefTo = definedName.RefersTo
347+
}
348+
if definedName.Scope == currentSheet {
349+
worksheetRefTo = definedName.RefersTo
350+
}
351+
}
352+
}
353+
refTo = workbookRefTo
354+
if worksheetRefTo != "" {
355+
refTo = worksheetRefTo
356+
}
357+
return
358+
}
359+
339360
// flatSqref convert reference sequence to cell coordinates list.
340361
func (f *File) flatSqref(sqref string) (cells map[int][][]int, err error) {
341362
var coordinates []int
@@ -365,7 +386,7 @@ func (f *File) flatSqref(sqref string) (cells map[int][][]int, err error) {
365386
return
366387
}
367388

368-
// inCoordinates provides a method to check if an coordinate is present in
389+
// inCoordinates provides a method to check if a coordinate is present in
369390
// coordinates array, and return the index of its location, otherwise
370391
// return -1.
371392
func inCoordinates(a [][]int, x []int) int {
@@ -391,7 +412,7 @@ func inStrSlice(a []string, x string, caseSensitive bool) int {
391412
return -1
392413
}
393414

394-
// inFloat64Slice provides a method to check if an element is present in an
415+
// inFloat64Slice provides a method to check if an element is present in a
395416
// float64 array, and return the index of its location, otherwise return -1.
396417
func inFloat64Slice(a []float64, x float64) int {
397418
for idx, n := range a {
@@ -405,7 +426,7 @@ func inFloat64Slice(a []float64, x float64) int {
405426
// boolPtr returns a pointer to a bool with the given value.
406427
func boolPtr(b bool) *bool { return &b }
407428

408-
// intPtr returns a pointer to a int with the given value.
429+
// intPtr returns a pointer to an int with the given value.
409430
func intPtr(i int) *int { return &i }
410431

411432
// float64Ptr returns a pointer to a float64 with the given value.
@@ -626,7 +647,7 @@ func (f *File) replaceNameSpaceBytes(path string, contentMarshal []byte) []byte
626647
return bytesReplace(contentMarshal, oldXmlns, newXmlns, -1)
627648
}
628649

629-
// addNameSpaces provides a function to add a XML attribute by the given
650+
// addNameSpaces provides a function to add an XML attribute by the given
630651
// component part path.
631652
func (f *File) addNameSpaces(path string, ns xml.Attr) {
632653
exist := false
@@ -715,7 +736,7 @@ var (
715736

716737
// bstrUnmarshal parses the binary basic string, this will trim escaped string
717738
// literal which not permitted in an XML 1.0 document. The basic string
718-
// variant type can store any valid Unicode character. Unicode characters
739+
// variant type can store any valid Unicode character. Unicode's characters
719740
// that cannot be directly represented in XML as defined by the XML 1.0
720741
// specification, shall be escaped using the Unicode numerical character
721742
// representation escape character format _xHHHH_, where H represents a

numfmt.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,9 @@ type languageInfo struct {
3333
type numberFormat struct {
3434
section []nfp.Section
3535
t time.Time
36-
sectionIdx int
37-
isNumberic, hours, seconds bool
38-
number float64
36+
sectionIdx int
37+
isNumeric, hours, seconds bool
38+
number float64
3939
ap, afterPoint, beforePoint, localCode, result, value, valueSectionType string
4040
}
4141

@@ -279,7 +279,7 @@ var (
279279
// prepareNumberic split the number into two before and after parts by a
280280
// decimal point.
281281
func (nf *numberFormat) prepareNumberic(value string) {
282-
if nf.isNumberic, _ = isNumeric(value); !nf.isNumberic {
282+
if nf.isNumeric, _ = isNumeric(value); !nf.isNumeric {
283283
return
284284
}
285285
}
@@ -297,7 +297,7 @@ func format(value, numFmt string) string {
297297
if section.Type != nf.valueSectionType {
298298
continue
299299
}
300-
if nf.isNumberic {
300+
if nf.isNumeric {
301301
switch section.Type {
302302
case nfp.TokenSectionPositive:
303303
return nf.positiveHandler()

0 commit comments

Comments
 (0)