/*
 * Copyright 2022-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "test-mongocrypt.h"

#include "mc-range-encoding-private.h"

#include <float.h> // DBL_MAX
#include <math.h>  // INFINITY, NAN
#include <stdint.h>

typedef struct {
    mc_getTypeInfo32_args_t args;
    mc_OSTType_Int32 expect;
    const char *expectError;
} Int32Test;

static void _test_RangeTest_Encode_Int32(_mongocrypt_tester_t *tester) {
    Int32Test tests[] = {
        /* Test cases copied from server Int32_NoBounds test ... begin */
        {.args = {.value = INT32_C(2147483647)},
         .expect = {.value = UINT32_C(4294967295), .min = 0, .max = UINT32_MAX}},
        {.args = {.value = 1}, .expect = {.value = UINT32_C(2147483649), .min = 0, .max = UINT32_MAX}},
        {.args = {.value = 0}, .expect = {.value = UINT32_C(2147483648), .min = 0, .max = UINT32_MAX}},
        {.args = {.value = -1}, .expect = {.value = UINT32_C(2147483647), .min = 0, .max = UINT32_MAX}},
        {.args = {.value = -2}, .expect = {.value = UINT32_C(2147483646), .min = 0, .max = UINT32_MAX}},
        {.args = {.value = INT32_C(-2147483647)}, .expect = {.value = 1, .min = 0, .max = UINT32_MAX}},
        {.args = {.value = INT32_MIN}, .expect = {.value = 0, .min = 0, .max = UINT32_MAX}},
        /* Test cases copied from server Int32_NoBounds test ... end */
        /* Test cases copied from server Int32_Bounds test .. begin */
        {.args = {.value = 1, .min = OPT_I32(1), .max = OPT_I32(3)}, .expect = {.value = 0, 0, .max = 2}},
        {.args = {.value = 0, .min = OPT_I32(0), .max = OPT_I32(1)}, .expect = {.value = 0, .min = 0, .max = 1}},
        {.args = {.value = -1, .min = OPT_I32(-1), .max = OPT_I32(0)}, .expect = {.value = 0, .min = 0, .max = 1}},
        {.args = {.value = -2, .min = OPT_I32(-2), .max = OPT_I32(0)}, .expect = {.value = 0, .min = 0, .max = 2}},
        {.args = {.value = INT32_C(-2147483647), .min = OPT_I32(INT32_MIN), .max = OPT_I32(1)},
         .expect = {.value = 1, .min = 0, .max = UINT32_C(2147483649)}},
        {.args = {.value = INT32_MIN, .min = OPT_I32(INT32_MIN), .max = OPT_I32(0)},
         .expect = {.value = 0, .min = 0, .max = UINT32_C(2147483648)}},
        {.args = {.value = 0, .min = OPT_I32(INT32_MIN), .max = OPT_I32(1)},
         .expect = {.value = UINT32_C(2147483648), .min = 0, .max = UINT32_C(2147483649)}},
        {.args = {.value = 1, .min = OPT_I32(INT32_MIN), .max = OPT_I32(2)},
         .expect = {.value = UINT32_C(2147483649), .min = 0, .max = UINT32_C(2147483650)}},
        {.args = {.value = INT32_C(2147483647), .min = OPT_I32(-2147483647), .max = OPT_I32(2147483647)},
         .expect = {.value = UINT32_C(4294967294), .min = 0, .max = UINT32_C(4294967294)}},
        {.args = {.value = INT32_C(2147483647), .min = OPT_I32(INT32_MIN), .max = OPT_I32(2147483647)},
         .expect = {.value = UINT32_C(4294967295), .min = 0, .max = UINT32_C(4294967295)}},
        {.args = {.value = 15, .min = OPT_I32(10), .max = OPT_I32(26)}, .expect = {.value = 5, .min = 0, .max = 16}},
        {.args = {.value = 15, .min = OPT_I32(-10), .max = OPT_I32(55)}, .expect = {.value = 25, .min = 0, .max = 65}},
        /* Test cases copied from server Int32_Bounds test ... end */
        /* Test cases copied from server Int32_Errors test ... begin */
        {.args = {.value = 1, .max = OPT_I32(2)},
         .expectError = "Must specify both a lower and upper bound or no bounds."},
        {.args = {.value = 1, .min = OPT_I32(0)},
         .expectError = "Must specify both a lower and upper bound or no bounds."},
        {.args = {.value = 1, .min = OPT_I32(2), .max = OPT_I32(1)},
         .expectError = "The minimum value must be less than the maximum value"},
        {.args = {.value = 1, .min = OPT_I32(2), .max = OPT_I32(3)},
         .expectError = "Value must be greater than or equal to the minimum "
                        "value and less than or equal to the maximum value"},
        {.args = {.value = 4, .min = OPT_I32(2), .max = OPT_I32(3)},
         .expectError = "Value must be greater than or equal to the minimum "
                        "value and less than or equal to the maximum value"},
        {.args = {.value = 4, .min = OPT_I32(INT32_MIN), .max = OPT_I32(INT32_MIN)},
         .expectError = "The minimum value must be less than the maximum value"},
        /* Test cases copied from server Int32_Errors test ... end */
    };

    for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
        Int32Test *test = tests + i;
        mongocrypt_status_t *const status = mongocrypt_status_new();

        // Print a description of the test case.
        TEST_PRINTF("_test_RangeTest_Encode_Int32: value=%" PRId32, test->args.value);
        if (test->args.min.set) {
            TEST_PRINTF(" min=%" PRId32, test->args.min.value);
        }
        if (test->args.max.set) {
            TEST_PRINTF(" max=%" PRId32, test->args.max.value);
        }
        TEST_PRINTF("\n");
        mc_OSTType_Int32 got;
        const bool ok = mc_getTypeInfo32(test->args, &got, status);
        if (test->expectError) {
            ASSERT_OR_PRINT_MSG(!ok, "expected error, but got none");
            ASSERT_STATUS_CONTAINS(status, test->expectError);
        } else {
            ASSERT_OK_STATUS(ok, status);
            ASSERT_CMPUINT32(got.value, ==, test->expect.value);
            ASSERT_CMPUINT32(got.min, ==, test->expect.min);
            ASSERT_CMPUINT32(got.max, ==, test->expect.max);
        }
        mongocrypt_status_destroy(status);
    }
}

typedef struct {
    mc_getTypeInfo64_args_t args;
    mc_OSTType_Int64 expect;
    const char *expectError;
} Int64Test;

