/*
 * 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-check-conversions-private.h"
#include "mc-range-encoding-private.h"

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

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.
      printf ("_test_RangeTest_Encode_Int32: value=%" PRId32, test->args.value);
      if (test->args.min.set) {
         printf (" min=%" PRId32, test->args.min.value);
      }
      if (test->args.max.set) {
         printf (" max=%" PRId32, test->args.max.value);
      }
      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.
      printf ("_test_RangeTest_Encode_Int64: value=%" PRId64, test->args.value);
      if (test->args.min.set) {
         printf (" min=%" PRId64, test->args.min.value);
      }
      if (test->args.max.set) {
         printf (" max=%" PRId64, test->args.max.value);
      }
      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);
   }
}

typedef struct {
   double value;
   mc_optional_double_t min;
   mc_optional_double_t max;
   mc_optional_uint32_t precision;
   uint64_t expect;
   mc_optional_uint64_t expectMax;
   const char *expectError;
} DoubleTest;

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_U32_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_U32_C (2),
       .min = OPT_DOUBLE_C (-100000),
       .max = OPT_DOUBLE_C (100000),
       .expect = 10000314,
       .expectMax = OPT_U64_C (33554431)},
      {.value = 3.141592653589,
       .precision = OPT_U32_C (3),
       .min = OPT_DOUBLE_C (-100000),
       .max = OPT_DOUBLE_C (100000),
       .expect = 100003141,
       .expectMax = OPT_U64_C (268435455)},
      {.value = 3.141592653589,
       .precision = OPT_U32_C (4),
       .min = OPT_DOUBLE_C (-100000),
       .max = OPT_DOUBLE_C (100000),
       .expect = 1000031415,
       .expectMax = OPT_U64_C (2147483647)},
      {.value = 3.141592653589,
       .precision = OPT_U32_C (5),
       .min = OPT_DOUBLE_C (-100000),
       .max = OPT_DOUBLE_C (100000),
       .expect = 10000314159,
       .expectMax = OPT_U64_C (34359738367)},
      {.value = 3.141592653589,
       .precision = OPT_U32_C (6),
       .min = OPT_DOUBLE_C (-100000),
       .max = OPT_DOUBLE_C (100000),
       .expect = 100003141592,
       .expectMax = OPT_U64_C (274877906943)},
      {.value = 3.141592653589,
       .precision = OPT_U32_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_U32_C (3),
       .expect = 1000,
       .expectMax = OPT_U64_C (4095)},
      {.value = 0,
       .max = OPT_DOUBLE_C (1),
       .min = OPT_DOUBLE_C (-1E5),
       .precision = OPT_U32_C (3),
       .expect = 100000000,
       .expectMax = OPT_U64_C (134217727)},
      {.value = -1E-33,
       .max = OPT_DOUBLE_C (1),
       .min = OPT_DOUBLE_C (-1E5),
       .precision = OPT_U32_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_U32_C (3),
       .expect = UINT64_C (9223372036854775808),
       // Expect precision not to be used.
       .expectMax = OPT_U64_C (UINT64_MAX)},
      {.value = 3.141592653589,
       .max = OPT_DOUBLE_C (5),
       .min = OPT_DOUBLE_C (0),
       .precision = OPT_U32_C (0),
       .expect = 3,
       .expectMax = OPT_U64_C (7)},
      {.value = 3.141592653589,
       .max = OPT_DOUBLE_C (5),
       .min = OPT_DOUBLE_C (0),
       .precision = OPT_U32_C (1),
       .expect = 31,
       .expectMax = OPT_U64_C (63)},
      {.value = 3.141592653589,
       .max = OPT_DOUBLE_C (5),
       .min = OPT_DOUBLE_C (0),
       .precision = OPT_U32_C (2),
       .expect = 314,
       .expectMax = OPT_U64_C (1023)},
      {.value = 3.141592653589,
       .max = OPT_DOUBLE_C (5),
       .min = OPT_DOUBLE_C (0),
       .precision = OPT_U32_C (3),
       .expect = 3141,
       .expectMax = OPT_U64_C (8191)},
      {.value = 3.141592653589,
       .max = OPT_DOUBLE_C (5),
       .min = OPT_DOUBLE_C (0),
       .precision = OPT_U32_C (16),
       .expect = 31415926535890000,
       .expectMax = OPT_U64_C (72057594037927935)},
      {.value = -5,
       .max = OPT_DOUBLE_C (-1),
       .min = OPT_DOUBLE_C (-10),
       .precision = OPT_U32_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_U32_C (3),
       .expect = 15326393489903895421ULL,
       // Expect precision not to be used.
       .expectMax = OPT_U64_C (UINT64_MAX)},
      {.value = 1E9,
       .max = OPT_DOUBLE_C (1E10),
       .min = OPT_DOUBLE_C (0),
       .precision = OPT_U32_C (3),
       .expect = 1000000000000,
       .expectMax = OPT_U64_C (17592186044415)},
      {.value = 1E9,
       .max = OPT_DOUBLE_C (1E10),
       .min = OPT_DOUBLE_C (0),
       .precision = OPT_U32_C (0),
       .expect = 1000000000,
       .expectMax = OPT_U64_C (17179869183)},
      {.value = -5,
       .max = OPT_DOUBLE_C (10),
       .min = OPT_DOUBLE_C (-10),
       .precision = OPT_U32_C (0),
       .expect = 5,
       .expectMax = OPT_U64_C (31)},
      {.value = -5,
       .max = OPT_DOUBLE_C (10),
       .min = OPT_DOUBLE_C (-10),
       .precision = OPT_U32_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_U32_C (35),
       .expect = 13381399884061196960ULL,
       // Expect precision not to be used.
       .expectMax = OPT_U64_C (UINT64_MAX)},
      /* Test cases copied from Double_Bounds_Precision ... end */
      {.value = -1,
       .min = OPT_DOUBLE_C (0),
       .max = OPT_DOUBLE_C (200),
       .precision = OPT_U32_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_U32_C (1),
       .expectError = "less than or equal to the maximum value"}};

   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) {
         printf ("_test_RangeTest_Encode_Double: value=%f, min=%f, max=%f, "
                 "precision=%" PRIu32 "\n",
                 test->value,
                 test->min.value,
                 test->max.value,
                 test->precision.value);
      } else {
         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_uint32_t precision;
   mlib_int128 expect;
   const char *expectError;
} Decimal128Test;

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_U32 (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_U32 (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_U32 (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_U32 (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_U32 (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_U32 (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_U32 (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_U32 (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_U32 (Precision),                    \
      .expect = MLIB_INT128_CAST (Expect),                 \
   }

#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_U32 (Precision), .expect = Expect,      \
   }

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

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

      ASSERT_EIBB_OVERFLOW (
         0,
         MC_DEC128_LARGEST_POSITIVE,
         MC_DEC128_LARGEST_NEGATIVE,
         3,
         mlib_int128_from_string ("170141183460469231731687303715884105728",
                                  NULL)),
      ASSERT_EIBB_OVERFLOW (
         0,
         DBL_MAX,
         DBL_MIN,
         3,
         mlib_int128_from_string ("170141183460469231731687303715884105728",
                                  NULL)),

      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_OVERFLOW (
         1E100,
         DBL_MAX,
         DBL_MIN,
         3,
         mlib_int128_from_string ("232572183460469231731687303715884099485",
                                  NULL)),

      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),
      // Test a range that requires > 64 bits.
      // min has more places after the decimal than precision.
      ASSERT_EIBB (5, 18446744073709551616, .01, 1, 49),

#undef ASSERT_EIBB
#undef ASSERT_EIBB_OVERFLOW

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

   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) {
         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 {
         printf ("_test_RangeTest_Encode_Decimal128: value=%s\n",
                 mc_dec128_to_string (test->value).str);
      }
      fflush (stdout);
      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_RangeTest_Encode_Double);
#if MONGOCRYPT_HAVE_DECIMAL128_SUPPORT
   INSTALL_TEST (_test_RangeTest_Encode_Decimal128);
#endif
}
