fpspreadsheet: Clip cell ranges at the sheet dimension limits. Add some test cases for it.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@9651 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz 2025-02-26 23:27:47 +00:00
parent 31951b323a
commit cfe6c14a8f
7 changed files with 162 additions and 52 deletions

View File

@ -5153,6 +5153,7 @@ const
var
i, n: Integer;
r, c: Cardinal;
r1, c1, r2, c2: Cardinal;
cell: PCell;
sheet: TsWorksheet;
book: TsWorkbook;
@ -5215,8 +5216,11 @@ begin
for idx := idx1 to idx2 do
begin
sheet := (arg.Worksheet as TsWorksheet).Workbook.GetWorksheetByIndex(idx);
for r := arg.ResCellRange.Row1 to arg.ResCellRange.Row2 do
for c := arg.ResCellRange.Col1 to arg.ResCellRange.Col2 do
r2 := sheet.GetLastOccupiedRowIndex;
sheet.GetSheetDim(r1, c1, r2, c2);
TrimCellRange(arg.ResCellRange, r1, c1, r2, c2);
for r := r1 to r2 do
for c := c1 to c2 do
begin
cell := sheet.FindCell(r, c);
if (cell <> nil) then

View File

@ -117,7 +117,8 @@ type
function GetArgValue(ArgIndex: Integer): Double;
function GetCellValue(ASheet: TsBasicWorksheet; ARow, ACol: Integer): Double;
procedure GetCompareParams(ArgIndex: Integer);
procedure GetRangeLimits(ArgIndex: Integer; out ARow1, ACol1, ARow2, ACol2: Integer);
procedure GetRangeLimits(ArgIndex: Integer; ASheet: TsBasicWorksheet;
out ARow1, ACol1, ARow2, ACol2: Cardinal);
function GetWorkbook: TsBasicWorkbook;
function GetWorksheet(ArgIndex: Integer): TsBasicWorksheet;
function SameRangeSize(ARange1, ARange2: TsCellRange3D): Boolean;
@ -276,7 +277,8 @@ end;
returns the result to be used by the formula engine. }
function TsFuncComparer.Execute: TsExpressionResult;
var
r, r1, r2, c, c1, c2, rIdx, cIdx: Integer;
r, r1, r2, c, c1, c2: Cardinal;
rIdx, cIdx: Integer;
critIdx: Integer;
critRangeIdx: Integer;
critSheet: TsBasicWorksheet;
@ -302,7 +304,7 @@ begin
sum := 0.0;
// Iterate over all value range cells
GetRangeLimits(FValueRangeIndex, r1, c1, r2, c2);
GetRangeLimits(FValueRangeIndex, valueSheet, r1, c1, r2, c2);
for r := r1 to r2 do
begin
for c := c1 to c2 do
@ -522,7 +524,8 @@ begin
end;
end;
procedure TsFuncComparer.GetRangeLimits(ArgIndex: Integer; out ARow1, ACol1, ARow2, ACol2: Integer);
procedure TsFuncComparer.GetRangeLimits(ArgIndex: Integer; ASheet: TsBasicWorksheet;
out ARow1, ACol1, ARow2, ACol2: Cardinal);
begin
case FArgs[ArgIndex].ResultType of
rtCell:
@ -532,10 +535,8 @@ begin
end;
rtCellRange:
begin
ARow1 := FArgs[ArgIndex].ResCellRange.Row1;
ARow2 := FArgs[ArgIndex].ResCellRange.Row2;
ACol1 := FArgs[ArgIndex].ResCellRange.Col1;
ACol2 := FArgs[ArgIndex].ResCellRange.Col2;
TsWorksheet(ASheet).GetSheetDim(ARow1, ACol1, ARow2, ACol2);
TrimCellRange(FArgs[ArgIndex].ResCellRange, ARow1, ACol1, ARow2, ACol2);
end;
end;
end;

View File