static void _test_RangeTest_Encode_Int64(_mongocrypt_tester_t *tester) {
    Int64Test tests[] = {
        /* Test cases copied from server Int64_NoBounds test ... begin */
        {.args = {.value = INT64_C(9223372036854775807)},
         .expect = {.value = UINT64_C(18446744073709551615), .min = 0, .max = UINT64_MAX}},
        {.args = {.value = 1}, .expect = {.value = UINT64_C(9223372036854775809), .min = 0, .max = UINT64_MAX}},
        {.args = {.value = 0}, .expect = {.value = UINT64_C(9223372036854775808), .min = 0, .max = UINT64_MAX}},
        {.args = {.value = -1}, .expect = {.value = UINT64_C(9223372036854775807), .min = 0, .max = UINT64_MAX}},
        {.args = {.value = -2}, .expect = {.value = UINT64_C(9223372036854775806), .min = 0, .max = UINT64_MAX}},
        {.args = {.value = INT64_C(-9223372036854775807)}, .expect = {.value = 1, .min = 0, .max = UINT64_MAX}},
        {.args = {.value = INT64_MIN}, .expect = {.value = 0, .min = 0, .max = UINT64_MAX}},
        /* Test cases copied from server Int64_NoBounds test ... end */
        /* Test cases copied from server Int64_Bounds test ... begin */
        {.args = {.value = 1, .min = OPT_I64(1), .max = OPT_I64(2)}, .expect = {.value = 0, .min = 0, .max = 1}},
        {.args = {.value = 0, .min = OPT_I64(0), .max = OPT_I64(1)}, .expect = {.value = 0, .min = 0, .max = 1}},
        {.args = {.value = -1, .min = OPT_I64(-1), .max = OPT_I64(0)}, .expect = {.value = 0, .min = 0, .max = 1}},
        {.args = {.value = -2, .min = OPT_I64(-2), .max = OPT_I64(0)}, .expect = {.value = 0, .min = 0, .max = 2}},
        {.args = {.value = INT64_C(-9223372036854775807), .min = OPT_I64(INT64_MIN), .max = OPT_I64(1)},
         .expect = {.value = 1, .min = 0, .max = UINT64_C(9223372036854775809)}},
        {.args = {.value = INT64_MIN, .min = OPT_I64(INT64_MIN), .max = OPT_I64(0)},
         .expect = {.value = 0, .min = 0, .max = UINT64_C(9223372036854775808)}},
        {.args = {.value = 0, .min = OPT_I64(INT64_MIN), .max = OPT_I64(37)},
         .expect = {.value = UINT64_C(9223372036854775808), .min = 0, .max = UINT64_C(9223372036854775845)}},
        {.args = {.value = 1, .min = OPT_I64(INT64_MIN), .max = OPT_I64(42)},
         .expect = {.value = UINT64_C(9223372036854775809), .min = 0, .max = UINT64_C(9223372036854775850)}},
        {.args = {.value = INT64_C(9223372036854775807),
                  .min = OPT_I64(-9223372036854775807),
                  .max = OPT_I64(9223372036854775807)},
         .expect = {.value = UINT64_C(18446744073709551614), .min = 0, .max = UINT64_C(18446744073709551614)}},
        {.args = {.value = INT64_C(9223372036854775807),
                  .min = OPT_I64(INT64_MIN),
                  .max = OPT_I64(9223372036854775807)},
         .expect = {.value = UINT64_C(18446744073709551615), .min = 0, .max = UINT64_C(18446744073709551615)}},
        {.args = {.value = 15, .min = OPT_I64(10), .max = OPT_I64(26)}, .expect = {.value = 5, .min = 0, .max = 16}},
        {.args = {.value = 15, .min = OPT_I64(-10), .max = OPT_I64(55)}, .expect = {.value = 25, .min = 0, .max = 65}},
        /* Test cases copied from server Int64_Bounds test ... end */
        /* Test cases copied from server Int64_Errors test ... begin */
        {.args = {.value = 1, .max = OPT_I64(2)},
         .expectError = "Must specify both a lower and upper bound or no bounds."},
        {.args = {.value = 1, .min = OPT_I64(0)},
         .expectError = "Must specify both a lower and upper bound or no bounds."},
        {.args = {.value = 1, .min = OPT_I64(2), .max = OPT_I64(1)},
         .expectError = "The minimum value must be less than the maximum value"},
        {.args = {.value = 1, .min = OPT_I64(2), .max = OPT_I64(3)},
         .expectError = "Value must be greater than or equal to the minimum "
                        "value and less than or equal to the maximum value"},
        {.args = {.value = 4, .min = OPT_I64(2), .max = OPT_I64(3)},
         .expectError = "Value must be greater than or equal to the minimum "
                        "value and less than or equal to the maximum value"},
        {.args = {.value = 4, .min = OPT_I64(INT64_MIN), .max = OPT_I64(INT64_MIN)},
         .expectError = "The minimum value must be less than the maximum value"},
        /* Test cases copied from server Int64_Errors test ... end */
    };

    for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
        Int64Test *test = tests + i;
        mongocrypt_status_t *const status = mongocrypt_status_new();

        // Print a description of the test case.
        TEST_PRINTF("_test_RangeTest_Encode_Int64: value=%" PRId64, test->args.value);
        if (test->args.min.set) {
            TEST_PRINTF(" min=%" PRId64, test->args.min.value);
        }
        if (test->args.max.set) {
            TEST_PRINTF(" max=%" PRId64, test->args.max.value);
        }
        TEST_PRINTF("\n");
        mc_OSTType_Int64 got;
        const bool ok = mc_getTypeInfo64(test->args, &got, status);
        if (test->expectError) {
            ASSERT_OR_PRINT_MSG(!ok, "expected error, but got none");
            ASSERT_STATUS_CONTAINS(status, test->expectError);
        } else {
            ASSERT_OK_STATUS(ok, status);
            ASSERT_CMPUINT64(got.value, ==, test->expect.value);
            ASSERT_CMPUINT64(got.min, ==, test->expect.min);
            ASSERT_CMPUINT64(got.max, ==, test->expect.max);
        }
        mongocrypt_status_destroy(status);
    }
}

#define INT_64_MAX_DOUBLE (double)18446744073709551615ull

