/*
 * 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-array-private.h"
#include "mc-optional-private.h"
#include "mc-range-encoding-private.h"
#include "mc-range-mincover-private.h"

enum {
    /// Why this number? The Decimal128 tests generate thousands of test strings,
    /// but we can't set this arbitrarily high, since we'll bump up on stack
    /// overflow on MSVC. This is large enough to capture all strings in all
    /// Decimal128 tests without overflowing the stack.
    MAX_MINCOVER_STRINGS = 4500
};

typedef struct {
    int32_t lowerBound;
    bool includeLowerBound;
    int32_t upperBound;
    bool includeUpperBound;
    mc_optional_int32_t min;
    mc_optional_int32_t max;
    size_t sparsity;
    uint32_t trimFactor;
    const char *expectMincoverStrings[MAX_MINCOVER_STRINGS];
    const char *expectError;
} Int32Test;

typedef struct {
    int64_t lowerBound;
    bool includeLowerBound;
    int64_t upperBound;
    bool includeUpperBound;
    mc_optional_int64_t min;
    mc_optional_int64_t max;
    size_t sparsity;
    const char *expectMincoverStrings[MAX_MINCOVER_STRINGS];
    const char *expectError;
} Int64Test;

typedef struct {
    double lowerBound;
    bool includeLowerBound;
    double upperBound;
    bool includeUpperBound;
    size_t sparsity;
    mc_optional_double_t min;
    mc_optional_double_t max;
    mc_optional_int32_t precision;
    const char *expectMincoverStrings[MAX_MINCOVER_STRINGS];
    const char *expectError;
} DoubleTest;

#if MONGOCRYPT_HAVE_DECIMAL128_SUPPORT()
typedef struct {
    mc_dec128 lowerBound;
    bool includeLowerBound;
    mc_dec128 upperBound;
    bool includeUpperBound;
    size_t sparsity;
    mc_optional_dec128_t min;
    mc_optional_dec128_t max;
    mc_optional_int32_t precision;
    const char *expectMincoverStrings[MAX_MINCOVER_STRINGS];
    const char *expectError;
} Decimal128Test;
#endif

typedef struct _test_getMincover_args {
    mc_mincover_t *(*getMincover)(void *tests, size_t idx, mongocrypt_status_t *status);
    const char *(*expectError)(void *tests, size_t idx, mongocrypt_status_t *status);
    const char *const *(*expectMincoverStrings)(void *tests, size_t idx);
    void (*dump)(void *tests, size_t idx, mc_mincover_t *got);
} _test_getMincover_args;

static mc_mincover_t *_test_getMincover32(void *tests, size_t idx, mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(tests);

    Int32Test *test = (Int32Test *)tests + idx;

    return mc_getMincoverInt32((mc_getMincoverInt32_args_t){.lowerBound = test->lowerBound,
                                                            .includeLowerBound = test->includeLowerBound,
                                                            .upperBound = test->upperBound,
                                                            .includeUpperBound = test->includeUpperBound,
                                                            .min = test->min,
                                                            .max = test->max,
                                                            .sparsity = test->sparsity,
                                                            .trimFactor = OPT_I32(test->trimFactor)},
                               status);
}

static mc_mincover_t *_test_getMincover64(void *tests, size_t idx, mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(tests);

    const uint32_t trimFactor = 0; // At present, all test cases expect trimFactor=0.
    Int64Test *const test = (Int64Test *)tests + idx;

    return mc_getMincoverInt64((mc_getMincoverInt64_args_t){.lowerBound = test->lowerBound,
                                                            .includeLowerBound = test->includeLowerBound,
                                                            .upperBound = test->upperBound,
                                                            .includeUpperBound = test->includeUpperBound,
                                                            .min = test->min,
                                                            .max = test->max,
                                                            .sparsity = test->sparsity,
                                                            .trimFactor = OPT_I32(trimFactor)},
                               status);
}

static mc_mincover_t *_test_getMincoverDouble_helper(void *tests, size_t idx, mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(tests);

    const uint32_t trimFactor = 0; // At present, all test cases expect trimFactor=0.
    DoubleTest *const test = (DoubleTest *)tests + idx;

    return mc_getMincoverDouble(
        (mc_getMincoverDouble_args_t){.lowerBound = test->lowerBound,
                                      .includeLowerBound = test->includeLowerBound,
                                      .upperBound = test->upperBound,
                                      .includeUpperBound = test->includeUpperBound,
                                      .sparsity = test->sparsity,
                                      .min = test->precision.set ? test->min : (mc_optional_double_t){0},
                                      .max = test->precision.set ? test->max : (mc_optional_double_t){0},
                                      .precision = test->precision,
                                      .trimFactor = OPT_I32(trimFactor)},
        status);
}

#if MONGOCRYPT_HAVE_DECIMAL128_SUPPORT()
static mc_mincover_t *_test_getMincoverDecimal128_helper(void *tests, size_t idx, mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(tests);

    Decimal128Test *const test = (Decimal128Test *)tests + idx;

    const uint32_t trimFactor = 0; // At present, all test cases expect trimFactor=0.
    return mc_getMincoverDecimal128(
        (mc_getMincoverDecimal128_args_t){.lowerBound = test->lowerBound,
                                          .includeLowerBound = test->includeLowerBound,
                                          .upperBound = test->upperBound,
                                          .includeUpperBound = test->includeUpperBound,
                                          .sparsity = test->sparsity,
                                          .min = test->precision.set ? test->min : (mc_optional_dec128_t){0},
                                          .max = test->precision.set ? test->max : (mc_optional_dec128_t){0},
                                          .precision = test->precision,
                                          .trimFactor = OPT_I32(trimFactor)},
        status);
}
#endif // MONGOCRYPT_HAVE_DECIMAL128_SUPPORT

static const char *_test_expectError32(void *tests, size_t idx, mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(tests);
    BSON_ASSERT_PARAM(status);
    return ((Int32Test *)tests + idx)->expectError;
}

static const char *_test_expectError64(void *tests, size_t idx, mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(tests);
    BSON_ASSERT_PARAM(status);
    return ((Int64Test *)tests + idx)->expectError;
}

static const char *_test_expectErrorDouble(void *tests, size_t idx, mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(tests);
    BSON_ASSERT_PARAM(status);
    DoubleTest *test = ((DoubleTest *)tests + idx);
    if (test->min.set && test->max.set && test->precision.set) {
        // Expect an error for tests including an invalid min/max/precision.
        uint32_t ignored;
        if (!mc_canUsePrecisionModeDouble(test->min.value, test->max.value, test->precision.value, &ignored, status)) {
            if (!mongocrypt_status_ok(status)) {
                return mongocrypt_status_message(status, NULL);
            }

            return "The domain of double values specified by the min, max, and precision cannot be represented in "
                   "fewer than 64 bits";
        }
    }
    return test->expectError;
}

#if MONGOCRYPT_HAVE_DECIMAL128_SUPPORT()
static const char *_test_expectErrorDecimal128(void *tests, size_t idx, mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(tests);
    BSON_ASSERT_PARAM(status);
    Decimal128Test *test = ((Decimal128Test *)tests + idx);
    if (test->min.set && test->max.set && test->precision.set) {
        // Expect an error for tests including an invalid min/max/precision.
        uint32_t ignored;
        if (!mc_canUsePrecisionModeDecimal(test->min.value, test->max.value, test->precision.value, &ignored, status)) {
            if (!mongocrypt_status_ok(status)) {
                return mongocrypt_status_message(status, NULL);
            }

            return "The domain of decimal values specified by the min, max, and precision cannot be represented in "
                   "fewer than 128 bits";
        }
    }
    return test->expectError;
}
#endif // MONGOCRYPT_HAVE_DECIMAL128_SUPPORT

static const char *const *_test_expectMincover32(void *tests, size_t idx) {
    BSON_ASSERT_PARAM(tests);
    return ((Int32Test *)tests + idx)->expectMincoverStrings;
}

static const char *const *_test_expectMincover64(void *tests, size_t idx) {
    BSON_ASSERT_PARAM(tests);
    return ((Int64Test *)tests + idx)->expectMincoverStrings;
}

static const char *const *_test_expectMincoverDouble(void *tests, size_t idx) {
    BSON_ASSERT_PARAM(tests);
    return ((DoubleTest *)tests + idx)->expectMincoverStrings;
}

#if MONGOCRYPT_HAVE_DECIMAL128_SUPPORT()
static const char *const *_test_expectMincoverDecimal128(void *tests, size_t idx) {
    BSON_ASSERT_PARAM(tests);
    return ((Decimal128Test *)tests + idx)->expectMincoverStrings;
}
#endif // MONGOCRYPT_HAVE_DECIMAL128_SUPPORT

static void _test_dump_32(void *tests, size_t idx, mc_mincover_t *got) {
    BSON_ASSERT_PARAM(tests);
    Int32Test *const test = (Int32Test *)tests + idx;
    TEST_STDERR_PRINTF("testcase: lowerBound=%" PRId32 " (%s) upperBound=%" PRId32 " (%s)",
                       test->lowerBound,
                       test->includeLowerBound ? "inclusive" : "exclusive",
                       test->upperBound,
                       test->includeUpperBound ? "inclusive" : "exclusive");
    if (test->min.set) {
        TEST_STDERR_PRINTF(" min=%" PRId32, test->min.value);
    }
    if (test->max.set) {
        TEST_STDERR_PRINTF(" max=%" PRId32, test->max.value);
    }
    TEST_STDERR_PRINTF(" sparsity=%zu\n", test->sparsity);
    TEST_STDERR_PRINTF("mincover expected ... begin\n");
    for (const char **p = test->expectMincoverStrings; *p; ++p) {
        TEST_STDERR_PRINTF("  %s\n", *p);
    }
    TEST_STDERR_PRINTF("mincover expected ... end\n");
    TEST_STDERR_PRINTF("mincover got ... begin\n");
    for (size_t i = 0; i < mc_mincover_len(got); i++) {
        TEST_STDERR_PRINTF("  %s\n", mc_mincover_get(got, i));
    }
    TEST_STDERR_PRINTF("mincover got ... end\n");
}

static void _test_dump_64(void *tests, size_t idx, mc_mincover_t *got) {
    BSON_ASSERT_PARAM(tests);
    Int64Test *const test = (Int64Test *)tests + idx;
    TEST_STDERR_PRINTF("testcase: lowerBound=%" PRId64 " (%s) upperBound=%" PRId64 " (%s)",
                       test->lowerBound,
                       test->includeLowerBound ? "inclusive" : "exclusive",
                       test->upperBound,
                       test->includeUpperBound ? "inclusive" : "exclusive");
    if (test->min.set) {
        TEST_STDERR_PRINTF(" min=%" PRId64, test->min.value);
    }
    if (test->max.set) {
        TEST_STDERR_PRINTF(" max=%" PRId64, test->max.value);
    }
    TEST_STDERR_PRINTF(" sparsity=%zu\n", test->sparsity);
    TEST_STDERR_PRINTF("mincover expected ... begin\n");
    for (const char **p = test->expectMincoverStrings; *p; ++p) {
        TEST_STDERR_PRINTF("  %s\n", *p);
    }
    TEST_STDERR_PRINTF("mincover expected ... end\n");
    TEST_STDERR_PRINTF("mincover got ... begin\n");
    for (size_t i = 0; i < mc_mincover_len(got); i++) {
        TEST_STDERR_PRINTF("  %s\n", mc_mincover_get(got, i));
    }
    TEST_STDERR_PRINTF("mincover got ... end\n");
}

static void _test_dump_Double(void *tests, size_t idx, mc_mincover_t *got) {
    BSON_ASSERT_PARAM(tests);
    DoubleTest *const test = (DoubleTest *)tests + idx;
    TEST_STDERR_PRINTF("testcase: lowerBound=%f (%s) upperBound=%f (%s)",
                       test->lowerBound,
                       test->includeLowerBound ? "inclusive" : "exclusive",
                       test->upperBound,
                       test->includeUpperBound ? "inclusive" : "exclusive");
    if (test->min.set) {
        TEST_STDERR_PRINTF(" min=%f", test->min.value);
    }
    if (test->max.set) {
        TEST_STDERR_PRINTF(" max=%f", test->max.value);
    }
    if (test->precision.set) {
        TEST_STDERR_PRINTF(" precision=%" PRId32, test->precision.value);
    }
    TEST_STDERR_PRINTF(" sparsity=%zu\n", test->sparsity);
    TEST_STDERR_PRINTF("mincover expected ... begin\n");
    for (const char **p = test->expectMincoverStrings; *p; ++p) {
        TEST_STDERR_PRINTF("  %s\n", *p);
    }
    TEST_STDERR_PRINTF("mincover expected ... end\n");
    TEST_STDERR_PRINTF("mincover got ... begin\n");
    for (size_t i = 0; i < mc_mincover_len(got); i++) {
        TEST_STDERR_PRINTF("  %s\n", mc_mincover_get(got, i));
    }
    TEST_STDERR_PRINTF("mincover got ... end\n");
}

#if MONGOCRYPT_HAVE_DECIMAL128_SUPPORT()
static void _test_dump_Decimal128(void *tests, size_t idx, mc_mincover_t *got) {
    BSON_ASSERT_PARAM(tests);
    Decimal128Test *const test = (Decimal128Test *)tests + idx;
    TEST_STDERR_PRINTF("testcase: lowerBound=%s (%s) upperBound=%s (%s)",
                       mc_dec128_to_string(test->lowerBound).str,
                       test->includeLowerBound ? "inclusive" : "exclusive",
                       mc_dec128_to_string(test->upperBound).str,
                       test->includeUpperBound ? "inclusive" : "exclusive");
    if (test->min.set) {
        TEST_STDERR_PRINTF(" min=%s", mc_dec128_to_string(test->min.value).str);
    }
    if (test->max.set) {
        TEST_STDERR_PRINTF(" max=%s", mc_dec128_to_string(test->max.value).str);
    }
    if (test->precision.set) {
        TEST_STDERR_PRINTF(" precision=%" PRIu32, test->precision.value);
    }
    TEST_STDERR_PRINTF(" sparsity=%zu\n", test->sparsity);
    TEST_STDERR_PRINTF("mincover expected ... begin\n");
    for (const char **p = test->expectMincoverStrings; *p; ++p) {
        TEST_STDERR_PRINTF("  %s\n", *p);
    }
    TEST_STDERR_PRINTF("mincover expected ... end\n");
    TEST_STDERR_PRINTF("mincover got ... begin\n");
    for (size_t i = 0; i < mc_mincover_len(got); i++) {
        TEST_STDERR_PRINTF("  %s\n", mc_mincover_get(got, i));
    }
    TEST_STDERR_PRINTF("mincover got ... end\n");
}
#endif // MONGOCRYPT_HAVE_DECIMAL128_SUPPORT

static void _test_getMincover_impl(void *tests, size_t num_tests, _test_getMincover_args args) {
    BSON_ASSERT_PARAM(tests);

    for (size_t i = 0; i < num_tests; i++) {
        mongocrypt_status_t *const status = mongocrypt_status_new();
        mc_mincover_t *got = args.getMincover(tests, i, status);
        const char *expectError = args.expectError(tests, i, status);
        if (expectError) {
            ASSERT_OR_PRINT_MSG(NULL == got, "expected error, got success");
            ASSERT_STATUS_CONTAINS(status, expectError);
            mongocrypt_status_destroy(status);
            continue;
        }
        ASSERT_OK_STATUS(got != NULL, status);

        size_t numGot = mc_mincover_len(got);
        const char *const *expectStrings = args.expectMincoverStrings(tests, i);

        const char *const *exp_iter = expectStrings;
        size_t nthItem = 0;
        for (; *exp_iter; ++nthItem, ++exp_iter) {
            if (nthItem > numGot) {
                // List length mismatch. Keep scanning, though. We'll use the
                // numbers later
                continue;
            }
            const char *gotItem = mc_mincover_get(got, nthItem);
            const char *expectItem = *exp_iter;

            if (0 == strcmp(gotItem, expectItem)) {
                // This one matches, Keep going.
                continue;
            }
            args.dump(tests, nthItem, got);
            TEST_ERROR("test %zu: mincover mismatch at index %zu:\n"
                       "      Got: %s\n"
                       " Expected: %s\n",
                       i,
                       nthItem,
                       gotItem,
                       expectItem);
        }

        if (nthItem != numGot) {
            args.dump(tests, i, got);
            TEST_ERROR("test %zu: Got the wrong number of mincover items. Expected %zu "
                       "items, but got %zu\n",
                       i,
                       nthItem,
                       numGot);
        }

        mc_mincover_destroy(got);
        mongocrypt_status_destroy(status);
    }
}

static void _test_getMincoverInt32(_mongocrypt_tester_t *tester) {
    static Int32Test tests[] = {
        {.lowerBound = 1,
         .includeLowerBound = false,
         .upperBound = 3,
         .includeUpperBound = true,
         .min = OPT_I32_C(0),
         .max = OPT_I32_C(7),
         .sparsity = 1,
         .expectMincoverStrings = {"01"}},
        {.lowerBound = 1,
         .includeLowerBound = true,
         .upperBound = 3,
         .includeUpperBound = false,
         .min = OPT_I32_C(0),
         .max = OPT_I32_C(7),
         .sparsity = 1,
         .expectMincoverStrings = {"001", "010"}},
        {.lowerBound = 1,
         .includeLowerBound = true,
         .upperBound = 3,
         .includeUpperBound = true,
         .min = OPT_I32_C(0),
         .max = OPT_I32_C(7),
         .sparsity = 1,
         .expectMincoverStrings = {"001", "01"}},
        {.lowerBound = 3,
         .includeLowerBound = true,
         .upperBound = 3,
         .includeUpperBound = true,
         .min = OPT_I32_C(0),
         .max = OPT_I32_C(7),
         .sparsity = 1,
         .expectMincoverStrings = {"011"}},
        {.lowerBound = 4,
         .includeLowerBound = true,
         .upperBound = 3,
         .includeUpperBound = true,
         .min = OPT_I32_C(0),
         .max = OPT_I32_C(7),
         .sparsity = 1,
         .expectError = "must be less than or equal to"},
        {.lowerBound = 1,
         .includeLowerBound = true,
         .upperBound = 8,
         .includeUpperBound = true,
         .min = OPT_I32_C(0),
         .max = OPT_I32_C(7),
         .sparsity = 1,
         .expectError = "less than or equal to the maximum value"},
        {.lowerBound = 0,
         .includeLowerBound = true,
         .upperBound = 7,
         .includeUpperBound = true,
         .min = OPT_I32_C(0),
         .max = OPT_I32_C(7),
         .sparsity = 1,
         .expectMincoverStrings = {"root"}},
        {.lowerBound = 0,
         .includeLowerBound = true,
         .upperBound = 7,
         .includeUpperBound = true,
         .min = OPT_I32_C(0),
         .max = OPT_I32_C(7),
         .sparsity = 1,
         .trimFactor = 1,
         .expectMincoverStrings = {"0", "1"}},
        {.lowerBound = 0,
         .includeLowerBound = true,
         .upperBound = 7,
         .includeUpperBound = true,
         .min = OPT_I32_C(0),
         .max = OPT_I32_C(7),
         .sparsity = 1,
         .trimFactor = 2,
         .expectMincoverStrings = {"00", "01", "10", "11"}},
        {.lowerBound = 0,
         .includeLowerBound = true,
         .upperBound = 7,
         .includeUpperBound = true,
         .min = OPT_I32_C(0),
         .max = OPT_I32_C(7),
         .sparsity = 1,
         .trimFactor = 3,
         .expectError =
             "Trim factor must be less than the number of bits (3) used to represent an element of the domain"},

#include "./data/range-min-cover/mincover_int32.cstruct"

    };

    _test_getMincover_impl(tests,
                           sizeof(tests) / sizeof(tests[0]),
                           (_test_getMincover_args){.getMincover = _test_getMincover32,
                                                    .expectMincoverStrings = _test_expectMincover32,
                                                    .expectError = _test_expectError32,
                                                    .dump = _test_dump_32});
}

static void _test_getMincoverInt64(_mongocrypt_tester_t *tester) {
    static Int64Test tests[] = {
        {.lowerBound = 1,
         .includeLowerBound = false,
         .upperBound = 3,
         .includeUpperBound = true,
         .min = OPT_I64_C(0),
         .max = OPT_I64_C(7),
         .sparsity = 1,
         .expectMincoverStrings = {"01"}},
        {.lowerBound = 1,
         .includeLowerBound = true,
         .upperBound = 3,
         .includeUpperBound = false,
         .min = OPT_I64_C(0),
         .max = OPT_I64_C(7),
         .sparsity = 1,
         .expectMincoverStrings = {"001", "010"}},
        {.lowerBound = 1,
         .includeLowerBound = true,
         .upperBound = 3,
         .includeUpperBound = true,
         .min = OPT_I64_C(0),
         .max = OPT_I64_C(7),
         .sparsity = 1,
         .expectMincoverStrings = {"001", "01"}},
        {.lowerBound = 3,
         .includeLowerBound = true,
         .upperBound = 3,
         .includeUpperBound = true,
         .min = OPT_I64_C(0),
         .max = OPT_I64_C(7),
         .sparsity = 1,
         .expectMincoverStrings = {"011"}},
        {.lowerBound = 4,
         .includeLowerBound = true,
         .upperBound = 3,
         .includeUpperBound = true,
         .min = OPT_I64_C(0),
         .max = OPT_I64_C(7),
         .sparsity = 1,
         .expectError = "must be less than or equal to"},
        {.lowerBound = 1,
         .includeLowerBound = true,
         .upperBound = 8,
         .includeUpperBound = true,
         .min = OPT_I64_C(0),
         .max = OPT_I64_C(7),
         .sparsity = 1,
         .expectError = "less than or equal to the maximum value"},

#include "./data/range-min-cover/mincover_int64.cstruct"

    };

    _test_getMincover_impl(tests,
                           sizeof(tests) / sizeof(tests[0]),
                           (_test_getMincover_args){.getMincover = _test_getMincover64,
                                                    .expectMincoverStrings = _test_expectMincover64,
                                                    .expectError = _test_expectError64,
                                                    .dump = _test_dump_64});
}

static void _test_getMincoverDouble(_mongocrypt_tester_t *tester) {
    static DoubleTest tests[] = {
#include "./data/range-min-cover/mincover_double.cstruct"
#include "./data/range-min-cover/mincover_double_precision.cstruct"
    };

    _test_getMincover_impl(tests,
                           sizeof(tests) / sizeof(tests[0]),
                           (_test_getMincover_args){.getMincover = _test_getMincoverDouble_helper,
                                                    .expectMincoverStrings = _test_expectMincoverDouble,
                                                    .expectError = _test_expectErrorDouble,
                                                    .dump = _test_dump_Double});
}

#if MONGOCRYPT_HAVE_DECIMAL128_SUPPORT()
static void _test_getMincoverDecimal128(_mongocrypt_tester_t *tester) {
    Decimal128Test tests[] = {
#include "./data/range-min-cover/mincover_decimal128.cstruct"
#include "./data/range-min-cover/mincover_decimal128_precision.cstruct"
    };

    _test_getMincover_impl(tests,
                           sizeof(tests) / sizeof(tests[0]),
                           (_test_getMincover_args){.getMincover = _test_getMincoverDecimal128_helper,
                                                    .expectMincoverStrings = _test_expectMincoverDecimal128,
                                                    .expectError = _test_expectErrorDecimal128,
                                                    .dump = _test_dump_Decimal128});
}
#endif // MONGOCRYPT_HAVE_DECIMAL128_SUPPORT

void _mongocrypt_tester_install_range_mincover(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(_test_getMincoverInt32);
    INSTALL_TEST(_test_getMincoverInt64);
    INSTALL_TEST(_test_getMincoverDouble);
#if MONGOCRYPT_HAVE_DECIMAL128_SUPPORT()
    INSTALL_TEST(_test_getMincoverDecimal128);
#endif
}
