From 57036aa6801de8b6e05e3a1b27285f4b8e25191d Mon Sep 17 00:00:00 2001 From: wp_xxyyzz Date: Mon, 30 Jun 2014 15:49:49 +0000 Subject: [PATCH] fpspreadsheet: Calculate most statistical and some string functions in rpn formulas. Beginning to handle missing arguments correctly. git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3256 8e941d3f-bd1b-0410-a28a-d453659cc2b4 --- components/fpspreadsheet/fpsmath.pas | 324 ++++++++++++++---- components/fpspreadsheet/fpspreadsheet.pas | 32 +- .../tests/testcases_calcrpnformula.inc | 218 ++++++++++++ 3 files changed, 487 insertions(+), 87 deletions(-) diff --git a/components/fpspreadsheet/fpsmath.pas b/components/fpspreadsheet/fpsmath.pas index 193ec07ac..aa5393f37 100644 --- a/components/fpspreadsheet/fpsmath.pas +++ b/components/fpspreadsheet/fpsmath.pas @@ -33,10 +33,6 @@ type procedure Delete(AIndex: Integer); end; -procedure FixMissingBool (var Arg: TsArgument; ABool: Boolean); -procedure FixMissingNumber(var Arg: TsArgument; ANumber: Double); -procedure FixMissingString(var Arg: TsArgument; AString: String); - function CreateBool(AValue: Boolean): TsArgument; function CreateNumber(AValue: Double): TsArgument; function CreateString(AValue: String): TsArgument; @@ -90,13 +86,30 @@ function fpsSINH (Args: TsArgumentStack; NumArgs: Integer): TsArgument; function fpsSQRT (Args: TsArgumentStack; NumArgs: Integer): TsArgument; function fpsTAN (Args: TsArgumentStack; NumArgs: Integer): TsArgument; function fpsTANH (Args: TsArgumentStack; NumArgs: Integer): TsArgument; -{ Logic } +{ Statistical functions } +function fpsAVEDEV (Args: TsArgumentStack; NumArgs: Integer): TsArgument; +function fpsAVERAGE (Args: TsArgumentStack; NumArgs: Integer): TsArgument; +function fpsCOUNT (Args: TsArgumentStack; NumArgs: Integer): TsArgument; +function fpsMAX (Args: TsArgumentStack; NumArgs: Integer): TsArgument; +function fpsMIN (Args: TsArgumentStack; NumArgs: Integer): TsArgument; +function fpsPRODUCT (Args: TsArgumentStack; NumArgs: Integer): TsArgument; +function fpsSTDEV (Args: TsArgumentStack; NumArgs: Integer): TsArgument; +function fpsSTDEVP (Args: TsArgumentStack; NumArgs: Integer): TsArgument; +function fpsSUM (Args: TsArgumentStack; NumArgs: Integer): TsArgument; +function fpsSUMSQ (Args: TsArgumentStack; NumArgs: Integer): TsArgument; +function fpsVAR (Args: TsArgumentStack; NumArgs: Integer): TsArgument; +function fpsVARP (Args: TsArgumentStack; NumArgs: Integer): TsArgument; +{ Logical functions } function fpsAND (Args: TsArgumentStack; NumArgs: Integer): TsArgument; function fpsFALSE (Args: TsArgumentStack; NumArgs: Integer): TsArgument; function fpsIF (Args: TsArgumentStack; NumArgs: Integer): TsArgument; function fpsNOT (Args: TsArgumentStack; NumArgs: Integer): TsArgument; function fpsOR (Args: TsArgumentStack; NumArgs: Integer): TsArgument; function fpsTRUE (Args: TsArgumentStack; NumArgs: Integer): TsArgument; +{ String functions } +function fpsLOWER (Args: TsArgumentStack; NumArgs: Integer): TsArgument; +function fpsTRIM (Args: TsArgumentStack; NumArgs: Integer): TsArgument; +function fpsUPPER (Args: TsArgumentStack; NumArgs: Integer): TsArgument; implementation @@ -104,9 +117,9 @@ uses Math; type - TBoolArray = array of boolean; + TBoolArray = array of boolean; TFloatArray = array of double; - TStrArray = array of string; + TStrArray = array of string; { TsArgumentStack } @@ -194,39 +207,6 @@ begin end; -{ Missing arguments } - -{@@ - Replaces a missing boolean argument by the passed boolean value - @param Arg Argument to be considered - @param ABool Replacement for the missing value -} -procedure FixMissingBool(var Arg: TsArgument; ABool: Boolean); -begin - if Arg.IsMissing then Arg.BoolValue := ABool; -end; - -{@@ - Replaces a missing number argument by the passed number value - @param Arg Argument to be considered - @param ANumber Replacement for the missing value -} -procedure FixMissingNumber(var Arg: TsArgument; ANumber: Double); -begin - if Arg.IsMissing then Arg.NumberValue := ANumber; -end; - -{@@ - Replaces a missing string argument by the passed string value - @param Arg Argument to be considered - @param AString Replacement for the missing value -} -procedure FixMissingString(var Arg: TsArgument; AString: String); -begin - if Arg.IsMissing then Arg.StringValue := AString; -end; - - { Preparing arguments } function GetBoolFromArgument(Arg: TsArgument; var AValue: Boolean): TsErrorValue; @@ -265,29 +245,34 @@ function CreateBool(AValue: Boolean): TsArgument; begin Result.ArgumentType := atBool; Result.Boolvalue := AValue; + Result.IsMissing := false; end; function CreateNumber(AValue: Double): TsArgument; begin Result.ArgumentType := atNumber; Result.NumberValue := AValue; + Result.IsMissing := false; end; function CreateString(AValue: String): TsArgument; begin Result.ArgumentType := atString; Result.StringValue := AValue; + Result.IsMissing := false; end; function CreateError(AError: TsErrorValue): TsArgument; begin Result.ArgumentType := atError; Result.ErrorValue := AError; + Result.IsMissing := false; end; function CreateEmpty: TsArgument; begin Result.ArgumentType := atEmpty; + Result.IsMissing := false; end; {@@ @@ -298,29 +283,47 @@ end; @param AValues (output) Array containing the retrieved boolean values. The array length is given by NumArgs. The data in the array are in the same order in which they were pushed onto the stack. + Missing arguments are not included in the array, the case + of missing arguments must be handled separately if the are + important. @param AErrArg (output) Argument containing an error code, e.g. errWrongType if non-boolean data were met on the stack. @return TRUE if everything was ok, FALSE, if AErrArg reports an error. } function PopBoolValues(Args: TsArgumentStack; NumArgs: Integer; out AValues: TBoolArray; out AErrArg: TsArgument): Boolean; var + arg: TsArgument; err: TsErrorValue; - i: Integer; + counter, j: Integer; + b: Boolean; begin SetLength(AValues, NumArgs); - // Pop the data in reverse order they were pushed! Otherwise they will be - // applied to the function in the wrong order. - for i := NumArgs-1 downto 0 do begin - err := GetBoolFromArgument(Args.Pop, AValues[i]); - if err <> errOK then begin - Result := false; - AErrArg := CreateError(err); - SetLength(AValues, 0); - exit; + j := 0; + for counter := 1 to NumArgs do begin + arg := Args.Pop; + if not arg.IsMissing then begin + err := GetBoolFromArgument(arg, b); + if err = errOK then begin + AValues[j] := b; + inc(j); + end else begin + Result := false; + AErrArg := CreateError(err); + SetLength(AValues, 0); + exit; + end; end; end; Result := true; AErrArg := CreateError(errOK); + SetLength(AValues, j); + // Flip array - we want to have the arguments in the array in the same order + // they were pushed. + for j:=0 to Length(AValues) div 2 - 1 do begin + b := AValues[j]; + AValues[j] := AValues[High(AValues)-j]; + AValues[High(AValues)-j] := b; + end; end; {@@ @@ -331,28 +334,46 @@ end; @param AValues (output) Array containing the retrieved float values. The array length is given by NumArgs. The data in the array are in the same order in which they were pushed onto the stack. + Missing arguments are not included in the array, the case + of missing arguments must be handled separately if the are + important. @param AErrArg (output) Argument containing an error code, e.g. errWrongType if non-float data were met on the stack. @return TRUE if everything was ok, FALSE, if AErrArg reports an error. } function PopFloatValues(Args: TsArgumentStack; NumArgs: Integer; out AValues: TFloatArray; out AErrArg: TsArgument): Boolean; var + arg: TsArgument; err: TsErrorValue; - i: Integer; + counter, j: Integer; + val: double; begin SetLength(AValues, NumArgs); - // Pop the data in reverse order they were pushed! Otherwise they will be - // applied to the function in the wrong order. - for i := NumArgs-1 downto 0 do begin - err := GetNumberFromArgument(Args.Pop, AValues[i]); - if err <> errOK then begin - Result := false; - SetLength(AValues, 0); - AErrArg := CreateError(errWrongType); - exit; + j := 0; + for counter := 1 to NumArgs do begin + arg := Args.Pop; + if not arg.IsMissing then begin + err := GetNumberFromArgument(arg, val); + if err = errOK then begin + AValues[j] := val; + inc(j); + end else begin + Result := false; + SetLength(AValues, 0); + AErrArg := CreateError(errWrongType); + exit; + end; end; end; Result := true; + SetLength(AValues, j); + // Flip array - we want to have the arguments in the array in the same order + // they were pushed. + for j:=0 to Length(AValues) div 2 - 1 do begin + val := AValues[j]; + AValues[j] := AValues[High(AValues)-j]; + AValues[High(AValues)-j] := val; + end; AErrArg := CreateError(errOK); end; @@ -364,29 +385,47 @@ end; @param AValues (output) Array containing the retrieved strings. The array length is given by NumArgs. The data in the array are in the same order in which they were pushed onto the stack. + Missing arguments are not included in the array, the case + of missing arguments must be handled separately if the are + important. @param AErrArg (output) Argument containing an error code , e.g. errWrongType if non-string data were met on the stack. @return TRUE if everything was ok, FALSE, if AErrArg reports an error. } function PopStringValues(Args: TsArgumentStack; NumArgs: Integer; out AValues: TStrArray; out AErrArg: TsArgument): Boolean; var + arg: TsArgument; err: TsErrorValue; - i: Integer; + counter, j: Integer; + s: String; begin SetLength(AValues, NumArgs); - // Pop the data in reverse order they were pushed! Otherwise they will be - // applied to the function in the wrong order. - for i := NumArgs-1 downto 0 do begin - err := GetStringFromArgument(Args.Pop, AValues[i]); - if err <> errOK then begin - Result := false; - AErrArg := CreateError(errWrongType); - SetLength(AValues, 0); - exit; + j := 0; + for counter := 1 to NumArgs do begin + arg := Args.Pop; + if not arg.IsMissing then begin + err := GetStringFromArgument(arg, s); + if err = errOK then begin + AValues[j] := s; + inc(j); + end else begin + Result := false; + AErrArg := CreateError(errWrongType); + SetLength(AValues, 0); + exit; + end; end; end; - Result :=true; + Result := true; AErrArg := CreateError(errOK); + SetLength(AValues, j); + // Flip array - we want to have the arguments in the array in the same order + // they were pushed. + for j:=0 to Length(AValues) div 2 - 1 do begin + s := AValues[j]; + AValues[j] := AValues[High(AValues)-j]; + AValues[High(AValues)-j] := s; + end; end; @@ -826,6 +865,122 @@ begin end; +{ Statistical functions } + +function fpsAVEDEV(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +// Average value of absolute deviations of data from their mean. +var + data: TFloatArray; + m: Double; + i: Integer; +begin + if PopFloatValues(Args, NumArgs, data, Result) then begin + m := Mean(data); + for i:=0 to High(data) do + data[i] := abs(data[i] - m); + m := Mean(data); + Result := CreateNumber(m) + end; +end; + +function fpsAVERAGE(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +var + data: TFloatArray; +begin + if PopFloatValues(Args, NumArgs, data, Result) then + Result := CreateNumber(Mean(data)) +end; + +function fpsCOUNT(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +{ Count non-missing arguments } +var + n, i: Integer; +begin + n := 0; + for i:=1 to NumArgs do + if not Args.Pop.IsMissing then inc(n); + Result := CreateNumber(n); +end; + +function fpsMAX(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +var + data: TFloatArray; +begin + if PopFloatValues(Args, NumArgs, data, Result) then + Result := CreateNumber(MaxValue(data)) +end; + +function fpsMIN(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +var + data: TFloatArray; +begin + if PopFloatValues(Args, NumArgs, data, Result) then + Result := CreateNumber(MinValue(data)) +end; + +function fpsPRODUCT(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +var + data: TFloatArray; + i: Integer; + p: Double; +begin + if PopFloatValues(Args, NumArgs, data, Result) then begin + p := 1.0; + for i:=0 to High(data) do + p := p * data[i]; + Result := CreateNumber(p); + end; +end; + +function fpsSTDEV(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +var + data: TFloatArray; +begin + if PopFloatValues(Args, NumArgs, data, Result) then + Result := CreateNumber(StdDev(data)) +end; + +function fpsSTDEVP(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +var + data: TFloatArray; +begin + if PopFloatValues(Args, NumArgs, data, Result) then + Result := CreateNumber(PopnStdDev(data)) +end; + +function fpsSUM(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +var + data: TFloatArray; +begin + if PopFloatValues(Args, NumArgs, data, Result) then + Result := CreateNumber(Sum(data)) +end; + +function fpsSUMSQ(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +var + data: TFloatArray; +begin + if PopFloatValues(Args, NumArgs, data, Result) then + Result := CreateNumber(SumOfSquares(data)) +end; + +function fpsVAR(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +var + data: TFloatArray; +begin + if PopFloatValues(Args, NumArgs, data, Result) then + Result := CreateNumber(Variance(data)) +end; + +function fpsVARP(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +var + data: TFloatArray; +begin + if PopFloatValues(Args, NumArgs, data, Result) then + Result := CreateNumber(PopnVariance(data)) +end; + + { Logical functions } function fpsAND(Args: TsArgumentStack; NumArgs: Integer): TsArgument; @@ -901,4 +1056,31 @@ begin Result := CreateBool(true); end; + +{ String functions } + +function fpsLOWER(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +var + data: TStrArray; +begin + if PopStringValues(Args, NumArgs, data, Result) then + Result := CreateString(lowercase(data[0])); +end; + +function fpsTRIM(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +var + data: TStrArray; +begin + if PopStringValues(Args, NumArgs, data, Result) then + Result := CreateString(trim(data[0])); +end; + +function fpsUPPER(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +var + data: TStrArray; +begin + if PopStringValues(Args, NumArgs, data, Result) then + Result := CreateString(uppercase(data[0])); +end; + end. diff --git a/components/fpspreadsheet/fpspreadsheet.pas b/components/fpspreadsheet/fpspreadsheet.pas index c3903e3e1..e9049283a 100755 --- a/components/fpspreadsheet/fpspreadsheet.pas +++ b/components/fpspreadsheet/fpspreadsheet.pas @@ -1164,30 +1164,30 @@ const (Symbol:'WEEKDAY'; MinParams:1; MaxParams:2; Func:nil), // fekWEEKDAY (Symbol:'YEAR'; MinParams:1; MaxParams:1; Func:nil), // fekYEAR { statistical } - (Symbol:'AVEDEV'; MinParams:1; MaxParams:30; Func:nil), // fekAVEDEV - (Symbol:'AVERAGE'; MinParams:1; MaxParams:30; Func:nil), // fekAVERAGE + (Symbol:'AVEDEV'; MinParams:1; MaxParams:30; Func:fpsAVEDEV), // fekAVEDEV + (Symbol:'AVERAGE'; MinParams:1; MaxParams:30; Func:fpsAVERAGE), // fekAVERAGE (Symbol:'BETADIST'; MinParams:3; MaxParams:5; Func:nil), // fekBETADIST (Symbol:'BETAINV'; MinParams:3; MaxParams:5; Func:nil), // fekBETAINV (Symbol:'BINOMDIST'; MinParams:4; MaxParams:4; Func:nil), // fekBINOMDIST (Symbol:'CHIDIST'; MinParams:2; MaxParams:2; Func:nil), // fekCHIDIST (Symbol:'CHIINV'; MinParams:2; MaxParams:2; Func:nil), // fekCHIINV - (Symbol:'COUNT'; MinParams:0; MaxParams:30; Func:nil), // fekCOUNT + (Symbol:'COUNT'; MinParams:0; MaxParams:30; Func:fpsCOUNT), // fekCOUNT (Symbol:'COUNTA'; MinParams:0; MaxParams:30; Func:nil), // fekCOUNTA (Symbol:'COUNTBLANK';MinParams:1; MaxParams:1; Func:nil), // fekCOUNTBLANK (Symbol:'COUNTIF'; MinParams:2; MaxParams:2; Func:nil), // fekCOUNTIF - (Symbol:'MAX'; MinParams:1; MaxParams:30; Func:nil), // fekMAX + (Symbol:'MAX'; MinParams:1; MaxParams:30; Func:fpsMAX), // fekMAX (Symbol:'MEDIAN'; MinParams:1; MaxParams:30; Func:nil), // fekMEDIAN - (Symbol:'MIN'; MinParams:1; MaxParams:30; Func:nil), // fekMIN + (Symbol:'MIN'; MinParams:1; MaxParams:30; Func:fpsMIN), // fekMIN (Symbol:'PERMUT'; MinParams:2; MaxParams:2; Func:nil), // fekPERMUT (Symbol:'POISSON'; MinParams:3; MaxParams:3; Func:nil), // fekPOISSON - (Symbol:'PRODUCT'; MinParams:0; MaxParams:30; Func:nil), // fekPRODUCT - (Symbol:'STDEV'; MinParams:1; MaxParams:30; Func:nil), // fekSTDEV - (Symbol:'STDEVP'; MinParams:1; MaxParams:30; Func:nil), // fekSTDEVP - (Symbol:'SUM'; MinParams:0; MaxParams:30; Func:nil), // fekSUM - (Symbol:'SUMIF'; MinParams:2; MaxParams:3; Func:nil), // fekSUMIF - (Symbol:'SUMSQ'; MinParams:0; MaxParams:30; Func:nil), // fekSUMSQ - (Symbol:'VAR'; MinParams:1; MaxParams:30; Func:nil), // fekVAR - (Symbol:'VARP'; MinParams:1; MaxParams:30; Func:nil), // fekVARP + (Symbol:'PRODUCT'; MinParams:0; MaxParams:30; Func:fpsPRODUCT), // fekPRODUCT + (Symbol:'STDEV'; MinParams:1; MaxParams:30; Func:fpsSTDEV), // fekSTDEV + (Symbol:'STDEVP'; MinParams:1; MaxParams:30; Func:fpsSTDEVP), // fekSTDEVP + (Symbol:'SUM'; MinParams:0; MaxParams:30; Func:fpsSUM), // fekSUM + (Symbol:'SUMIF'; MinParams:2; MaxParams:3; Func:nil), // fekSUMIF + (Symbol:'SUMSQ'; MinParams:0; MaxParams:30; Func:fpsSUMSQ), // fekSUMSQ + (Symbol:'VAR'; MinParams:1; MaxParams:30; Func:fpsVAR), // fekVAR + (Symbol:'VARP'; MinParams:1; MaxParams:30; Func:fpsVARP), // fekVARP { financial } (Symbol:'FV'; MinParams:3; MaxParams:5; Func:nil), // fekFV (Symbol:'NPER'; MinParams:3; MaxParams:5; Func:nil), // fekNPER @@ -1205,14 +1205,14 @@ const (Symbol:'CHAR'; MinParams:1; MaxParams:1; Func:nil), // fekCHAR (Symbol:'CODE'; MinParams:1; MaxParams:1; Func:nil), // fekCODE (Symbol:'LEFT'; MinParams:1; MaxParams:2; Func:nil), // fekLEFT - (Symbol:'LOWER'; MinParams:1; MaxParams:1; Func:nil), // fekLOWER + (Symbol:'LOWER'; MinParams:1; MaxParams:1; Func:fpsLOWER), // fekLOWER (Symbol:'MID'; MinParams:3; MaxParams:3; Func:nil), // fekMID (Symbol:'PROPER'; MinParams:1; MaxParams:1; Func:nil), // fekPROPER (Symbol:'REPLACE'; MinParams:4; MaxParams:4; Func:nil), // fekREPLACE (Symbol:'RIGHT'; MinParams:1; MaxParams:2; Func:nil), // fekRIGHT (Symbol:'SUBSTITUTE';MinParams:3; MaxParams:4; Func:nil), // fekSUBSTITUTE - (Symbol:'TRIM'; MinParams:1; MaxParams:1; Func:nil), // fekTRIM - (Symbol:'UPPER'; MinParams:1; MaxParams:1; Func:nil), // fekUPPER + (Symbol:'TRIM'; MinParams:1; MaxParams:1; Func:fpsTRIM), // fekTRIM + (Symbol:'UPPER'; MinParams:1; MaxParams:1; Func:fpsUPPER), // fekUPPER { lookup/reference } (Symbol:'COLUMN'; MinParams:0; MaxParams:1; Func:nil), // fekCOLUMN (Symbol:'COLUMNS'; MinParams:1; MaxParams:1; Func:nil), // fekCOLUMNS diff --git a/components/fpspreadsheet/tests/testcases_calcrpnformula.inc b/components/fpspreadsheet/tests/testcases_calcrpnformula.inc index 6309cab6f..d3222d12f 100644 --- a/components/fpspreadsheet/tests/testcases_calcrpnformula.inc +++ b/components/fpspreadsheet/tests/testcases_calcrpnformula.inc @@ -680,6 +680,193 @@ SetLength(sollValues, Row+1); sollValues[Row] := CreateNumber(tanh(0.1)); +{------------------------------------------------------------------------------} +{ Statistical functions } +{------------------------------------------------------------------------------} + + // AVEDEV + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=AVEDEV(1, 1.1, 1.2, 0.9, 0.8)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(1.0, + RPNNumber(1.1, + RPNNumber(1.2, + RPNNumber(0.9, + RPNNumber(0.8, + RPNFunc(fekAVEDEV, 5, nil)))))))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumber(mean([0.0, 0.1, 0.2, 0.1, 0.2])); + // these values are the absolute deviations from mean (1.0) + // AVERAGE + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=AVERAGE(1, 1.1, 1.2, 0.9, 0.8)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(1.0, + RPNNumber(1.1, + RPNNumber(1.2, + RPNNumber(0.9, + RPNNumber(0.8, + RPNFunc(fekAVERAGE, 5, nil)))))))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumber(mean([1.0, 1.1, 1.2, 0.9, 0.8])); + + // COUNT (no missing values) + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=COUNT(1, 1.1, 1.2, 0.9, 0.8)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(1.0, + RPNNumber(1.1, + RPNNumber(1.2, + RPNNumber(0.9, + RPNNumber(0.8, + RPNFunc(fekCOUNT, 5, nil)))))))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumber(5); + + // COUNT (with missing values) + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=COUNT(1, , 1.2, , 0.8)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(1.0, + RPNMissingArg( + RPNNumber(1.2, + RPNMissingArg( + RPNNumber(0.8, + RPNFunc(fekCOUNT, 5, nil)))))))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumber(3); + + // MAX + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=MAX(1, 1.1, 1.2, 0.9, 0.8)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(1.0, + RPNNumber(1.1, + RPNNumber(1.2, + RPNNumber(0.9, + RPNNumber(0.8, + RPNFunc(fekMAX, 5, nil)))))))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumber(MaxValue([1.0, 1.1, 1.2, 0.9, 0.8])); + + // MAX (with missing values) + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=MAX(1, , , 0.9, 0.8)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(1.0, + RPNMissingArg( + RPNMissingArg( + RPNNumber(0.9, + RPNNumber(0.8, + RPNFunc(fekMAX, 5, nil)))))))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumber(MaxValue([1.0, {1.1, 1.2,} 0.9, 0.8])); + + // MIN + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=MIN(1, 1.1, 1.2, 0.9, 0.8)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(1.0, + RPNNumber(1.1, + RPNNumber(1.2, + RPNNumber(0.9, + RPNNumber(0.8, + RPNFunc(fekMIN, 5, nil)))))))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumber(MinValue([1.0, 1.1, 1.2, 0.9, 0.8])); + + // PRODUCT + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=PRODUCT(1, 1.1, 1.2, 0.9, 0.8)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(1.0, + RPNNumber(1.1, + RPNNumber(1.2, + RPNNumber(0.9, + RPNNumber(0.8, + RPNFunc(fekPRODUCT, 5, nil)))))))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumber(1.0*1.1*1.2*0.9*0.8); + + // STDEV + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=STDEV(1, 1.1, 1.2, 0.9, 0.8)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(1.0, + RPNNumber(1.1, + RPNNumber(1.2, + RPNNumber(0.9, + RPNNumber(0.8, + RPNFunc(fekSTDEV, 5, nil)))))))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumber(StdDev([1.0, 1.1, 1.2, 0.9, 0.8])); + + // STDEVP + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=STDEVP(1, 1.1, 1.2, 0.9, 0.8)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(1.0, + RPNNumber(1.1, + RPNNumber(1.2, + RPNNumber(0.9, + RPNNumber(0.8, + RPNFunc(fekSTDEVP, 5, nil)))))))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumber(PopnStdDev([1.0, 1.1, 1.2, 0.9, 0.8])); + + // SUM + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=SUM(1, 1.1, 1.2, 0.9, 0.8)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(1.0, + RPNNumber(1.1, + RPNNumber(1.2, + RPNNumber(0.9, + RPNNumber(0.8, + RPNFunc(fekSUM, 5, nil)))))))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumber(Sum([1.0, 1.1, 1.2, 0.9, 0.8])); + + // SUMSQ + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=SUMSQ(1, 1.1, 1.2, 0.9, 0.8)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(1.0, + RPNNumber(1.1, + RPNNumber(1.2, + RPNNumber(0.9, + RPNNumber(0.8, + RPNFunc(fekSUMSQ, 5, nil)))))))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumber(SumOfSquares([1.0, 1.1, 1.2, 0.9, 0.8])); + + // VAR + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=VAR(1, 1.1, 1.2, 0.9, 0.8)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(1.0, + RPNNumber(1.1, + RPNNumber(1.2, + RPNNumber(0.9, + RPNNumber(0.8, + RPNFunc(fekVAR, 5, nil)))))))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumber(Variance([1.0, 1.1, 1.2, 0.9, 0.8])); + + // VARP + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=VARP(1, 1.1, 1.2, 0.9, 0.8)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(1.0, + RPNNumber(1.1, + RPNNumber(1.2, + RPNNumber(0.9, + RPNNumber(0.8, + RPNFunc(fekVARP, 5, nil)))))))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumber(PopnVariance([1.0, 1.1, 1.2, 0.9, 0.8])); + + {------------------------------------------------------------------------------} { Logical functions } {------------------------------------------------------------------------------} @@ -854,3 +1041,34 @@ sollValues[Row] := CreateString('A'); +{------------------------------------------------------------------------------} +{ String functions } +{------------------------------------------------------------------------------} + + // Lower case + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=LOWER("Hallo word")'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNString('Hallo world', + RPNFunc(fekLOWER, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateString(LowerCase('Hallo world')); + + // Trim + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=TRIM(" Hallo word ")'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNString(' Hallo world ', + RPNFunc(fekTRIM, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateString(Trim(' Hallo world ')); + + // Upper case + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=UPPER("Hallo word")'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNString('Hallo world', + RPNFunc(fekUPPER, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateString(UpperCase('Hallo world')); +