@ -487,6 +487,8 @@ type
function GetLastOccupiedRowIndex: Cardinal;
function GetLastRowIndex(AForceCalculation: Boolean = false): Cardinal;
function GetLastRowNumber: Cardinal; deprecated 'Use GetLastRowIndex';
procedure GetSheetDim(out AFirstRow, AFirstCol, ALastRow, ALastCol: Cardinal;
AForceCalculation: Boolean = false);
{ Data manipulation methods - For Rows and Cols }
function AddCol(ACol: Cardinal): PCol;
@ -2744,7 +2746,7 @@ begin
end;
{@@ ----------------------------------------------------------------------------
Deprecated, use GetLastColIndex instead
Deprecated, use GetLastRowIndex instead
@seeAlso GetLastColIndex
-------------------------------------------------------------------------------}
@ -2753,6 +2755,44 @@ begin
Result := GetLastRowIndex;
end;
procedure TsWorksheet.GetSheetDim(out AFirstRow, AFirstCol, ALastRow, ALastCol: Cardinal;
AForceCalculation: Boolean = false);
var
cell: PCell;
begin
if AForceCalculation then
begin
cell := FCells.GetFirstCell;
if cell <> nil then
begin
AFirstRow := cell^.Row;
AFirstCol := cell^.Col;
end else
begin
AFirstRow := 0;
AfirstCol := 0;
end;
cell := FCells.GetLastCell;
if cell <> nil then
begin
ALastRow := cell^.Row;
ALastCol := cell^.Col;
end else
begin
ALastRow := AFirstRow;
ALastCol := AFirstCol;
end;
end else
begin
AFirstRow := FFirstRowIndex;
AFirstCol := FFirstColIndex;
ALastRow := FLastRowIndex;
ALastCol := FLastColIndex;
end;
end;
{@@ ----------------------------------------------------------------------------
Reads the contents of a cell and returns an user readable text
representing the contents of the cell.

View File

@ -255,6 +255,9 @@ function Range(ARow, ACol: Cardinal): TsCellRange; overload;
function Range(ARow1, ACol1, ARow2, ACol2: Cardinal): TsCellRange; overload;
function Range3D(ASheetIdx1, ASheetIdx2: Integer; ARow1, ACol1, ARow2, ACol2: Cardinal): TsCellRange3D;
procedure TrimCellRange(ARange: TsCellRange; var ARow1, ACol1, ARow2, ACol2: Cardinal);
procedure TrimCellRange(ARange: TsCellRange3D; var ARow1, ACol1, ARow2, ACol2: Cardinal);
{$IF FPC_FullVersion < 30200}
function FMod(const a, b: Double): Double; inline; overload;
{$IFEND}
@ -3060,6 +3063,22 @@ begin
Result.Sheet2 := ASheetIdx2;
end;
procedure TrimCellRange(ARange: TsCellRange; var ARow1, ACol1, ARow2, ACol2: Cardinal);
begin
if ARange.Row1 > ARow1 then ARow1 := ARange.Row1;
if ARange.Col1 > ACol1 then ACol1 := ARange.Col1;
if ARange.Row2 < ARow2 then ARow2 := ARange.Row2;
if ARange.Col2 < ACol2 then ACol2 := ARange.Col2;
end;
procedure TrimCellRange(ARange: TsCellRange3D; var ARow1, ACol1, ARow2, ACol2: Cardinal);
begin
if ARange.Row1 > ARow1 then ARow1 := ARange.Row1;
if ARange.Col1 > ACol1 then ACol1 := ARange.Col1;
if ARange.Row2 < ARow2 then ARow2 := ARange.Row2;
if ARange.Col2 < ACol2 then ACol2 := ARange.Col2;
end;
{$IF FPC_FullVersion < 30200}
function FMod(const a, b: Double): Double;
begin

View File

@ -71,7 +71,8 @@ type
procedure Test_ADDRESS;
procedure Test_COLUMN;
// procedure Test_HYPERLINK -- to be written
procedure Test_INDEX;
procedure Test_INDEX_1;
procedure Test_INDEX_2;
procedure Test_INDIRECT;
procedure Test_MATCH;
procedure Test_ROW;

View File

@ -109,7 +109,7 @@ begin
CheckEquals(2, FWorksheet.ReadAsNumber(0, 2), 'Formula #4 COLUMN(B2) (B2 contains error) result mismatch');
end;
procedure TCalcLookupFormulaTests.Test_INDEX;
procedure TCalcLookupFormulaTests.Test_INDEX_1;
var
sh: TsWorksheet;
begin
@ -126,9 +126,11 @@ begin
FWorksheet.WriteFormula(10, 0, '=INDEX(B2:C6,3,1)');
FWorksheet.CalcFormulas;
CheckEquals(42, FWorksheet.ReadAsNumber(10,0), 'Formula #1 INDEX(B2:F3,3,1) result mismatch');
end;
procedure TCalcLookupFormulaTests.Test_INDEX_2;
begin
// Sample similar to that in unit formulatests:
FWorksheet.Clear;
FWorksheet.WriteText (0, 0, 'A'); // A1
FWorksheet.WriteText (0, 1, 'B'); // B1
@ -183,11 +185,11 @@ begin
FOtherWorksheet.WriteFormula(0, 5, 'SUM(INDEX(Sheet1!A1:C3,0,2))'); // Sum of numbers in 2nd column of A1:C3
FWorkbook.CalcFormulas;
CheckEquals(42, FOtherWorksheet.ReadAsNumber(0, 5), 'Formula #6 SUM(Sheet1!INDEX(A1:C3,0,2)) result mismatch');
CheckEquals(42, FOtherWorksheet.ReadAsNumber(0, 5), 'Formula #12 SUM(Sheet1!INDEX(A1:C3,0,2)) result mismatch');
FOtherWorksheet.WriteFormula(0, 5, 'SUM(INDEX(Sheet1!A1:C3,2,0))'); // Sum of numbers in 2nd row of A1:C3
FWorkbook.CalcFormulas;
CheckEquals(60, FOtherWorksheet.ReadAsNumber(0, 5), 'Formula #7 SUM(Sheet1!INDEX(A1:C3,2,0)) result mismatch');
CheckEquals(60, FOtherWorksheet.ReadAsNumber(0, 5), 'Formula #13 SUM(Sheet1!INDEX(A1:C3,2,0)) result mismatch');
end;
procedure TCalcLookupFormulaTests.Test_INDIRECT;

View File

@ -238,7 +238,7 @@ begin
FWorksheet.CalcFormulas;
CheckEquals(STR_ERR_ILLEGAL_REF, FWorksheet.ReadAsText(0, 2), 'Formula #8 AVERAGEIF(A1:A10,"abc",B1:B10) result mismatch');
// ToDo: CompareStringsWithWildcards does not handle a mask such as "*b" like Excel
// ToDo: CompareStringsWithWildcards does not handle a mask such as "*b" like Excel // wp: really? I thought this was fixed!
{
// Search for text cells by wildcards
FWorksheet.WriteFormula(0, 2, '=AVERAGEIF(A1:A8,"*bc",B1:B8)');
@ -461,13 +461,15 @@ begin
end;
procedure TCalcStatsFormulaTests.Test_COUNTIF;
var
t: TDateTime;
begin
// Test data, range A1:B5
FWorksheet.WriteNumber (0, 0, 10); FWorksheet.WriteFormula(0, 1, '=SQRT(-1)'); // --> #NUM!
FWorksheet.WriteNumber (1, 0, -20); FWorksheet.WriteBlank (1, 1);
FWorksheet.WriteFormula(2, 0, '=(1=1)'); FWorksheet.WriteNumber (2, 1, 0);
FWorksheet.WriteText (3, 0, ''); FWorksheet.WriteText (3, 1, '5');
FWorksheet.WriteText (4, 0, 'abc'); FWorksheet.WriteText (4, 1, 'ABC');
FWorksheet.WriteNumber (0, 0, 10); FWorksheet.WriteFormula (0, 1, '=SQRT(-1)'); // --> #NUM!
FWorksheet.WriteNumber (1, 0, -20); FWorksheet.WriteBlank (1, 1);
FWorksheet.WriteFormula (2, 0, '=(1=1)'); FWorksheet.WriteNumber (2, 1, 0);
FWorksheet.WriteText (3, 0, ''); FWorksheet.WriteText (3, 1, '5');
FWorksheet.WriteText (4, 0, 'abc'); FWorksheet.WriteText (4, 1, 'ABC');
FWorksheet.WriteBoolValue(5, 0, false); FWorksheet.WriteErrorValue(5, 1, errOverflow); // --> #NUM!
// Counts the elements in A1:B6 which are equal to "abc" (case-insensitive)
@ -498,44 +500,52 @@ begin
// Counts the elements in A1:B6 which are FALSE
FWorksheet.WriteFormula(0, 2, '=COUNTIF(A1:B6,FALSE)');
FWorksheet.CalcFormulas;
CheckEquals(1, FWorksheet.ReadAsNumber(0, 2), 'Formula #4 COUNTIF(A1:B6,FALSE) result mismatch');
CheckEquals(1, FWorksheet.ReadAsNumber(0, 2), 'Formula #6 COUNTIF(A1:B6,FALSE) result mismatch');
// Counts the elements in A1:B5 which are #NUM!
FWorksheet.WriteFormula(0, 2, '=COUNTIF(A1:B6,#NUM!)');
FWorksheet.CalcFormulas;
CheckEquals(2, FWorksheet.ReadAsNumber(0, 2), 'Formula #6 COUNTIF(A1:B6,#NUM!) result mismatch');
CheckEquals(2, FWorksheet.ReadAsNumber(0, 2), 'Formula #7 COUNTIF(A1:B6,#NUM!) result mismatch');
// Error in 1st argument
FWorksheet.WriteFormula(0, 2, '=COUNTIF(#REF!,1)');
FWorksheet.CalcFormulas;
CheckEquals(STR_ERR_ILLEGAL_REF, FWorksheet.ReadAsText(0, 2), 'Formula #7 COUNTIF(#REF!,1) result mismatch');
CheckEquals(STR_ERR_ILLEGAL_REF, FWorksheet.ReadAsText(0, 2), 'Formula #8 COUNTIF(#REF!,1) result mismatch');
// Error in both arguments
FWorksheet.WriteFormula(0, 2, '=COUNTIF(#REF!,#REF!)');
FWorksheet.CalcFormulas;
CheckEquals(STR_ERR_ILLEGAL_REF, FWorksheet.ReadAsText(0, 2), 'Formula #8 COUNTIF(#REF!,#REF!) result mismatch');
CheckEquals(STR_ERR_ILLEGAL_REF, FWorksheet.ReadAsText(0, 2), 'Formula #9 COUNTIF(#REF!,#REF!) result mismatch');
// Count the elements in A1:B6 which are equal to cell A15 (empty)
FWorksheet.WriteFormula(0, 2, '=COUNTIF(A1:B6,A15)');
FWorksheet.CalcFormulas;
CheckEquals(2, FWorksheet.ReadAsNumber(0, 2), 'Formula #9 COUNTIF(A1:B6,A15) (A15 empty) result mismatch');
CheckEquals(2, FWorksheet.ReadAsNumber(0, 2), 'Formula #10 COUNTIF(A1:B6,A15) (A15 empty) result mismatch');
// Count the elements in A1:B6 which are equal to cell A15 (value 10)
FWorksheet.WriteNumber(14, 0, 10);
FWorksheet.WriteFormula(0, 2, '=COUNTIF(A1:B6,A15)');
FWorksheet.CalcFormulas;
CheckEquals(1, FWorksheet.ReadAsNumber(0, 2), 'Formula #10 COUNTIF(A1:B6,A15) (A15 = 10) result mismatch');
CheckEquals(1, FWorksheet.ReadAsNumber(0, 2), 'Formula #11 COUNTIF(A1:B6,A15) (A15 = 10) result mismatch');
// Count the elements in A1:B6 which are < cell A15 (value 10)
FWorksheet.WriteFormula(0, 2, '=COUNTIF(A1:B6,"<"&A15)');
FWorksheet.CalcFormulas;
CheckEquals(3, FWorksheet.ReadAsNumber(0, 2), 'Formula #11 COUNTIF(A1:B6,"<"&A15) (A15 = 10) result mismatch');
CheckEquals(3, FWorksheet.ReadAsNumber(0, 2), 'Formula #12 COUNTIF(A1:B6,"<"&A15) (A15 = 10) result mismatch');
// Count the elements in A1:B6 which are equal to cell A15 (error value #NUM!)
FWorksheet.WriteErrorValue(14, 0, errOverflow);
FWorksheet.WriteFormula(0, 2, '=COUNTIF(A1:B6,A15)');
FWorksheet.CalcFormulas;
CheckEquals(2, FWorksheet.ReadAsNumber(0, 2), 'Formula #12 COUNTIF(A1:B6,A15) (A15 = #NUM!) result mismatch');
CheckEquals(2, FWorksheet.ReadAsNumber(0, 2), 'Formula #13 COUNTIF(A1:B6,A15) (A15 = #NUM!) result mismatch');
// Huge range
FWorksheet.WriteFormula(0, 1, '=COUNTIF(A2:AZ999999,"abc")'); // A2: do not include formula cell in range
t := Now;
FWorksheet.CalcFormulas;
t := Now-t;
CheckEquals(2, FWorksheet.ReadAsNumber(0, 1), 'Formula #14 COUNTIF(A2:AZ999999,"abc") result mismatch');
CheckEquals(true, t < 0.1/(24*60*60), 'Formula #14 COUNTIF(A2:AZ999999,"abc") takes too long');
end;
procedure TCalcStatsFormulaTests.Test_MAX;
@ -978,6 +988,8 @@ begin
end;
procedure TCalcStatsFormulaTests.Test_SUM;
var
t: TDateTime;
begin
// Test data
FWorksheet.WriteNumber (0, 0, 10);
@ -1021,50 +1033,63 @@ begin
CheckEquals(STR_ERR_DIVIDE_BY_ZERO, FWorksheet.ReadAsText(0, 1), 'Formula #8 SUM(1/0) result mismatch');
// Count in cell references
FWorksheet.WriteFormula(0, 1, '=SUM(A1)');
FWorksheet.CalcFormulas;
CheckEquals(10, FWorksheet.ReadAsNumber(0, 1), 'Formula #7 SUM(A1) result mismatch');
CheckEquals(10, FWorksheet.ReadAsNumber(0, 1), 'Formula #9 SUM(A1) result mismatch');
FWorksheet.WriteFormula(0, 1, '=SUM(A10)'); // empty cell
FWorksheet.CalcFormulas;
CheckEquals(0, FWorksheet.ReadAsNumber(0, 1), 'Formula #8 SUM(A10) result mismatch');
CheckEquals(0, FWorksheet.ReadAsNumber(0, 1), 'Formula #10 SUM(A10) result mismatch');
FWorksheet.WriteFormula(0, 1, '=SUM(A1,A2)');
FWorksheet.CalcFormulas;
CheckEquals(30, FWorksheet.ReadAsNumber(0, 1), 'Formula #9 SUM(A1,A2) result mismatch');
CheckEquals(30, FWorksheet.ReadAsNumber(0, 1), 'Formula #11 SUM(A1,A2) result mismatch');
FWorksheet.WriteFormula(0, 1, '=SUM(A1:A3)'); // "real" numeric values
// "real" numeric values
FWorksheet.WriteFormula(0, 1, '=SUM(A1:A3)');
FWorksheet.CalcFormulas;
CheckEquals(60, FWorksheet.ReadAsNumber(0, 1), 'Formula #10 SUM(A1:A3) result mismatch');
CheckEquals(60, FWorksheet.ReadAsNumber(0, 1), 'Formula #12 SUM(A1:A3) result mismatch');
FWorksheet.WriteFormula(0, 1, '=SUM(A1,A2:A3)'); // several ranges
// Several ranges
FWorksheet.WriteFormula(0, 1, '=SUM(A1,A2:A3)');
FWorksheet.CalcFormulas;
CheckEquals(60, FWorksheet.ReadAsNumber(0, 1), 'Formula #11 SUM(A1,A2:A3) result mismatch');
CheckEquals(60, FWorksheet.ReadAsNumber(0, 1), 'Formula #13 SUM(A1,A2:A3) result mismatch');
// Huge range
FWorksheet.WriteFormula(0, 1, '=SUM(A1:A3,A10:A999999)');
t := Now;
FWorksheet.CalcFormulas;
t := Now-t;
CheckEquals(60, FWorksheet.ReadAsNumber(0, 1), 'Formula #14 SUM(A1:A3,A10:A999999) result mismatch');
CheckEquals(true, t < 0.1/(24*60*60), 'Formula #14 SUM(A1:A3,A10:A999999) takes too long');
// Cell references pointing to string cells
FWorksheet.WriteFormula(0, 1, '=SUM(A1:A4)'); // real and string numeric values
FWorksheet.CalcFormulas;
CheckEquals(100, FWorksheet.ReadAsNumber(0, 1), 'Formula #12 SUM(A1:A4) result mismatch');
CheckEquals(100, FWorksheet.ReadAsNumber(0, 1), 'Formula #15 SUM(A1:A4) result mismatch');
FWorksheet.WriteFormula(0, 1, '=SUM(A1:A5)'); // real and string values --> ignore string
FWorksheet.CalcFormulas;
CheckEquals(100, FWorksheet.ReadAsNumber(0, 1), 'Formula #13 SUM(A1:A5) result mismatch');
CheckEquals(100, FWorksheet.ReadAsNumber(0, 1), 'Formula #16 SUM(A1:A5) result mismatch');
FWorksheet.WriteFormula(0, 1, '=SUM(A1:A4,A8:A10)'); // real and string values and blanks
FWorksheet.CalcFormulas;
CheckEquals(100, FWorksheet.ReadAsNumber(0, 1), 'Formula #14 SUM(A1:A4,A8:A10) result mismatch');
CheckEquals(100, FWorksheet.ReadAsNumber(0, 1), 'Formula #17 SUM(A1:A4,A8:A10) result mismatch');
// Error propagation
FWorksheet.WriteFormula(0, 1, '=SUM(A1, 1/0, A2)'); // error in argument
FWorksheet.CalcFormulas;
CheckEquals(STR_ERR_DIVIDE_BY_ZERO, FWorksheet.ReadAsText(0, 1), 'Formula #15 SUM(A1, 1/0, A2) result mismatch');
CheckEquals(STR_ERR_DIVIDE_BY_ZERO, FWorksheet.ReadAsText(0, 1), 'Formula #18 SUM(A1, 1/0, A2) result mismatch');
FWorksheet.WriteFormula(0, 1, '=SUM(A1:A6)'); // error in cell
FWorksheet.CalcFormulas;
CheckEquals(STR_ERR_DIVIDE_BY_ZERO, FWorksheet.ReadAsText(0, 1), 'Formula #16 SUM(A:A6) result mismatch');
CheckEquals(STR_ERR_DIVIDE_BY_ZERO, FWorksheet.ReadAsText(0, 1), 'Formula #19 SUM(A:A6) result mismatch');
end;
procedure TCalcStatsFormulaTests.Test_SUMIF;
var
t: TDateTime;
begin
// Test data, range A1:B5
FWorksheet.WriteNumber (0, 0, 10); FWorksheet.WriteNumber (0, 1, -1);
@ -1217,6 +1242,14 @@ begin
CheckEquals(1, FWorksheet.ReadAsNumber(0, 2), 'Formula #22 SUMIF(A1:B5,A15) (A1=A15=#DIV/0!) result mismatch');
//CheckEquals(STR_ERR_DIVIDE_BY_ZERO, FWorksheet.ReadAsText(0, 2), 'Formula #21 SUMIF(A1:B5,A15) (A15=#DIV/0!) result mismatch');
// Huge range
FWorksheet.WriteFormula(0, 1, '=SUMIF(A2:Z999999,0)'); // A2: do not include formula cell in range
t := Now;
FWorksheet.CalcFormulas;
t := Now-t;
CheckEquals(0, FWorksheet.ReadAsNumber(0, 1), 'Formula #22 SUMIF(A2:Z999999,0) result mismatch');
CheckEquals(true, t < 0.1/(24*60*60), 'Formula #22 SUMIF(A2:Z999999,0) takes too long');
end;
procedure TCalcStatsFormulaTests.Test_SUMSQ;
@ -1265,50 +1298,52 @@ begin
// Count in cell references
FWorksheet.WriteFormula(0, 1, '=SUMSQ(A1)');
FWorksheet.CalcFormulas;
CheckEquals(1, FWorksheet.ReadAsNumber(0, 1), 'Formula #7 SUMSQ(A1) result mismatch');
CheckEquals(1, FWorksheet.ReadAsNumber(0, 1), 'Formula #9 SUMSQ(A1) result mismatch');
FWorksheet.WriteFormula(0, 1, '=SUMSQ(A10)'); // empty cell
FWorksheet.CalcFormulas;
CheckEquals(0, FWorksheet.ReadAsNumber(0, 1), 'Formula #8 SUMSQ(A10) result mismatch');
CheckEquals(0, FWorksheet.ReadAsNumber(0, 1), 'Formula #10 SUMSQ(A10) result mismatch');
FWorksheet.WriteFormula(0, 1, '=SUMSQ(A1,A2)');
FWorksheet.CalcFormulas;
CheckEquals(5, FWorksheet.ReadAsNumber(0, 1), 'Formula #9 SUMSQ(A1,A2) result mismatch');
CheckEquals(5, FWorksheet.ReadAsNumber(0, 1), 'Formula #11 SUMSQ(A1,A2) result mismatch');
FWorksheet.WriteFormula(0, 1, '=SUMSQ(A1:A3)'); // "real" numeric values
FWorksheet.CalcFormulas;
CheckEquals(14, FWorksheet.ReadAsNumber(0, 1), 'Formula #10 SUMSQ(A1:A3) result mismatch');
CheckEquals(14, FWorksheet.ReadAsNumber(0, 1), 'Formula #12 SUMSQ(A1:A3) result mismatch');
FWorksheet.WriteFormula(0, 1, '=SUMSQ(A1,A2:A3)'); // several ranges
FWorksheet.CalcFormulas;
CheckEquals(14, FWorksheet.ReadAsNumber(0, 1), 'Formula #11 SUMSQ(A1,A2:A3) result mismatch');
CheckEquals(14, FWorksheet.ReadAsNumber(0, 1), 'Formula #13 SUMSQ(A1,A2:A3) result mismatch');
// Cell references pointing to string cells
FWorksheet.WriteFormula(0, 1, '=SUMSQ(A1:A4)'); // "real" and string numeric values
FWorksheet.CalcFormulas;
CheckEquals(30, FWorksheet.ReadAsNumber(0, 1), 'Formula #12 SUMSQ(A1:A4) result mismatch');
CheckEquals(30, FWorksheet.ReadAsNumber(0, 1), 'Formula #14 SUMSQ(A1:A4) result mismatch');
FWorksheet.WriteFormula(0, 1, '=SUMSQ(A1:A5)'); // "real" and string values --> ignore string
FWorksheet.CalcFormulas;
CheckEquals(30, FWorksheet.ReadAsNumber(0, 1), 'Formula #13 SUMSQ(A1:A5) result mismatch');
CheckEquals(30, FWorksheet.ReadAsNumber(0, 1), 'Formula #15 SUMSQ(A1:A5) result mismatch');
FWorksheet.WriteFormula(0, 1, '=SUMSQ(A1:A4,A8:A10)'); // real and string values and blanks
FWorksheet.CalcFormulas;
CheckEquals(30, FWorksheet.ReadAsNumber(0, 1), 'Formula #14 SUMSQ(A1:A4,A8:A10) result mismatch');
CheckEquals(30, FWorksheet.ReadAsNumber(0, 1), 'Formula #16 SUMSQ(A1:A4,A8:A10) result mismatch');
// Error propagation
FWorksheet.WriteFormula(0, 1, '=SUMSQ(A1:A5,1/0)'); // error in argument --> error in result
FWorksheet.CalcFormulas;
CheckEquals(STR_ERR_DIVIDE_BY_ZERO, FWorksheet.ReadAsText(0, 1), 'Formula #15 SUMSQ(A1, 1/0, A2) result mismatch');
CheckEquals(STR_ERR_DIVIDE_BY_ZERO, FWorksheet.ReadAsText(0, 1), 'Formula #17 SUMSQ(A1, 1/0, A2) result mismatch');
FWorksheet.WriteFormula(0, 1, '=SUMSQ(A1:A6)'); // error in cell --> error in result
FWorksheet.CalcFormulas;
CheckEquals(STR_ERR_DIVIDE_BY_ZERO, FWorksheet.ReadAsText(0, 1), 'Formula #16 SUMSQ(A1:A6) result mismatch');
CheckEquals(STR_ERR_DIVIDE_BY_ZERO, FWorksheet.ReadAsText(0, 1), 'Formula #18 SUMSQ(A1:A6) result mismatch');
end;
procedure TCalcStatsFormulaTests.Test_VAR;
const
EPS = 1E-8;
var
t: TDateTime;
begin
// Test data
FWorksheet.WriteNumber (0, 0, 1);
@ -1393,6 +1428,14 @@ begin
FWorksheet.WriteFormula(0, 1, '=VAR(A1:A6)'); // error in cell
FWorksheet.CalcFormulas;
CheckEquals(STR_ERR_DIVIDE_BY_ZERO, FWorksheet.ReadAsText(0, 1), 'Formula #16 VAR(A:A6) result mismatch');
// Huge range
FWorksheet.WriteFormula(0, 1, '=VAR(A1:A3,A10:A9999999)');
t := Now();
FWorksheet.CalcFormulas;
t := Now() - t;
CheckEquals(4.333333333, FWorksheet.ReadAsNumber(0, 1), EPS, 'Formula #17 VAR(A1:A3,A10:9999999) result mismatch');
CheckEquals(true, t < 0.1/(24*60*60), 'Formula #17 VAR(A1:A3,A10:A9999999) takes too long');
end;
procedure TCalcStatsFormulaTests.Test_VARP;