From c2a5a2dd5db125e4ec34b319c273884a62108f39 Mon Sep 17 00:00:00 2001 From: wp_xxyyzz Date: Mon, 30 Jun 2014 13:21:04 +0000 Subject: [PATCH] fpspreadsheet: Implement fpn formula calculation of all math functions available in fps. Test cases passed. git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3255 8e941d3f-bd1b-0410-a28a-d453659cc2b4 --- components/fpspreadsheet/fpsmath.pas | 288 +++++++++++++- components/fpspreadsheet/fpspreadsheet.pas | 50 +-- .../fpspreadsheet/tests/formulatests.pas | 18 +- .../fpspreadsheet/tests/spreadtestgui.lpi | 6 + .../tests/testcases_calcrpnformula.inc | 364 ++++++++++++++++++ 5 files changed, 693 insertions(+), 33 deletions(-) diff --git a/components/fpspreadsheet/fpsmath.pas b/components/fpspreadsheet/fpsmath.pas index 1666c521f..193ec07ac 100644 --- a/components/fpspreadsheet/fpsmath.pas +++ b/components/fpspreadsheet/fpsmath.pas @@ -64,7 +64,33 @@ function fpsGreaterEqual(Args: TsArgumentStack; NumArgs: Integer): TsArgument; function fpsLess (Args: TsArgumentStack; NumArgs: Integer): TsArgument; function fpsLessEqual (Args: TsArgumentStack; NumArgs: Integer): TsArgument; function fpsNotEqual (Args: TsArgumentStack; NumArgs: Integer): TsArgument; - +{ Math } +function fpsABS (Args: TsArgumentStack; NumArgs: Integer): TsArgument; +function fpsACOS (Args: TsArgumentStack; NumArgs: Integer): TsArgument; +function fpsACOSH (Args: TsArgumentStack; NumArgs: Integer): TsArgument; +function fpsASIN (Args: TsArgumentStack; NumArgs: Integer): TsArgument; +function fpsASINH (Args: TsArgumentStack; NumArgs: Integer): TsArgument; +function fpsATAN (Args: TsArgumentStack; NumArgs: Integer): TsArgument; +function fpsATANH (Args: TsArgumentStack; NumArgs: Integer): TsArgument; +function fpsCOS (Args: TsArgumentStack; NumArgs: Integer): TsArgument; +function fpsCOSH (Args: TsArgumentStack; NumArgs: Integer): TsArgument; +function fpsDEGREES (Args: TsArgumentStack; NumArgs: Integer): TsArgument; +function fpsEXP (Args: TsArgumentStack; NumArgs: Integer): TsArgument; +function fpsINT (Args: TsArgumentStack; NumArgs: Integer): TsArgument; +function fpsLN (Args: TsArgumentStack; NumArgs: Integer): TsArgument; +function fpsLOG (Args: TsArgumentStack; NumArgs: Integer): TsArgument; +function fpsLOG10 (Args: TsArgumentStack; NumArgs: Integer): TsArgument; +function fpsPI (Args: TsArgumentStack; NumArgs: Integer): TsArgument; +function fpsRADIANS (Args: TsArgumentStack; NumArgs: Integer): TsArgument; +function fpsRAND (Args: TsArgumentStack; NumArgs: Integer): TsArgument; +function fpsROUND (Args: TsArgumentStack; NumArgs: Integer): TsArgument; +function fpsSIGN (Args: TsArgumentStack; NumArgs: Integer): TsArgument; +function fpsSIN (Args: TsArgumentStack; NumArgs: Integer): TsArgument; +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 } function fpsAND (Args: TsArgumentStack; NumArgs: Integer): TsArgument; function fpsFALSE (Args: TsArgumentStack; NumArgs: Integer): TsArgument; function fpsIF (Args: TsArgumentStack; NumArgs: Integer): TsArgument; @@ -527,7 +553,6 @@ begin Result := CreateBool(false); end; - function fpsNotEqual(Args: TsArgumentStack; NumArgs: Integer): TsArgument; var arg1, arg2: TsArgument; @@ -544,6 +569,265 @@ begin Result := CreateBool(false); end; + +{ Math functions } + +function fpsABS(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +var + data: TFloatArray; +begin + if PopFloatValues(Args, 1, data, Result) then + Result := CreateNumber(abs(data[0])); +end; + +function fpsACOS(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +var + data: TFloatArray; +begin + if PopFloatValues(Args, 1, data, Result) then begin + if InRange(data[0], -1, +1) then + Result := CreateNumber(arccos(data[0])) + else + Result := CreateError(errOverflow); // #NUM! + end; +end; + +function fpsACOSH(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +var + data: TFloatArray; +begin + if PopFloatValues(Args, 1, data, Result) then begin + if data[0] >= 1 then + Result := CreateNumber(arccosh(data[0])) + else + Result := CreateError(errOverflow); // #NUM! + end; +end; + +function fpsASIN(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +var + data: TFloatArray; +begin + if PopFloatValues(Args, 1, data, Result) then begin + if InRange(data[0], -1, +1) then + Result := CreateNumber(arcsin(data[0])) + else + Result := CreateError(errOverflow); // #NUM! + end; +end; + +function fpsASINH(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +var + data: TFloatArray; +begin + if PopFloatValues(Args, 1, data, Result) then + Result := CreateNumber(arcsinh(data[0])); +end; + +function fpsATAN(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +var + data: TFloatArray; +begin + if PopFloatValues(Args, 1, data, Result) then + Result := CreateNumber(arctan(data[0])); +end; + +function fpsATANH(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +var + data: TFloatArray; +begin + if PopFloatValues(Args, 1, data, Result) then begin + if (data[0] > -1) and (data[0] < +1) then + Result := CreateNumber(arctanh(data[0])) + else + Result := CreateError(errOverflow); // #NUM! + end; +end; + +function fpsCOS(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +var + data: TFloatArray; +begin + if PopFloatValues(Args, 1, data, Result) then + Result := CreateNumber(cos(data[0])); +end; + +function fpsCOSH(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +var + data: TFloatArray; +begin + if PopFloatValues(Args, 1, data, Result) then + Result := CreateNumber(cosh(data[0])); +end; + +function fpsDEGREES(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +var + data: TFloatArray; +begin + if PopFloatValues(Args, 1, data, Result) then + Result := CreateNumber(RadToDeg(data[0])); +end; + +function fpsEXP(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +var + data: TFloatArray; +begin + if PopFloatValues(Args, 1, data, Result) then + Result := CreateNumber(exp(data[0])); +end; + +function fpsINT(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +var + data: TFloatArray; +begin + if PopFloatValues(Args, 1, data, Result) then + Result := CreateNumber(floor(data[0])); +end; + +function fpsLN(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +var + data: TFloatArray; +begin + if PopFloatValues(Args, 1, data, Result) then begin + if (data[0] > 0) then + Result := CreateNumber(ln(data[0])) + else + Result := CreateError(errOverflow); // #NUM! + end; +end; + +function fpsLOG(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +var + arg_base, arg_number: TsArgument; + data: TFloatArray; + base: Double; +begin + base := 10; + if NumArgs = 2 then begin + arg_base := Args.Pop; + if not arg_base.IsMissing then begin + if arg_base.ArgumentType <> atNumber then begin + Result := CreateError(errWrongType); + exit; + end; + base := arg_base.NumberValue; + end; + end; + + if base < 0 then begin + Result := CreateError(errOverflow); + exit; + end; + + arg_number := Args.Pop; + if arg_number.ArgumentType <> atNumber then begin + Result := CreateError(errWrongType); + exit; + end; + + if arg_number.NumberValue > 0 then + Result := CreateNumber(logn(base, arg_number.NumberValue)) + else + Result := CreateError(errOverflow); +end; + +function fpsLOG10(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +var + data: TFloatArray; +begin + if PopFloatValues(Args, 1, data, Result) then begin + if (data[0] > 0) then + Result := CreateNumber(log10(data[0])) + else + Result := CreateError(errOverflow); // #NUM! + end; +end; + +function fpsPI(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +begin + Result := CreateNumber(pi); +end; + +function fpsRADIANS(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +var + data: TFloatArray; +begin + if PopFloatValues(Args, 1, data, Result) then + Result := CreateNumber(degtorad(data[0])) +end; + +function fpsRAND(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +begin + Result := CreateNumber(random); +end; + +function fpsROUND(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +var + data: TFloatArray; +begin + if PopFloatValues(Args, 2, data, Result) then + Result := CreateNumber(RoundTo(data[0], round(data[1]))) +end; + +function fpsSIGN(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +var + data: TFloatArray; +begin + if PopFloatValues(Args, 1, data, Result) then + Result := CreateNumber(sign(data[0])) +end; + +function fpsSIN(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +var + data: TFloatArray; +begin + if PopFloatValues(Args, 1, data, Result) then + Result := CreateNumber(sin(data[0])) +end; + +function fpsSINH(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +var + data: TFloatArray; +begin + if PopFloatValues(Args, 1, data, Result) then + Result := CreateNumber(sinh(data[0])) +end; + +function fpsSQRT(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +var + data: TFloatArray; +begin + if PopFloatValues(Args, 1, data, Result) then begin + if data[0] >= 0.0 then + Result := CreateNumber(sqrt(data[0])) + else + Result := CreateError(errOverflow); + end; +end; + +function fpsTAN(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +var + data: TFloatArray; +begin + if PopFloatValues(Args, 1, data, Result) then begin + if frac(data[0] / (pi*0.5)) = 0 then + Result := CreateError(errOverflow) + else + Result := CreateNumber(tan(data[0])) + end; +end; + +function fpsTANH(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +var + data: TFloatArray; +begin + if PopFloatValues(Args, 1, data, Result) then + Result := CreateNumber(tanh(data[0])) +end; + + +{ Logical functions } + function fpsAND(Args: TsArgumentStack; NumArgs: Integer): TsArgument; var data: TBoolArray; diff --git a/components/fpspreadsheet/fpspreadsheet.pas b/components/fpspreadsheet/fpspreadsheet.pas index 2f705d719..c3903e3e1 100755 --- a/components/fpspreadsheet/fpspreadsheet.pas +++ b/components/fpspreadsheet/fpspreadsheet.pas @@ -1123,31 +1123,31 @@ const (Symbol:'<>'; MinParams:2; MaxParams:2; Func:fpsNotEqual), // fekNotEqual (Symbol:''; MinParams:1; MaxParams:1; Func:nil), // fekParen { math } - (Symbol:'ABS'; MinParams:1; MaxParams:1; Func:nil), // fekABS - (Symbol:'ACOS'; MinParams:1; MaxParams:1; Func:nil), // fekACOS - (Symbol:'ACOSH'; MinParams:1; MaxParams:1; Func:nil), // fekACOSH - (Symbol:'ASIN'; MinParams:1; MaxParams:1; Func:nil), // fekASIN - (Symbol:'ASINH'; MinParams:1; MaxParams:1; Func:nil), // fekASINH - (Symbol:'ATAN'; MinParams:1; MaxParams:1; Func:nil), // fekATAN - (Symbol:'ATANH'; MinParams:1; MaxParams:1; Func:nil), // fekATANH, - (Symbol:'COS'; MinParams:1; MaxParams:1; Func:nil), // fekCOS - (Symbol:'COSH'; MinParams:1; MaxParams:1; Func:nil), // fekCOSH - (Symbol:'DEGREES'; MinParams:1; MaxParams:1; Func:nil), // fekDEGREES - (Symbol:'EXP'; MinParams:1; MaxParams:1; Func:nil), // fekEXP - (Symbol:'INT'; MinParams:1; MaxParams:1; Func:nil), // fekINT - (Symbol:'LN'; MinParams:1; MaxParams:1; Func:nil), // fekLN - (Symbol:'LOG'; MinParams:1; MaxParams:2; Func:nil), // fekLOG, - (Symbol:'LOG10'; MinParams:1; MaxParams:1; Func:nil), // fekLOG10 - (Symbol:'PI'; MinParams:0; MaxParams:0; Func:nil), // fekPI - (Symbol:'RADIANS'; MinParams:1; MaxParams:1; Func:nil), // fekRADIANS - (Symbol:'RAND'; MinParams:0; MaxParams:0; Func:nil), // fekRAND - (Symbol:'ROUND'; MinParams:2; MaxParams:2; Func:nil), // fekROUND, - (Symbol:'SIGN'; MinParams:1; MaxParams:1; Func:nil), // fekSIGN - (Symbol:'SIN'; MinParams:1; MaxParams:1; Func:nil), // fekSIN - (Symbol:'SINH'; MinParams:1; MaxParams:1; Func:nil), // fekSINH - (Symbol:'SQRT'; MinParams:1; MaxParams:1; Func:nil), // fekSQRT, - (Symbol:'TAN'; MinParams:1; MaxParams:1; Func:nil), // fekTAN - (Symbol:'TANH'; MinParams:1; MaxParams:1; Func:nil), // fekTANH, + (Symbol:'ABS'; MinParams:1; MaxParams:1; Func:fpsABS), // fekABS + (Symbol:'ACOS'; MinParams:1; MaxParams:1; Func:fpsACOS), // fekACOS + (Symbol:'ACOSH'; MinParams:1; MaxParams:1; Func:fpsACOSH), // fekACOSH + (Symbol:'ASIN'; MinParams:1; MaxParams:1; Func:fpsASIN), // fekASIN + (Symbol:'ASINH'; MinParams:1; MaxParams:1; Func:fpsASINH), // fekASINH + (Symbol:'ATAN'; MinParams:1; MaxParams:1; Func:fpsATAN), // fekATAN + (Symbol:'ATANH'; MinParams:1; MaxParams:1; Func:fpsATANH), // fekATANH, + (Symbol:'COS'; MinParams:1; MaxParams:1; Func:fpsCOS), // fekCOS + (Symbol:'COSH'; MinParams:1; MaxParams:1; Func:fpsCOSH), // fekCOSH + (Symbol:'DEGREES'; MinParams:1; MaxParams:1; Func:fpsDEGREES), // fekDEGREES + (Symbol:'EXP'; MinParams:1; MaxParams:1; Func:fpsEXP), // fekEXP + (Symbol:'INT'; MinParams:1; MaxParams:1; Func:fpsINT), // fekINT + (Symbol:'LN'; MinParams:1; MaxParams:1; Func:fpsLN), // fekLN + (Symbol:'LOG'; MinParams:1; MaxParams:2; Func:fpsLOG), // fekLOG, + (Symbol:'LOG10'; MinParams:1; MaxParams:1; Func:fpsLOG10), // fekLOG10 + (Symbol:'PI'; MinParams:0; MaxParams:0; Func:fpsPI), // fekPI + (Symbol:'RADIANS'; MinParams:1; MaxParams:1; Func:fpsRADIANS), // fekRADIANS + (Symbol:'RAND'; MinParams:0; MaxParams:0; Func:fpsRAND), // fekRAND + (Symbol:'ROUND'; MinParams:2; MaxParams:2; Func:fpsROUND), // fekROUND, + (Symbol:'SIGN'; MinParams:1; MaxParams:1; Func:fpsSIGN), // fekSIGN + (Symbol:'SIN'; MinParams:1; MaxParams:1; Func:fpsSIN), // fekSIN + (Symbol:'SINH'; MinParams:1; MaxParams:1; Func:fpsSINH), // fekSINH + (Symbol:'SQRT'; MinParams:1; MaxParams:1; Func:fpsSQRT), // fekSQRT, + (Symbol:'TAN'; MinParams:1; MaxParams:1; Func:fpsTAN), // fekTAN + (Symbol:'TANH'; MinParams:1; MaxParams:1; Func:fpsTANH), // fekTANH, { date/time } (Symbol:'DATE'; MinParams:3; MaxParams:3; Func:nil), // fekDATE (Symbol:'DATEDIF'; MinParams:3; MaxParams:3; Func:nil), // fekDATEDIF diff --git a/components/fpspreadsheet/tests/formulatests.pas b/components/fpspreadsheet/tests/formulatests.pas index 0859418bc..c2a6893a5 100644 --- a/components/fpspreadsheet/tests/formulatests.pas +++ b/components/fpspreadsheet/tests/formulatests.pas @@ -142,7 +142,7 @@ var expected: TsArgument; cell: PCell; sollValues: array of TsArgument; - a, b: Double; + formula: String; begin TempFile := GetTempFileName; @@ -174,6 +174,7 @@ begin fail('Error in test code. Failed to get named worksheet'); for Row := 0 to MyWorksheet.GetLastRowIndex do begin + formula := MyWorksheet.ReadAsUTF8Text(Row, 0); cell := MyWorksheet.FindCell(Row, 1); if (cell = nil) then fail('Error in test code: Failed to get cell ' + CellNotation(MyWorksheet, Row, 1)); @@ -186,22 +187,27 @@ begin end; expected := SollValues[row]; CheckEquals(ord(expected.ArgumentType), ord(actual.ArgumentType), - 'Test read calculated formula data type mismatch, cell '+CellNotation(MyWorkSheet,Row,1)); + 'Test read calculated formula data type mismatch, formula "' + formula + + '", cell '+CellNotation(MyWorkSheet,Row,1)); case actual.ArgumentType of atBool: CheckEquals(BoolToStr(expected.BoolValue), BoolToStr(actual.BoolValue), - 'Test read calculated formula result mismatch, cell '+CellNotation(MyWorkSheet,Row,1)); + 'Test read calculated formula result mismatch, formula "' + formula + + '", cell '+CellNotation(MyWorkSheet,Row,1)); atNumber: CheckEquals(expected.NumberValue, actual.NumberValue, - 'Test read calculated formula result mismatch, cell '+CellNotation(MyWorkSheet,Row,1)); + 'Test read calculated formula result mismatch, formula "' + formula + + '", cell '+CellNotation(MyWorkSheet,Row,1)); atString: CheckEquals(expected.StringValue, actual.StringValue, - 'Test read calculated formula result mismatch, cell '+CellNotation(MyWorkSheet,Row,1)); + 'Test read calculated formula result mismatch, formula "' + formula + + '", cell '+CellNotation(MyWorkSheet,Row,1)); atError: CheckEquals( GetEnumName(TypeInfo(TsErrorValue), ord(expected.ErrorValue)), GetEnumname(TypeInfo(TsErrorValue), ord(actual.ErrorValue)), - 'Test read calculated formula error value mismatch, cell '+CellNotation(MyWorkSheet,Row,1)); + 'Test read calculated formula error value mismatch, formula ' + formula + + ', cell '+CellNotation(MyWorkSheet,Row,1)); end; end; diff --git a/components/fpspreadsheet/tests/spreadtestgui.lpi b/components/fpspreadsheet/tests/spreadtestgui.lpi index ad3f18290..f9bb72a93 100644 --- a/components/fpspreadsheet/tests/spreadtestgui.lpi +++ b/components/fpspreadsheet/tests/spreadtestgui.lpi @@ -47,6 +47,9 @@ + + + @@ -156,6 +159,9 @@ + + + diff --git a/components/fpspreadsheet/tests/testcases_calcrpnformula.inc b/components/fpspreadsheet/tests/testcases_calcrpnformula.inc index d5690456c..6309cab6f 100644 --- a/components/fpspreadsheet/tests/testcases_calcrpnformula.inc +++ b/components/fpspreadsheet/tests/testcases_calcrpnformula.inc @@ -321,6 +321,369 @@ SetLength(sollValues, Row+1); sollValues[Row] := CreateBool(1<>1); + + +{------------------------------------------------------------------------------} +{ Math } +{------------------------------------------------------------------------------} + + // ABS + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=abs(-1)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(-1, + RPNFunc(fekABS, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumber(abs(-1)); + + // ARCCOS - valid result + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=acos(0.1)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(0.1, + RPNFunc(fekACOS, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumber(arccos(0.1)); + + // ACOS - error result (arccos is not defined outside the interval [-1..1] + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=acos(-2)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(-2, + RPNFunc(fekACOS, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateError(errOverFlow); + + // ARCCOSH - valid result + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=acosh(1.1)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(1.1, + RPNFunc(fekACOSH, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumber(arccosh(1.1)); + + // ACOSH - error result (arccos is not defined below 1 + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=acosh(0.9)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(0.9, + RPNFunc(fekACOSH, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateError(errOverFlow); + + // ASIN + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=asin(0.1)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(0.1, + RPNFunc(fekASIN, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumber(arcsin(0.1)); + + // ASIN - error result (arcsin is not defined outside the interval [-1..1] + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=asin(-2)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(-2, + RPNFunc(fekASIN, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateError(errOverFlow); + + // ARCSINH + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=asinh(1.1)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(1.1, + RPNFunc(fekASINH, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumber(arcsinh(1.1)); + + // ATAN + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=atan(0.1)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(0.1, + RPNFunc(fekATAN, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumber(arctan(0.1)); + + // ATANH - valid result + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=atanh(0.1)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(0.1, + RPNFunc(fekATANH, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumber(arctanh(0.1)); + + // ATANH - error result (arctan is only defined within ]-1,1[ + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=atanh(1.0)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(1.0, + RPNFunc(fekATANH, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateError(errOverFlow); + + // COS + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=cos(0.1)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(0.1, + RPNFunc(fekCOS, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumber(cos(0.1)); + + // COSH + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=cosh(0.1)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(0.1, + RPNFunc(fekCOSH, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumber(cosh(0.1)); + + // DEGREES + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=degrees(1.0)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(1.0, + RPNFunc(fekDEGREES, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumber(radToDeg(1.0)); + + // EXP + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=exp(0.1)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(0.1, + RPNFunc(fekEXP, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumber(exp(0.1)); + + // INT (positive argument) + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=int(0.1)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(0.1, + RPNFunc(fekINT, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumber(floor(0.1)); + + // INT (negative argument) + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=int(-0.1)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(-0.1, + RPNFunc(fekINT, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumber(floor(-0.1)); + + // LN - valid result + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=ln(0.1)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(0.1, + RPNFunc(fekLN, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumber(ln(0.1)); + + // LN - error due to argument = 0 + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=ln(0.0)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(0.0, + RPNFunc(fekLN, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateError(errOverflow); + + // LN - error due to argument < 0 + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=ln(-0.1)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(-0.1, + RPNFunc(fekLN, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateError(errOverflow); + + // LOG10 - valid result + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=log10(0.1)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(0.1, + RPNFunc(fekLOG10, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumber(log10(0.1)); + + // LOG10 - error due to argument = 0 + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=log10(0.0)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(0.0, + RPNFunc(fekLOG10, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateError(errOverflow); + + // LOG10 - error due to argument < 0 + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=log10(-0.1)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(-0.1, + RPNFunc(fekLOG10, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateError(errOverflow); + + // LOG - valid result (2 arguments) + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=log(0.1, 2)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(0.1, + RPNNumber(2, + RPNFunc(fekLOG, 2, nil))))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumber(logn(2, 0.1)); + + // LOG - valid result (2 arguments, base missing) + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=log(0.1, )'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(0.1, + RPNMissingArg( + RPNFunc(fekLOG, 2, nil))))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumber(logn(10, 0.1)); + + // LOG - valid result (1 argument) + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=log(0.1)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(0.1, + RPNFunc(fekLOG, 1, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumber(logn(10, 0.1)); + + // LOG - negative base + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=log(0.1, -2)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(0.1, + RPNNumber(-2, + RPNFunc(fekLOG, 2, nil))))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateError(errOverflow); + + // LOG - negative value + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=log(-0.1, 2)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(-0.1, + RPNNumber(2.0, + RPNFunc(fekLOG, 2, nil))))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateError(errOverflow); + + // PI + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=pi()'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNFunc(fekPI, nil))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumber(pi); + + // RADIANS + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=radians(60)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(60, + RPNFunc(fekRADIANS, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumber(degtorad(60)); + + // RAND + // Test omitted because we cannot enforce getting the same random number back + // when we are reading the file. + + // ROUND + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=ROUND(pi(), 2)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNFunc(fekPI, + RPNNumber(2, + RPNFunc(fekROUND, nil))))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumber(RoundTo(pi, 2)); + + // SIGN + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=sign(-0.1)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(-0.1, + RPNFunc(fekSIGN, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumber(sign(-0.1)); + + // SIN + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=sin(0.1)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(0.1, + RPNFunc(fekSIN, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumber(sin(0.1)); + + // SINH + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=sinh(0.1)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(0.1, + RPNFunc(fekSINH, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumber(sinh(0.1)); + + // SQRT - valid result + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=sqrt(0.1)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(0.1, + RPNFunc(fekSQRT, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumber(sqrt(0.1)); + + // SQRT - error (negative argument) + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=sqrt(-0.1)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(-0.1, + RPNFunc(fekSQRT, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateError(errOverflow); + + // TAN - valid result + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=tan(0.1)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(0.1, + RPNFunc(fekTAN, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumber(tan(0.1)); + + // TAN - error (argument = pi/2) + // This test is omitted because it is difficult to exactly hit pi/2. + + // TANH + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=tanh(0.1)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(0.1, + RPNFunc(fekTANH, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumber(tanh(0.1)); + +{------------------------------------------------------------------------------} +{ Logical functions } +{------------------------------------------------------------------------------} + // AND of one values (bool) inc(Row); MyWorksheet.WriteUTF8Text(Row, 0, '=AND(true)'); @@ -490,3 +853,4 @@ SetLength(sollValues, Row+1); sollValues[Row] := CreateString('A'); +