diff options
author | Patrick Meredith <pmeredit@gmail.com> | 2018-10-12 16:59:29 -0400 |
---|---|---|
committer | Charlie Swanson <charlie.swanson@mongodb.com> | 2018-12-22 21:20:56 -0500 |
commit | 43cd5d0e35df926eeaacec3f8d676bb5f6dd287b (patch) | |
tree | 42040670b45de7a3dcde2bf8c59f1f89cd4d01a3 | |
parent | 8195b17e78a2f0617289e1d8f3b318e1f2c8f077 (diff) | |
download | mongo-43cd5d0e35df926eeaacec3f8d676bb5f6dd287b.tar.gz |
SERVER-32930 Add trigonometric expressions to aggregation
Closes #1287
Signed-off-by: Charlie Swanson <charlie.swanson@mongodb.com>
-rw-r--r-- | jstests/aggregation/expressions/expression_trigonometric.js | 254 | ||||
-rw-r--r-- | src/mongo/db/pipeline/SConscript | 2 | ||||
-rw-r--r-- | src/mongo/db/pipeline/expression.h | 46 | ||||
-rw-r--r-- | src/mongo/db/pipeline/expression_trigonometric.cpp | 220 | ||||
-rw-r--r-- | src/mongo/db/pipeline/expression_trigonometric.h | 237 | ||||
-rw-r--r-- | src/mongo/db/pipeline/expression_trigonometric_test.cpp | 1406 | ||||
-rw-r--r-- | src/mongo/platform/decimal128.cpp | 152 | ||||
-rw-r--r-- | src/mongo/platform/decimal128.h | 88 | ||||
-rw-r--r-- | src/mongo/platform/decimal128_test.cpp | 263 |
9 files changed, 2666 insertions, 2 deletions
diff --git a/jstests/aggregation/expressions/expression_trigonometric.js b/jstests/aggregation/expressions/expression_trigonometric.js new file mode 100644 index 00000000000..192e9743b62 --- /dev/null +++ b/jstests/aggregation/expressions/expression_trigonometric.js @@ -0,0 +1,254 @@ +// SERVER-32930: Basic integration tests for trigonometric aggregation expressions. + +(function() { + "use strict"; + // For assertErrorCode. + load("jstests/aggregation/extras/utils.js"); + + const coll = db.expression_trigonometric; + coll.drop(); + // We need at least one document in the collection in order to test expressions, add it here. + assert.commandWorked(coll.insert({})); + + // Helper for testing that op returns expResult. + function testOp(op, expResult) { + const pipeline = [{$project: {_id: 0, result: op}}]; + assert.eq(coll.aggregate(pipeline).toArray(), [{result: expResult}]); + } + + // Helper for testing that the aggregation expression 'op' returns expResult, approximately, + // since NumberDecimal has so many representations for a given number (0 versus 0e-40 for + // instance). + function testOpApprox(op, expResult) { + const pipeline = [{$project: {_id: 0, result: {$abs: {$subtract: [op, expResult]}}}}]; + assert.lt(coll.aggregate(pipeline).toArray(), [{result: NumberDecimal("0.00000005")}]); + } + + // Simple successful int input. + testOp({$acos: NumberInt(1)}, 0); + testOp({$acosh: NumberInt(1)}, 0); + testOp({$asin: NumberInt(0)}, 0); + testOp({$asinh: NumberInt(0)}, 0); + testOp({$atan: NumberInt(0)}, 0); + testOp({$atan2: [NumberInt(0), NumberInt(1)]}, 0); + testOp({$atan2: [NumberInt(0), NumberInt(0)]}, 0); + testOp({$atanh: NumberInt(0)}, 0); + testOp({$cos: NumberInt(0)}, 1); + testOp({$cosh: NumberInt(0)}, 1); + testOp({$sin: NumberInt(0)}, 0); + testOp({$sinh: NumberInt(0)}, 0); + testOp({$tan: NumberInt(0)}, 0); + testOp({$tanh: NumberInt(0)}, 0); + testOp({$degreesToRadians: NumberInt(0)}, 0); + testOp({$radiansToDegrees: NumberInt(0)}, 0); + + // Simple successful long input. + testOp({$acos: NumberLong(1)}, 0); + testOp({$acosh: NumberLong(1)}, 0); + testOp({$asin: NumberLong(0)}, 0); + testOp({$asinh: NumberLong(0)}, 0); + testOp({$atan: NumberLong(0)}, 0); + testOp({$atan2: [NumberLong(0), NumberLong(1)]}, 0); + testOp({$atan2: [NumberLong(0), NumberLong(0)]}, 0); + testOp({$atanh: NumberLong(0)}, 0); + testOp({$cos: NumberLong(0)}, 1); + testOp({$cosh: NumberLong(0)}, 1); + testOp({$sin: NumberLong(0)}, 0); + testOp({$sinh: NumberLong(0)}, 0); + testOp({$tan: NumberLong(0)}, 0); + testOp({$tanh: NumberLong(0)}, 0); + testOp({$degreesToRadians: NumberLong(0)}, 0); + testOp({$radiansToDegrees: NumberLong(0)}, 0); + + // Simple successful double input. + testOp({$acos: 1}, 0); + testOp({$acosh: 1}, 0); + testOp({$asin: 0}, 0); + testOp({$asinh: 0}, 0); + testOp({$atan: 0}, 0); + testOp({$atan2: [0, 1]}, 0); + testOp({$atan2: [0, 0]}, 0); + testOp({$atanh: 0}, 0); + testOp({$cos: 0}, 1); + testOp({$cosh: 0}, 1); + testOp({$sin: 0}, 0); + testOp({$sinh: 0}, 0); + testOp({$tan: 0}, 0); + testOp({$tanh: 0}, 0); + testOp({$degreesToRadians: 0}, 0); + testOp({$radiansToDegrees: 0}, 0); + + // Simple successful decimal input. + testOpApprox({$acos: NumberDecimal(1)}, NumberDecimal(0)); + testOpApprox({$acosh: NumberDecimal(1)}, NumberDecimal(0)); + testOpApprox({$asin: NumberDecimal(0)}, NumberDecimal(0)); + testOpApprox({$asinh: NumberDecimal(0)}, NumberDecimal(0)); + testOpApprox({$atan: NumberDecimal(0)}, NumberDecimal(0)); + testOpApprox({$atan2: [NumberDecimal(0), 1]}, NumberDecimal(0)); + testOpApprox({$atan2: [NumberDecimal(0), 0]}, NumberDecimal(0)); + testOpApprox({$atanh: NumberDecimal(0)}, NumberDecimal(0)); + testOpApprox({$cos: NumberDecimal(0)}, NumberDecimal(1)); + testOpApprox({$cosh: NumberDecimal(0)}, NumberDecimal(1)); + testOpApprox({$sin: NumberDecimal(0)}, NumberDecimal(0)); + testOpApprox({$sinh: NumberDecimal(0)}, NumberDecimal(0)); + testOpApprox({$tan: NumberDecimal(0)}, NumberDecimal(0)); + testOpApprox({$tanh: NumberDecimal(0)}, NumberDecimal(0)); + testOpApprox({$degreesToRadians: NumberDecimal(0)}, NumberDecimal(0)); + testOpApprox({$radiansToDegrees: NumberDecimal(0)}, NumberDecimal(0)); + + // Infinity input produces out of bounds error. + assertErrorCode(coll, [{$project: {a: {$acos: -Infinity}}}], 50989); + assertErrorCode(coll, [{$project: {a: {$acos: NumberDecimal('-Infinity')}}}], 50989); + assertErrorCode(coll, [{$project: {a: {$acos: Infinity}}}], 50989); + assertErrorCode(coll, [{$project: {a: {$acos: NumberDecimal('Infinity')}}}], 50989); + + assertErrorCode(coll, [{$project: {a: {$acosh: -Infinity}}}], 50989); + assertErrorCode(coll, [{$project: {a: {$acosh: NumberDecimal('-Infinity')}}}], 50989); + + assertErrorCode(coll, [{$project: {a: {$asin: -Infinity}}}], 50989); + assertErrorCode(coll, [{$project: {a: {$asin: NumberDecimal('-Infinity')}}}], 50989); + assertErrorCode(coll, [{$project: {a: {$asin: Infinity}}}], 50989); + assertErrorCode(coll, [{$project: {a: {$asin: NumberDecimal('Infinity')}}}], 50989); + + assertErrorCode(coll, [{$project: {a: {$atanh: -Infinity}}}], 50989); + assertErrorCode(coll, [{$project: {a: {$atanh: NumberDecimal('-Infinity')}}}], 50989); + assertErrorCode(coll, [{$project: {a: {$atanh: Infinity}}}], 50989); + assertErrorCode(coll, [{$project: {a: {$atanh: NumberDecimal('Infinity')}}}], 50989); + + assertErrorCode(coll, [{$project: {a: {$cos: -Infinity}}}], 50989); + assertErrorCode(coll, [{$project: {a: {$cos: NumberDecimal('-Infinity')}}}], 50989); + assertErrorCode(coll, [{$project: {a: {$cos: Infinity}}}], 50989); + assertErrorCode(coll, [{$project: {a: {$cos: NumberDecimal('Infinity')}}}], 50989); + + assertErrorCode(coll, [{$project: {a: {$sin: -Infinity}}}], 50989); + assertErrorCode(coll, [{$project: {a: {$sin: NumberDecimal('-Infinity')}}}], 50989); + assertErrorCode(coll, [{$project: {a: {$sin: Infinity}}}], 50989); + assertErrorCode(coll, [{$project: {a: {$sin: NumberDecimal('Infinity')}}}], 50989); + + assertErrorCode(coll, [{$project: {a: {$tan: -Infinity}}}], 50989); + assertErrorCode(coll, [{$project: {a: {$tan: NumberDecimal('-Infinity')}}}], 50989); + assertErrorCode(coll, [{$project: {a: {$tan: Infinity}}}], 50989); + assertErrorCode(coll, [{$project: {a: {$tan: NumberDecimal('Infinity')}}}], 50989); + + // Infinity input produces Infinity as output. + testOp({$acosh: NumberDecimal('Infinity')}, NumberDecimal('Infinity')); + testOp({$acosh: Infinity}, Infinity); + + testOp({$asinh: NumberDecimal('Infinity')}, NumberDecimal('Infinity')); + testOp({$asinh: NumberDecimal('-Infinity')}, NumberDecimal('-Infinity')); + testOp({$asinh: Infinity}, Infinity); + testOp({$asinh: -Infinity}, -Infinity); + testOp({$cosh: NumberDecimal('Infinity')}, NumberDecimal('Infinity')); + testOp({$cosh: NumberDecimal('-Infinity')}, NumberDecimal('Infinity')); + testOp({$cosh: Infinity}, Infinity); + testOp({$cosh: -Infinity}, Infinity); + testOp({$sinh: NumberDecimal('Infinity')}, NumberDecimal('Infinity')); + testOp({$sinh: NumberDecimal('-Infinity')}, NumberDecimal('-Infinity')); + testOp({$sinh: Infinity}, Infinity); + testOp({$sinh: -Infinity}, -Infinity); + + // Infinity produces finite output (due to asymptotic bounds). + testOpApprox({$atan: NumberDecimal('Infinity')}, NumberDecimal(Math.PI / 2)); + testOpApprox({$atan: NumberDecimal('-Infinity')}, NumberDecimal(Math.Pi / 2)); + testOpApprox({$atan: Infinity}, Math.PI / 2); + testOpApprox({$atan: -Infinity}, -Math.PI / 2); + + testOpApprox({$atan2: [NumberDecimal('Infinity'), 0]}, NumberDecimal(Math.PI / 2)); + testOpApprox({$atan2: [NumberDecimal('-Infinity'), 0]}, NumberDecimal(-Math.PI / 2)); + testOpApprox({$atan2: [NumberDecimal('-Infinity'), NumberDecimal("Infinity")]}, + NumberDecimal(-Math.PI / 4)); + testOpApprox({$atan2: [NumberDecimal('-Infinity'), NumberDecimal("-Infinity")]}, + NumberDecimal(-3 * Math.PI / 4)); + testOpApprox({$atan2: [NumberDecimal('0'), NumberDecimal("-Infinity")]}, + NumberDecimal(Math.PI)); + testOpApprox({$atan2: [NumberDecimal('0'), NumberDecimal("Infinity")]}, NumberDecimal(0)); + + testOp({$tanh: NumberDecimal('Infinity')}, NumberDecimal('1')); + testOp({$tanh: NumberDecimal('-Infinity')}, NumberDecimal('-1')); + + // Finite input produces infinite outputs. + testOp({$atanh: NumberDecimal(1)}, NumberDecimal('Infinity')); + testOp({$atanh: NumberDecimal(-1)}, NumberDecimal('-Infinity')); + testOp({$atanh: 1}, Infinity); + testOp({$atanh: -1}, -Infinity); + + testOp({$tanh: Infinity}, 1); + testOp({$tanh: -Infinity}, -1); + + // Int argument out of bounds. + assertErrorCode(coll, [{$project: {a: {$acos: NumberInt(-2)}}}], 50989); + assertErrorCode(coll, [{$project: {a: {$acos: NumberInt(2)}}}], 50989); + assertErrorCode(coll, [{$project: {a: {$asin: NumberInt(-2)}}}], 50989); + assertErrorCode(coll, [{$project: {a: {$asin: NumberInt(2)}}}], 50989); + assertErrorCode(coll, [{$project: {a: {$acosh: NumberInt(0)}}}], 50989); + assertErrorCode(coll, [{$project: {a: {$atanh: NumberInt(2)}}}], 50989); + assertErrorCode(coll, [{$project: {a: {$atanh: NumberInt(-2)}}}], 50989); + + // Long argument out of bounds. + assertErrorCode(coll, [{$project: {a: {$acos: NumberLong(-2)}}}], 50989); + assertErrorCode(coll, [{$project: {a: {$acos: NumberLong(2)}}}], 50989); + assertErrorCode(coll, [{$project: {a: {$asin: NumberLong(-2)}}}], 50989); + assertErrorCode(coll, [{$project: {a: {$asin: NumberLong(2)}}}], 50989); + assertErrorCode(coll, [{$project: {a: {$acosh: NumberLong(0)}}}], 50989); + assertErrorCode(coll, [{$project: {a: {$atanh: NumberLong(2)}}}], 50989); + assertErrorCode(coll, [{$project: {a: {$atanh: NumberLong(-2)}}}], 50989); + + // Double argument out of bounds. + assertErrorCode(coll, [{$project: {a: {$acos: -1.1}}}], 50989); + assertErrorCode(coll, [{$project: {a: {$acos: 1.1}}}], 50989); + assertErrorCode(coll, [{$project: {a: {$asin: -1.1}}}], 50989); + assertErrorCode(coll, [{$project: {a: {$asin: 1.1}}}], 50989); + assertErrorCode(coll, [{$project: {a: {$acosh: 0.9}}}], 50989); + assertErrorCode(coll, [{$project: {a: {$atanh: -1.00001}}}], 50989); + assertErrorCode(coll, [{$project: {a: {$atanh: 1.00001}}}], 50989); + + // Decimal argument out of bounds. + assertErrorCode(coll, [{$project: {a: {$acos: NumberDecimal(-1.1)}}}], 50989); + assertErrorCode(coll, [{$project: {a: {$acos: NumberDecimal(1.1)}}}], 50989); + assertErrorCode(coll, [{$project: {a: {$asin: NumberDecimal(-1.1)}}}], 50989); + assertErrorCode(coll, [{$project: {a: {$asin: NumberDecimal(1.1)}}}], 50989); + assertErrorCode(coll, [{$project: {a: {$acosh: NumberDecimal(0.9)}}}], 50989); + assertErrorCode(coll, [{$project: {a: {$atanh: NumberDecimal(-1.00001)}}}], 50989); + assertErrorCode(coll, [{$project: {a: {$atanh: NumberDecimal(1.000001)}}}], 50989); + + // Check NaN is preserved. + ["$acos", "$asin", "$atan", "$cos", "$sin", "$tan"].forEach(op => { + testOp({[op]: NaN}, NaN); + testOp({[op]: NumberDecimal(NaN)}, NumberDecimal(NaN)); + // Check the hyperbolic version of each function. + testOp({[op + 'h']: NaN}, NaN); + testOp({[op + 'h']: NumberDecimal(NaN)}, NumberDecimal(NaN)); + }); + + ["$radiansToDegrees", "$degreesToRadians"].forEach(op => { + testOp({[op]: NaN}, NaN); + testOp({[op]: NumberDecimal(NaN)}, NumberDecimal(NaN)); + testOp({[op]: -Infinity}, -Infinity); + testOp({[op]: NumberDecimal(-Infinity)}, NumberDecimal(-Infinity)); + testOp({[op]: Infinity}, Infinity); + testOp({[op]: NumberDecimal(Infinity)}, NumberDecimal(Infinity)); + }); + + testOp({$atan2: [NumberDecimal('NaN'), NumberDecimal('NaN')]}, NumberDecimal('NaN')); + testOp({$atan2: [NumberDecimal('NaN'), NumberDecimal('0')]}, NumberDecimal('NaN')); + testOp({$atan2: [NumberDecimal('0'), NumberDecimal('NaN')]}, NumberDecimal('NaN')); + + // Non-numeric input. + assertErrorCode(coll, [{$project: {a: {$acos: "string"}}}], 28765); + assertErrorCode(coll, [{$project: {a: {$acosh: "string"}}}], 28765); + assertErrorCode(coll, [{$project: {a: {$asin: "string"}}}], 28765); + assertErrorCode(coll, [{$project: {a: {$asinh: "string"}}}], 28765); + assertErrorCode(coll, [{$project: {a: {$atan: "string"}}}], 28765); + assertErrorCode(coll, [{$project: {a: {$atan2: ["string", "string"]}}}], 51044); + assertErrorCode(coll, [{$project: {a: {$atan2: ["string", 0.0]}}}], 51044); + assertErrorCode(coll, [{$project: {a: {$atan2: [0.0, "string"]}}}], 51045); + assertErrorCode(coll, [{$project: {a: {$atanh: "string"}}}], 28765); + assertErrorCode(coll, [{$project: {a: {$cos: "string"}}}], 28765); + assertErrorCode(coll, [{$project: {a: {$cosh: "string"}}}], 28765); + assertErrorCode(coll, [{$project: {a: {$sin: "string"}}}], 28765); + assertErrorCode(coll, [{$project: {a: {$sinh: "string"}}}], 28765); + assertErrorCode(coll, [{$project: {a: {$tan: "string"}}}], 28765); + assertErrorCode(coll, [{$project: {a: {$tanh: "string"}}}], 28765); + assertErrorCode(coll, [{$project: {a: {$degreesToRadians: "string"}}}], 28765); + assertErrorCode(coll, [{$project: {a: {$radiansToDegrees: "string"}}}], 28765); +}()); diff --git a/src/mongo/db/pipeline/SConscript b/src/mongo/db/pipeline/SConscript index 71270f0e72f..bfd47c1a11d 100644 --- a/src/mongo/db/pipeline/SConscript +++ b/src/mongo/db/pipeline/SConscript @@ -135,6 +135,7 @@ env.Library( target='expression', source=[ 'expression.cpp', + 'expression_trigonometric.cpp', ], LIBDEPS=[ '$BUILD_DIR/mongo/db/query/datetime/date_time_support', @@ -445,6 +446,7 @@ env.CppUnitTest( 'expression_convert_test.cpp', 'expression_date_test.cpp', 'expression_test.cpp', + 'expression_trigonometric_test.cpp', ], LIBDEPS=[ '$BUILD_DIR/mongo/db/query/query_test_service_context', diff --git a/src/mongo/db/pipeline/expression.h b/src/mongo/db/pipeline/expression.h index 82e8f245be0..31d1e185d34 100644 --- a/src/mongo/db/pipeline/expression.h +++ b/src/mongo/db/pipeline/expression.h @@ -435,7 +435,7 @@ public: explicit ExpressionSingleNumericArg(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionFixedArity<SubClass, 1>(expCtx) {} - virtual ~ExpressionSingleNumericArg() {} + virtual ~ExpressionSingleNumericArg() = default; Value evaluate(const Document& root) const final { Value arg = this->vpOperand[0]->evaluate(root); @@ -454,6 +454,49 @@ public: }; /** + * Inherit from this class if your expression takes exactly two numeric arguments. + */ +template <typename SubClass> +class ExpressionTwoNumericArgs : public ExpressionFixedArity<SubClass, 2> { +public: + explicit ExpressionTwoNumericArgs(const boost::intrusive_ptr<ExpressionContext>& expCtx) + : ExpressionFixedArity<SubClass, 2>(expCtx) {} + + virtual ~ExpressionTwoNumericArgs() = default; + + /** + * Evaluate performs the type checking necessary to make sure that both arguments are numeric, + * then calls the evaluateNumericArgs on the two numeric args: + * 1. If either input is nullish, it returns null. + * 2. If either input is not numeric, it throws an error. + * 3. Call evaluateNumericArgs on the two numeric args. + */ + Value evaluate(const Document& root) const final { + Value arg1 = this->vpOperand[0]->evaluate(root); + if (arg1.nullish()) + return Value(BSONNULL); + uassert(51044, + str::stream() << this->getOpName() << " only supports numeric types, not " + << typeName(arg1.getType()), + arg1.numeric()); + Value arg2 = this->vpOperand[1]->evaluate(root); + if (arg2.nullish()) + return Value(BSONNULL); + uassert(51045, + str::stream() << this->getOpName() << " only supports numeric types, not " + << typeName(arg2.getType()), + arg2.numeric()); + + return evaluateNumericArgs(arg1, arg2); + } + + /** + * Evaluate the expression on exactly two numeric arguments. + */ + virtual Value evaluateNumericArgs(const Value& numericArg1, const Value& numericArg2) const = 0; +}; + +/** * A constant expression. Repeated calls to evaluate() will always return the same thing. */ class ExpressionConstant final : public Expression { @@ -665,7 +708,6 @@ public: const char* getOpName() const final; }; - class ExpressionAdd final : public ExpressionVariadic<ExpressionAdd> { public: explicit ExpressionAdd(const boost::intrusive_ptr<ExpressionContext>& expCtx) diff --git a/src/mongo/db/pipeline/expression_trigonometric.cpp b/src/mongo/db/pipeline/expression_trigonometric.cpp new file mode 100644 index 00000000000..6fdf7cdcc4e --- /dev/null +++ b/src/mongo/db/pipeline/expression_trigonometric.cpp @@ -0,0 +1,220 @@ +/** + * Copyright (C) 2018-present MongoDB, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * <http://www.mongodb.com/licensing/server-side-public-license>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the Server Side Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#include "mongo/platform/basic.h" + +#include "expression_trigonometric.h" + +namespace mongo { + +/* ----------------------- Inclusive Bounded Trigonometric Functions ---------------------------- */ + +#define CREATE_BOUNDED_TRIGONOMETRIC_CLASS(className, funcName, boundType, lowerBound, upperBound) \ + class Expression##className final \ + : public ExpressionBoundedTrigonometric<Expression##className, boundType> { \ + public: \ + explicit Expression##className(const boost::intrusive_ptr<ExpressionContext>& expCtx) \ + : ExpressionBoundedTrigonometric(expCtx) {} \ + double getLowerBound() const final { \ + return lowerBound; \ + } \ + \ + double getUpperBound() const final { \ + return upperBound; \ + } \ + \ + double doubleFunc(double arg) const final { \ + return std::funcName(arg); \ + } \ + \ + Decimal128 decimalFunc(Decimal128 arg) const final { \ + return arg.funcName(); \ + } \ + \ + const char* getOpName() const final { \ + return "$" #funcName; \ + } \ + }; \ + REGISTER_EXPRESSION(funcName, Expression##className::parse); + + +/** + * Inclusive Bounds + */ +CREATE_BOUNDED_TRIGONOMETRIC_CLASS(ArcCosine, acos, InclusiveBoundType, -1.0, 1.0); + +CREATE_BOUNDED_TRIGONOMETRIC_CLASS(ArcSine, asin, InclusiveBoundType, -1.0, 1.0); + +CREATE_BOUNDED_TRIGONOMETRIC_CLASS(HyperbolicArcTangent, atanh, InclusiveBoundType, -1.0, 1.0); + +CREATE_BOUNDED_TRIGONOMETRIC_CLASS( + HyperbolicArcCosine, acosh, InclusiveBoundType, 1.0, std::numeric_limits<double>::infinity()); + +/** + * Exclusive Bounds + */ +CREATE_BOUNDED_TRIGONOMETRIC_CLASS(Cosine, + cos, + ExclusiveBoundType, + -std::numeric_limits<double>::infinity(), + std::numeric_limits<double>::infinity()); + +CREATE_BOUNDED_TRIGONOMETRIC_CLASS(Sine, + sin, + ExclusiveBoundType, + -std::numeric_limits<double>::infinity(), + std::numeric_limits<double>::infinity()); + +CREATE_BOUNDED_TRIGONOMETRIC_CLASS(Tangent, + tan, + ExclusiveBoundType, + -std::numeric_limits<double>::infinity(), + std::numeric_limits<double>::infinity()); + +#undef CREATE_BOUNDED_TRIGONOMETRIC_CLASS + +/* ----------------------- Unbounded Trigonometric Functions ---------------------------- */ + + +#define CREATE_TRIGONOMETRIC_CLASS(className, funcName) \ + class Expression##className final \ + : public ExpressionUnboundedTrigonometric<Expression##className> { \ + public: \ + explicit Expression##className(const boost::intrusive_ptr<ExpressionContext>& expCtx) \ + : ExpressionUnboundedTrigonometric(expCtx) {} \ + \ + double doubleFunc(double arg) const final { \ + return std::funcName(arg); \ + } \ + \ + Decimal128 decimalFunc(Decimal128 arg) const final { \ + return arg.funcName(); \ + } \ + \ + const char* getOpName() const final { \ + return "$" #funcName; \ + } \ + }; \ + REGISTER_EXPRESSION(funcName, Expression##className::parse); + +CREATE_TRIGONOMETRIC_CLASS(ArcTangent, atan); +CREATE_TRIGONOMETRIC_CLASS(HyperbolicArcSine, asinh); +CREATE_TRIGONOMETRIC_CLASS(HyperbolicCosine, cosh); +CREATE_TRIGONOMETRIC_CLASS(HyperbolicSine, sinh); +CREATE_TRIGONOMETRIC_CLASS(HyperbolicTangent, tanh); + +#undef CREATE_TRIGONOMETRIC_CLASS + + +/* ----------------------- ExpressionArcTangent2 ---------------------------- */ + +class ExpressionArcTangent2 final : public ExpressionTwoNumericArgs<ExpressionArcTangent2> { +public: + explicit ExpressionArcTangent2(const boost::intrusive_ptr<ExpressionContext>& expCtx) + : ExpressionTwoNumericArgs(expCtx) {} + + Value evaluateNumericArgs(const Value& numericArg1, const Value& numericArg2) const final { + auto totalType = BSONType::NumberDouble; + // If the type of either argument is NumberDecimal, we promote to Decimal128. + if (numericArg1.getType() == BSONType::NumberDecimal || + numericArg2.getType() == BSONType::NumberDecimal) { + totalType = BSONType::NumberDecimal; + } + switch (totalType) { + case BSONType::NumberDecimal: { + auto dec = numericArg1.coerceToDecimal(); + return Value(dec.atan2(numericArg2.coerceToDecimal())); + } + case BSONType::NumberDouble: { + return Value( + std::atan2(numericArg1.coerceToDouble(), numericArg2.coerceToDouble())); + } + default: + MONGO_UNREACHABLE; + } + } + + const char* getOpName() const final { + return "$atan2"; + } +}; +REGISTER_EXPRESSION(atan2, ExpressionArcTangent2::parse); + + +/* ----------------------- ExpressionDegreesToRadians and ExpressionRadiansToDegrees ---- */ + +static constexpr double kDoublePi = 3.141592653589793; +static constexpr double kDoublePiOver180 = kDoublePi / 180.0; +static constexpr double kDouble180OverPi = 180.0 / kDoublePi; + +static Value doDegreeRadiansConversion(const Value& numericArg, + Decimal128 decimalFactor, + double doubleFactor) { + switch (numericArg.getType()) { + case BSONType::NumberDecimal: + return Value(numericArg.getDecimal().multiply(decimalFactor)); + default: + return Value(numericArg.coerceToDouble() * doubleFactor); + } +} + +class ExpressionDegreesToRadians final + : public ExpressionSingleNumericArg<ExpressionDegreesToRadians> { +public: + explicit ExpressionDegreesToRadians(const boost::intrusive_ptr<ExpressionContext>& expCtx) + : ExpressionSingleNumericArg(expCtx) {} + + Value evaluateNumericArg(const Value& numericArg) const final { + return doDegreeRadiansConversion(numericArg, Decimal128::kPiOver180, kDoublePiOver180); + } + + const char* getOpName() const final { + return "$degreesToRadians"; + } +}; + +REGISTER_EXPRESSION(degreesToRadians, ExpressionDegreesToRadians::parse); + +class ExpressionRadiansToDegrees final + : public ExpressionSingleNumericArg<ExpressionRadiansToDegrees> { +public: + explicit ExpressionRadiansToDegrees(const boost::intrusive_ptr<ExpressionContext>& expCtx) + : ExpressionSingleNumericArg(expCtx) {} + + Value evaluateNumericArg(const Value& numericArg) const final { + return doDegreeRadiansConversion(numericArg, Decimal128::k180OverPi, kDouble180OverPi); + } + + const char* getOpName() const final { + return "$radiansToDegrees"; + } +}; + +REGISTER_EXPRESSION(radiansToDegrees, ExpressionRadiansToDegrees::parse); +} // namespace mongo diff --git a/src/mongo/db/pipeline/expression_trigonometric.h b/src/mongo/db/pipeline/expression_trigonometric.h new file mode 100644 index 00000000000..41f10ca2e29 --- /dev/null +++ b/src/mongo/db/pipeline/expression_trigonometric.h @@ -0,0 +1,237 @@ +/** + * Copyright (C) 2018-present MongoDB, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * <http://www.mongodb.com/licensing/server-side-public-license>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the Server Side Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#pragma once + +#include "expression.h" + +namespace mongo { + +/** + * InclusiveBoundType defines the necessary configuration for inclusively bounded trig functions. + */ +struct InclusiveBoundType { + // We use a static method rather than a field because the value, as an std::string, would need + // to be initialized out of line. This method will be inlined, and result in no overhead. + static std::string leftBracket() { + return "["; + } + + static std::string rightBracket() { + return "]"; + } + + static bool checkUpperBound(double input, double bound) { + return input <= bound; + } + + static bool checkUpperBound(Decimal128 input, double bound) { + return input.isLessEqual(Decimal128(bound)); + } + + static bool checkLowerBound(double input, double bound) { + return input >= bound; + } + + static bool checkLowerBound(Decimal128 input, double bound) { + return input.isGreaterEqual(Decimal128(bound)); + } +}; + +/** + * ExclusiveBoundType defines the necessary configuration for exclusively bounded trig functions. + */ +struct ExclusiveBoundType { + // We use a static method rather than a field because the value, as an std::string, would need + // to be initialized out of line. This method will be inlined, and result in no overhead. + static std::string leftBracket() { + return "("; + } + + static std::string rightBracket() { + return ")"; + } + + static bool checkUpperBound(double input, double bound) { + return input < bound; + } + + static bool checkUpperBound(Decimal128 input, double bound) { + return input.isLess(Decimal128(bound)); + } + + static bool checkLowerBound(double input, double bound) { + return input > bound; + } + + static bool checkLowerBound(Decimal128 input, double bound) { + return input.isGreater(Decimal128(bound)); + } +}; + +/** + * ExpressionBoundedTrigonometric is the type of all trigonometric functions that take one argument + * and have lower and upper bounds, either inclusive or exclusive, as defined by the BoundType + * template argument. + */ +template <typename BoundedTrigType, typename BoundType> +class ExpressionBoundedTrigonometric : public ExpressionSingleNumericArg<BoundedTrigType> { +public: + explicit ExpressionBoundedTrigonometric(const boost::intrusive_ptr<ExpressionContext>& expCtx) + : ExpressionSingleNumericArg<BoundedTrigType>(expCtx) {} + + std::string toString(double d) const { + return str::stream() << d; + } + + std::string toString(Decimal128 d) const { + return d.toString(); + } + + bool isnan(double d) const { + return std::isnan(d); + } + + bool isnan(Decimal128 d) const { + return d.isNaN(); + } + + template <typename T> + bool checkBounds(T input) const { + return BoundType::checkLowerBound(input, getLowerBound()) && + BoundType::checkUpperBound(input, getUpperBound()); + } + + /** + * assertBounds uasserts if checkBounds returns false, meaning that the input is out of bounds. + */ + template <typename T> + void assertBounds(T input) const { + uassert(50989, + str::stream() << "cannot apply " << getOpName() << " to " << toString(input) + << ", value must in " + << BoundType::leftBracket() + << getLowerBound() + << "," + << getUpperBound() + << BoundType::rightBracket(), + checkBounds(input)); + } + + /** + * evaluateNumericArg evaluates the implented trig function on one numericArg. + */ + Value evaluateNumericArg(const Value& numericArg) const { + switch (numericArg.getType()) { + case BSONType::NumberDouble: { + auto input = numericArg.getDouble(); + if (isnan(input)) { + return numericArg; + } + assertBounds(input); + return Value(doubleFunc(input)); + } + case BSONType::NumberDecimal: { + auto input = numericArg.getDecimal(); + if (isnan(input)) { + return numericArg; + } + assertBounds(input); + return Value(decimalFunc(input)); + } + default: { + auto input = static_cast<double>(numericArg.getLong()); + if (isnan(input)) { + return numericArg; + } + assertBounds(input); + return Value(doubleFunc(input)); + } + } + } + + /** + * Since bounds are always either +/-Infinity or integral values, double has enough precision. + */ + virtual double getLowerBound() const = 0; + virtual double getUpperBound() const = 0; + /** + * doubleFunc performs the double version of the implemented trig function, e.g. std::sin() + */ + virtual double doubleFunc(double x) const = 0; + /** + * decimalFunc performs the decimal128 version of the implemented trig function, e.g. d.sin() + */ + virtual Decimal128 decimalFunc(Decimal128 x) const = 0; + /** + * getOpName returns the name of the operation, e.g., $sin + */ + virtual const char* getOpName() const = 0; +}; + +/** + * ExpressionUnboundedTrigonometric is the type for all trigonometric functions that do not have + * upper or lower bounds. + */ +template <typename TrigType> +class ExpressionUnboundedTrigonometric : public ExpressionSingleNumericArg<TrigType> { +public: + explicit ExpressionUnboundedTrigonometric(const boost::intrusive_ptr<ExpressionContext>& expCtx) + : ExpressionSingleNumericArg<TrigType>(expCtx) {} + + /** + * evaluateNumericArg evaluates the implented trig function on one numericArg. + */ + Value evaluateNumericArg(const Value& numericArg) const override { + switch (numericArg.getType()) { + case BSONType::NumberDouble: + return Value(doubleFunc(numericArg.getDouble())); + case BSONType::NumberDecimal: + return Value(decimalFunc(numericArg.getDecimal())); + default: { + auto num = static_cast<double>(numericArg.getLong()); + return Value(doubleFunc(num)); + } + } + } + + /** + * doubleFunc performs the double version of the implemented trig function, e.g. std::sinh() + */ + virtual double doubleFunc(double x) const = 0; + /** + * decimalFunc performs the decimal128 version of the implemented trig function, e.g. d.sinh() + */ + virtual Decimal128 decimalFunc(Decimal128 x) const = 0; + /** + * getOpName returns the name of the operation, e.g., $sinh + */ + virtual const char* getOpName() const = 0; +}; +} // namespace mongo diff --git a/src/mongo/db/pipeline/expression_trigonometric_test.cpp b/src/mongo/db/pipeline/expression_trigonometric_test.cpp new file mode 100644 index 00000000000..35756404dd4 --- /dev/null +++ b/src/mongo/db/pipeline/expression_trigonometric_test.cpp @@ -0,0 +1,1406 @@ +/** + * Copyright (C) 2018-present MongoDB, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * <http://www.mongodb.com/licensing/server-side-public-license>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the Server Side Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#include "mongo/platform/basic.h" + +#include "mongo/db/pipeline/expression_trigonometric.h" + +#include "mongo/db/pipeline/document_value_test_util.h" +#include "mongo/db/pipeline/expression_context_for_test.h" +#include "mongo/unittest/unittest.h" + +namespace expression_tests { + +using boost::intrusive_ptr; +using namespace mongo; + +// assertApproxEq is a helper function for asserting approximate results. +static void assertApproxEq(const Value& evaluated, const Value& expected) { + ASSERT_EQ(evaluated.getType(), expected.getType()); + if (expected.nullish()) { + ASSERT_VALUE_EQ(expected, evaluated); + } else { + ASSERT_VALUE_LT( + Value(evaluated.coerceToDecimal().subtract(expected.coerceToDecimal()).toAbs()), + Value(Decimal128(".000001"))); + } +} + +// A testing class for testing approximately equal results for one argument numeric expressions. +static void assertEvaluates(const std::string& expressionName, Value input, Value output) { + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + auto obj = BSON(expressionName << BSON_ARRAY(input)); + auto vps = expCtx->variablesParseState; + auto expression = Expression::parseExpression(expCtx, obj, vps); + Value result = expression->evaluate(Document()); + ASSERT_EQUALS(result.getType(), output.getType()); + assertApproxEq(result, output); +} + + +// A testing class for testing approximately equal results for two argument numeric expressions. +static void assertEvaluates(const std::string& expressionName, + Value input1, + Value input2, + Value output) { + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + auto obj = BSON(expressionName << BSON_ARRAY(input1 << input2)); + auto vps = expCtx->variablesParseState; + auto expression = Expression::parseExpression(expCtx, obj, vps); + Value result = expression->evaluate(Document()); + ASSERT_EQUALS(result.getType(), output.getType()); + assertApproxEq(result, output); +} + +/* ------------------------- ExpressionArcSine -------------------------- */ +/** + * Test values were generated using the 64 bit std version of asin, and help + * ensure that we are calling the correct functions. + * + */ + +TEST(ExpressionArcSineTest, IntArg) { + assertEvaluates("$asin", Value(0), Value(0.0)); + assertEvaluates("$asin", Value(1), Value(1.57079632679)); +} + +TEST(ExpressionArcSineTest, LongArg) { + assertEvaluates("$asin", Value(0LL), Value(0.0)); + assertEvaluates("$asin", Value(1LL), Value(1.57079632679)); +} + +TEST(ExpressionArcSineTest, DoubleArg) { + assertEvaluates("$asin", Value(0.0), Value(0.0)); + assertEvaluates("$asin", Value(0.1), Value(0.100167421162)); + assertEvaluates("$asin", Value(0.2), Value(0.20135792079)); + assertEvaluates("$asin", Value(0.3), Value(0.304692654015)); + assertEvaluates("$asin", Value(0.4), Value(0.411516846067)); + assertEvaluates("$asin", Value(0.5), Value(0.523598775598)); + assertEvaluates("$asin", Value(0.6), Value(0.643501108793)); + assertEvaluates("$asin", Value(0.7), Value(0.775397496611)); + assertEvaluates("$asin", Value(0.8), Value(0.927295218002)); + assertEvaluates("$asin", Value(0.9), Value(1.119769515)); + assertEvaluates("$asin", Value(1.0), Value(1.57079632679)); +} + +TEST(ExpressionArcSineTest, DecimalArg) { + assertEvaluates("$asin", Value(Decimal128("0.0")), Value(Decimal128("0.0"))); + assertEvaluates("$asin", Value(Decimal128("0.1")), Value(Decimal128("0.100167421162"))); + assertEvaluates("$asin", Value(Decimal128("0.2")), Value(Decimal128("0.20135792079"))); + assertEvaluates("$asin", Value(Decimal128("0.3")), Value(Decimal128("0.304692654015"))); + assertEvaluates("$asin", Value(Decimal128("0.4")), Value(Decimal128("0.411516846067"))); + assertEvaluates("$asin", Value(Decimal128("0.5")), Value(Decimal128("0.523598775598"))); + assertEvaluates("$asin", Value(Decimal128("0.6")), Value(Decimal128("0.643501108793"))); + assertEvaluates("$asin", Value(Decimal128("0.7")), Value(Decimal128("0.775397496611"))); + assertEvaluates("$asin", Value(Decimal128("0.8")), Value(Decimal128("0.927295218002"))); + assertEvaluates("$asin", Value(Decimal128("0.9")), Value(Decimal128("1.119769515"))); + assertEvaluates("$asin", Value(Decimal128("1.0")), Value(Decimal128("1.57079632679"))); +} + +TEST(ExpressionArcSineTest, NullArg) { + assertEvaluates("$asin", Value(BSONNULL), Value(BSONNULL)); +} + +/* ------------------------- ExpressionArcCosine -------------------------- */ +/** + * Test values were generated using the 64 bit std version of acos, and help + * ensure that we are calling the correct functions. + * + */ + +TEST(ExpressionArcCosineTest, IntArg) { + assertEvaluates("$acos", Value(0), Value(1.57079632679)); + assertEvaluates("$acos", Value(1), Value(0.0)); +} + +TEST(ExpressionArcCosineTest, LongArg) { + assertEvaluates("$acos", Value(0LL), Value(1.57079632679)); + assertEvaluates("$acos", Value(1LL), Value(0.0)); +} + +TEST(ExpressionArcCosineTest, DoubleArg) { + assertEvaluates("$acos", Value(0.0), Value(1.57079632679)); + assertEvaluates("$acos", Value(0.1), Value(1.47062890563)); + assertEvaluates("$acos", Value(0.2), Value(1.369438406)); + assertEvaluates("$acos", Value(0.3), Value(1.26610367278)); + assertEvaluates("$acos", Value(0.4), Value(1.15927948073)); + assertEvaluates("$acos", Value(0.5), Value(1.0471975512)); + assertEvaluates("$acos", Value(0.6), Value(0.927295218002)); + assertEvaluates("$acos", Value(0.7), Value(0.795398830184)); + assertEvaluates("$acos", Value(0.8), Value(0.643501108793)); + assertEvaluates("$acos", Value(0.9), Value(0.451026811796)); + assertEvaluates("$acos", Value(1.0), Value(0.0)); +} + +TEST(ExpressionArcCosineTest, DecimalArg) { + assertEvaluates("$acos", Value(Decimal128("0.0")), Value(Decimal128("1.57079632679"))); + assertEvaluates("$acos", Value(Decimal128("0.1")), Value(Decimal128("1.47062890563"))); + assertEvaluates("$acos", Value(Decimal128("0.2")), Value(Decimal128("1.369438406"))); + assertEvaluates("$acos", Value(Decimal128("0.3")), Value(Decimal128("1.26610367278"))); + assertEvaluates("$acos", Value(Decimal128("0.4")), Value(Decimal128("1.15927948073"))); + assertEvaluates("$acos", Value(Decimal128("0.5")), Value(Decimal128("1.0471975512"))); + assertEvaluates("$acos", Value(Decimal128("0.6")), Value(Decimal128("0.927295218002"))); + assertEvaluates("$acos", Value(Decimal128("0.7")), Value(Decimal128("0.795398830184"))); + assertEvaluates("$acos", Value(Decimal128("0.8")), Value(Decimal128("0.643501108793"))); + assertEvaluates("$acos", Value(Decimal128("0.9")), Value(Decimal128("0.451026811796"))); + assertEvaluates("$acos", Value(Decimal128("1.0")), Value(Decimal128("0.0"))); +} + +TEST(ExpressionArcCosineTest, NullArg) { + assertEvaluates("$acos", Value(BSONNULL), Value(BSONNULL)); +} + +/* ------------------------- ExpressionArcTangent -------------------------- */ +/** + * Test values were generated using the 64 bit std version of atan, and help + * ensure that we are calling the correct functions. + * + */ + +TEST(ExpressionArcTangentTest, IntArg) { + assertEvaluates("$atan", Value(-1), Value(-0.785398163397)); + assertEvaluates("$atan", Value(0), Value(0.0)); + assertEvaluates("$atan", Value(1), Value(0.785398163397)); +} + +TEST(ExpressionArcTangentTest, LongArg) { + assertEvaluates("$atan", Value(-1LL), Value(-0.785398163397)); + assertEvaluates("$atan", Value(0LL), Value(0.0)); + assertEvaluates("$atan", Value(1LL), Value(0.785398163397)); +} + +TEST(ExpressionArcTangentTest, DoubleArg) { + assertEvaluates("$atan", Value(-1.5), Value(-0.982793723247)); + assertEvaluates("$atan", Value(-1.0471975512), Value(-0.80844879263)); + assertEvaluates("$atan", Value(-0.785398163397), Value(-0.665773750028)); + assertEvaluates("$atan", Value(0), Value(0.0)); + assertEvaluates("$atan", Value(0.785398163397), Value(0.665773750028)); + assertEvaluates("$atan", Value(1.0471975512), Value(0.80844879263)); + assertEvaluates("$atan", Value(1.5), Value(0.982793723247)); +} + +TEST(ExpressionArcTangentTest, DecimalArg) { + assertEvaluates("$atan", Value(Decimal128("-1.5")), Value(Decimal128("-0.982793723247"))); + assertEvaluates( + "$atan", Value(Decimal128("-1.0471975512")), Value(Decimal128("-0.80844879263"))); + assertEvaluates( + "$atan", Value(Decimal128("-0.785398163397")), Value(Decimal128("-0.665773750028"))); + assertEvaluates("$atan", Value(Decimal128("0")), Value(Decimal128("0.0"))); + assertEvaluates( + "$atan", Value(Decimal128("0.785398163397")), Value(Decimal128("0.665773750028"))); + assertEvaluates("$atan", Value(Decimal128("1.0471975512")), Value(Decimal128("0.80844879263"))); + assertEvaluates("$atan", Value(Decimal128("1.5")), Value(Decimal128("0.982793723247"))); +} + +TEST(ExpressionArcTangentTest, NullArg) { + assertEvaluates("$atan", Value(BSONNULL), Value(BSONNULL)); +} + +/* ------------------------- ExpressionArcTangent2 -------------------------- */ +/** + * Test values were generated using the 64 bit std version of atan2, and help + * ensure that we are calling the correct functions. + * + */ + +TEST(ExpressionArcTangent2Test, TwoIntArgs) { + assertEvaluates("$atan2", Value(1), Value(0), Value(1.57079632679)); + assertEvaluates("$atan2", Value(0), Value(1), Value(0.0)); + assertEvaluates("$atan2", Value(-1), Value(0), Value(-1.57079632679)); + assertEvaluates("$atan2", Value(0), Value(-1), Value(3.14159265359)); +} + +TEST(ExpressionArcTangent2Test, TwoLongArg) { + assertEvaluates("$atan2", Value(1LL), Value(0LL), Value(1.57079632679)); + assertEvaluates("$atan2", Value(0LL), Value(1LL), Value(0.0)); + assertEvaluates("$atan2", Value(-1LL), Value(0LL), Value(-1.57079632679)); + assertEvaluates("$atan2", Value(0LL), Value(-1LL), Value(3.14159265359)); +} + +TEST(ExpressionArcTangent2Test, LongIntArg) { + assertEvaluates("$atan2", Value(1LL), Value(0), Value(1.57079632679)); + assertEvaluates("$atan2", Value(0LL), Value(1), Value(0.0)); + assertEvaluates("$atan2", Value(-1LL), Value(0), Value(-1.57079632679)); + assertEvaluates("$atan2", Value(0LL), Value(-1), Value(3.14159265359)); +} + +TEST(ExpressionArcTangent2Test, IntLongArg) { + assertEvaluates("$atan2", Value(1), Value(0LL), Value(1.57079632679)); + assertEvaluates("$atan2", Value(0), Value(1LL), Value(0.0)); + assertEvaluates("$atan2", Value(-1), Value(0LL), Value(-1.57079632679)); + assertEvaluates("$atan2", Value(0), Value(-1LL), Value(3.14159265359)); +} + +TEST(ExpressionArcTangent2Test, TwoDoubleArg) { + assertEvaluates("$atan2", Value(1.0), Value(0.0), Value(1.57079632679)); + assertEvaluates("$atan2", Value(0.866025403784), Value(0.5), Value(1.0471975512)); + assertEvaluates("$atan2", Value(0.707106781187), Value(0.707106781187), Value(0.785398163397)); + assertEvaluates("$atan2", Value(0.5), Value(0.866025403784), Value(0.523598775598)); + assertEvaluates("$atan2", Value(6.12323399574e-17), Value(1.0), Value(6.12323399574e-17)); + assertEvaluates("$atan2", Value(-0.5), Value(0.866025403784), Value(-0.523598775598)); + assertEvaluates( + "$atan2", Value(-0.707106781187), Value(0.707106781187), Value(-0.785398163397)); + assertEvaluates("$atan2", Value(-0.866025403784), Value(0.5), Value(-1.0471975512)); + assertEvaluates("$atan2", Value(-1.0), Value(1.22464679915e-16), Value(-1.57079632679)); + assertEvaluates("$atan2", Value(-0.866025403784), Value(-0.5), Value(-2.09439510239)); + assertEvaluates( + "$atan2", Value(-0.707106781187), Value(-0.707106781187), Value(-2.35619449019)); + assertEvaluates("$atan2", Value(-0.5), Value(-0.866025403784), Value(-2.61799387799)); + assertEvaluates("$atan2", Value(-1.83697019872e-16), Value(-1.0), Value(-3.14159265359)); + assertEvaluates("$atan2", Value(0.5), Value(-0.866025403784), Value(2.61799387799)); + assertEvaluates("$atan2", Value(0.707106781187), Value(-0.707106781187), Value(2.35619449019)); + assertEvaluates("$atan2", Value(0.866025403784), Value(-0.5), Value(2.09439510239)); + assertEvaluates("$atan2", Value(1.0), Value(-2.44929359829e-16), Value(1.57079632679)); +} + +TEST(ExpressionArcTangent2Test, TwoDecimalArg) { + assertEvaluates("$atan2", + Value(Decimal128("1.0")), + Value(Decimal128("0.0")), + Value(Decimal128("1.57079632679"))); + assertEvaluates("$atan2", + Value(Decimal128("0.866025403784")), + Value(Decimal128("0.5")), + Value(Decimal128("1.0471975512"))); + assertEvaluates("$atan2", + Value(Decimal128("0.707106781187")), + Value(Decimal128("0.707106781187")), + Value(Decimal128("0.785398163397"))); + assertEvaluates("$atan2", + Value(Decimal128("0.5")), + Value(Decimal128("0.866025403784")), + Value(Decimal128("0.523598775598"))); + assertEvaluates("$atan2", + Value(Decimal128("6.12323399574e-17")), + Value(Decimal128("1.0")), + Value(Decimal128("6.12323399574e-17"))); + assertEvaluates("$atan2", + Value(Decimal128("-0.5")), + Value(Decimal128("0.866025403784")), + Value(Decimal128("-0.523598775598"))); + assertEvaluates("$atan2", + Value(Decimal128("-0.707106781187")), + Value(Decimal128("0.707106781187")), + Value(Decimal128("-0.785398163397"))); + assertEvaluates("$atan2", + Value(Decimal128("-0.866025403784")), + Value(Decimal128("0.5")), + Value(Decimal128("-1.0471975512"))); + assertEvaluates("$atan2", + Value(Decimal128("-1.0")), + Value(Decimal128("1.22464679915e-16")), + Value(Decimal128("-1.57079632679"))); + assertEvaluates("$atan2", + Value(Decimal128("-0.866025403784")), + Value(Decimal128("-0.5")), + Value(Decimal128("-2.09439510239"))); + assertEvaluates("$atan2", + Value(Decimal128("-0.707106781187")), + Value(Decimal128("-0.707106781187")), + Value(Decimal128("-2.35619449019"))); + assertEvaluates("$atan2", + Value(Decimal128("-0.5")), + Value(Decimal128("-0.866025403784")), + Value(Decimal128("-2.61799387799"))); + assertEvaluates("$atan2", + Value(Decimal128("-1.83697019872e-16")), + Value(Decimal128("-1.0")), + Value(Decimal128("-3.14159265359"))); + assertEvaluates("$atan2", + Value(Decimal128("0.5")), + Value(Decimal128("-0.866025403784")), + Value(Decimal128("2.61799387799"))); + assertEvaluates("$atan2", + Value(Decimal128("0.707106781187")), + Value(Decimal128("-0.707106781187")), + Value(Decimal128("2.35619449019"))); + assertEvaluates("$atan2", + Value(Decimal128("0.866025403784")), + Value(Decimal128("-0.5")), + Value(Decimal128("2.09439510239"))); + assertEvaluates("$atan2", + Value(Decimal128("1.0")), + Value(Decimal128("-2.44929359829e-16")), + Value(Decimal128("1.57079632679"))); +} + +TEST(ExpressionArcTangent2Test, DoubleDecimalArg) { + assertEvaluates( + "$atan2", Value(1.0), Value(Decimal128("0.0")), Value(Decimal128("1.57079632679"))); + assertEvaluates("$atan2", + Value(0.866025403784), + Value(Decimal128("0.5")), + Value(Decimal128("1.0471975512"))); + assertEvaluates("$atan2", + Value(0.707106781187), + Value(Decimal128("0.707106781187")), + Value(Decimal128("0.785398163397"))); + assertEvaluates("$atan2", + Value(0.5), + Value(Decimal128("0.866025403784")), + Value(Decimal128("0.523598775598"))); + assertEvaluates("$atan2", + Value(6.12323399574e-17), + Value(Decimal128("1.0")), + Value(Decimal128("6.12323399574e-17"))); + assertEvaluates("$atan2", + Value(-0.5), + Value(Decimal128("0.866025403784")), + Value(Decimal128("-0.523598775598"))); + assertEvaluates("$atan2", + Value(-0.707106781187), + Value(Decimal128("0.707106781187")), + Value(Decimal128("-0.785398163397"))); + assertEvaluates("$atan2", + Value(-0.866025403784), + Value(Decimal128("0.5")), + Value(Decimal128("-1.0471975512"))); + assertEvaluates("$atan2", + Value(-1.0), + Value(Decimal128("1.22464679915e-16")), + Value(Decimal128("-1.57079632679"))); + assertEvaluates("$atan2", + Value(-0.866025403784), + Value(Decimal128("-0.5")), + Value(Decimal128("-2.09439510239"))); + assertEvaluates("$atan2", + Value(-0.707106781187), + Value(Decimal128("-0.707106781187")), + Value(Decimal128("-2.35619449019"))); + assertEvaluates("$atan2", + Value(-0.5), + Value(Decimal128("-0.866025403784")), + Value(Decimal128("-2.61799387799"))); + assertEvaluates("$atan2", + Value(-1.83697019872e-16), + Value(Decimal128("-1.0")), + Value(Decimal128("-3.14159265359"))); + assertEvaluates("$atan2", + Value(0.5), + Value(Decimal128("-0.866025403784")), + Value(Decimal128("2.61799387799"))); + assertEvaluates("$atan2", + Value(0.707106781187), + Value(Decimal128("-0.707106781187")), + Value(Decimal128("2.35619449019"))); + assertEvaluates("$atan2", + Value(0.866025403784), + Value(Decimal128("-0.5")), + Value(Decimal128("2.09439510239"))); + assertEvaluates("$atan2", + Value(1.0), + Value(Decimal128("-2.44929359829e-16")), + Value(Decimal128("1.57079632679"))); +} + +TEST(ExpressionArcTangent2Test, DecimalDoubleArg) { + assertEvaluates( + "$atan2", Value(Decimal128("1.0")), Value(0.0), Value(Decimal128("1.57079632679"))); + assertEvaluates("$atan2", + Value(Decimal128("0.866025403784")), + Value(0.5), + Value(Decimal128("1.0471975512"))); + assertEvaluates("$atan2", + Value(Decimal128("0.707106781187")), + Value(0.707106781187), + Value(Decimal128("0.785398163397"))); + assertEvaluates("$atan2", + Value(Decimal128("0.5")), + Value(0.866025403784), + Value(Decimal128("0.523598775598"))); + assertEvaluates("$atan2", + Value(Decimal128("6.12323399574e-17")), + Value(1.0), + Value(Decimal128("6.12323399574e-17"))); + assertEvaluates("$atan2", + Value(Decimal128("-0.5")), + Value(0.866025403784), + Value(Decimal128("-0.523598775598"))); + assertEvaluates("$atan2", + Value(Decimal128("-0.707106781187")), + Value(0.707106781187), + Value(Decimal128("-0.785398163397"))); + assertEvaluates("$atan2", + Value(Decimal128("-0.866025403784")), + Value(0.5), + Value(Decimal128("-1.0471975512"))); + assertEvaluates("$atan2", + Value(Decimal128("-1.0")), + Value(1.22464679915e-16), + Value(Decimal128("-1.57079632679"))); + assertEvaluates("$atan2", + Value(Decimal128("-0.866025403784")), + Value(-0.5), + Value(Decimal128("-2.09439510239"))); + assertEvaluates("$atan2", + Value(Decimal128("-0.707106781187")), + Value(-0.707106781187), + Value(Decimal128("-2.35619449019"))); + assertEvaluates("$atan2", + Value(Decimal128("-0.5")), + Value(-0.866025403784), + Value(Decimal128("-2.61799387799"))); + assertEvaluates("$atan2", + Value(Decimal128("-1.83697019872e-16")), + Value(-1.0), + Value(Decimal128("-3.14159265359"))); + assertEvaluates("$atan2", + Value(Decimal128("0.5")), + Value(-0.866025403784), + Value(Decimal128("2.61799387799"))); + assertEvaluates("$atan2", + Value(Decimal128("0.707106781187")), + Value(-0.707106781187), + Value(Decimal128("2.35619449019"))); + assertEvaluates("$atan2", + Value(Decimal128("0.866025403784")), + Value(-0.5), + Value(Decimal128("2.09439510239"))); + assertEvaluates("$atan2", + Value(Decimal128("1.0")), + Value(-2.44929359829e-16), + Value(Decimal128("1.57079632679"))); +} + +TEST(ExpressionArcTangent2Test, NullArg) { + assertEvaluates("$atan2", Value(BSONNULL), Value(BSONNULL), Value(BSONNULL)); + assertEvaluates("$atan2", Value(1), Value(BSONNULL), Value(BSONNULL)); + assertEvaluates("$atan2", Value(BSONNULL), Value(1), Value(BSONNULL)); +} + +/* ------------------------- ExpressionCosine -------------------------- */ +/** + * Test values were generated using the 64 bit std version of acos, and help + * ensure that we are calling the correct functions. + * + */ + +TEST(ExpressionCosineTest, IntArg) { + assertEvaluates("$cos", Value(0), Value(1.0)); + assertEvaluates("$cos", Value(1), Value(0.540302305868)); + assertEvaluates("$cos", Value(2), Value(-0.416146836547)); + assertEvaluates("$cos", Value(3), Value(-0.9899924966)); + assertEvaluates("$cos", Value(4), Value(-0.653643620864)); + assertEvaluates("$cos", Value(5), Value(0.283662185463)); + assertEvaluates("$cos", Value(6), Value(0.96017028665)); +} + +TEST(ExpressionCosineTest, LongArg) { + assertEvaluates("$cos", Value(0LL), Value(1.0)); + assertEvaluates("$cos", Value(1LL), Value(0.540302305868)); + assertEvaluates("$cos", Value(2LL), Value(-0.416146836547)); + assertEvaluates("$cos", Value(3LL), Value(-0.9899924966)); + assertEvaluates("$cos", Value(4LL), Value(-0.653643620864)); + assertEvaluates("$cos", Value(5LL), Value(0.283662185463)); + assertEvaluates("$cos", Value(6LL), Value(0.96017028665)); +} + +TEST(ExpressionCosineTest, DoubleArg) { + assertEvaluates("$cos", Value(0.0), Value(1.0)); + assertEvaluates("$cos", Value(0.523598775598), Value(0.866025403784)); + assertEvaluates("$cos", Value(0.785398163397), Value(0.707106781187)); + assertEvaluates("$cos", Value(1.0471975512), Value(0.5)); + assertEvaluates("$cos", Value(1.57079632679), Value(6.12323399574e-17)); + assertEvaluates("$cos", Value(2.09439510239), Value(-0.5)); + assertEvaluates("$cos", Value(2.35619449019), Value(-0.707106781187)); + assertEvaluates("$cos", Value(2.61799387799), Value(-0.866025403784)); + assertEvaluates("$cos", Value(3.14159265359), Value(-1.0)); + assertEvaluates("$cos", Value(3.66519142919), Value(-0.866025403784)); + assertEvaluates("$cos", Value(3.92699081699), Value(-0.707106781187)); + assertEvaluates("$cos", Value(4.18879020479), Value(-0.5)); + assertEvaluates("$cos", Value(4.71238898038), Value(-1.83697019872e-16)); + assertEvaluates("$cos", Value(5.23598775598), Value(0.5)); + assertEvaluates("$cos", Value(5.49778714378), Value(0.707106781187)); + assertEvaluates("$cos", Value(5.75958653158), Value(0.866025403784)); + assertEvaluates("$cos", Value(6.28318530718), Value(1.0)); +} + +TEST(ExpressionCosineTest, DecimalArg) { + assertEvaluates("$cos", Value(Decimal128("0.0")), Value(Decimal128("1.0"))); + assertEvaluates( + "$cos", Value(Decimal128("0.523598775598")), Value(Decimal128("0.866025403784"))); + assertEvaluates( + "$cos", Value(Decimal128("0.785398163397")), Value(Decimal128("0.707106781187"))); + assertEvaluates("$cos", Value(Decimal128("1.0471975512")), Value(Decimal128("0.5"))); + assertEvaluates( + "$cos", Value(Decimal128("1.57079632679")), Value(Decimal128("6.12323399574e-17"))); + assertEvaluates("$cos", Value(Decimal128("2.09439510239")), Value(Decimal128("-0.5"))); + assertEvaluates( + "$cos", Value(Decimal128("2.35619449019")), Value(Decimal128("-0.707106781187"))); + assertEvaluates( + "$cos", Value(Decimal128("2.61799387799")), Value(Decimal128("-0.866025403784"))); + assertEvaluates("$cos", Value(Decimal128("3.14159265359")), Value(Decimal128("-1.0"))); + assertEvaluates( + "$cos", Value(Decimal128("3.66519142919")), Value(Decimal128("-0.866025403784"))); + assertEvaluates( + "$cos", Value(Decimal128("3.92699081699")), Value(Decimal128("-0.707106781187"))); + assertEvaluates("$cos", Value(Decimal128("4.18879020479")), Value(Decimal128("-0.5"))); + assertEvaluates( + "$cos", Value(Decimal128("4.71238898038")), Value(Decimal128("-1.83697019872e-16"))); + assertEvaluates("$cos", Value(Decimal128("5.23598775598")), Value(Decimal128("0.5"))); + assertEvaluates( + "$cos", Value(Decimal128("5.49778714378")), Value(Decimal128("0.707106781187"))); + assertEvaluates( + "$cos", Value(Decimal128("5.75958653158")), Value(Decimal128("0.866025403784"))); + assertEvaluates("$cos", Value(Decimal128("6.28318530718")), Value(Decimal128("1.0"))); +} + +TEST(ExpressionCosineTest, NullArg) { + assertEvaluates("$cos", Value(BSONNULL), Value(BSONNULL)); +} + +/* ------------------------- ExpressionHyperbolicCosine -------------------------- */ +/** + * Test values were generated using the 64 bit std version of cosh, and help + * ensure that we are calling the correct functions. + * + */ + +TEST(ExpressionHyperbolicCosineTest, IntArg) { + assertEvaluates("$cosh", Value(0), Value(1.0)); + assertEvaluates("$cosh", Value(1), Value(1.54308063482)); + assertEvaluates("$cosh", Value(2), Value(3.76219569108)); + assertEvaluates("$cosh", Value(3), Value(10.0676619958)); + assertEvaluates("$cosh", Value(4), Value(27.308232836)); + assertEvaluates("$cosh", Value(5), Value(74.2099485248)); + assertEvaluates("$cosh", Value(6), Value(201.715636122)); +} + +TEST(ExpressionHyperbolicCosineTest, LongArg) { + assertEvaluates("$cosh", Value(0LL), Value(1.0)); + assertEvaluates("$cosh", Value(1LL), Value(1.54308063482)); + assertEvaluates("$cosh", Value(2LL), Value(3.76219569108)); + assertEvaluates("$cosh", Value(3LL), Value(10.0676619958)); + assertEvaluates("$cosh", Value(4LL), Value(27.308232836)); + assertEvaluates("$cosh", Value(5LL), Value(74.2099485248)); + assertEvaluates("$cosh", Value(6LL), Value(201.715636122)); +} + +TEST(ExpressionHyperbolicCosineTest, DoubleArg) { + assertEvaluates("$cosh", Value(0.0), Value(1.0)); + assertEvaluates("$cosh", Value(0.523598775598), Value(1.14023832108)); + assertEvaluates("$cosh", Value(0.785398163397), Value(1.32460908925)); + assertEvaluates("$cosh", Value(1.0471975512), Value(1.6002868577)); + assertEvaluates("$cosh", Value(1.57079632679), Value(2.50917847866)); + assertEvaluates("$cosh", Value(2.09439510239), Value(4.12183605387)); + assertEvaluates("$cosh", Value(2.35619449019), Value(5.32275214952)); + assertEvaluates("$cosh", Value(2.61799387799), Value(6.89057236498)); + assertEvaluates("$cosh", Value(3.14159265359), Value(11.5919532755)); + assertEvaluates("$cosh", Value(3.66519142919), Value(19.5446063168)); + assertEvaluates("$cosh", Value(3.92699081699), Value(25.3868611924)); + assertEvaluates("$cosh", Value(4.18879020479), Value(32.97906491)); + assertEvaluates("$cosh", Value(4.71238898038), Value(55.6633808904)); + assertEvaluates("$cosh", Value(5.23598775598), Value(93.9599750339)); + assertEvaluates("$cosh", Value(5.49778714378), Value(122.07757934)); + assertEvaluates("$cosh", Value(5.75958653158), Value(158.610147472)); + assertEvaluates("$cosh", Value(6.28318530718), Value(267.746761484)); +} + +TEST(ExpressionHyperbolicCosineTest, DecimalArg) { + assertEvaluates("$cosh", Value(Decimal128("0.0")), Value(Decimal128("1.0"))); + assertEvaluates( + "$cosh", Value(Decimal128("0.523598775598")), Value(Decimal128("1.14023832108"))); + assertEvaluates( + "$cosh", Value(Decimal128("0.785398163397")), Value(Decimal128("1.32460908925"))); + assertEvaluates("$cosh", Value(Decimal128("1.0471975512")), Value(Decimal128("1.6002868577"))); + assertEvaluates( + "$cosh", Value(Decimal128("1.57079632679")), Value(Decimal128("2.50917847866"))); + assertEvaluates( + "$cosh", Value(Decimal128("2.09439510239")), Value(Decimal128("4.12183605387"))); + assertEvaluates( + "$cosh", Value(Decimal128("2.35619449019")), Value(Decimal128("5.32275214952"))); + assertEvaluates( + "$cosh", Value(Decimal128("2.61799387799")), Value(Decimal128("6.89057236498"))); + assertEvaluates( + "$cosh", Value(Decimal128("3.14159265359")), Value(Decimal128("11.5919532755"))); + assertEvaluates( + "$cosh", Value(Decimal128("3.66519142919")), Value(Decimal128("19.5446063168"))); + assertEvaluates( + "$cosh", Value(Decimal128("3.92699081699")), Value(Decimal128("25.3868611924"))); + assertEvaluates("$cosh", Value(Decimal128("4.18879020479")), Value(Decimal128("32.97906491"))); + assertEvaluates( + "$cosh", Value(Decimal128("4.71238898038")), Value(Decimal128("55.6633808904"))); + assertEvaluates( + "$cosh", Value(Decimal128("5.23598775598")), Value(Decimal128("93.9599750339"))); + assertEvaluates("$cosh", Value(Decimal128("5.49778714378")), Value(Decimal128("122.07757934"))); + assertEvaluates( + "$cosh", Value(Decimal128("5.75958653158")), Value(Decimal128("158.610147472"))); + assertEvaluates( + "$cosh", Value(Decimal128("6.28318530718")), Value(Decimal128("267.746761484"))); +} + +TEST(ExpressionHyperbolicCosineTest, NullArg) { + assertEvaluates("$cosh", Value(BSONNULL), Value(BSONNULL)); +} + +/* ------------------------- ExpressionSine -------------------------- */ +/** + * Test values were generated using the 64 bit std version of sin, and help + * ensure that we are calling the correct functions. + * + */ + +TEST(ExpressionSineTest, IntArg) { + assertEvaluates("$sin", Value(0), Value(0.0)); + assertEvaluates("$sin", Value(1), Value(0.841470984808)); + assertEvaluates("$sin", Value(2), Value(0.909297426826)); + assertEvaluates("$sin", Value(3), Value(0.14112000806)); + assertEvaluates("$sin", Value(4), Value(-0.756802495308)); + assertEvaluates("$sin", Value(5), Value(-0.958924274663)); + assertEvaluates("$sin", Value(6), Value(-0.279415498199)); +} + +TEST(ExpressionSineTest, LongArg) { + assertEvaluates("$sin", Value(0LL), Value(0.0)); + assertEvaluates("$sin", Value(1LL), Value(0.841470984808)); + assertEvaluates("$sin", Value(2LL), Value(0.909297426826)); + assertEvaluates("$sin", Value(3LL), Value(0.14112000806)); + assertEvaluates("$sin", Value(4LL), Value(-0.756802495308)); + assertEvaluates("$sin", Value(5LL), Value(-0.958924274663)); + assertEvaluates("$sin", Value(6LL), Value(-0.279415498199)); +} + +TEST(ExpressionSineTest, DoubleArg) { + assertEvaluates("$sin", Value(0.0), Value(0.0)); + assertEvaluates("$sin", Value(0.523598775598), Value(0.5)); + assertEvaluates("$sin", Value(0.785398163397), Value(0.707106781187)); + assertEvaluates("$sin", Value(1.0471975512), Value(0.866025403784)); + assertEvaluates("$sin", Value(1.57079632679), Value(1.0)); + assertEvaluates("$sin", Value(2.09439510239), Value(0.866025403784)); + assertEvaluates("$sin", Value(2.35619449019), Value(0.707106781187)); + assertEvaluates("$sin", Value(2.61799387799), Value(0.5)); + assertEvaluates("$sin", Value(3.14159265359), Value(1.22464679915e-16)); + assertEvaluates("$sin", Value(3.66519142919), Value(-0.5)); + assertEvaluates("$sin", Value(3.92699081699), Value(-0.707106781187)); + assertEvaluates("$sin", Value(4.18879020479), Value(-0.866025403784)); + assertEvaluates("$sin", Value(4.71238898038), Value(-1.0)); + assertEvaluates("$sin", Value(5.23598775598), Value(-0.866025403784)); + assertEvaluates("$sin", Value(5.49778714378), Value(-0.707106781187)); + assertEvaluates("$sin", Value(5.75958653158), Value(-0.5)); + assertEvaluates("$sin", Value(6.28318530718), Value(-2.44929359829e-16)); +} + +TEST(ExpressionSineTest, DecimalArg) { + assertEvaluates("$sin", Value(Decimal128("0.0")), Value(Decimal128("0.0"))); + assertEvaluates("$sin", Value(Decimal128("0.523598775598")), Value(Decimal128("0.5"))); + assertEvaluates( + "$sin", Value(Decimal128("0.785398163397")), Value(Decimal128("0.707106781187"))); + assertEvaluates("$sin", Value(Decimal128("1.0471975512")), Value(Decimal128("0.866025403784"))); + assertEvaluates("$sin", Value(Decimal128("1.57079632679")), Value(Decimal128("1.0"))); + assertEvaluates( + "$sin", Value(Decimal128("2.09439510239")), Value(Decimal128("0.866025403784"))); + assertEvaluates( + "$sin", Value(Decimal128("2.35619449019")), Value(Decimal128("0.707106781187"))); + assertEvaluates("$sin", Value(Decimal128("2.61799387799")), Value(Decimal128("0.5"))); + assertEvaluates( + "$sin", Value(Decimal128("3.14159265359")), Value(Decimal128("1.22464679915e-16"))); + assertEvaluates("$sin", Value(Decimal128("3.66519142919")), Value(Decimal128("-0.5"))); + assertEvaluates( + "$sin", Value(Decimal128("3.92699081699")), Value(Decimal128("-0.707106781187"))); + assertEvaluates( + "$sin", Value(Decimal128("4.18879020479")), Value(Decimal128("-0.866025403784"))); + assertEvaluates("$sin", Value(Decimal128("4.71238898038")), Value(Decimal128("-1.0"))); + assertEvaluates( + "$sin", Value(Decimal128("5.23598775598")), Value(Decimal128("-0.866025403784"))); + assertEvaluates( + "$sin", Value(Decimal128("5.49778714378")), Value(Decimal128("-0.707106781187"))); + assertEvaluates("$sin", Value(Decimal128("5.75958653158")), Value(Decimal128("-0.5"))); + assertEvaluates( + "$sin", Value(Decimal128("6.28318530718")), Value(Decimal128("-2.44929359829e-16"))); +} + +TEST(ExpressionSineTest, NullArg) { + assertEvaluates("$sin", Value(BSONNULL), Value(BSONNULL)); +} + +/* ------------------------- ExpressionHyperbolicSine -------------------------- */ +/* + * Test values were generated using the 64 bit std version of sinh, and help + * ensure that we are calling the correct functions. + */ + +TEST(ExpressionHyperbolicSineTest, IntArg) { + assertEvaluates("$sinh", Value(0), Value(0.0)); + assertEvaluates("$sinh", Value(1), Value(1.17520119364)); + assertEvaluates("$sinh", Value(2), Value(3.62686040785)); + assertEvaluates("$sinh", Value(3), Value(10.0178749274)); + assertEvaluates("$sinh", Value(4), Value(27.2899171971)); + assertEvaluates("$sinh", Value(5), Value(74.2032105778)); + assertEvaluates("$sinh", Value(6), Value(201.71315737)); +} + +TEST(ExpressionHyperbolicSineTest, LongArg) { + assertEvaluates("$sinh", Value(0LL), Value(0.0)); + assertEvaluates("$sinh", Value(1LL), Value(1.17520119364)); + assertEvaluates("$sinh", Value(2LL), Value(3.62686040785)); + assertEvaluates("$sinh", Value(3LL), Value(10.0178749274)); + assertEvaluates("$sinh", Value(4LL), Value(27.2899171971)); + assertEvaluates("$sinh", Value(5LL), Value(74.2032105778)); + assertEvaluates("$sinh", Value(6LL), Value(201.71315737)); +} + +TEST(ExpressionHyperbolicSineTest, DoubleArg) { + assertEvaluates("$sinh", Value(0.0), Value(0.0)); + assertEvaluates("$sinh", Value(0.523598775598), Value(0.547853473888)); + assertEvaluates("$sinh", Value(0.785398163397), Value(0.868670961486)); + assertEvaluates("$sinh", Value(1.0471975512), Value(1.24936705052)); + assertEvaluates("$sinh", Value(1.57079632679), Value(2.30129890231)); + assertEvaluates("$sinh", Value(2.09439510239), Value(3.9986913428)); + assertEvaluates("$sinh", Value(2.35619449019), Value(5.22797192468)); + assertEvaluates("$sinh", Value(2.61799387799), Value(6.81762330413)); + assertEvaluates("$sinh", Value(3.14159265359), Value(11.5487393573)); + assertEvaluates("$sinh", Value(3.66519142919), Value(19.5190070464)); + assertEvaluates("$sinh", Value(3.92699081699), Value(25.3671583194)); + assertEvaluates("$sinh", Value(4.18879020479), Value(32.9639002901)); + assertEvaluates("$sinh", Value(4.71238898038), Value(55.6543975994)); + assertEvaluates("$sinh", Value(5.23598775598), Value(93.9546534685)); + assertEvaluates("$sinh", Value(5.49778714378), Value(122.073483515)); + assertEvaluates("$sinh", Value(5.75958653158), Value(158.606995057)); + assertEvaluates("$sinh", Value(6.28318530718), Value(267.744894041)); +} + +TEST(ExpressionHyperbolicSineTest, DecimalArg) { + assertEvaluates("$sinh", Value(Decimal128("0.0")), Value(Decimal128("0.0"))); + assertEvaluates( + "$sinh", Value(Decimal128("0.523598775598")), Value(Decimal128("0.547853473888"))); + assertEvaluates( + "$sinh", Value(Decimal128("0.785398163397")), Value(Decimal128("0.868670961486"))); + assertEvaluates("$sinh", Value(Decimal128("1.0471975512")), Value(Decimal128("1.24936705052"))); + assertEvaluates( + "$sinh", Value(Decimal128("1.57079632679")), Value(Decimal128("2.30129890231"))); + assertEvaluates("$sinh", Value(Decimal128("2.09439510239")), Value(Decimal128("3.9986913428"))); + assertEvaluates( + "$sinh", Value(Decimal128("2.35619449019")), Value(Decimal128("5.22797192468"))); + assertEvaluates( + "$sinh", Value(Decimal128("2.61799387799")), Value(Decimal128("6.81762330413"))); + assertEvaluates( + "$sinh", Value(Decimal128("3.14159265359")), Value(Decimal128("11.5487393573"))); + assertEvaluates( + "$sinh", Value(Decimal128("3.66519142919")), Value(Decimal128("19.5190070464"))); + assertEvaluates( + "$sinh", Value(Decimal128("3.92699081699")), Value(Decimal128("25.3671583194"))); + assertEvaluates( + "$sinh", Value(Decimal128("4.18879020479")), Value(Decimal128("32.9639002901"))); + assertEvaluates( + "$sinh", Value(Decimal128("4.71238898038")), Value(Decimal128("55.6543975994"))); + assertEvaluates( + "$sinh", Value(Decimal128("5.23598775598")), Value(Decimal128("93.9546534685"))); + assertEvaluates( + "$sinh", Value(Decimal128("5.49778714378")), Value(Decimal128("122.073483515"))); + assertEvaluates( + "$sinh", Value(Decimal128("5.75958653158")), Value(Decimal128("158.606995057"))); + assertEvaluates( + "$sinh", Value(Decimal128("6.28318530718")), Value(Decimal128("267.744894041"))); +} + +TEST(ExpressionHyperbolicSineTest, NullArg) { + assertEvaluates("$sinh", Value(BSONNULL), Value(BSONNULL)); +} + +/* ------------------------- ExpressionTangent -------------------------- */ +/** + * Test values were generated using the 64 bit std version of tan, and help + * ensure that we are calling the correct functions. + * + */ + +TEST(ExpressionTangentTest, IntArg) { + assertEvaluates("$tan", Value(-1), Value(-1.55740772465)); + assertEvaluates("$tan", Value(0), Value(0.0)); + assertEvaluates("$tan", Value(1), Value(1.55740772465)); +} + +TEST(ExpressionTangentTest, LongArg) { + assertEvaluates("$tan", Value(-1LL), Value(-1.55740772465)); + assertEvaluates("$tan", Value(0LL), Value(0.0)); + assertEvaluates("$tan", Value(1LL), Value(1.55740772465)); +} + +TEST(ExpressionTangentTest, DoubleArg) { + assertEvaluates("$tan", Value(-1.5), Value(-14.1014199472)); + assertEvaluates("$tan", Value(-1.0471975512), Value(-1.73205080757)); + assertEvaluates("$tan", Value(-0.785398163397), Value(-1.0)); + assertEvaluates("$tan", Value(0), Value(0.0)); + assertEvaluates("$tan", Value(0.785398163397), Value(1.0)); + assertEvaluates("$tan", Value(1.0471975512), Value(1.73205080757)); + assertEvaluates("$tan", Value(1.5), Value(14.1014199472)); +} + +TEST(ExpressionTangentTest, DecimalArg) { + assertEvaluates("$tan", Value(Decimal128("-1.5")), Value(Decimal128("-14.1014199472"))); + assertEvaluates( + "$tan", Value(Decimal128("-1.0471975512")), Value(Decimal128("-1.73205080757"))); + assertEvaluates("$tan", Value(Decimal128("-0.785398163397")), Value(Decimal128("-1.0"))); + assertEvaluates("$tan", Value(Decimal128("0")), Value(Decimal128("0.0"))); + assertEvaluates("$tan", Value(Decimal128("0.785398163397")), Value(Decimal128("1.0"))); + assertEvaluates("$tan", Value(Decimal128("1.0471975512")), Value(Decimal128("1.73205080757"))); + assertEvaluates("$tan", Value(Decimal128("1.5")), Value(Decimal128("14.1014199472"))); +} + +TEST(ExpressionTangentTest, NullArg) { + assertEvaluates("$tan", Value(BSONNULL), Value(BSONNULL)); +} + +/* ------------------------- ExpressionHyperbolicTangent -------------------------- */ +/** + * Test values were generated using the 64 bit std version of tanh, and help + * ensure that we are calling the correct functions. + * + */ +TEST(ExpressionHyperbolicTangentTest, IntArg) { + assertEvaluates("$tanh", Value(0), Value(0.0)); + assertEvaluates("$tanh", Value(1), Value(0.761594155956)); + assertEvaluates("$tanh", Value(2), Value(0.964027580076)); + assertEvaluates("$tanh", Value(3), Value(0.995054753687)); + assertEvaluates("$tanh", Value(4), Value(0.999329299739)); + assertEvaluates("$tanh", Value(5), Value(0.999909204263)); + assertEvaluates("$tanh", Value(6), Value(0.999987711651)); +} + +TEST(ExpressionHyperbolicTangentTest, LongArg) { + assertEvaluates("$tanh", Value(0LL), Value(0.0)); + assertEvaluates("$tanh", Value(1LL), Value(0.761594155956)); + assertEvaluates("$tanh", Value(2LL), Value(0.964027580076)); + assertEvaluates("$tanh", Value(3LL), Value(0.995054753687)); + assertEvaluates("$tanh", Value(4LL), Value(0.999329299739)); + assertEvaluates("$tanh", Value(5LL), Value(0.999909204263)); + assertEvaluates("$tanh", Value(6LL), Value(0.999987711651)); +} + +TEST(ExpressionHyperbolicTangentTest, DoubleArg) { + assertEvaluates("$tanh", Value(0.0), Value(0.0)); + assertEvaluates("$tanh", Value(0.523598775598), Value(0.480472778156)); + assertEvaluates("$tanh", Value(0.785398163397), Value(0.655794202633)); + assertEvaluates("$tanh", Value(1.0471975512), Value(0.780714435359)); + assertEvaluates("$tanh", Value(1.57079632679), Value(0.917152335667)); + assertEvaluates("$tanh", Value(2.09439510239), Value(0.970123821166)); + assertEvaluates("$tanh", Value(2.35619449019), Value(0.982193380007)); + assertEvaluates("$tanh", Value(2.61799387799), Value(0.989413207353)); + assertEvaluates("$tanh", Value(3.14159265359), Value(0.996272076221)); + assertEvaluates("$tanh", Value(3.66519142919), Value(0.998690213046)); + assertEvaluates("$tanh", Value(3.92699081699), Value(0.999223894879)); + assertEvaluates("$tanh", Value(4.18879020479), Value(0.999540174353)); + assertEvaluates("$tanh", Value(4.71238898038), Value(0.999838613989)); + assertEvaluates("$tanh", Value(5.23598775598), Value(0.999943363486)); + assertEvaluates("$tanh", Value(5.49778714378), Value(0.999966449)); + assertEvaluates("$tanh", Value(5.75958653158), Value(0.99998012476)); + assertEvaluates("$tanh", Value(6.28318530718), Value(0.99999302534)); +} + +TEST(ExpressionHyperbolicTangentTest, DecimalArg) { + assertEvaluates("$tanh", Value(Decimal128("0.0")), Value(Decimal128("0.0"))); + assertEvaluates( + "$tanh", Value(Decimal128("0.523598775598")), Value(Decimal128("0.480472778156"))); + assertEvaluates( + "$tanh", Value(Decimal128("0.785398163397")), Value(Decimal128("0.655794202633"))); + assertEvaluates( + "$tanh", Value(Decimal128("1.0471975512")), Value(Decimal128("0.780714435359"))); + assertEvaluates( + "$tanh", Value(Decimal128("1.57079632679")), Value(Decimal128("0.917152335667"))); + assertEvaluates( + "$tanh", Value(Decimal128("2.09439510239")), Value(Decimal128("0.970123821166"))); + assertEvaluates( + "$tanh", Value(Decimal128("2.35619449019")), Value(Decimal128("0.982193380007"))); + assertEvaluates( + "$tanh", Value(Decimal128("2.61799387799")), Value(Decimal128("0.989413207353"))); + assertEvaluates( + "$tanh", Value(Decimal128("3.14159265359")), Value(Decimal128("0.996272076221"))); + assertEvaluates( + "$tanh", Value(Decimal128("3.66519142919")), Value(Decimal128("0.998690213046"))); + assertEvaluates( + "$tanh", Value(Decimal128("3.92699081699")), Value(Decimal128("0.999223894879"))); + assertEvaluates( + "$tanh", Value(Decimal128("4.18879020479")), Value(Decimal128("0.999540174353"))); + assertEvaluates( + "$tanh", Value(Decimal128("4.71238898038")), Value(Decimal128("0.999838613989"))); + assertEvaluates( + "$tanh", Value(Decimal128("5.23598775598")), Value(Decimal128("0.999943363486"))); + assertEvaluates("$tanh", Value(Decimal128("5.49778714378")), Value(Decimal128("0.999966449"))); + assertEvaluates( + "$tanh", Value(Decimal128("5.75958653158")), Value(Decimal128("0.99998012476"))); + assertEvaluates( + "$tanh", Value(Decimal128("6.28318530718")), Value(Decimal128("0.99999302534"))); +} + +TEST(ExpressionHyperbolicTangentTest, NullArg) { + assertEvaluates("$tanh", Value(BSONNULL), Value(BSONNULL)); +} + +/* ------------------------- ExpressionHyperbolicArcCosine -------------------------- */ +/** + * Test values were generated using the 64 bit std version of acosh, and help + * ensure that we are calling the correct functions. + * + */ + +TEST(ExpressionHyperbolicArcCosineTest, IntArg) { + assertEvaluates("$acosh", Value(1), Value(0.000000)); + assertEvaluates("$acosh", Value(2), Value(1.316958)); + assertEvaluates("$acosh", Value(3), Value(1.762747)); + assertEvaluates("$acosh", Value(4), Value(2.063437)); + assertEvaluates("$acosh", Value(5), Value(2.292432)); + assertEvaluates("$acosh", Value(6), Value(2.477889)); + assertEvaluates("$acosh", Value(7), Value(2.633916)); + assertEvaluates("$acosh", Value(8), Value(2.768659)); + assertEvaluates("$acosh", Value(9), Value(2.887271)); +} + +TEST(ExpressionHyperbolicArcCosineTest, LongArg) { + assertEvaluates("$acosh", Value(1LL), Value(0.000000)); + assertEvaluates("$acosh", Value(2LL), Value(1.316958)); + assertEvaluates("$acosh", Value(3LL), Value(1.762747)); + assertEvaluates("$acosh", Value(4LL), Value(2.063437)); + assertEvaluates("$acosh", Value(5LL), Value(2.292432)); + assertEvaluates("$acosh", Value(6LL), Value(2.477889)); + assertEvaluates("$acosh", Value(7LL), Value(2.633916)); + assertEvaluates("$acosh", Value(8LL), Value(2.768659)); + assertEvaluates("$acosh", Value(9LL), Value(2.887271)); +} + +TEST(ExpressionHyperbolicArcCosineTest, DoubleArg) { + assertEvaluates("$acosh", Value(1.000000), Value(0.000000)); + assertEvaluates("$acosh", Value(1.200000), Value(0.622363)); + assertEvaluates("$acosh", Value(1.400000), Value(0.867015)); + assertEvaluates("$acosh", Value(1.600000), Value(1.046968)); + assertEvaluates("$acosh", Value(1.800000), Value(1.192911)); + assertEvaluates("$acosh", Value(2.000000), Value(1.316958)); + assertEvaluates("$acosh", Value(2.200000), Value(1.425417)); + assertEvaluates("$acosh", Value(2.400000), Value(1.522079)); + assertEvaluates("$acosh", Value(2.600000), Value(1.609438)); + assertEvaluates("$acosh", Value(2.800000), Value(1.689236)); + assertEvaluates("$acosh", Value(3.000000), Value(1.762747)); + assertEvaluates("$acosh", Value(3.200000), Value(1.830938)); + assertEvaluates("$acosh", Value(3.400000), Value(1.894559)); + assertEvaluates("$acosh", Value(3.600000), Value(1.954208)); + assertEvaluates("$acosh", Value(3.800000), Value(2.010367)); + assertEvaluates("$acosh", Value(4.000000), Value(2.063437)); + assertEvaluates("$acosh", Value(4.200000), Value(2.113748)); + assertEvaluates("$acosh", Value(4.400000), Value(2.161581)); + assertEvaluates("$acosh", Value(4.600000), Value(2.207174)); + assertEvaluates("$acosh", Value(4.800000), Value(2.250731)); + assertEvaluates("$acosh", Value(5.000000), Value(2.292432)); + assertEvaluates("$acosh", Value(5.200000), Value(2.332429)); + assertEvaluates("$acosh", Value(5.400000), Value(2.370860)); + assertEvaluates("$acosh", Value(5.600000), Value(2.407845)); + assertEvaluates("$acosh", Value(5.800000), Value(2.443489)); + assertEvaluates("$acosh", Value(6.000000), Value(2.477889)); + assertEvaluates("$acosh", Value(6.200000), Value(2.511128)); + assertEvaluates("$acosh", Value(6.400000), Value(2.543285)); + assertEvaluates("$acosh", Value(6.600000), Value(2.574428)); + assertEvaluates("$acosh", Value(6.800000), Value(2.604619)); + assertEvaluates("$acosh", Value(7.000000), Value(2.633916)); + assertEvaluates("$acosh", Value(7.200000), Value(2.662370)); + assertEvaluates("$acosh", Value(7.400000), Value(2.690030)); + assertEvaluates("$acosh", Value(7.600000), Value(2.716939)); + assertEvaluates("$acosh", Value(7.800000), Value(2.743136)); + assertEvaluates("$acosh", Value(8.000000), Value(2.768659)); + assertEvaluates("$acosh", Value(8.200000), Value(2.793542)); + assertEvaluates("$acosh", Value(8.400000), Value(2.817817)); + assertEvaluates("$acosh", Value(8.600000), Value(2.841512)); + assertEvaluates("$acosh", Value(8.800000), Value(2.864655)); + assertEvaluates("$acosh", Value(9.000000), Value(2.887271)); + assertEvaluates("$acosh", Value(9.200000), Value(2.909384)); + assertEvaluates("$acosh", Value(9.400000), Value(2.931015)); + assertEvaluates("$acosh", Value(9.600000), Value(2.952187)); + assertEvaluates("$acosh", Value(9.800000), Value(2.972916)); + assertEvaluates("$acosh", Value(10.000000), Value(2.993223)); +} + +TEST(ExpressionHyperbolicArcCosineTest, DecimalArg) { + assertEvaluates("$acosh", Value(Decimal128(1.000000)), Value(Decimal128(0.000000))); + assertEvaluates("$acosh", Value(Decimal128(1.200000)), Value(Decimal128(0.622363))); + assertEvaluates("$acosh", Value(Decimal128(1.400000)), Value(Decimal128(0.867015))); + assertEvaluates("$acosh", Value(Decimal128(1.600000)), Value(Decimal128(1.046968))); + assertEvaluates("$acosh", Value(Decimal128(1.800000)), Value(Decimal128(1.192911))); + assertEvaluates("$acosh", Value(Decimal128(2.000000)), Value(Decimal128(1.316958))); + assertEvaluates("$acosh", Value(Decimal128(2.200000)), Value(Decimal128(1.425417))); + assertEvaluates("$acosh", Value(Decimal128(2.400000)), Value(Decimal128(1.522079))); + assertEvaluates("$acosh", Value(Decimal128(2.600000)), Value(Decimal128(1.609438))); + assertEvaluates("$acosh", Value(Decimal128(2.800000)), Value(Decimal128(1.689236))); + assertEvaluates("$acosh", Value(Decimal128(3.000000)), Value(Decimal128(1.762747))); + assertEvaluates("$acosh", Value(Decimal128(3.200000)), Value(Decimal128(1.830938))); + assertEvaluates("$acosh", Value(Decimal128(3.400000)), Value(Decimal128(1.894559))); + assertEvaluates("$acosh", Value(Decimal128(3.600000)), Value(Decimal128(1.954208))); + assertEvaluates("$acosh", Value(Decimal128(3.800000)), Value(Decimal128(2.010367))); + assertEvaluates("$acosh", Value(Decimal128(4.000000)), Value(Decimal128(2.063437))); + assertEvaluates("$acosh", Value(Decimal128(4.200000)), Value(Decimal128(2.113748))); + assertEvaluates("$acosh", Value(Decimal128(4.400000)), Value(Decimal128(2.161581))); + assertEvaluates("$acosh", Value(Decimal128(4.600000)), Value(Decimal128(2.207174))); + assertEvaluates("$acosh", Value(Decimal128(4.800000)), Value(Decimal128(2.250731))); + assertEvaluates("$acosh", Value(Decimal128(5.000000)), Value(Decimal128(2.292432))); + assertEvaluates("$acosh", Value(Decimal128(5.200000)), Value(Decimal128(2.332429))); + assertEvaluates("$acosh", Value(Decimal128(5.400000)), Value(Decimal128(2.370860))); + assertEvaluates("$acosh", Value(Decimal128(5.600000)), Value(Decimal128(2.407845))); + assertEvaluates("$acosh", Value(Decimal128(5.800000)), Value(Decimal128(2.443489))); + assertEvaluates("$acosh", Value(Decimal128(6.000000)), Value(Decimal128(2.477889))); + assertEvaluates("$acosh", Value(Decimal128(6.200000)), Value(Decimal128(2.511128))); + assertEvaluates("$acosh", Value(Decimal128(6.400000)), Value(Decimal128(2.543285))); + assertEvaluates("$acosh", Value(Decimal128(6.600000)), Value(Decimal128(2.574428))); + assertEvaluates("$acosh", Value(Decimal128(6.800000)), Value(Decimal128(2.604619))); + assertEvaluates("$acosh", Value(Decimal128(7.000000)), Value(Decimal128(2.633916))); + assertEvaluates("$acosh", Value(Decimal128(7.200000)), Value(Decimal128(2.662370))); + assertEvaluates("$acosh", Value(Decimal128(7.400000)), Value(Decimal128(2.690030))); + assertEvaluates("$acosh", Value(Decimal128(7.600000)), Value(Decimal128(2.716939))); + assertEvaluates("$acosh", Value(Decimal128(7.800000)), Value(Decimal128(2.743136))); + assertEvaluates("$acosh", Value(Decimal128(8.000000)), Value(Decimal128(2.768659))); + assertEvaluates("$acosh", Value(Decimal128(8.200000)), Value(Decimal128(2.793542))); + assertEvaluates("$acosh", Value(Decimal128(8.400000)), Value(Decimal128(2.817817))); + assertEvaluates("$acosh", Value(Decimal128(8.600000)), Value(Decimal128(2.841512))); + assertEvaluates("$acosh", Value(Decimal128(8.800000)), Value(Decimal128(2.864655))); + assertEvaluates("$acosh", Value(Decimal128(9.000000)), Value(Decimal128(2.887271))); + assertEvaluates("$acosh", Value(Decimal128(9.200000)), Value(Decimal128(2.909384))); + assertEvaluates("$acosh", Value(Decimal128(9.400000)), Value(Decimal128(2.931015))); + assertEvaluates("$acosh", Value(Decimal128(9.600000)), Value(Decimal128(2.952187))); + assertEvaluates("$acosh", Value(Decimal128(9.800000)), Value(Decimal128(2.972916))); + assertEvaluates("$acosh", Value(Decimal128(10.000000)), Value(Decimal128(2.993223))); +} + +TEST(ExpressionHyperbolicArcCosineTest, NullArg) { + assertEvaluates("$acosh", Value(BSONNULL), Value(BSONNULL)); +} + +/* ------------------------- ExpressionHyperbolicArcSine -------------------------- */ +/** + * Test values were generated using the 64 bit std version of asinh, and help + * ensure that we are calling the correct functions. + * + */ + +TEST(ExpressionHyperbolicArcSineTest, IntArg) { + assertEvaluates("$asinh", Value(1), Value(0.881374)); + assertEvaluates("$asinh", Value(2), Value(1.443635)); + assertEvaluates("$asinh", Value(3), Value(1.818446)); + assertEvaluates("$asinh", Value(4), Value(2.094713)); + assertEvaluates("$asinh", Value(5), Value(2.312438)); + assertEvaluates("$asinh", Value(6), Value(2.491780)); + assertEvaluates("$asinh", Value(7), Value(2.644121)); + assertEvaluates("$asinh", Value(8), Value(2.776472)); + assertEvaluates("$asinh", Value(9), Value(2.893444)); +} + +TEST(ExpressionHyperbolicArcSineTest, LongArg) { + assertEvaluates("$asinh", Value(1LL), Value(0.881374)); + assertEvaluates("$asinh", Value(2LL), Value(1.443635)); + assertEvaluates("$asinh", Value(3LL), Value(1.818446)); + assertEvaluates("$asinh", Value(4LL), Value(2.094713)); + assertEvaluates("$asinh", Value(5LL), Value(2.312438)); + assertEvaluates("$asinh", Value(6LL), Value(2.491780)); + assertEvaluates("$asinh", Value(7LL), Value(2.644121)); + assertEvaluates("$asinh", Value(8LL), Value(2.776472)); + assertEvaluates("$asinh", Value(9LL), Value(2.893444)); +} + +TEST(ExpressionHyperbolicArcSineTest, DoubleArg) { + assertEvaluates("$asinh", Value(1.000000), Value(0.881374)); + assertEvaluates("$asinh", Value(1.200000), Value(1.015973)); + assertEvaluates("$asinh", Value(1.400000), Value(1.137982)); + assertEvaluates("$asinh", Value(1.600000), Value(1.248983)); + assertEvaluates("$asinh", Value(1.800000), Value(1.350441)); + assertEvaluates("$asinh", Value(2.000000), Value(1.443635)); + assertEvaluates("$asinh", Value(2.200000), Value(1.529660)); + assertEvaluates("$asinh", Value(2.400000), Value(1.609438)); + assertEvaluates("$asinh", Value(2.600000), Value(1.683743)); + assertEvaluates("$asinh", Value(2.800000), Value(1.753229)); + assertEvaluates("$asinh", Value(3.000000), Value(1.818446)); + assertEvaluates("$asinh", Value(3.200000), Value(1.879864)); + assertEvaluates("$asinh", Value(3.400000), Value(1.937879)); + assertEvaluates("$asinh", Value(3.600000), Value(1.992836)); + assertEvaluates("$asinh", Value(3.800000), Value(2.045028)); + assertEvaluates("$asinh", Value(4.000000), Value(2.094713)); + assertEvaluates("$asinh", Value(4.200000), Value(2.142112)); + assertEvaluates("$asinh", Value(4.400000), Value(2.187422)); + assertEvaluates("$asinh", Value(4.600000), Value(2.230814)); + assertEvaluates("$asinh", Value(4.800000), Value(2.272441)); + assertEvaluates("$asinh", Value(5.000000), Value(2.312438)); + assertEvaluates("$asinh", Value(5.200000), Value(2.350926)); + assertEvaluates("$asinh", Value(5.400000), Value(2.388011)); + assertEvaluates("$asinh", Value(5.600000), Value(2.423792)); + assertEvaluates("$asinh", Value(5.800000), Value(2.458355)); + assertEvaluates("$asinh", Value(6.000000), Value(2.491780)); + assertEvaluates("$asinh", Value(6.200000), Value(2.524138)); + assertEvaluates("$asinh", Value(6.400000), Value(2.555494)); + assertEvaluates("$asinh", Value(6.600000), Value(2.585907)); + assertEvaluates("$asinh", Value(6.800000), Value(2.615433)); + assertEvaluates("$asinh", Value(7.000000), Value(2.644121)); + assertEvaluates("$asinh", Value(7.200000), Value(2.672016)); + assertEvaluates("$asinh", Value(7.400000), Value(2.699162)); + assertEvaluates("$asinh", Value(7.600000), Value(2.725596)); + assertEvaluates("$asinh", Value(7.800000), Value(2.751355)); + assertEvaluates("$asinh", Value(8.000000), Value(2.776472)); + assertEvaluates("$asinh", Value(8.200000), Value(2.800979)); + assertEvaluates("$asinh", Value(8.400000), Value(2.824903)); + assertEvaluates("$asinh", Value(8.600000), Value(2.848273)); + assertEvaluates("$asinh", Value(8.800000), Value(2.871112)); + assertEvaluates("$asinh", Value(9.000000), Value(2.893444)); + assertEvaluates("$asinh", Value(9.200000), Value(2.915291)); + assertEvaluates("$asinh", Value(9.400000), Value(2.936674)); + assertEvaluates("$asinh", Value(9.600000), Value(2.957612)); + assertEvaluates("$asinh", Value(9.800000), Value(2.978123)); + assertEvaluates("$asinh", Value(10.000000), Value(2.998223)); +} + +TEST(ExpressionHyperbolicArcSineTest, DecimalArg) { + assertEvaluates("$asinh", Value(Decimal128(1.000000)), Value(Decimal128(0.881374))); + assertEvaluates("$asinh", Value(Decimal128(1.200000)), Value(Decimal128(1.015973))); + assertEvaluates("$asinh", Value(Decimal128(1.400000)), Value(Decimal128(1.137982))); + assertEvaluates("$asinh", Value(Decimal128(1.600000)), Value(Decimal128(1.248983))); + assertEvaluates("$asinh", Value(Decimal128(1.800000)), Value(Decimal128(1.350441))); + assertEvaluates("$asinh", Value(Decimal128(2.000000)), Value(Decimal128(1.443635))); + assertEvaluates("$asinh", Value(Decimal128(2.200000)), Value(Decimal128(1.529660))); + assertEvaluates("$asinh", Value(Decimal128(2.400000)), Value(Decimal128(1.609438))); + assertEvaluates("$asinh", Value(Decimal128(2.600000)), Value(Decimal128(1.683743))); + assertEvaluates("$asinh", Value(Decimal128(2.800000)), Value(Decimal128(1.753229))); + assertEvaluates("$asinh", Value(Decimal128(3.000000)), Value(Decimal128(1.818446))); + assertEvaluates("$asinh", Value(Decimal128(3.200000)), Value(Decimal128(1.879864))); + assertEvaluates("$asinh", Value(Decimal128(3.400000)), Value(Decimal128(1.937879))); + assertEvaluates("$asinh", Value(Decimal128(3.600000)), Value(Decimal128(1.992836))); + assertEvaluates("$asinh", Value(Decimal128(3.800000)), Value(Decimal128(2.045028))); + assertEvaluates("$asinh", Value(Decimal128(4.000000)), Value(Decimal128(2.094713))); + assertEvaluates("$asinh", Value(Decimal128(4.200000)), Value(Decimal128(2.142112))); + assertEvaluates("$asinh", Value(Decimal128(4.400000)), Value(Decimal128(2.187422))); + assertEvaluates("$asinh", Value(Decimal128(4.600000)), Value(Decimal128(2.230814))); + assertEvaluates("$asinh", Value(Decimal128(4.800000)), Value(Decimal128(2.272441))); + assertEvaluates("$asinh", Value(Decimal128(5.000000)), Value(Decimal128(2.312438))); + assertEvaluates("$asinh", Value(Decimal128(5.200000)), Value(Decimal128(2.350926))); + assertEvaluates("$asinh", Value(Decimal128(5.400000)), Value(Decimal128(2.388011))); + assertEvaluates("$asinh", Value(Decimal128(5.600000)), Value(Decimal128(2.423792))); + assertEvaluates("$asinh", Value(Decimal128(5.800000)), Value(Decimal128(2.458355))); + assertEvaluates("$asinh", Value(Decimal128(6.000000)), Value(Decimal128(2.491780))); + assertEvaluates("$asinh", Value(Decimal128(6.200000)), Value(Decimal128(2.524138))); + assertEvaluates("$asinh", Value(Decimal128(6.400000)), Value(Decimal128(2.555494))); + assertEvaluates("$asinh", Value(Decimal128(6.600000)), Value(Decimal128(2.585907))); + assertEvaluates("$asinh", Value(Decimal128(6.800000)), Value(Decimal128(2.615433))); + assertEvaluates("$asinh", Value(Decimal128(7.000000)), Value(Decimal128(2.644121))); + assertEvaluates("$asinh", Value(Decimal128(7.200000)), Value(Decimal128(2.672016))); + assertEvaluates("$asinh", Value(Decimal128(7.400000)), Value(Decimal128(2.699162))); + assertEvaluates("$asinh", Value(Decimal128(7.600000)), Value(Decimal128(2.725596))); + assertEvaluates("$asinh", Value(Decimal128(7.800000)), Value(Decimal128(2.751355))); + assertEvaluates("$asinh", Value(Decimal128(8.000000)), Value(Decimal128(2.776472))); + assertEvaluates("$asinh", Value(Decimal128(8.200000)), Value(Decimal128(2.800979))); + assertEvaluates("$asinh", Value(Decimal128(8.400000)), Value(Decimal128(2.824903))); + assertEvaluates("$asinh", Value(Decimal128(8.600000)), Value(Decimal128(2.848273))); + assertEvaluates("$asinh", Value(Decimal128(8.800000)), Value(Decimal128(2.871112))); + assertEvaluates("$asinh", Value(Decimal128(9.000000)), Value(Decimal128(2.893444))); + assertEvaluates("$asinh", Value(Decimal128(9.200000)), Value(Decimal128(2.915291))); + assertEvaluates("$asinh", Value(Decimal128(9.400000)), Value(Decimal128(2.936674))); + assertEvaluates("$asinh", Value(Decimal128(9.600000)), Value(Decimal128(2.957612))); + assertEvaluates("$asinh", Value(Decimal128(9.800000)), Value(Decimal128(2.978123))); + assertEvaluates("$asinh", Value(Decimal128(10.000000)), Value(Decimal128(2.998223))); +} + +TEST(ExpressionHyperbolicArcSineTest, NullArg) { + assertEvaluates("$asinh", Value(BSONNULL), Value(BSONNULL)); +} + +/* ------------------------- ExpressionHyperbolicArcTangent -------------------------- */ +/** + * Test values were generated using the 64 bit std version of tanh, and help + * ensure that we are calling the correct functions. + * + */ +TEST(ExpressionHyperbolicArcTangentTest, IntArg) { + assertEvaluates("$atanh", Value(0), Value(0.000000)); +} + +TEST(ExpressionHyperbolicArcTangentTest, LongArg) { + assertEvaluates("$atanh", Value(0LL), Value(0.000000)); +} + +TEST(ExpressionHyperbolicArcTangentTest, DoubleArg) { + assertEvaluates("$atanh", Value(-0.990000), Value(-2.646652)); + assertEvaluates("$atanh", Value(-0.790000), Value(-1.071432)); + assertEvaluates("$atanh", Value(-0.590000), Value(-0.677666)); + assertEvaluates("$atanh", Value(-0.390000), Value(-0.411800)); + assertEvaluates("$atanh", Value(-0.190000), Value(-0.192337)); + assertEvaluates("$atanh", Value(0.010000), Value(0.010000)); + assertEvaluates("$atanh", Value(0.210000), Value(0.213171)); + assertEvaluates("$atanh", Value(0.410000), Value(0.435611)); + assertEvaluates("$atanh", Value(0.610000), Value(0.708921)); + assertEvaluates("$atanh", Value(0.810000), Value(1.127029)); +} + +TEST(ExpressionHyperbolicArcTangentTest, DecimalArg) { + assertEvaluates("$atanh", Value(Decimal128(-0.990000)), Value(Decimal128(-2.646652))); + assertEvaluates("$atanh", Value(Decimal128(-0.790000)), Value(Decimal128(-1.071432))); + assertEvaluates("$atanh", Value(Decimal128(-0.590000)), Value(Decimal128(-0.677666))); + assertEvaluates("$atanh", Value(Decimal128(-0.390000)), Value(Decimal128(-0.411800))); + assertEvaluates("$atanh", Value(Decimal128(-0.190000)), Value(Decimal128(-0.192337))); + assertEvaluates("$atanh", Value(Decimal128(0.010000)), Value(Decimal128(0.010000))); + assertEvaluates("$atanh", Value(Decimal128(0.210000)), Value(Decimal128(0.213171))); + assertEvaluates("$atanh", Value(Decimal128(0.410000)), Value(Decimal128(0.435611))); + assertEvaluates("$atanh", Value(Decimal128(0.610000)), Value(Decimal128(0.708921))); + assertEvaluates("$atanh", Value(Decimal128(0.810000)), Value(Decimal128(1.127029))); +} + +TEST(ExpressionHyperbolicArcTangentTest, NullArg) { + assertEvaluates("$atanh", Value(BSONNULL), Value(BSONNULL)); +} + +/* ------------------------- ExpressionRadiansToDegrees -------------------------- */ +TEST(ExpressionRadiansToDegreesTest, IntArg) { + assertEvaluates("$radiansToDegrees", Value(0), Value(0.0)); + assertEvaluates("$radiansToDegrees", Value(1), Value(57.2957795131)); + assertEvaluates("$radiansToDegrees", Value(2), Value(114.591559026)); + assertEvaluates("$radiansToDegrees", Value(3), Value(171.887338539)); + assertEvaluates("$radiansToDegrees", Value(4), Value(229.183118052)); + assertEvaluates("$radiansToDegrees", Value(5), Value(286.478897565)); + assertEvaluates("$radiansToDegrees", Value(6), Value(343.774677078)); +} + +TEST(ExpressionRadiansToDegreesTest, LongArg) { + assertEvaluates("$radiansToDegrees", Value(0LL), Value(0.0)); + assertEvaluates("$radiansToDegrees", Value(1LL), Value(57.2957795131)); + assertEvaluates("$radiansToDegrees", Value(2LL), Value(114.591559026)); + assertEvaluates("$radiansToDegrees", Value(3LL), Value(171.887338539)); + assertEvaluates("$radiansToDegrees", Value(4LL), Value(229.183118052)); + assertEvaluates("$radiansToDegrees", Value(5LL), Value(286.478897565)); + assertEvaluates("$radiansToDegrees", Value(6LL), Value(343.774677078)); +} + +TEST(ExpressionRadiansToDegreesTest, DoubleArg) { + assertEvaluates("$radiansToDegrees", Value(0.0), Value(0.0)); + assertEvaluates("$radiansToDegrees", Value(0.523598775598), Value(30.0)); + assertEvaluates("$radiansToDegrees", Value(0.785398163397), Value(45.0)); + assertEvaluates("$radiansToDegrees", Value(1.0471975512), Value(60.0)); + assertEvaluates("$radiansToDegrees", Value(1.57079632679), Value(90.0)); + assertEvaluates("$radiansToDegrees", Value(2.09439510239), Value(120.0)); + assertEvaluates("$radiansToDegrees", Value(2.35619449019), Value(135.0)); + assertEvaluates("$radiansToDegrees", Value(2.61799387799), Value(150.0)); + assertEvaluates("$radiansToDegrees", Value(3.14159265359), Value(180.0)); + assertEvaluates("$radiansToDegrees", Value(3.66519142919), Value(210.0)); + assertEvaluates("$radiansToDegrees", Value(3.92699081699), Value(225.0)); + assertEvaluates("$radiansToDegrees", Value(4.18879020479), Value(240.0)); + assertEvaluates("$radiansToDegrees", Value(4.71238898038), Value(270.0)); + assertEvaluates("$radiansToDegrees", Value(5.23598775598), Value(300.0)); + assertEvaluates("$radiansToDegrees", Value(5.49778714378), Value(315.0)); + assertEvaluates("$radiansToDegrees", Value(5.75958653158), Value(330.0)); + assertEvaluates("$radiansToDegrees", Value(6.28318530718), Value(360.0)); +} + +TEST(ExpressionRadiansToDegreesTest, DecimalArg) { + assertEvaluates("$radiansToDegrees", Value(Decimal128("0.0")), Value(Decimal128("0.0"))); + assertEvaluates( + "$radiansToDegrees", Value(Decimal128("0.523598775598")), Value(Decimal128("30.0"))); + assertEvaluates( + "$radiansToDegrees", Value(Decimal128("0.785398163397")), Value(Decimal128("45.0"))); + assertEvaluates( + "$radiansToDegrees", Value(Decimal128("1.0471975512")), Value(Decimal128("60.0"))); + assertEvaluates( + "$radiansToDegrees", Value(Decimal128("1.57079632679")), Value(Decimal128("90.0"))); + assertEvaluates( + "$radiansToDegrees", Value(Decimal128("2.09439510239")), Value(Decimal128("120.0"))); + assertEvaluates( + "$radiansToDegrees", Value(Decimal128("2.35619449019")), Value(Decimal128("135.0"))); + assertEvaluates( + "$radiansToDegrees", Value(Decimal128("2.61799387799")), Value(Decimal128("150.0"))); + assertEvaluates( + "$radiansToDegrees", Value(Decimal128("3.14159265359")), Value(Decimal128("180.0"))); + assertEvaluates( + "$radiansToDegrees", Value(Decimal128("3.66519142919")), Value(Decimal128("210.0"))); + assertEvaluates( + "$radiansToDegrees", Value(Decimal128("3.92699081699")), Value(Decimal128("225.0"))); + assertEvaluates( + "$radiansToDegrees", Value(Decimal128("4.18879020479")), Value(Decimal128("240.0"))); + assertEvaluates( + "$radiansToDegrees", Value(Decimal128("4.71238898038")), Value(Decimal128("270.0"))); + assertEvaluates( + "$radiansToDegrees", Value(Decimal128("5.23598775598")), Value(Decimal128("300.0"))); + assertEvaluates( + "$radiansToDegrees", Value(Decimal128("5.49778714378")), Value(Decimal128("315.0"))); + assertEvaluates( + "$radiansToDegrees", Value(Decimal128("5.75958653158")), Value(Decimal128("330.0"))); + assertEvaluates( + "$radiansToDegrees", Value(Decimal128("6.28318530718")), Value(Decimal128("360.0"))); +} + +TEST(ExpressionRadiansToDegreesTest, NullArg) { + assertEvaluates("$radiansToDegrees", Value(BSONNULL), Value(BSONNULL)); +} + +/* ------------------------- ExpressionDegreesToRadians -------------------------- */ +TEST(ExpressionDegreesToRadiansTest, IntArg) { + assertEvaluates("$degreesToRadians", Value(0), Value(0.0)); + assertEvaluates("$degreesToRadians", Value(45), Value(0.785398163397)); + assertEvaluates("$degreesToRadians", Value(90), Value(1.57079632679)); + assertEvaluates("$degreesToRadians", Value(135), Value(2.35619449019)); + assertEvaluates("$degreesToRadians", Value(180), Value(3.14159265359)); + assertEvaluates("$degreesToRadians", Value(225), Value(3.92699081699)); + assertEvaluates("$degreesToRadians", Value(270), Value(4.71238898038)); + assertEvaluates("$degreesToRadians", Value(315), Value(5.49778714378)); + assertEvaluates("$degreesToRadians", Value(360), Value(6.28318530718)); +} + +TEST(ExpressionDegreesToRadiansTest, LongArg) { + assertEvaluates("$degreesToRadians", Value(0LL), Value(0.0)); + assertEvaluates("$degreesToRadians", Value(45LL), Value(0.785398163397)); + assertEvaluates("$degreesToRadians", Value(90LL), Value(1.57079632679)); + assertEvaluates("$degreesToRadians", Value(135LL), Value(2.35619449019)); + assertEvaluates("$degreesToRadians", Value(180LL), Value(3.14159265359)); + assertEvaluates("$degreesToRadians", Value(225LL), Value(3.92699081699)); + assertEvaluates("$degreesToRadians", Value(270LL), Value(4.71238898038)); + assertEvaluates("$degreesToRadians", Value(315LL), Value(5.49778714378)); + assertEvaluates("$degreesToRadians", Value(360LL), Value(6.28318530718)); +} + +TEST(ExpressionDegreesToRadiansTest, DoubleArg) { + assertEvaluates("$degreesToRadians", Value(0), Value(0.0)); + assertEvaluates("$degreesToRadians", Value(45), Value(0.785398163397)); + assertEvaluates("$degreesToRadians", Value(90), Value(1.57079632679)); + assertEvaluates("$degreesToRadians", Value(135), Value(2.35619449019)); + assertEvaluates("$degreesToRadians", Value(180), Value(3.14159265359)); + assertEvaluates("$degreesToRadians", Value(225), Value(3.92699081699)); + assertEvaluates("$degreesToRadians", Value(270), Value(4.71238898038)); + assertEvaluates("$degreesToRadians", Value(315), Value(5.49778714378)); + assertEvaluates("$degreesToRadians", Value(360), Value(6.28318530718)); +} + +TEST(ExpressionDegreesToRadiansTest, DecimalArg) { + assertEvaluates("$degreesToRadians", Value(Decimal128("0")), Value(Decimal128("0.0"))); + assertEvaluates( + "$degreesToRadians", Value(Decimal128("45")), Value(Decimal128("0.785398163397"))); + assertEvaluates( + "$degreesToRadians", Value(Decimal128("90")), Value(Decimal128("1.57079632679"))); + assertEvaluates( + "$degreesToRadians", Value(Decimal128("135")), Value(Decimal128("2.35619449019"))); + assertEvaluates( + "$degreesToRadians", Value(Decimal128("180")), Value(Decimal128("3.14159265359"))); + assertEvaluates( + "$degreesToRadians", Value(Decimal128("225")), Value(Decimal128("3.92699081699"))); + assertEvaluates( + "$degreesToRadians", Value(Decimal128("270")), Value(Decimal128("4.71238898038"))); + assertEvaluates( + "$degreesToRadians", Value(Decimal128("315")), Value(Decimal128("5.49778714378"))); + assertEvaluates( + "$degreesToRadians", Value(Decimal128("360")), Value(Decimal128("6.28318530718"))); +} + +TEST(ExpressionDegreesToRadiansTest, NullArg) { + assertEvaluates("$degreesToRadians", Value(BSONNULL), Value(BSONNULL)); +} +} // namespace expression_trigonometric_test diff --git a/src/mongo/platform/decimal128.cpp b/src/mongo/platform/decimal128.cpp index 51b7ffe5269..2a0f6b0c1ea 100644 --- a/src/mongo/platform/decimal128.cpp +++ b/src/mongo/platform/decimal128.cpp @@ -322,6 +322,110 @@ Decimal128 Decimal128::toAbs() const { return Decimal128(libraryTypeToValue(dec128)); } +Decimal128 Decimal128::acos(RoundingMode roundMode) const { + std::uint32_t throwAwayFlag = 0; + return acos(&throwAwayFlag); +} + +Decimal128 Decimal128::acos(std::uint32_t* signalingFlags, RoundingMode roundMode) const { + BID_UINT128 current = decimal128ToLibraryType(_value); + current = bid128_acos(current, roundMode, signalingFlags); + return Decimal128{libraryTypeToValue(current)}; +} + +Decimal128 Decimal128::acosh(RoundingMode roundMode) const { + std::uint32_t throwAwayFlag = 0; + return acosh(&throwAwayFlag); +} + +Decimal128 Decimal128::acosh(std::uint32_t* signalingFlags, RoundingMode roundMode) const { + BID_UINT128 current = decimal128ToLibraryType(_value); + current = bid128_acosh(current, roundMode, signalingFlags); + return Decimal128{libraryTypeToValue(current)}; +} + +Decimal128 Decimal128::asin(RoundingMode roundMode) const { + std::uint32_t throwAwayFlag = 0; + return asin(&throwAwayFlag); +} + +Decimal128 Decimal128::asin(std::uint32_t* signalingFlags, RoundingMode roundMode) const { + BID_UINT128 current = decimal128ToLibraryType(_value); + current = bid128_asin(current, roundMode, signalingFlags); + return Decimal128{libraryTypeToValue(current)}; +} + +Decimal128 Decimal128::asinh(RoundingMode roundMode) const { + std::uint32_t throwAwayFlag = 0; + return asinh(&throwAwayFlag); +} + +Decimal128 Decimal128::asinh(std::uint32_t* signalingFlags, RoundingMode roundMode) const { + BID_UINT128 current = decimal128ToLibraryType(_value); + current = bid128_asinh(current, roundMode, signalingFlags); + return Decimal128{libraryTypeToValue(current)}; +} + +Decimal128 Decimal128::atan(RoundingMode roundMode) const { + std::uint32_t throwAwayFlag = 0; + return atan(&throwAwayFlag); +} + +Decimal128 Decimal128::atan(std::uint32_t* signalingFlags, RoundingMode roundMode) const { + BID_UINT128 current = decimal128ToLibraryType(_value); + current = bid128_atan(current, roundMode, signalingFlags); + return Decimal128{libraryTypeToValue(current)}; +} + +Decimal128 Decimal128::atan2(const Decimal128& other, RoundingMode roundMode) const { + std::uint32_t throwAwayFlag = 0; + return atan2(other, &throwAwayFlag, roundMode); +} + +Decimal128 Decimal128::atan2(const Decimal128& other, + std::uint32_t* signalingFlags, + RoundingMode roundMode) const { + BID_UINT128 current = decimal128ToLibraryType(_value); + BID_UINT128 divisor = decimal128ToLibraryType(other.getValue()); + current = bid128_atan2(current, divisor, roundMode, signalingFlags); + Decimal128::Value value = libraryTypeToValue(current); + Decimal128 result(value); + return result; +} + +Decimal128 Decimal128::atanh(RoundingMode roundMode) const { + std::uint32_t throwAwayFlag = 0; + return atanh(&throwAwayFlag); +} + +Decimal128 Decimal128::atanh(std::uint32_t* signalingFlags, RoundingMode roundMode) const { + BID_UINT128 current = decimal128ToLibraryType(_value); + current = bid128_atanh(current, roundMode, signalingFlags); + return Decimal128{libraryTypeToValue(current)}; +} + +Decimal128 Decimal128::cosh(RoundingMode roundMode) const { + std::uint32_t throwAwayFlag = 0; + return cosh(&throwAwayFlag); +} + +Decimal128 Decimal128::cosh(std::uint32_t* signalingFlags, RoundingMode roundMode) const { + BID_UINT128 current = decimal128ToLibraryType(_value); + current = bid128_cosh(current, roundMode, signalingFlags); + return Decimal128{libraryTypeToValue(current)}; +} + +Decimal128 Decimal128::cos(RoundingMode roundMode) const { + std::uint32_t throwAwayFlag = 0; + return cos(&throwAwayFlag); +} + +Decimal128 Decimal128::cos(std::uint32_t* signalingFlags, RoundingMode roundMode) const { + BID_UINT128 current = decimal128ToLibraryType(_value); + current = bid128_cos(current, roundMode, signalingFlags); + return Decimal128{libraryTypeToValue(current)}; +} + std::int32_t Decimal128::toInt(RoundingMode roundMode) const { std::uint32_t throwAwayFlag = 0; return toInt(&throwAwayFlag, roundMode); @@ -424,6 +528,28 @@ double Decimal128::toDouble(std::uint32_t* signalingFlags, RoundingMode roundMod return bid128_to_binary64(dec128, roundMode, signalingFlags); } +Decimal128 Decimal128::sin(RoundingMode roundMode) const { + std::uint32_t throwAwayFlag = 0; + return sin(&throwAwayFlag); +} + +Decimal128 Decimal128::sin(std::uint32_t* signalingFlags, RoundingMode roundMode) const { + BID_UINT128 current = decimal128ToLibraryType(_value); + current = bid128_sin(current, roundMode, signalingFlags); + return Decimal128{libraryTypeToValue(current)}; +} + +Decimal128 Decimal128::sinh(RoundingMode roundMode) const { + std::uint32_t throwAwayFlag = 0; + return sinh(&throwAwayFlag); +} + +Decimal128 Decimal128::sinh(std::uint32_t* signalingFlags, RoundingMode roundMode) const { + BID_UINT128 current = decimal128ToLibraryType(_value); + current = bid128_sinh(current, roundMode, signalingFlags); + return Decimal128{libraryTypeToValue(current)}; +} + std::string Decimal128::toString() const { // If the decimal is a variant of NaN (i.e. sNaN, -NaN, +NaN, etc...) or a variant of // Inf (i.e. +Inf, Inf, -Inf), return either NaN, Infinity, or -Infinity @@ -490,6 +616,28 @@ std::string Decimal128::toString() const { return result; } +Decimal128 Decimal128::tanh(RoundingMode roundMode) const { + std::uint32_t throwAwayFlag = 0; + return tanh(&throwAwayFlag); +} + +Decimal128 Decimal128::tanh(std::uint32_t* signalingFlags, RoundingMode roundMode) const { + BID_UINT128 current = decimal128ToLibraryType(_value); + current = bid128_tanh(current, roundMode, signalingFlags); + return Decimal128{libraryTypeToValue(current)}; +} + +Decimal128 Decimal128::tan(RoundingMode roundMode) const { + std::uint32_t throwAwayFlag = 0; + return tan(&throwAwayFlag); +} + +Decimal128 Decimal128::tan(std::uint32_t* signalingFlags, RoundingMode roundMode) const { + BID_UINT128 current = decimal128ToLibraryType(_value); + current = bid128_tan(current, roundMode, signalingFlags); + return Decimal128{libraryTypeToValue(current)}; +} + std::string Decimal128::_convertToScientificNotation(StringData coefficient, int adjustedExponent) const { int cLength = coefficient.size(); @@ -822,6 +970,10 @@ const Decimal128 Decimal128::kNegativeInfinity(Decimal128::Value({0ull, 0xf8ull const Decimal128 Decimal128::kPositiveNaN(Decimal128::Value({0ull, 0x7cull << 56})); const Decimal128 Decimal128::kNegativeNaN(Decimal128::Value({0ull, 0xfcull << 56})); +const Decimal128 Decimal128::kPi("3.14159265358979323846264338327950288419716939937510"); +const Decimal128 Decimal128::kPiOver180(Decimal128::kPi.divide(Decimal128("180"))); +const Decimal128 Decimal128::k180OverPi(Decimal128("180").divide(Decimal128::kPi)); + std::ostream& operator<<(std::ostream& stream, const Decimal128& value) { return stream << value.toString(); } diff --git a/src/mongo/platform/decimal128.h b/src/mongo/platform/decimal128.h index 5a1508317aa..525d3fbf994 100644 --- a/src/mongo/platform/decimal128.h +++ b/src/mongo/platform/decimal128.h @@ -72,6 +72,10 @@ public: static const Decimal128 kPositiveNaN; static const Decimal128 kNegativeNaN; + static const Decimal128 kPi; + static const Decimal128 kPiOver180; + static const Decimal128 k180OverPi; + static const uint32_t kMaxBiasedExponent = 6143 + 6144; // Biased exponent of a Decimal128 with least significant digit in the units place static const int32_t kExponentBias = 6143 + 33; @@ -242,6 +246,90 @@ public: Decimal128 toAbs() const; /** + * Returns the acos value of this. + */ + Decimal128 acos(RoundingMode roundMode = kRoundTiesToEven) const; + Decimal128 acos(std::uint32_t* signalingFlags, RoundingMode roundMode = kRoundTiesToEven) const; + + /** + * Returns the acosh value of this. + */ + Decimal128 acosh(RoundingMode roundMode = kRoundTiesToEven) const; + Decimal128 acosh(std::uint32_t* signalingFlags, + RoundingMode roundMode = kRoundTiesToEven) const; + + /** + * Returns the asin value of this. + */ + Decimal128 asin(RoundingMode roundMode = kRoundTiesToEven) const; + Decimal128 asin(std::uint32_t* signalingFlags, RoundingMode roundMode = kRoundTiesToEven) const; + + /** + * Returns the asinh value of this. + */ + Decimal128 asinh(RoundingMode roundMode = kRoundTiesToEven) const; + Decimal128 asinh(std::uint32_t* signalingFlags, + RoundingMode roundMode = kRoundTiesToEven) const; + + /** + * Returns the atan value of this. + */ + Decimal128 atan(RoundingMode roundMode = kRoundTiesToEven) const; + Decimal128 atan(std::uint32_t* signalingFlags, RoundingMode roundMode = kRoundTiesToEven) const; + + /** + * Returns the atan2(this, other) rather than atan2(other,this), which + * would produce different results. + */ + Decimal128 atan2(const Decimal128& other, RoundingMode roundMode = kRoundTiesToEven) const; + Decimal128 atan2(const Decimal128& other, + std::uint32_t* signalingFlags, + RoundingMode roundMode = kRoundTiesToEven) const; + + /** + * Returns the atanh value of this. + */ + Decimal128 atanh(RoundingMode roundMode = kRoundTiesToEven) const; + Decimal128 atanh(std::uint32_t* signalingFlags, + RoundingMode roundMode = kRoundTiesToEven) const; + + /** + * Returns the cos value of this. + */ + Decimal128 cos(RoundingMode roundMode = kRoundTiesToEven) const; + Decimal128 cos(std::uint32_t* signalingFlags, RoundingMode roundMode = kRoundTiesToEven) const; + + /** + * Returns the cosh value of this. + */ + Decimal128 cosh(RoundingMode roundMode = kRoundTiesToEven) const; + Decimal128 cosh(std::uint32_t* signalingFlags, RoundingMode roundMode = kRoundTiesToEven) const; + + /** + * Returns the sin value of this. + */ + Decimal128 sin(RoundingMode roundMode = kRoundTiesToEven) const; + Decimal128 sin(std::uint32_t* signalingFlags, RoundingMode roundMode = kRoundTiesToEven) const; + + /** + * Returns the sinh value of this. + */ + Decimal128 sinh(RoundingMode roundMode = kRoundTiesToEven) const; + Decimal128 sinh(std::uint32_t* signalingFlags, RoundingMode roundMode = kRoundTiesToEven) const; + /** + * Returns the tan value of this. + */ + Decimal128 tan(RoundingMode roundMode = kRoundTiesToEven) const; + Decimal128 tan(std::uint32_t* signalingFlags, RoundingMode roundMode = kRoundTiesToEven) const; + + /** + * Returns the tanh value of this. + */ + Decimal128 tanh(RoundingMode roundMode = kRoundTiesToEven) const; + Decimal128 tanh(std::uint32_t* signalingFlags, RoundingMode roundMode = kRoundTiesToEven) const; + + + /** * Returns `this` with inverted sign bit */ Decimal128 negate() const { diff --git a/src/mongo/platform/decimal128_test.cpp b/src/mongo/platform/decimal128_test.cpp index 14621296664..760a3c79762 100644 --- a/src/mongo/platform/decimal128_test.cpp +++ b/src/mongo/platform/decimal128_test.cpp @@ -1292,4 +1292,267 @@ TEST(Decimal128Test, TestDecimal128GetLargestNegativeExponentZero) { ASSERT_EQUALS(d.getValue().low64, largestNegativeExponentZeroLow64); } +/** +* Test data was generated using 64 bit versions of these functions, so we must test +* approximate results. +*/ + +void assertDecimal128ApproxEqual(Decimal128 x, Decimal128 y) { + ASSERT_TRUE(x.subtract(y).toAbs().isLess(Decimal128("0.00000005"))); +} + +/** + * A few tests need exact comparisons to test boundary conditions + */ + +void assertDecimal128ExactlyEqual(Decimal128 x, Decimal128 y) { + ASSERT_EQUALS(x.getValue().high64, y.getValue().high64); + ASSERT_EQUALS(x.getValue().low64, y.getValue().low64); +} + +TEST(Decimal128Test, TestAsin) { + assertDecimal128ApproxEqual(Decimal128("-1.0").asin(), Decimal128("-1.57079632679")); + assertDecimal128ApproxEqual(Decimal128("-0.9").asin(), Decimal128("-1.119769515")); + assertDecimal128ApproxEqual(Decimal128("-0.8").asin(), Decimal128("-0.927295218002")); + assertDecimal128ApproxEqual(Decimal128("-0.7").asin(), Decimal128("-0.775397496611")); + assertDecimal128ApproxEqual(Decimal128("-0.6").asin(), Decimal128("-0.643501108793")); + assertDecimal128ApproxEqual(Decimal128("-0.5").asin(), Decimal128("-0.523598775598")); + assertDecimal128ApproxEqual(Decimal128("-0.4").asin(), Decimal128("-0.411516846067")); + assertDecimal128ApproxEqual(Decimal128("-0.3").asin(), Decimal128("-0.304692654015")); + assertDecimal128ApproxEqual(Decimal128("-0.2").asin(), Decimal128("-0.20135792079")); + assertDecimal128ApproxEqual(Decimal128("-0.1").asin(), Decimal128("-0.100167421162")); + assertDecimal128ApproxEqual(Decimal128("0.0").asin(), Decimal128("0.0")); + assertDecimal128ApproxEqual(Decimal128("0.1").asin(), Decimal128("0.100167421162")); + assertDecimal128ApproxEqual(Decimal128("0.2").asin(), Decimal128("0.20135792079")); + assertDecimal128ApproxEqual(Decimal128("0.3").asin(), Decimal128("0.304692654015")); + assertDecimal128ApproxEqual(Decimal128("0.4").asin(), Decimal128("0.411516846067")); + assertDecimal128ApproxEqual(Decimal128("0.5").asin(), Decimal128("0.523598775598")); + assertDecimal128ApproxEqual(Decimal128("0.6").asin(), Decimal128("0.643501108793")); + assertDecimal128ApproxEqual(Decimal128("0.7").asin(), Decimal128("0.775397496611")); + assertDecimal128ApproxEqual(Decimal128("0.8").asin(), Decimal128("0.927295218002")); + assertDecimal128ApproxEqual(Decimal128("0.9").asin(), Decimal128("1.119769515")); + assertDecimal128ApproxEqual(Decimal128("1.0").asin(), Decimal128("1.57079632679")); +} + +TEST(Decimal128Test, TestAcos) { + // The intel decimal library has a bug at -1 where it returns 0. + // + // uncomment this test when we update to the new intel decimal library. + // assertDecimal128ExactlyEqual(Decimal128("-1").acos(), Decimal128::kPi); + assertDecimal128ExactlyEqual(Decimal128("-0.9999999999999999999999999999999997").acos(), + Decimal128("3.141592653589793213967745955447722")); + // Back to normal tests. + assertDecimal128ApproxEqual(Decimal128("-0.9").acos(), Decimal128("2.69056584179")); + assertDecimal128ApproxEqual(Decimal128("-0.8").acos(), Decimal128("2.4980915448")); + assertDecimal128ApproxEqual(Decimal128("-0.7").acos(), Decimal128("2.34619382341")); + assertDecimal128ApproxEqual(Decimal128("-0.6").acos(), Decimal128("2.21429743559")); + assertDecimal128ApproxEqual(Decimal128("-0.5").acos(), Decimal128("2.09439510239")); + assertDecimal128ApproxEqual(Decimal128("-0.4").acos(), Decimal128("1.98231317286")); + assertDecimal128ApproxEqual(Decimal128("-0.3").acos(), Decimal128("1.87548898081")); + assertDecimal128ApproxEqual(Decimal128("-0.2").acos(), Decimal128("1.77215424759")); + assertDecimal128ApproxEqual(Decimal128("-0.1").acos(), Decimal128("1.67096374796")); + assertDecimal128ApproxEqual(Decimal128("0.0").acos(), Decimal128("1.57079632679")); + assertDecimal128ApproxEqual(Decimal128("0.1").acos(), Decimal128("1.47062890563")); + assertDecimal128ApproxEqual(Decimal128("0.2").acos(), Decimal128("1.369438406")); + assertDecimal128ApproxEqual(Decimal128("0.3").acos(), Decimal128("1.26610367278")); + assertDecimal128ApproxEqual(Decimal128("0.4").acos(), Decimal128("1.15927948073")); + assertDecimal128ApproxEqual(Decimal128("0.5").acos(), Decimal128("1.0471975512")); + assertDecimal128ApproxEqual(Decimal128("0.6").acos(), Decimal128("0.927295218002")); + assertDecimal128ApproxEqual(Decimal128("0.7").acos(), Decimal128("0.795398830184")); + assertDecimal128ApproxEqual(Decimal128("0.8").acos(), Decimal128("0.643501108793")); + assertDecimal128ApproxEqual(Decimal128("0.9").acos(), Decimal128("0.451026811796")); + assertDecimal128ApproxEqual(Decimal128("1.0").acos(), Decimal128("0.0")); +} + +TEST(Decimal128Test, TestAcosh) { + assertDecimal128ApproxEqual(Decimal128("1.0").acosh(), Decimal128("0.0")); + assertDecimal128ApproxEqual(Decimal128("1.1").acosh(), Decimal128("0.443568254385")); + assertDecimal128ApproxEqual(Decimal128("1.5").acosh(), Decimal128("0.962423650119")); + assertDecimal128ApproxEqual(Decimal128("2").acosh(), Decimal128("1.31695789692")); + assertDecimal128ApproxEqual(Decimal128("2.5").acosh(), Decimal128("1.56679923697")); + assertDecimal128ApproxEqual(Decimal128("3").acosh(), Decimal128("1.76274717404")); +} + + +TEST(Decimal128Test, TestAtanh) { + assertDecimal128ApproxEqual(Decimal128("-0.9").atanh(), Decimal128("-1.47221948958")); + assertDecimal128ApproxEqual(Decimal128("-0.8").atanh(), Decimal128("-1.09861228867")); + assertDecimal128ApproxEqual(Decimal128("-0.7").atanh(), Decimal128("-0.867300527694")); + assertDecimal128ApproxEqual(Decimal128("-0.6").atanh(), Decimal128("-0.69314718056")); + assertDecimal128ApproxEqual(Decimal128("-0.5").atanh(), Decimal128("-0.549306144334")); + assertDecimal128ApproxEqual(Decimal128("-0.4").atanh(), Decimal128("-0.423648930194")); + assertDecimal128ApproxEqual(Decimal128("-0.3").atanh(), Decimal128("-0.309519604203")); + assertDecimal128ApproxEqual(Decimal128("-0.2").atanh(), Decimal128("-0.202732554054")); + assertDecimal128ApproxEqual(Decimal128("-0.1").atanh(), Decimal128("-0.100335347731")); + assertDecimal128ApproxEqual(Decimal128("0.0").atanh(), Decimal128("0.0")); + assertDecimal128ApproxEqual(Decimal128("0.1").atanh(), Decimal128("0.100335347731")); + assertDecimal128ApproxEqual(Decimal128("0.2").atanh(), Decimal128("0.202732554054")); + assertDecimal128ApproxEqual(Decimal128("0.3").atanh(), Decimal128("0.309519604203")); + assertDecimal128ApproxEqual(Decimal128("0.4").atanh(), Decimal128("0.423648930194")); + assertDecimal128ApproxEqual(Decimal128("0.5").atanh(), Decimal128("0.549306144334")); + assertDecimal128ApproxEqual(Decimal128("0.6").atanh(), Decimal128("0.69314718056")); + assertDecimal128ApproxEqual(Decimal128("0.7").atanh(), Decimal128("0.867300527694")); + assertDecimal128ApproxEqual(Decimal128("0.8").atanh(), Decimal128("1.09861228867")); + assertDecimal128ApproxEqual(Decimal128("0.9").atanh(), Decimal128("1.47221948958")); +} + +TEST(Decimal128Test, TestAtan) { + assertDecimal128ApproxEqual(Decimal128("-1.5").atan(), Decimal128("-0.982793723247")); + assertDecimal128ApproxEqual(Decimal128("-1.0471975512").atan(), Decimal128("-0.80844879263")); + assertDecimal128ApproxEqual(Decimal128("-0.785398163397").atan(), + Decimal128("-0.665773750028")); + assertDecimal128ApproxEqual(Decimal128("0").atan(), Decimal128("0.0")); + assertDecimal128ApproxEqual(Decimal128("0.785398163397").atan(), Decimal128("0.665773750028")); + assertDecimal128ApproxEqual(Decimal128("1.0471975512").atan(), Decimal128("0.80844879263")); + assertDecimal128ApproxEqual(Decimal128("1.5").atan(), Decimal128("0.982793723247")); +} + +TEST(Decimal128Test, TestAtan2) { + assertDecimal128ApproxEqual(Decimal128("1.0").atan2(Decimal128("0.0")), + Decimal128("1.57079632679")); + assertDecimal128ApproxEqual(Decimal128("0.866025403784").atan2(Decimal128("0.5")), + Decimal128("1.0471975512")); + assertDecimal128ApproxEqual(Decimal128("0.707106781187").atan2(Decimal128("0.707106781187")), + Decimal128("0.785398163397")); + assertDecimal128ApproxEqual(Decimal128("0.5").atan2(Decimal128("0.866025403784")), + Decimal128("0.523598775598")); + assertDecimal128ApproxEqual(Decimal128("6.12323399574e-17").atan2(Decimal128("1.0")), + Decimal128("6.12323399574e-17")); + assertDecimal128ApproxEqual(Decimal128("-0.5").atan2(Decimal128("0.866025403784")), + Decimal128("-0.523598775598")); + assertDecimal128ApproxEqual(Decimal128("-0.707106781187").atan2(Decimal128("0.707106781187")), + Decimal128("-0.785398163397")); + assertDecimal128ApproxEqual(Decimal128("-0.866025403784").atan2(Decimal128("0.5")), + Decimal128("-1.0471975512")); + assertDecimal128ApproxEqual(Decimal128("-1.0").atan2(Decimal128("1.22464679915e-16")), + Decimal128("-1.57079632679")); + assertDecimal128ApproxEqual(Decimal128("-0.866025403784").atan2(Decimal128("-0.5")), + Decimal128("-2.09439510239")); + assertDecimal128ApproxEqual(Decimal128("-0.707106781187").atan2(Decimal128("-0.707106781187")), + Decimal128("-2.35619449019")); + assertDecimal128ApproxEqual(Decimal128("-0.5").atan2(Decimal128("-0.866025403784")), + Decimal128("-2.61799387799")); + assertDecimal128ApproxEqual(Decimal128("-1.83697019872e-16").atan2(Decimal128("-1.0")), + Decimal128("-3.14159265359")); + assertDecimal128ApproxEqual(Decimal128("0.5").atan2(Decimal128("-0.866025403784")), + Decimal128("2.61799387799")); + assertDecimal128ApproxEqual(Decimal128("0.707106781187").atan2(Decimal128("-0.707106781187")), + Decimal128("2.35619449019")); + assertDecimal128ApproxEqual(Decimal128("0.866025403784").atan2(Decimal128("-0.5")), + Decimal128("2.09439510239")); + assertDecimal128ApproxEqual(Decimal128("1.0").atan2(Decimal128("-2.44929359829e-16")), + Decimal128("1.57079632679")); +} + +TEST(Decimal128Test, TestCos) { + assertDecimal128ApproxEqual(Decimal128("0.0").cos(), Decimal128("1.0")); + assertDecimal128ApproxEqual(Decimal128("0.523598775598").cos(), Decimal128("0.866025403784")); + assertDecimal128ApproxEqual(Decimal128("0.785398163397").cos(), Decimal128("0.707106781187")); + assertDecimal128ApproxEqual(Decimal128("1.0471975512").cos(), Decimal128("0.5")); + assertDecimal128ApproxEqual(Decimal128("1.57079632679").cos(), Decimal128("6.12323399574e-17")); + assertDecimal128ApproxEqual(Decimal128("2.09439510239").cos(), Decimal128("-0.5")); + assertDecimal128ApproxEqual(Decimal128("2.35619449019").cos(), Decimal128("-0.707106781187")); + assertDecimal128ApproxEqual(Decimal128("2.61799387799").cos(), Decimal128("-0.866025403784")); + assertDecimal128ApproxEqual(Decimal128("3.14159265359").cos(), Decimal128("-1.0")); + assertDecimal128ApproxEqual(Decimal128("3.66519142919").cos(), Decimal128("-0.866025403784")); + assertDecimal128ApproxEqual(Decimal128("3.92699081699").cos(), Decimal128("-0.707106781187")); + assertDecimal128ApproxEqual(Decimal128("4.18879020479").cos(), Decimal128("-0.5")); + assertDecimal128ApproxEqual(Decimal128("4.71238898038").cos(), + Decimal128("-1.83697019872e-16")); + assertDecimal128ApproxEqual(Decimal128("5.23598775598").cos(), Decimal128("0.5")); + assertDecimal128ApproxEqual(Decimal128("5.49778714378").cos(), Decimal128("0.707106781187")); + assertDecimal128ApproxEqual(Decimal128("5.75958653158").cos(), Decimal128("0.866025403784")); + assertDecimal128ApproxEqual(Decimal128("6.28318530718").cos(), Decimal128("1.0")); +} + +TEST(Decimal128Test, TestCosh) { + assertDecimal128ApproxEqual(Decimal128("0.0").cosh(), Decimal128("1.0")); + assertDecimal128ApproxEqual(Decimal128("0.523598775598").cosh(), Decimal128("1.14023832108")); + assertDecimal128ApproxEqual(Decimal128("0.785398163397").cosh(), Decimal128("1.32460908925")); + assertDecimal128ApproxEqual(Decimal128("1.0471975512").cosh(), Decimal128("1.6002868577")); + assertDecimal128ApproxEqual(Decimal128("1.57079632679").cosh(), Decimal128("2.50917847866")); + assertDecimal128ApproxEqual(Decimal128("2.09439510239").cosh(), Decimal128("4.12183605387")); + assertDecimal128ApproxEqual(Decimal128("2.35619449019").cosh(), Decimal128("5.32275214952")); + assertDecimal128ApproxEqual(Decimal128("2.61799387799").cosh(), Decimal128("6.89057236498")); + assertDecimal128ApproxEqual(Decimal128("3.14159265359").cosh(), Decimal128("11.5919532755")); + assertDecimal128ApproxEqual(Decimal128("3.66519142919").cosh(), Decimal128("19.5446063168")); + assertDecimal128ApproxEqual(Decimal128("3.92699081699").cosh(), Decimal128("25.3868611924")); + assertDecimal128ApproxEqual(Decimal128("4.18879020479").cosh(), Decimal128("32.97906491")); + assertDecimal128ApproxEqual(Decimal128("4.71238898038").cosh(), Decimal128("55.6633808904")); + assertDecimal128ApproxEqual(Decimal128("5.23598775598").cosh(), Decimal128("93.9599750339")); + assertDecimal128ApproxEqual(Decimal128("5.49778714378").cosh(), Decimal128("122.07757934")); + assertDecimal128ApproxEqual(Decimal128("5.75958653158").cosh(), Decimal128("158.610147472")); + assertDecimal128ApproxEqual(Decimal128("6.28318530718").cosh(), Decimal128("267.746761484")); +} + +TEST(Decimal128Test, TestSin) { + assertDecimal128ApproxEqual(Decimal128("0.0").sin(), Decimal128("0.0")); + assertDecimal128ApproxEqual(Decimal128("0.523598775598").sin(), Decimal128("0.5")); + assertDecimal128ApproxEqual(Decimal128("0.785398163397").sin(), Decimal128("0.707106781187")); + assertDecimal128ApproxEqual(Decimal128("1.0471975512").sin(), Decimal128("0.866025403784")); + assertDecimal128ApproxEqual(Decimal128("1.57079632679").sin(), Decimal128("1.0")); + assertDecimal128ApproxEqual(Decimal128("2.09439510239").sin(), Decimal128("0.866025403784")); + assertDecimal128ApproxEqual(Decimal128("2.35619449019").sin(), Decimal128("0.707106781187")); + assertDecimal128ApproxEqual(Decimal128("2.61799387799").sin(), Decimal128("0.5")); + assertDecimal128ApproxEqual(Decimal128("3.14159265359").sin(), Decimal128("1.22464679915e-16")); + assertDecimal128ApproxEqual(Decimal128("3.66519142919").sin(), Decimal128("-0.5")); + assertDecimal128ApproxEqual(Decimal128("3.92699081699").sin(), Decimal128("-0.707106781187")); + assertDecimal128ApproxEqual(Decimal128("4.18879020479").sin(), Decimal128("-0.866025403784")); + assertDecimal128ApproxEqual(Decimal128("4.71238898038").sin(), Decimal128("-1.0")); + assertDecimal128ApproxEqual(Decimal128("5.23598775598").sin(), Decimal128("-0.866025403784")); + assertDecimal128ApproxEqual(Decimal128("5.49778714378").sin(), Decimal128("-0.707106781187")); + assertDecimal128ApproxEqual(Decimal128("5.75958653158").sin(), Decimal128("-0.5")); + assertDecimal128ApproxEqual(Decimal128("6.28318530718").sin(), + Decimal128("-2.44929359829e-16")); +} + +TEST(Decimal128Test, TestSinh) { + assertDecimal128ApproxEqual(Decimal128("0.0").sinh(), Decimal128("0.0")); + assertDecimal128ApproxEqual(Decimal128("0.523598775598").sinh(), Decimal128("0.547853473888")); + assertDecimal128ApproxEqual(Decimal128("0.785398163397").sinh(), Decimal128("0.868670961486")); + assertDecimal128ApproxEqual(Decimal128("1.0471975512").sinh(), Decimal128("1.24936705052")); + assertDecimal128ApproxEqual(Decimal128("1.57079632679").sinh(), Decimal128("2.30129890231")); + assertDecimal128ApproxEqual(Decimal128("2.09439510239").sinh(), Decimal128("3.9986913428")); + assertDecimal128ApproxEqual(Decimal128("2.35619449019").sinh(), Decimal128("5.22797192468")); + assertDecimal128ApproxEqual(Decimal128("2.61799387799").sinh(), Decimal128("6.81762330413")); + assertDecimal128ApproxEqual(Decimal128("3.14159265359").sinh(), Decimal128("11.5487393573")); + assertDecimal128ApproxEqual(Decimal128("3.66519142919").sinh(), Decimal128("19.5190070464")); + assertDecimal128ApproxEqual(Decimal128("3.92699081699").sinh(), Decimal128("25.3671583194")); + assertDecimal128ApproxEqual(Decimal128("4.18879020479").sinh(), Decimal128("32.9639002901")); + assertDecimal128ApproxEqual(Decimal128("4.71238898038").sinh(), Decimal128("55.6543975994")); + assertDecimal128ApproxEqual(Decimal128("5.23598775598").sinh(), Decimal128("93.9546534685")); + assertDecimal128ApproxEqual(Decimal128("5.49778714378").sinh(), Decimal128("122.073483515")); + assertDecimal128ApproxEqual(Decimal128("5.75958653158").sinh(), Decimal128("158.606995057")); + assertDecimal128ApproxEqual(Decimal128("6.28318530718").sinh(), Decimal128("267.744894041")); +} + +TEST(Decimal128Test, TestTan) { + assertDecimal128ApproxEqual(Decimal128("-1.5").tan(), Decimal128("-14.1014199472")); + assertDecimal128ApproxEqual(Decimal128("-1.0471975512").tan(), Decimal128("-1.73205080757")); + assertDecimal128ApproxEqual(Decimal128("-0.785398163397").tan(), Decimal128("-1.0")); + assertDecimal128ApproxEqual(Decimal128("0").tan(), Decimal128("0.0")); + assertDecimal128ApproxEqual(Decimal128("0.785398163397").tan(), Decimal128("1.0")); + assertDecimal128ApproxEqual(Decimal128("1.0471975512").tan(), Decimal128("1.73205080757")); + assertDecimal128ApproxEqual(Decimal128("1.5").tan(), Decimal128("14.1014199472")); +} + +TEST(Decimal128Test, TestTanh) { + assertDecimal128ApproxEqual(Decimal128("0.0").tanh(), Decimal128("0.0")); + assertDecimal128ApproxEqual(Decimal128("0.523598775598").tanh(), Decimal128("0.480472778156")); + assertDecimal128ApproxEqual(Decimal128("0.785398163397").tanh(), Decimal128("0.655794202633")); + assertDecimal128ApproxEqual(Decimal128("1.0471975512").tanh(), Decimal128("0.780714435359")); + assertDecimal128ApproxEqual(Decimal128("1.57079632679").tanh(), Decimal128("0.917152335667")); + assertDecimal128ApproxEqual(Decimal128("2.09439510239").tanh(), Decimal128("0.970123821166")); + assertDecimal128ApproxEqual(Decimal128("2.35619449019").tanh(), Decimal128("0.982193380007")); + assertDecimal128ApproxEqual(Decimal128("2.61799387799").tanh(), Decimal128("0.989413207353")); + assertDecimal128ApproxEqual(Decimal128("3.14159265359").tanh(), Decimal128("0.996272076221")); + assertDecimal128ApproxEqual(Decimal128("3.66519142919").tanh(), Decimal128("0.998690213046")); + assertDecimal128ApproxEqual(Decimal128("3.92699081699").tanh(), Decimal128("0.999223894879")); + assertDecimal128ApproxEqual(Decimal128("4.18879020479").tanh(), Decimal128("0.999540174353")); + assertDecimal128ApproxEqual(Decimal128("4.71238898038").tanh(), Decimal128("0.999838613989")); + assertDecimal128ApproxEqual(Decimal128("5.23598775598").tanh(), Decimal128("0.999943363486")); + assertDecimal128ApproxEqual(Decimal128("5.49778714378").tanh(), Decimal128("0.999966449")); + assertDecimal128ApproxEqual(Decimal128("5.75958653158").tanh(), Decimal128("0.99998012476")); + assertDecimal128ApproxEqual(Decimal128("6.28318530718").tanh(), Decimal128("0.99999302534")); +} } // namespace mongo |