static void _test_canUsePrecisionModeDouble(_mongocrypt_tester_t *tester) {
#define CAN_USE_PRECISION_MODE(lb, ub, prc, expected, expected_bits_out)                                               \
    if (1) {                                                                                                           \
        uint32_t bits_out = 0;                                                                                         \
        mongocrypt_status_t *const status = mongocrypt_status_new();                                                   \
        TEST_PRINTF("_test_canUsePrecisionModeDecimal, min: %f, max: %f, prc: %" PRId32, lb, ub, prc);                 \
        bool result = mc_canUsePrecisionModeDouble(lb, ub, prc, &bits_out, status);                                    \
        ASSERT_OK_STATUS(mongocrypt_status_ok(status), status);                                                        \
        ASSERT(result == expected);                                                                                    \
        ASSERT_CMPINT32(expected_bits_out, ==, bits_out);                                                              \
        mongocrypt_status_destroy(status);                                                                             \
    } else                                                                                                             \
        ((void)0)

#define CAN_USE_PRECISION_MODE_ERRORS(lb, ub, prc, error)                                                              \
    if (1) {                                                                                                           \
        mongocrypt_status_t *const status = mongocrypt_status_new();                                                   \
        TEST_PRINTF("_test_canUsePrecisionModeDecimal errors, min: %f, max: %f, prc: %" PRId32, lb, ub, prc);          \
        uint32_t bits_out = 0;                                                                                         \
        bool result = mc_canUsePrecisionModeDouble(lb, ub, prc, &bits_out, status);                                    \
        ASSERT_OR_PRINT_MSG(!result, "expected error, but got none");                                                  \
        ASSERT_STATUS_CONTAINS(status, error);                                                                         \
        mongocrypt_status_destroy(status);                                                                             \
    } else                                                                                                             \
        ((void)0)

    CAN_USE_PRECISION_MODE(1.0, 16.0, INT32_C(0), true, 4);
    CAN_USE_PRECISION_MODE(0.0, 16.0, INT32_C(0), true, 5);
    // 2^53 + 1 is where double starts to lose precision, so we need to ensure that we get the
    // correct value for max_bits out.
    CAN_USE_PRECISION_MODE_ERRORS(1.0,
                                  9007199254740992.0,
                                  INT32_C(0),
                                  "Invalid upper bound for double precision. Absolute");
    CAN_USE_PRECISION_MODE_ERRORS(0.0,
                                  9007199254740992.0,
                                  INT32_C(0),
                                  "Invalid upper bound for double precision. Absolute");

    CAN_USE_PRECISION_MODE(2.718281, 314.159265, 6, true, 29);

    CAN_USE_PRECISION_MODE_ERRORS(-1000000000.0,
                                  9223372036844775424.0,
                                  INT32_C(0),
                                  "Invalid upper bound for double precision. Absolute");

    CAN_USE_PRECISION_MODE_ERRORS(2.710000,
                                  314.150000,
                                  INT32_C(2),
                                  "Invalid upper bound for double precision. Fractional digits");
    CAN_USE_PRECISION_MODE_ERRORS(314.150000, 350.0, 2, "Invalid lower bound for double precision. Fractional digits");

    CAN_USE_PRECISION_MODE_ERRORS((double)9007199254740992,
                                  INT_64_MAX_DOUBLE,
                                  INT32_C(0),
                                  "Invalid upper bound for double precision. Absolute scaled value");
    CAN_USE_PRECISION_MODE_ERRORS(-1 * INT_64_MAX_DOUBLE,
                                  1.0,
                                  INT32_C(0),
                                  "Invalid lower bound for double precision. Absolute scaled value");
    CAN_USE_PRECISION_MODE_ERRORS(-92233720368547.0,
                                  92233720368547.0,
                                  INT32_C(5),
                                  "Invalid upper bound for double precision. Absolute");

#undef CAN_USE_PRECISION_MODE
#undef CAN_USE_PRECISION_MODE_ERRORS
}

typedef struct {
    double value;
    mc_optional_double_t min;
    mc_optional_double_t max;
    mc_optional_int32_t precision;
    uint64_t expect;
    mc_optional_uint64_t expectMax;
    const char *expectError;
    bool use_range_v1; // By default, use range v2.
} DoubleTest;

// Smallest and Largest integer that fits in a double before precision is lost
#define DOUBLE_MIN_SAFE_INT -9007199254740992 // -2^53
#define DOUBLE_MAX_SAFE_INT 9007199254740992  // 2^53

static void _test_RangeTest_Encode_Double(_mongocrypt_tester_t *tester) {
    DoubleTest tests[] = {/* Test cases copied from server Double_Bounds test ... begin */
                          // Larger numbers map to larger uint64
                          {.value = -1111, .expect = UINT64_C(4570770991734587392)},
                          {.value = -111, .expect = UINT64_C(4585860689314185216)},
                          {.value = -11, .expect = UINT64_C(4600989969312382976)},
                          {.value = -10, .expect = UINT64_C(4601552919265804288)},
                          {.value = -3, .expect = UINT64_C(4609434218613702656)},
                          {.value = -2, .expect = UINT64_C(4611686018427387904)},

                          {.value = -1, .expect = UINT64_C(4616189618054758400)},
                          {.value = 1, .expect = UINT64_C(13830554455654793216)},
                          {.value = 22, .expect = UINT64_C(13850257704024539136)},
                          {.value = 333, .expect = UINT64_C(13867937850999177216)},

                          // Larger exponents map to larger uint64
                          {.value = 33E56, .expect = UINT64_C(14690973652625833878)},
                          {.value = 22E57, .expect = UINT64_C(14703137697061005818)},
                          {.value = 11E58, .expect = UINT64_C(14713688953586463292)},

                          // Smaller exponents map to smaller uint64
                          {.value = 1E-6, .expect = UINT64_C(13740701229962882445)},
                          {.value = 1E-7, .expect = UINT64_C(13725520251343122248)},
                          {.value = 1E-8, .expect = UINT64_C(13710498295186492474)},
                          {.value = 1E-56, .expect = UINT64_C(12992711961033031890)},
                          {.value = 1E-57, .expect = UINT64_C(12977434315086142017)},
                          {.value = 1E-58, .expect = UINT64_C(12962510038552207822)},

                          // Smaller negative exponents map to smaller uint64
                          {.value = -1E-06, .expect = UINT64_C(4706042843746669171)},
                          {.value = -1E-07, .expect = UINT64_C(4721223822366429368)},
                          {.value = -1E-08, .expect = UINT64_C(4736245778523059142)},
                          {.value = -1E-56, .expect = UINT64_C(5454032112676519726)},
                          {.value = -1E-57, .expect = UINT64_C(5469309758623409599)},
                          {.value = -1E-58, .expect = UINT64_C(5484234035157343794)},

                          // Larger exponents map to larger uint64
                          {.value = -33E+56, .expect = UINT64_C(3755770421083717738)},
                          {.value = -22E+57, .expect = UINT64_C(3743606376648545798)},
                          {.value = -11E+58, .expect = UINT64_C(3733055120123088324)},

                          {.value = 0, .expect = UINT64_C(9223372036854775808)},
                          {.value = -0.0, .expect = UINT64_C(9223372036854775808)},
                          /* Test cases copied from server Double_Bounds test ... end */
                          /* Test cases copied from server Double_Errors test ... begin */
                          {.value = INFINITY, .expectError = "Infinity and NaN double values are not supported."},
                          {.value = NAN, .expectError = "Infinity and NaN double values are not supported."},
                          /* Test cases copied from server Double_Errors test ... end */

                          /* Test cases copied from Double_Bounds_Precision ... begin */
                          {.value = 3.141592653589,
                           .precision = OPT_I32_C(1),
                           .min = OPT_DOUBLE_C(-100000),
                           .max = OPT_DOUBLE_C(100000),
                           .expect = UINT64_C(1000031),
                           .expectMax = OPT_U64_C(2097151)},
                          {.value = 3.141592653589,
                           .precision = OPT_I32_C(2),
                           .min = OPT_DOUBLE_C(-100000),
                           .max = OPT_DOUBLE_C(100000),
                           .expect = 10000314,
                           .expectMax = OPT_U64_C(33554431)},
                          {.value = 3.141592653589,
                           .precision = OPT_I32_C(3),
                           .min = OPT_DOUBLE_C(-100000),
                           .max = OPT_DOUBLE_C(100000),
                           .expect = 100003141,
                           .expectMax = OPT_U64_C(268435455)},
                          {.value = 3.141592653589,
                           .precision = OPT_I32_C(4),
                           .min = OPT_DOUBLE_C(-100000),
                           .max = OPT_DOUBLE_C(100000),
                           .expect = 1000031415,
                           .expectMax = OPT_U64_C(2147483647)},
                          {.value = 3.141592653589,
                           .precision = OPT_I32_C(5),
                           .min = OPT_DOUBLE_C(-100000),
                           .max = OPT_DOUBLE_C(100000),
                           .expect = 10000314159,
                           .expectMax = OPT_U64_C(34359738367)},
                          {.value = 3.141592653589,
                           .precision = OPT_I32_C(6),
                           .min = OPT_DOUBLE_C(-100000),
                           .max = OPT_DOUBLE_C(100000),
                           .expect = 100003141592,
                           .expectMax = OPT_U64_C(274877906943)},
                          {.value = 3.141592653589,
                           .precision = OPT_I32_C(7),
                           .min = OPT_DOUBLE_C(-100000),
                           .max = OPT_DOUBLE_C(100000),
                           .expect = 1000031415926,
                           .expectMax = OPT_U64_C(2199023255551)},
                          {.value = 0,
                           .max = OPT_DOUBLE_C(1),
                           .min = OPT_DOUBLE_C(-1),
                           .precision = OPT_I32_C(3),
                           .expect = 1000,
                           .expectMax = OPT_U64_C(4095)},
                          {.value = 0,
                           .max = OPT_DOUBLE_C(1),
                           .min = OPT_DOUBLE_C(-1E5),
                           .precision = OPT_I32_C(3),
                           .expect = 100000000,
                           .expectMax = OPT_U64_C(134217727)},
                          {.value = -1E-33,
                           .max = OPT_DOUBLE_C(1),
                           .min = OPT_DOUBLE_C(-1E5),
                           .precision = OPT_I32_C(3),
                           .expect = 100000000,
                           .expectMax = OPT_U64_C(134217727)},
                          {.value = 0,
                           .max = OPT_DOUBLE_C(DBL_MAX),
                           .min = OPT_DOUBLE_C(-DBL_MAX),
                           .precision = OPT_I32_C(3),
                           // Applying min/max/precision result in a domain needing >= 64 bits to represent.
                           // For range v2, expect an error.
                           .expectError = "Invalid upper bound for double precision."},
                          {.value = 0,
                           .max = OPT_DOUBLE_C(DBL_MAX),
                           .min = OPT_DOUBLE_C(-DBL_MAX),
                           .precision = OPT_I32_C(3),
                           // Applying min/max/precision result in a domain needing >= 64 bits to represent.
                           // For range v2, expect an error.
                           .expectError = "Invalid upper bound for double precision."},
                          {.value = 3.141592653589,
                           .max = OPT_DOUBLE_C(5),
                           .min = OPT_DOUBLE_C(0),
                           .precision = OPT_I32_C(0),
                           .expect = 3,
                           .expectMax = OPT_U64_C(7)},
                          {.value = 3.141592653589,
                           .max = OPT_DOUBLE_C(5),
                           .min = OPT_DOUBLE_C(0),
                           .precision = OPT_I32_C(1),
                           .expect = 31,
                           .expectMax = OPT_U64_C(63)},
                          {.value = 3.141592653589,
                           .max = OPT_DOUBLE_C(5),
                           .min = OPT_DOUBLE_C(0),
                           .precision = OPT_I32_C(2),
                           .expect = 314,
                           .expectMax = OPT_U64_C(1023)},
                          {.value = 3.141592653589,
                           .max = OPT_DOUBLE_C(5),
                           .min = OPT_DOUBLE_C(0),
                           .precision = OPT_I32_C(3),
                           .expect = 3141,
                           .expectMax = OPT_U64_C(8191)},
                          {.value = 3.141592653589,
                           .max = OPT_DOUBLE_C(5),
                           .min = OPT_DOUBLE_C(0),
                           .precision = OPT_I32_C(16),
                           .expectError = "Invalid upper bound for double precision"},
                          {.value = -5,
                           .max = OPT_DOUBLE_C(-1),
                           .min = OPT_DOUBLE_C(-10),
                           .precision = OPT_I32_C(3),
                           .expect = 5000,
                           .expectMax = OPT_U64_C(16383)},
                          {.value = 1E100,
                           .max = OPT_DOUBLE_C(DBL_MAX),
                           .min = OPT_DOUBLE_C(-DBL_MAX),
                           .precision = OPT_I32_C(3),
                           // Applying min/max/precision result in a domain needing >= 64 bits to represent.
                           // For range v2, expect an error.
                           .expectError = "Invalid upper bound for double precision."},
                          {.value = 1E100,
                           .max = OPT_DOUBLE_C(DBL_MAX),
                           .min = OPT_DOUBLE_C(-DBL_MAX),
                           .precision = OPT_I32_C(3),
                           // Applying min/max/precision result in a domain needing >= 64 bits to represent.
                           // For range v2, expect an error.
                           .expectError = "Invalid upper bound for double precision."},
                          {.value = 1E9,
                           .max = OPT_DOUBLE_C(1E10),
                           .min = OPT_DOUBLE_C(0),
                           .precision = OPT_I32_C(3),
                           .expect = 1000000000000,
                           .expectMax = OPT_U64_C(17592186044415)},
                          {.value = 1E9,
                           .max = OPT_DOUBLE_C(1E10),
                           .min = OPT_DOUBLE_C(0),
                           .precision = OPT_I32_C(0),
                           .expect = 1000000000,
                           .expectMax = OPT_U64_C(17179869183)},
                          {.value = -5,
                           .max = OPT_DOUBLE_C(10),
                           .min = OPT_DOUBLE_C(-10),
                           .precision = OPT_I32_C(0),
                           .expect = 5,
                           .expectMax = OPT_U64_C(31)},
                          {.value = -5,
                           .max = OPT_DOUBLE_C(10),
                           .min = OPT_DOUBLE_C(-10),
                           .precision = OPT_I32_C(2),
                           .expect = 500,
                           .expectMax = OPT_U64_C(4095)},
                          {.value = 1E-30,
                           .max = OPT_DOUBLE_C(10E-30),
                           .min = OPT_DOUBLE_C(1E-30),
                           .precision = OPT_I32_C(35),
                           // Applying min/max/precision result in a domain needing >= 53 bits to represent.
                           // For range v2, expect an error.
                           .expectError = "Invalid upper bound for double precision."},
                          {.value = 1E-30,
                           .max = OPT_DOUBLE_C(10E-30),
                           .min = OPT_DOUBLE_C(1E-30),
                           .precision = OPT_I32_C(35),
                           // Applying min/max/precision result in a domain needing >= 53 bits to represent.
                           // For range v2, expect an error.
                           .expectError = "Invalid upper bound for double precision."},
                          /* Test max and min integer bounds for doubles */
                          {.value = DOUBLE_MIN_SAFE_INT,
                           .max = OPT_DOUBLE_C(DOUBLE_MAX_SAFE_INT),
                           .min = OPT_DOUBLE_C(DOUBLE_MIN_SAFE_INT),
                           .precision = OPT_I32_C(0),
                           .expect = 0,
                           // Applying min/max/precision result in a domain needing >= 53 bits to represent.
                           // For range v2, expect an error.
                           .expectError = "Invalid upper bound for double precision. Absolute"},
                          {.value = 900719925474099.6,
                           .max = OPT_DOUBLE_C(900719925474100.0),
                           .min = OPT_DOUBLE_C(900719925474099.0),
                           .precision = OPT_I32_C(0),
                           .expect = 0,
                           .expectMax = OPT_U64_C(1)},
                          // Domain size is small but min/max * 10^precision loses precision.
                          {.value = 900719925474099.6,
                           .max = OPT_DOUBLE_C(900719925474100.0),
                           .min = OPT_DOUBLE_C(900719925474099.0),
                           .precision = OPT_I32_C(1),
                           .expectError = "Invalid upper bound for double precision. Absolute"},
                          {.value = -900719925474099.6,
                           .max = OPT_DOUBLE_C(-900719925474099.0),
                           .min = OPT_DOUBLE_C(-900719925474100.0),
                           .precision = OPT_I32_C(1),
                           .expectError = "Invalid lower bound for double precision. Absolute"},
                          // 2^52
                          // The expected values increase by 4503599627370496 * 2^(i-52) + j
                          // i.e. the gaps between integers as the exponent increases since doubles lose precision as
                          // the exponent increases
                          {.value = 0,
                           .max = OPT_DOUBLE_C(4503599627370496),
                           .min = OPT_DOUBLE_C(-4503599627370496),
                           .precision = OPT_I32_C(0),
                           .expect = 0,
                           // Applying min/max/precision result in a domain needing >= 53 bits to represent.
                           // For range v2, expect an error.
                           .expectError = "The domain of double values specified by the min"},
                          /* Test cases copied from Double_Bounds_Precision ... end */
                          {.value = -1,
                           .min = OPT_DOUBLE_C(0),
                           .max = OPT_DOUBLE_C(200),
                           .precision = OPT_I32_C(1),
                           .expectError = "greater than or equal to the minimum value"},
                          {.value = -1,
                           .min = OPT_DOUBLE_C(0),
                           .max = OPT_DOUBLE_C(201),
                           .precision = OPT_I32_C(1),
                           .expectError = "less than or equal to the maximum value"},
                          {// Expect error due to precision exceeding INT32_MAX.
                           .value = 1,
                           .min = OPT_DOUBLE_C(1),
                           .max = OPT_DOUBLE_C(2),
                           .precision = OPT_I32_C(-1),
                           .expectError = "Precision must be non-negative"},
                          {// Expect error due to precision exceeding max finite double.
                           // The largest double value is 1.7976931348623157x10^308. 10^309 results in infinity.
                           .value = 1,
                           .min = OPT_DOUBLE_C(0),
                           .max = OPT_DOUBLE_C(1),
                           .precision = OPT_I32_C(309),
                           .expectError = "Precision is too large"}};

    for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
        DoubleTest *test = tests + i;
        mongocrypt_status_t *const status = mongocrypt_status_new();

        if (test->min.set && test->max.set && test->precision.set) {
            TEST_PRINTF("_test_RangeTest_Encode_Double: value=%f, min=%f, max=%f, "
                        "precision=%" PRId32 "\n",
                        test->value,
                        test->min.value,
                        test->max.value,
                        test->precision.value);
        } else {
            TEST_PRINTF("_test_RangeTest_Encode_Double: value=%f\n", test->value);
        }

        mc_OSTType_Double got;
        const bool ok = mc_getTypeInfoDouble((mc_getTypeInfoDouble_args_t){.value = test->value,
                                                                           .min = test->min,
                                                                           .max = test->max,
                                                                           .precision = test->precision},
                                             &got,
                                             status);
        if (test->expectError) {
            ASSERT_OR_PRINT_MSG(!ok, "expected error, but got none");
            ASSERT_STATUS_CONTAINS(status, test->expectError);
        } else {
            ASSERT_OK_STATUS(ok, status);
            ASSERT_CMPUINT64(got.value, ==, test->expect);
            ASSERT_CMPUINT64(got.min, ==, 0);
            ASSERT_CMPUINT64(got.max, ==, test->expectMax.set ? test->expectMax.value : UINT64_MAX);
        }
        mongocrypt_status_destroy(status);
    }
}

#if MONGOCRYPT_HAVE_DECIMAL128_SUPPORT()
typedef struct {
    mc_dec128 value;
    mc_optional_dec128_t min;
    mc_optional_dec128_t max;
    mc_optional_int32_t precision;
    mlib_int128 expect;
    const char *expectError;
    bool use_range_v1; // By default, use range v2.
} Decimal128Test;

static void _test_canUsePrecisionModeDecimal(_mongocrypt_tester_t *tester) {
#define CAN_USE_PRECISION_MODE(lb, ub, prc, expected, expected_bits_out)                                               \
    {                                                                                                                  \
        uint32_t bits_out = 0;                                                                                         \
        mongocrypt_status_t *const status = mongocrypt_status_new();                                                   \
        TEST_PRINTF("_test_canUsePrecisionModeDecimal, min: %s, max: %s, prc: %" PRIu32,                               \
                    mc_dec128_to_string(lb).str,                                                                       \
                    mc_dec128_to_string(ub).str,                                                                       \
                    prc);                                                                                              \
        bool result = mc_canUsePrecisionModeDecimal(lb, ub, prc, &bits_out, status);                                   \
        ASSERT_OK_STATUS(mongocrypt_status_ok(status), status);                                                        \
        ASSERT(result == expected);                                                                                    \
        ASSERT_CMPINT32(expected_bits_out, ==, bits_out);                                                              \
        mongocrypt_status_destroy(status);                                                                             \
    }

#define CAN_USE_PRECISION_MODE_ERRORS(lb, ub, prc, error)                                                              \
    {                                                                                                                  \
        mongocrypt_status_t *const status = mongocrypt_status_new();                                                   \
        TEST_PRINTF("_test_canUsePrecisionModeDecimal errors, min: %s, max: %s, prc: %" PRIu32,                        \
                    mc_dec128_to_string(lb).str,                                                                       \
                    mc_dec128_to_string(ub).str,                                                                       \
                    prc);                                                                                              \
        uint32_t bits_out = 0;                                                                                         \
        bool result = mc_canUsePrecisionModeDecimal(lb, ub, prc, &bits_out, status);                                   \
        ASSERT_OR_PRINT_MSG(!result, "expected error, but got none");                                                  \
        ASSERT_STATUS_CONTAINS(status, error);                                                                         \
        mongocrypt_status_destroy(status);                                                                             \
    }

    CAN_USE_PRECISION_MODE(MC_DEC128(1), MC_DEC128(16), 0, true, 4);
    CAN_USE_PRECISION_MODE(MC_DEC128(0), MC_DEC128(16), 0, true, 5);

    // It is unclear where Decimal128 looses precision, so we choose an arbitrarily large value
    // and make sure that max_bits is correct for that boundary.
    CAN_USE_PRECISION_MODE(MC_DEC128(1), mc_dec128_from_string("324518553658426726783156020576256"), 0, true, 108);
    CAN_USE_PRECISION_MODE(MC_DEC128(0), mc_dec128_from_string("324518553658426726783156020576256"), 0, true, 109);

    CAN_USE_PRECISION_MODE(mc_dec128_from_string("-100000000000000000000000000000000"),
                           mc_dec128_from_string("170141183460469231731687303715880000000"),
                           0,
                           false,
                           128);

    CAN_USE_PRECISION_MODE_ERRORS(mc_dec128_from_string("788545.12392843"),
                                  mc_dec128_from_string("4607431769000000.129834923"),
                                  4,
                                  "Invalid upper bound for Decimal128 precision. Fractional digits");
    CAN_USE_PRECISION_MODE_ERRORS(mc_dec128_from_string("788545.12392843"),
                                  mc_dec128_from_string("7885451.2"),
                                  4,
                                  "Invalid lower bound for Decimal128 precision. Fractional digits");
    CAN_USE_PRECISION_MODE_ERRORS(mc_dec128_from_string("324518553658426726783156020576256"),
                                  mc_dec128_from_string("340282366920938463463374607431768211455"),
                                  10,
                                  "Invalid upper bound for Decimal128 precision. Absolute scaled");

    CAN_USE_PRECISION_MODE_ERRORS(mc_dec128_from_string("-340282366920938463463374607431768211455"),
                                  mc_dec128_from_string("-3245185536584267267831560"),
                                  10,
                                  "Invalid lower bound for Decimal128 precision. Absolute scaled");

    CAN_USE_PRECISION_MODE_ERRORS(mc_dec128_from_string("-17014118346046923173168730371588.0000000"),
                                  mc_dec128_from_string("17014118346046923173168730371588.0000000"),
                                  7,
                                  "Invalid value for precision");

    CAN_USE_PRECISION_MODE_ERRORS(MC_DEC128(788545.000000),
                                  mc_dec128_from_string("340282366920938463463374607431769000000.000000"),
                                  0,
                                  "Invalid upper bound for Decimal128 precision. Absolute scaled");

#undef CAN_USE_PRECISION_MODE
#undef CAN_USE_PRECISION_MODE_ERRORS
}

static void _test_RangeTest_Encode_Decimal128(_mongocrypt_tester_t *tester) {
    Decimal128Test tests[] = {
#define CASE(Value, ExpectStr)                                                                                         \
    (Decimal128Test){.value = mc_dec128_from_string(#Value), .expect = mlib_int128_from_string(ExpectStr, NULL)}
        /* Test cases copied from server Decimal128_Bounds test ... begin */
        // Larger numbers map to larger int128
        CASE(-1234567890E7, "108549948892579231731687303715884111887"),
        CASE(-1234567890E6, "108559948892579231731687303715884111886"),
        CASE(-1234567890E5, "108569948892579231731687303715884111885"),
        CASE(-1234567890E4, "108579948892579231731687303715884111884"),
        CASE(-1234567890E3, "108589948892579231731687303715884111883"),
        CASE(-1234567890E2, "108599948892579231731687303715884111882"),
        CASE(-1234567890E1, "108609948892579231731687303715884111881"),
        CASE(-123456789012345, "108569948892579108281687303715884111885"),
        CASE(-12345678901234, "108579948892579108331687303715884111884"),
        CASE(-1234567890123, "108589948892579108731687303715884111883"),
        CASE(-123456789012, "108599948892579111731687303715884111882"),
        CASE(-12345678901, "108609948892579131731687303715884111881"),
        CASE(-1234567890, "108619948892579231731687303715884111880"),
        CASE(-99999999, "108631183460569231731687303715884111878"),
        CASE(-8888888, "108642294572469231731687303715884111877"),
        CASE(-777777, "108653405690469231731687303715884111876"),
        CASE(-66666, "108664516860469231731687303715884111875"),
        CASE(-5555, "108675628460469231731687303715884111874"),
        CASE(-444, "108686743460469231731687303715884111873"),
        CASE(-334, "108687843460469231731687303715884111873"),
        CASE(-333, "108687853460469231731687303715884111873"),
        CASE(-44, "108696783460469231731687303715884111872"),
        CASE(-33, "108697883460469231731687303715884111872"),
        CASE(-22, "108698983460469231731687303715884111872"),
        CASE(-5, "108706183460469231731687303715884111871"),
        CASE(-4, "108707183460469231731687303715884111871"),
        CASE(-3, "108708183460469231731687303715884111871"),
        CASE(-2, "108709183460469231731687303715884111871"),
        CASE(-1, "108710183460469231731687303715884111871"),
        CASE(0, "170141183460469231731687303715884105728"),
        CASE(1, "231572183460469231731687303715884099585"),
        CASE(2, "231573183460469231731687303715884099585"),
        CASE(3, "231574183460469231731687303715884099585"),
        CASE(4, "231575183460469231731687303715884099585"),
        CASE(5, "231576183460469231731687303715884099585"),
        CASE(22, "231583383460469231731687303715884099584"),
        CASE(33, "231584483460469231731687303715884099584"),
        CASE(44, "231585583460469231731687303715884099584"),
        CASE(333, "231594513460469231731687303715884099583"),
        CASE(334, "231594523460469231731687303715884099583"),
        CASE(444, "231595623460469231731687303715884099583"),
        CASE(5555, "231606738460469231731687303715884099582"),
        CASE(66666, "231617850060469231731687303715884099581"),
        CASE(777777, "231628961230469231731687303715884099580"),
        CASE(8888888, "231640072348469231731687303715884099579"),
        CASE(33E56, "232144483460469231731687303715884099528"),
        CASE(22E57, "232153383460469231731687303715884099527"),
        CASE(11E58, "232162283460469231731687303715884099526"),

        // Smaller exponents map to smaller int128
        CASE(1E-6, "231512183460469231731687303715884099591"),
        CASE(1E-7, "231502183460469231731687303715884099592"),
        CASE(1E-8, "231492183460469231731687303715884099593"),
        CASE(1E-56, "231012183460469231731687303715884099641"),
        CASE(1E-57, "231002183460469231731687303715884099642"),
        CASE(1E-58, "230992183460469231731687303715884099643"),

        // Smaller negative exponents map to smaller int128
        CASE(-1E-6, "108770183460469231731687303715884111865"),
        CASE(-1E-7, "108780183460469231731687303715884111864"),
        CASE(-1E-8, "108790183460469231731687303715884111863"),
        CASE(-1E-56, "109270183460469231731687303715884111815"),
        CASE(-1E-57, "109280183460469231731687303715884111814"),
        CASE(-1E-58, "109290183460469231731687303715884111813"),

        // Larger exponents map to larger int128
        CASE(-33E56, "108137883460469231731687303715884111928"),
        CASE(-22E57, "108128983460469231731687303715884111929"),
        CASE(-11E58, "108120083460469231731687303715884111930"),

        (Decimal128Test){.value = MC_DEC128_LARGEST_POSITIVE,
                         .expect = mlib_int128_from_string("293021183460469231731687303715884093440", NULL)},
        (Decimal128Test){.value = MC_DEC128_SMALLEST_POSITIVE,
                         .expect = mlib_int128_from_string("170141183460469231731687303715884105729", NULL)},
        (Decimal128Test){.value = MC_DEC128_LARGEST_NEGATIVE,
                         .expect = mlib_int128_from_string("47261183460469231731687303715884118016", NULL)},
        (Decimal128Test){.value = MC_DEC128_SMALLEST_NEGATIVE,
                         .expect = mlib_int128_from_string("170141183460469231731687303715884105727", NULL)},
        (Decimal128Test){.value = MC_DEC128_NORMALIZED_ZERO,
                         .expect = mlib_int128_from_string("170141183460469231731687303715884105728", NULL)},
        (Decimal128Test){.value = MC_DEC128_NEGATIVE_EXPONENT_ZERO,
                         .expect = mlib_int128_from_string("170141183460469231731687303715884105728", NULL)},
    /* Test cases copied from server Decimal128_Bounds test ... end */

#define ERROR_CASE(Value, Min, Max, Precision, ErrorString)                                                            \
    (Decimal128Test){                                                                                                  \
        .value = Value,                                                                                                \
        .min = Min,                                                                                                    \
        .max = Max,                                                                                                    \
        .precision = Precision,                                                                                        \
        .expectError = ErrorString,                                                                                    \
    }

        ERROR_CASE(MC_DEC128_C(1),
                   OPT_NULLOPT,
                   OPT_MC_DEC128(MC_DEC128_C(2)),
                   OPT_I32(5),
                   "min, max, and precision must all be set or must all be unset"),
        ERROR_CASE(MC_DEC128_C(1),
                   OPT_MC_DEC128(MC_DEC128_C(0)),
                   OPT_NULLOPT,
                   OPT_I32(5),
                   "min, max, and precision must all be set or must all be unset"),
        ERROR_CASE(MC_DEC128_C(1),
                   OPT_MC_DEC128(MC_DEC128_C(2)),
                   OPT_MC_DEC128(MC_DEC128_C(1)),
                   OPT_I32(5),
                   "The minimum value must be less than the maximum value"),

        ERROR_CASE(MC_DEC128_C(1),
                   OPT_MC_DEC128(MC_DEC128_C(2)),
                   OPT_MC_DEC128(MC_DEC128_C(3)),
                   OPT_I32(5),
                   "Value must be greater than or equal to the minimum value "
                   "and less than or equal to the maximum value"),
        ERROR_CASE(MC_DEC128_C(4),
                   OPT_MC_DEC128(MC_DEC128_C(2)),
                   OPT_MC_DEC128(MC_DEC128_C(3)),
                   OPT_I32(5),
                   "Value must be greater than or equal to the minimum value "
                   "and less than or equal to the maximum value"),

        ERROR_CASE(MC_DEC128_POSITIVE_INFINITY,
                   OPT_NULLOPT,
                   OPT_NULLOPT,
                   OPT_NULLOPT,
                   "Infinity and Nan Decimal128 values are not supported."),
        ERROR_CASE(MC_DEC128_NEGATIVE_INFINITY,
                   OPT_NULLOPT,
                   OPT_NULLOPT,
                   OPT_NULLOPT,
                   "Infinity and Nan Decimal128 values are not supported."),

        ERROR_CASE(MC_DEC128_POSITIVE_NAN,
                   OPT_NULLOPT,
                   OPT_NULLOPT,
                   OPT_NULLOPT,
                   "Infinity and Nan Decimal128 values are not supported."),

        ERROR_CASE(MC_DEC128_NEGATIVE_NAN,
                   OPT_NULLOPT,
                   OPT_NULLOPT,
                   OPT_NULLOPT,
                   "Infinity and Nan Decimal128 values are not supported."),

/* Test cases copied from Decimal128_Bounds_Precision ... begin */
#define ASSERT_EIBP(Value, Precision, Expect)                                                                          \
    (Decimal128Test){                                                                                                  \
        .value = mc_dec128_from_string(Value),                                                                         \
        .min = OPT_MC_DEC128(MC_DEC128_C(-100000)),                                                                    \
        .max = OPT_MC_DEC128(MC_DEC128_C(100000)),                                                                     \
        .precision = OPT_I32(Precision),                                                                               \
        .expect = MLIB_INT128(Expect),                                                                                 \
    }
        ASSERT_EIBP("3.141592653589E-1", 10, 1000003141592653),
        ASSERT_EIBP("31.41592653589E-2", 10, 1000003141592653),
        ASSERT_EIBP("314.1592653589E-3", 10, 1000003141592653),
        ASSERT_EIBP("3141.592653589E-4", 10, 1000003141592653),
        ASSERT_EIBP("31415.92653589E-5", 10, 1000003141592653),
        ASSERT_EIBP("314159.2653589E-6", 10, 1000003141592653),
        ASSERT_EIBP("3141592.653589E-7", 10, 1000003141592653),
        ASSERT_EIBP("31415926.53589E-8", 10, 1000003141592653),
#undef ASSERT_EIBP

#define ASSERT_EIBPL(Value, Precision, Expect)                                                                         \
    (Decimal128Test){                                                                                                  \
        .value = mc_dec128_from_string(Value),                                                                         \
        .min = OPT_MC_DEC128(MC_DEC128_C(-100000)),                                                                    \
        .max = OPT_MC_DEC128(mc_dec128_from_string("1E22")),                                                           \
        .precision = OPT_I32(Precision),                                                                               \
        .expect = mlib_int128_from_string(Expect, NULL),                                                               \
    }

        ASSERT_EIBPL("3.1415926535897932384626433832795E20", 5, "31415926535897942384626433"),
        ASSERT_EIBPL("3.1415926535897932384626433832795E20", 6, "314159265358979423846264338"),

        ASSERT_EIBPL("3.1415926535897932384626433832795E20", 7, "3141592653589794238462643383"),

        ASSERT_EIBPL("3.1415926535897932384626433832795E20", 8, "31415926535897942384626433832"),
#undef ASSERT_EIBPL

#define ASSERT_EIBP(Value, Precision, Expect)                                                                          \
    (Decimal128Test){                                                                                                  \
        .value = mc_dec128_from_string(#Value),                                                                        \
        .min = OPT_MC_DEC128(MC_DEC128_C(-100000)),                                                                    \
        .max = OPT_MC_DEC128(MC_DEC128_C(100000)),                                                                     \
        .precision = OPT_I32(Precision),                                                                               \
        .expect = MLIB_INT128_CAST(Expect),                                                                            \
    }
        ASSERT_EIBP(3.141592653589, 1, 1000031),
        ASSERT_EIBP(3.141592653589, 2, 10000314),
        ASSERT_EIBP(3.141592653589, 3, 100003141),
        ASSERT_EIBP(3.141592653589, 4, 1000031415),
        ASSERT_EIBP(3.141592653589, 5, 10000314159),
        ASSERT_EIBP(3.141592653589, 6, 100003141592),
        ASSERT_EIBP(3.141592653589, 7, 1000031415926),
#undef ASSERT_EIBP

#define ASSERT_EIBB(Val, Max, Min, Precision, Expect)                                                                  \
    (Decimal128Test){                                                                                                  \
        .value = mc_dec128_from_string(#Val),                                                                          \
        .min = OPT_MC_DEC128(mc_dec128_from_string(#Min)),                                                             \
        .max = OPT_MC_DEC128(mc_dec128_from_string(#Max)),                                                             \
        .precision = OPT_I32(Precision),                                                                               \
        .expect = MLIB_INT128_CAST(Expect),                                                                            \
    }

// ASSERT_EIBB_OVERFLOW defines cases where applying min/max/precision result in a domain needing >= 128 bits.
// For range v1, expect precision to be ignored.
// For range v2, expect an error.
#define ASSERT_EIBB_OVERFLOW(Val, Max, Min, Precision, Expect)                                                         \
    (Decimal128Test){                                                                                                  \
        .value = mc_dec128_from_string(#Val),                                                                          \
        .min = OPT_MC_DEC128(mc_dec128_from_string(#Min)),                                                             \
        .max = OPT_MC_DEC128(mc_dec128_from_string(#Max)),                                                             \
        .precision = OPT_I32(Precision),                                                                               \
        .expect = Expect,                                                                                              \
        .use_range_v1 = true,                                                                                          \
    },                                                                                                                 \
        (Decimal128Test) {                                                                                             \
        .value = mc_dec128_from_string(#Val), .min = OPT_MC_DEC128(mc_dec128_from_string(#Min)),                       \
        .max = OPT_MC_DEC128(mc_dec128_from_string(#Max)), .precision = OPT_I32(Precision),                            \
        .expectError = "The domain of decimal values specified by the min, max, and precision "                        \
                       "cannot be represented in fewer than 128 bits"                                                  \
    }

#define ASSERT_EIBB_ERROR(Val, Max, Min, Precision, Expect, Error)                                                     \
    (Decimal128Test) {                                                                                                 \
        .value = mc_dec128_from_string(#Val), .min = OPT_MC_DEC128(mc_dec128_from_string(#Min)),                       \
        .max = OPT_MC_DEC128(mc_dec128_from_string(#Max)), .precision = OPT_I32(Precision), .expectError = Error       \
    }

        ASSERT_EIBB(0, 1, -1, 3, 1000),
        ASSERT_EIBB(0, 1, -1E5, 3, 100000000),

        ASSERT_EIBB(-1E-33, 1, -1E5, 3, 100000000),

        ASSERT_EIBB_ERROR(0,
                          MC_DEC128_LARGEST_POSITIVE,
                          MC_DEC128_LARGEST_NEGATIVE,
                          3,
                          mlib_int128_from_string("170141183460469231731687303715884105728", NULL),
                          "Invalid upper bound for Decimal128 precision. Max is infinite."),

        ASSERT_EIBB_ERROR(0,
                          DBL_MAX,
                          DBL_MIN,
                          3,
                          mlib_int128_from_string("170141183460469231731687303715884105728", NULL),
                          "Invalid upper bound for Decimal128 precision. Max is infinite."),

        ASSERT_EIBB(3.141592653589, 5, 0, 0, 3),
        ASSERT_EIBB(3.141592653589, 5, 0, 1, 31),

        ASSERT_EIBB(3.141592653589, 5, 0, 2, 314),

        ASSERT_EIBB(3.141592653589, 5, 0, 3, 3141),
        ASSERT_EIBB(3.141592653589, 5, 0, 16, 31415926535890000),

        ASSERT_EIBB(-5, -1, -10, 3, 5000),

        ASSERT_EIBB_ERROR(1E100,
                          DBL_MAX,
                          DBL_MIN,
                          3,
                          mlib_int128_from_string("232572183460469231731687303715884099485", NULL),
                          "Invalid upper bound for Decimal128 precision. Max is infinite."),

        ASSERT_EIBB(1E9, 1E10, 0, 3, 1000000000000),
        ASSERT_EIBB(1E9, 1E10, 0, 0, 1000000000),

        ASSERT_EIBB(-5, 10, -10, 0, 5),
        ASSERT_EIBB(-5, 10, -10, 2, 500),

        ASSERT_EIBB(5E-30, 10E-30, 1E-30, 35, 400000),

        // Test a range that requires > 64 bits.
        ASSERT_EIBB(5, 18446744073709551616, .1, 1, 49),

#undef ASSERT_EIBB
#undef ASSERT_EIBB_OVERFLOW
#undef ASSERT_EIBB_ERROR

        /* Test cases copied from Decimal128_Bounds_Precision ... end */

        {// Expect error due to precision exceeding INT32_MAX.
         .min = OPT_MC_DEC128(MC_DEC128_C(1)),
         .max = OPT_MC_DEC128(MC_DEC128_C(2)),
         .precision = OPT_I32(-1),
         .expectError = "Precision must be non-negative"},
        {// Expect error due to precision exceeding max finite Decimal128.
         // The largest decimal128 value is 9.99999...x10^6144. 10^6145 results in infinity.
         .min = OPT_MC_DEC128(MC_DEC128_C(0)),
         .max = OPT_MC_DEC128(MC_DEC128_C(1)),
         .precision = OPT_I32(6145),
         .expectError = "Precision is too large"}};

    for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
        Decimal128Test *test = tests + i;
        mongocrypt_status_t *const status = mongocrypt_status_new();

        if (test->min.set && test->max.set && test->precision.set) {
            TEST_PRINTF("_test_RangeTest_Encode_Decimal128: value=%s, min=%s, max=%s, "
                        "precision=%" PRIu32 "\n",
                        mc_dec128_to_string(test->value).str,
                        mc_dec128_to_string(test->min.value).str,
                        mc_dec128_to_string(test->max.value).str,
                        test->precision.value);
        } else {
            TEST_PRINTF("_test_RangeTest_Encode_Decimal128: value=%s\n", mc_dec128_to_string(test->value).str);
        }
        mc_OSTType_Decimal128 got;
        const bool ok = mc_getTypeInfoDecimal128(
            (mc_getTypeInfoDecimal128_args_t){
                .value = test->value,
                .min = test->min,
                .max = test->max,
                .precision = test->precision,
            },
            &got,
            status);
        if (test->expectError) {
            ASSERT_OR_PRINT_MSG(!ok, "expected error, but got none");
            ASSERT_STATUS_CONTAINS(status, test->expectError);
        } else {
            ASSERT_OK_STATUS(ok, status);

            ASSERT_CMPINT128_EQ(got.value, test->expect);
            ASSERT_CMPINT128_EQ(got.min, MLIB_INT128(0));
        }
        mongocrypt_status_destroy(status);
    }
}

#endif // MONGOCRYPT_HAVE_DECIMAL128_SUPPORT

void _mongocrypt_tester_install_range_encoding(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(_test_RangeTest_Encode_Int32);
    INSTALL_TEST(_test_RangeTest_Encode_Int64);
    INSTALL_TEST(_test_canUsePrecisionModeDouble);
    INSTALL_TEST(_test_RangeTest_Encode_Double);
#if MONGOCRYPT_HAVE_DECIMAL128_SUPPORT()
    INSTALL_TEST(_test_canUsePrecisionModeDecimal);
    INSTALL_TEST(_test_RangeTest_Encode_Decimal128);
#endif
}
