/*
 * Copyright 2009-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 <bson/bson.h>

#include <TestSuite.h>


/*
 *----------------------------------------------------------------------------
 * decimal128_to_string tests
 *
 * The following tests confirm functionality of the decimal128_to_string
 *function.
 *
 * All decimal test data is generated using the Intel Decimal Floating-Point
 * Math Library's bid128_from_string routine.
 *
 *----------------------------------------------------------------------------
 */


#define DECIMAL128_FROM_ULLS(dec, h, l) \
   do {                                 \
      (dec).high = (h);                 \
      (dec).low = (l);                  \
   } while (0)


static void
test_decimal128_to_string__infinity(void)
{
   bson_decimal128_t positive_infinity;
   bson_decimal128_t negative_infinity;
   char bid_string[BSON_DECIMAL128_STRING];

   DECIMAL128_FROM_ULLS(positive_infinity, 0x7800000000000000, 0);
   DECIMAL128_FROM_ULLS(negative_infinity, 0xf800000000000000, 0);

   bson_decimal128_to_string(&positive_infinity, bid_string);
   ASSERT_CMPSTR(bid_string, "Infinity");

   bson_decimal128_to_string(&negative_infinity, bid_string);
   ASSERT_CMPSTR(bid_string, "-Infinity");
}


static void
test_decimal128_to_string__nan(void)
{
   bson_decimal128_t dec_pnan;
   bson_decimal128_t dec_nnan;
   bson_decimal128_t dec_psnan;
   bson_decimal128_t dec_nsnan;
   bson_decimal128_t dec_payload_nan;

   /* All the above should just be NaN. */
   char bid_string[BSON_DECIMAL128_STRING];

   DECIMAL128_FROM_ULLS(dec_pnan, 0x7c00000000000000, 0);
   DECIMAL128_FROM_ULLS(dec_nnan, 0xfc00000000000000, 0);
   DECIMAL128_FROM_ULLS(dec_psnan, 0x7e00000000000000, 0);
   DECIMAL128_FROM_ULLS(dec_nsnan, 0xfe00000000000000, 0);
   DECIMAL128_FROM_ULLS(dec_payload_nan, 0x7e00000000000000, 12);

   bson_decimal128_to_string(&dec_pnan, bid_string);
   ASSERT_CMPSTR(bid_string, "NaN");

   bson_decimal128_to_string(&dec_nnan, bid_string);
   ASSERT_CMPSTR(bid_string, "NaN");

   bson_decimal128_to_string(&dec_psnan, bid_string);
   ASSERT_CMPSTR(bid_string, "NaN");

   bson_decimal128_to_string(&dec_nsnan, bid_string);
   ASSERT_CMPSTR(bid_string, "NaN");

   bson_decimal128_to_string(&dec_payload_nan, bid_string);
   ASSERT_CMPSTR(bid_string, "NaN");
}


static void
test_decimal128_to_string__regular(void)
{
   char bid_string[BSON_DECIMAL128_STRING];
   bson_decimal128_t one;
   bson_decimal128_t zero;
   bson_decimal128_t two;
   bson_decimal128_t negative_one;
   bson_decimal128_t negative_zero;
   bson_decimal128_t tenth;
   bson_decimal128_t smallest_regular;
   bson_decimal128_t largest_regular;
   bson_decimal128_t trailing_zeros;
   bson_decimal128_t all_digits;
   bson_decimal128_t full_house;
   bson_decimal128_t untruncated_significand;
   bson_decimal128_t truncated_significand;

   DECIMAL128_FROM_ULLS(one, 0x3040000000000000, 0x0000000000000001);
   DECIMAL128_FROM_ULLS(zero, 0x3040000000000000, 0x0000000000000000);
   DECIMAL128_FROM_ULLS(two, 0x3040000000000000, 0x0000000000000002);
   DECIMAL128_FROM_ULLS(negative_one, 0xb040000000000000, 0x0000000000000001);
   DECIMAL128_FROM_ULLS(negative_zero, 0xb040000000000000, 0x0000000000000000);

   // 0.1
   DECIMAL128_FROM_ULLS(tenth, 0x303e000000000000, 0x0000000000000001);

   // 0.001234
   DECIMAL128_FROM_ULLS(smallest_regular, 0x3034000000000000, 0x00000000000004d2);

   // 12345789012
   DECIMAL128_FROM_ULLS(largest_regular, 0x3040000000000000, 0x0000001cbe991a14);

   // 0.00123400000
   DECIMAL128_FROM_ULLS(trailing_zeros, 0x302a000000000000, 0x00000000075aef40);

   // 0.1234567890123456789012345678901234
   DECIMAL128_FROM_ULLS(all_digits, 0x2ffc3cde6fff9732, 0xde825cd07e96aff2);

   // 5192296858534827628530496329220095
   DECIMAL128_FROM_ULLS(full_house, 0x3040ffffffffffff, 0xffffffffffffffff);

   // 0.0000010384593717069655257060992658440191 (inexact rounding)
   DECIMAL128_FROM_ULLS(untruncated_significand, 0x2ff1ffffffffffff, 0xffffffffffffffff);

   // -0.000001038459371706965525706099265844019 (inexact rounding)
   DECIMAL128_FROM_ULLS(truncated_significand, 0xaff1ffffffffffff, 0xffffffffffffffff);

   bson_decimal128_to_string(&one, bid_string);
   ASSERT_CMPSTR("1", bid_string);

   bson_decimal128_to_string(&zero, bid_string);
   ASSERT_CMPSTR("0", bid_string);

   bson_decimal128_to_string(&two, bid_string);
   ASSERT_CMPSTR("2", bid_string);

   bson_decimal128_to_string(&negative_one, bid_string);
   ASSERT_CMPSTR("-1", bid_string);

   bson_decimal128_to_string(&negative_zero, bid_string);
   ASSERT_CMPSTR("-0", bid_string);

   bson_decimal128_to_string(&tenth, bid_string);
   ASSERT_CMPSTR("0.1", bid_string);

   bson_decimal128_to_string(&smallest_regular, bid_string);
   ASSERT_CMPSTR("0.001234", bid_string);

   bson_decimal128_to_string(&largest_regular, bid_string);
   ASSERT_CMPSTR("123456789012", bid_string);

   bson_decimal128_to_string(&trailing_zeros, bid_string);
   ASSERT_CMPSTR("0.00123400000", bid_string);

   bson_decimal128_to_string(&all_digits, bid_string);
   ASSERT_CMPSTR("0.1234567890123456789012345678901234", bid_string);

   bson_decimal128_to_string(&full_house, bid_string);
   ASSERT_CMPSTR("5192296858534827628530496329220095", bid_string);

   bson_decimal128_to_string(&untruncated_significand, bid_string);
   ASSERT_CMPSTR("0.0000010384593717069655257060992658440191", bid_string);

   bson_decimal128_to_string(&truncated_significand, bid_string);
   ASSERT_CMPSTR("-0.000001038459371706965525706099265844019", bid_string);
}


static void
test_decimal128_to_string__scientific(void)
{
   char bid_string[BSON_DECIMAL128_STRING];

   bson_decimal128_t huge;                     /* 1.000000000000000000000000000000000E+6144 */
   bson_decimal128_t tiny;                     /* 1E-6176 */
   bson_decimal128_t neg_tiny;                 /* -1E-6176 */
   bson_decimal128_t large;                    /* 9.999987654321E+112 */
   bson_decimal128_t largest;                  /* 9.999999999999999999999999999999999E+6144 */
   bson_decimal128_t tiniest;                  /* 9.999999999999999999999999999999999E-6143 */
   bson_decimal128_t trailing_zero;            /* 1.050E9 */
   bson_decimal128_t one_trailing_zero;        /* 1.050E4 */
   bson_decimal128_t move_decimal;             /* 105 */
   bson_decimal128_t move_decimal_after;       /* 1.05E3 */
   bson_decimal128_t trailing_zero_no_decimal; /* 1E3 */

   DECIMAL128_FROM_ULLS(huge, 0x5ffe314dc6448d93, 0x38c15b0a00000000);
   DECIMAL128_FROM_ULLS(tiny, 0x0000000000000000, 0x0000000000000001);
   DECIMAL128_FROM_ULLS(neg_tiny, 0x8000000000000000, 0x0000000000000001);
   DECIMAL128_FROM_ULLS(large, 0x3108000000000000, 0x000009184db63eb1);
   DECIMAL128_FROM_ULLS(largest, 0x5fffed09bead87c0, 0x378d8e63ffffffff);
   DECIMAL128_FROM_ULLS(tiniest, 0x0001ed09bead87c0, 0x378d8e63ffffffff);
   DECIMAL128_FROM_ULLS(trailing_zero, 0x304c000000000000, 0x000000000000041a);
   DECIMAL128_FROM_ULLS(one_trailing_zero, 0x3042000000000000, 0x000000000000041a);
   DECIMAL128_FROM_ULLS(move_decimal, 0x3040000000000000, 0x0000000000000069);
   DECIMAL128_FROM_ULLS(move_decimal_after, 0x3042000000000000, 0x0000000000000069);
   DECIMAL128_FROM_ULLS(trailing_zero_no_decimal, 0x3046000000000000, 0x0000000000000001);

   bson_decimal128_to_string(&huge, bid_string);
   ASSERT_CMPSTR("1.000000000000000000000000000000000E+6144", bid_string);

   bson_decimal128_to_string(&tiny, bid_string);
   ASSERT_CMPSTR("1E-6176", bid_string);

   bson_decimal128_to_string(&neg_tiny, bid_string);
   ASSERT_CMPSTR("-1E-6176", bid_string);

   bson_decimal128_to_string(&neg_tiny, bid_string);
   ASSERT_CMPSTR("-1E-6176", bid_string);

   bson_decimal128_to_string(&large, bid_string);
   ASSERT_CMPSTR("9.999987654321E+112", bid_string);

   bson_decimal128_to_string(&largest, bid_string);
   ASSERT_CMPSTR("9.999999999999999999999999999999999E+6144", bid_string);

   bson_decimal128_to_string(&tiniest, bid_string);
   ASSERT_CMPSTR("9.999999999999999999999999999999999E-6143", bid_string);

   bson_decimal128_to_string(&trailing_zero, bid_string);
   ASSERT_CMPSTR("1.050E+9", bid_string);

   bson_decimal128_to_string(&one_trailing_zero, bid_string);
   ASSERT_CMPSTR("1.050E+4", bid_string);

   bson_decimal128_to_string(&move_decimal, bid_string);
   ASSERT_CMPSTR("105", bid_string);

   bson_decimal128_to_string(&move_decimal_after, bid_string);
   ASSERT_CMPSTR("1.05E+3", bid_string);

   bson_decimal128_to_string(&trailing_zero_no_decimal, bid_string);
   ASSERT_CMPSTR("1E+3", bid_string);
}


static void
test_decimal128_to_string__zeros(void)
{
   char bid_string[BSON_DECIMAL128_STRING];

   bson_decimal128_t zero;         /* 0 */
   bson_decimal128_t pos_exp_zero; /* 0E+300 */
   bson_decimal128_t neg_exp_zero; /* 0E-600 */

   DECIMAL128_FROM_ULLS(zero, 0x3040000000000000, 0x0000000000000000);
   DECIMAL128_FROM_ULLS(pos_exp_zero, 0x3298000000000000, 0x0000000000000000);
   DECIMAL128_FROM_ULLS(neg_exp_zero, 0x2b90000000000000, 0x0000000000000000);

   bson_decimal128_to_string(&zero, bid_string);
   ASSERT_CMPSTR("0", bid_string);

   bson_decimal128_to_string(&pos_exp_zero, bid_string);
   ASSERT_CMPSTR("0E+300", bid_string);

   bson_decimal128_to_string(&neg_exp_zero, bid_string);
   ASSERT_CMPSTR("0E-600", bid_string);
}


#define IS_NAN(dec) (dec).high == 0x7c00000000000000ull


static void
test_decimal128_from_string__invalid_inputs(void)
{
   bson_decimal128_t dec;

   ASSERT(!bson_decimal128_from_string(".", &dec));
   ASSERT(IS_NAN(dec));
   ASSERT(!bson_decimal128_from_string("1e", &dec));
   ASSERT(IS_NAN(dec));
   ASSERT(!bson_decimal128_from_string(".e", &dec));
   ASSERT(IS_NAN(dec));
   ASSERT(!bson_decimal128_from_string("", &dec));
   ASSERT(IS_NAN(dec));
   ASSERT(!bson_decimal128_from_string("invalid", &dec));
   ASSERT(IS_NAN(dec));
   ASSERT(!bson_decimal128_from_string("in", &dec));
   ASSERT(IS_NAN(dec));
   ASSERT(!bson_decimal128_from_string("i", &dec));
   ASSERT(IS_NAN(dec));
   ASSERT(!bson_decimal128_from_string("E02", &dec));
   ASSERT(IS_NAN(dec));
   ASSERT(!bson_decimal128_from_string("..1", &dec));
   ASSERT(IS_NAN(dec));
   ASSERT(!bson_decimal128_from_string("1abcede", &dec));
   ASSERT(IS_NAN(dec));
   ASSERT(!bson_decimal128_from_string("1.24abc", &dec));
   ASSERT(IS_NAN(dec));
   ASSERT(!bson_decimal128_from_string("1.24abcE+02", &dec));
   ASSERT(IS_NAN(dec));
   ASSERT(!bson_decimal128_from_string("1.24E+02abc2d", &dec));
   ASSERT(IS_NAN(dec));
   ASSERT(!bson_decimal128_from_string("E+02", &dec));
   ASSERT(IS_NAN(dec));
   ASSERT(!bson_decimal128_from_string("e+02", &dec));
   ASSERT(IS_NAN(dec));

   ASSERT(!bson_decimal128_from_string_w_len(".", 1, &dec));
   ASSERT(IS_NAN(dec));
   ASSERT(!bson_decimal128_from_string_w_len("1e", 2, &dec));
   ASSERT(IS_NAN(dec));
   ASSERT(!bson_decimal128_from_string_w_len(".e", 2, &dec));
   ASSERT(IS_NAN(dec));
   ASSERT(!bson_decimal128_from_string_w_len("", 0, &dec));
   ASSERT(IS_NAN(dec));
   ASSERT(!bson_decimal128_from_string_w_len("invalid", 7, &dec));
   ASSERT(IS_NAN(dec));
   ASSERT(!bson_decimal128_from_string_w_len("in", 2, &dec));
   ASSERT(IS_NAN(dec));
   ASSERT(!bson_decimal128_from_string_w_len("i", 1, &dec));
   ASSERT(IS_NAN(dec));
   ASSERT(!bson_decimal128_from_string_w_len("E02", 3, &dec));
   ASSERT(IS_NAN(dec));
   ASSERT(!bson_decimal128_from_string_w_len("..1", 3, &dec));
   ASSERT(IS_NAN(dec));
   ASSERT(!bson_decimal128_from_string_w_len("1abcede", 7, &dec));
   ASSERT(IS_NAN(dec));
   ASSERT(!bson_decimal128_from_string_w_len("1.24abc", 7, &dec));
   ASSERT(IS_NAN(dec));
   ASSERT(!bson_decimal128_from_string_w_len("1.24abcE+02", 11, &dec));
   ASSERT(IS_NAN(dec));
   ASSERT(!bson_decimal128_from_string_w_len("1.24E+02abc2d", 13, &dec));
   ASSERT(IS_NAN(dec));
   ASSERT(!bson_decimal128_from_string_w_len("E+02", 4, &dec));
   ASSERT(IS_NAN(dec));
   ASSERT(!bson_decimal128_from_string_w_len("e+02", 4, &dec));
   ASSERT(IS_NAN(dec));
}


static void
test_decimal128_from_string__nan(void)
{
   bson_decimal128_t dec;

   ASSERT(bson_decimal128_from_string("NaN", &dec));
   ASSERT(IS_NAN(dec));
   ASSERT(bson_decimal128_from_string("+NaN", &dec));
   ASSERT(IS_NAN(dec));
   ASSERT(bson_decimal128_from_string("-NaN", &dec));
   ASSERT(IS_NAN(dec));
   ASSERT(bson_decimal128_from_string("-nan", &dec));
   ASSERT(IS_NAN(dec));
   ASSERT(bson_decimal128_from_string("+nan", &dec));
   ASSERT(IS_NAN(dec));
   ASSERT(bson_decimal128_from_string("nan", &dec));
   ASSERT(IS_NAN(dec));
   ASSERT(bson_decimal128_from_string("Nan", &dec));
   ASSERT(IS_NAN(dec));
   ASSERT(bson_decimal128_from_string("+Nan", &dec));
   ASSERT(IS_NAN(dec));
   ASSERT(bson_decimal128_from_string("-Nan", &dec));
   ASSERT(IS_NAN(dec));

   ASSERT(bson_decimal128_from_string_w_len("NaN", 3, &dec));
   ASSERT(IS_NAN(dec));
   ASSERT(bson_decimal128_from_string_w_len("+NaN", 4, &dec));
   ASSERT(IS_NAN(dec));
   ASSERT(bson_decimal128_from_string_w_len("-NaN", 4, &dec));
   ASSERT(IS_NAN(dec));
   ASSERT(bson_decimal128_from_string_w_len("-nan", 4, &dec));
   ASSERT(IS_NAN(dec));
   ASSERT(bson_decimal128_from_string_w_len("+nan", 4, &dec));
   ASSERT(IS_NAN(dec));
   ASSERT(bson_decimal128_from_string_w_len("nan", 3, &dec));
   ASSERT(IS_NAN(dec));
   ASSERT(bson_decimal128_from_string_w_len("Nan", 3, &dec));
   ASSERT(IS_NAN(dec));
   ASSERT(bson_decimal128_from_string_w_len("+Nan", 4, &dec));
   ASSERT(IS_NAN(dec));
   ASSERT(bson_decimal128_from_string_w_len("-Nan", 4, &dec));
   ASSERT(IS_NAN(dec));
}


#define IS_PINFINITY(dec) (dec).high == 0x7800000000000000
#define IS_NINFINITY(dec) (dec).high == 0xf800000000000000


static void
test_decimal128_from_string__infinity(void)
{
   bson_decimal128_t dec;

   ASSERT(bson_decimal128_from_string("Infinity", &dec));
   ASSERT(IS_PINFINITY(dec));
   ASSERT(bson_decimal128_from_string("+Infinity", &dec));
   ASSERT(IS_PINFINITY(dec));
   ASSERT(bson_decimal128_from_string("+Inf", &dec));
   ASSERT(IS_PINFINITY(dec));
   ASSERT(bson_decimal128_from_string("-Inf", &dec));
   ASSERT(IS_NINFINITY(dec));
   ASSERT(bson_decimal128_from_string("-Infinity", &dec));
   ASSERT(IS_NINFINITY(dec));

   ASSERT(bson_decimal128_from_string_w_len("Infinity", 8, &dec));
   ASSERT(IS_PINFINITY(dec));
   ASSERT(bson_decimal128_from_string_w_len("+Infinity", 9, &dec));
   ASSERT(IS_PINFINITY(dec));
   ASSERT(bson_decimal128_from_string_w_len("+Inf", 4, &dec));
   ASSERT(IS_PINFINITY(dec));
   ASSERT(bson_decimal128_from_string_w_len("-Inf", 4, &dec));
   ASSERT(IS_NINFINITY(dec));
   ASSERT(bson_decimal128_from_string_w_len("-Infinity", 9, &dec));
   ASSERT(IS_NINFINITY(dec));
}


static bool
decimal128_equal(bson_decimal128_t *dec, uint64_t high, uint64_t low)
{
   bson_decimal128_t test;
   DECIMAL128_FROM_ULLS(test, high, low);
   return memcmp(dec, &test, sizeof(*dec)) == 0;
}

static void
test_decimal128_from_string__simple(void)
{
   bson_decimal128_t one;
   bson_decimal128_t negative_one;
   bson_decimal128_t zero;
   bson_decimal128_t negative_zero;
   bson_decimal128_t number;
   bson_decimal128_t number_two;
   bson_decimal128_t negative_number;
   bson_decimal128_t fractional_number;
   bson_decimal128_t leading_zeros;
   bson_decimal128_t leading_insignificant_zeros;
   bson_decimal128_t untruncated_significand;
   bson_decimal128_t truncated_significand;

   ASSERT(bson_decimal128_from_string("1", &one));
   ASSERT(bson_decimal128_from_string("-1", &negative_one));
   ASSERT(bson_decimal128_from_string("0", &zero));
   ASSERT(bson_decimal128_from_string("-0", &negative_zero));
   ASSERT(bson_decimal128_from_string("12345678901234567", &number));
   ASSERT(bson_decimal128_from_string("989898983458", &number_two));
   ASSERT(bson_decimal128_from_string("-12345678901234567", &negative_number));

   ASSERT(bson_decimal128_from_string("0.12345", &fractional_number));
   ASSERT(bson_decimal128_from_string("0.0012345", &leading_zeros));

   ASSERT(bson_decimal128_from_string("00012345678901234567", &leading_insignificant_zeros));


   // Inexact Rounding Error
   ASSERT(!bson_decimal128_from_string("0.0000010384593717069655257060992658440191", &untruncated_significand));
   ASSERT(!bson_decimal128_from_string("-0.0000010384593717069655257060992658440191", &truncated_significand));

   // OK
   ASSERT(bson_decimal128_from_string("0.000001038459371706965525706099265844019", &untruncated_significand));
   ASSERT(bson_decimal128_from_string("-0.000001038459371706965525706099265844019", &truncated_significand));

   ASSERT(decimal128_equal(&one, 0x3040000000000000, 0x0000000000000001));
   ASSERT(decimal128_equal(&negative_one, 0xb040000000000000, 0x0000000000000001));
   ASSERT(decimal128_equal(&zero, 0x3040000000000000, 0x0000000000000000));
   ASSERT(decimal128_equal(&negative_zero, 0xb040000000000000, 0x0000000000000000));
   ASSERT(decimal128_equal(&number, 0x3040000000000000, 0x002bdc545d6b4b87));
   ASSERT(decimal128_equal(&number_two, 0x3040000000000000, 0x000000e67a93c822));
   ASSERT(decimal128_equal(&negative_number, 0xb040000000000000, 0x002bdc545d6b4b87));
   ASSERT(decimal128_equal(&fractional_number, 0x3036000000000000, 0x0000000000003039));
   ASSERT(decimal128_equal(&leading_zeros, 0x3032000000000000, 0x0000000000003039));
   ASSERT(decimal128_equal(&leading_insignificant_zeros, 0x3040000000000000, 0x002bdc545d6b4b87));

   ASSERT(decimal128_equal(&untruncated_significand, 0x2ff2333333333333, 0x3333333333333333));
   ASSERT(decimal128_equal(&truncated_significand, 0xaff2333333333333, 0x3333333333333333));

   ASSERT(bson_decimal128_from_string_w_len("1", 1, &one));
   ASSERT(bson_decimal128_from_string_w_len("-1", 2, &negative_one));
   ASSERT(bson_decimal128_from_string_w_len("0", 1, &zero));
   ASSERT(bson_decimal128_from_string_w_len("-0", 2, &negative_zero));
   ASSERT(bson_decimal128_from_string_w_len("12345678901234567", 17, &number));
   ASSERT(bson_decimal128_from_string_w_len("989898983458", 12, &number_two));
   ASSERT(bson_decimal128_from_string_w_len("-12345678901234567", 18, &negative_number));

   ASSERT(bson_decimal128_from_string_w_len("0.12345", 7, &fractional_number));
   ASSERT(bson_decimal128_from_string_w_len("0.0012345", 9, &leading_zeros));

   ASSERT(bson_decimal128_from_string_w_len("00012345678901234567", 20, &leading_insignificant_zeros));

   // Inexact Rounding Error
   ASSERT(
      !bson_decimal128_from_string_w_len("0.0000010384593717069655257060992658440191", 42, &untruncated_significand));
   ASSERT(
      !bson_decimal128_from_string_w_len("-0.0000010384593717069655257060992658440191", 43, &truncated_significand));

   // OK
   ASSERT(bson_decimal128_from_string_w_len("0.000001038459371706965525706099265844019", 41, &untruncated_significand));
   ASSERT(bson_decimal128_from_string_w_len("-0.000001038459371706965525706099265844019", 42, &truncated_significand));

   ASSERT(decimal128_equal(&one, 0x3040000000000000, 0x0000000000000001));
   ASSERT(decimal128_equal(&negative_one, 0xb040000000000000, 0x0000000000000001));
   ASSERT(decimal128_equal(&zero, 0x3040000000000000, 0x0000000000000000));
   ASSERT(decimal128_equal(&negative_zero, 0xb040000000000000, 0x0000000000000000));
   ASSERT(decimal128_equal(&number, 0x3040000000000000, 0x002bdc545d6b4b87));
   ASSERT(decimal128_equal(&number_two, 0x3040000000000000, 0x000000e67a93c822));
   ASSERT(decimal128_equal(&negative_number, 0xb040000000000000, 0x002bdc545d6b4b87));
   ASSERT(decimal128_equal(&fractional_number, 0x3036000000000000, 0x0000000000003039));
   ASSERT(decimal128_equal(&leading_zeros, 0x3032000000000000, 0x0000000000003039));
   ASSERT(decimal128_equal(&leading_insignificant_zeros, 0x3040000000000000, 0x002bdc545d6b4b87));

   ASSERT(decimal128_equal(&untruncated_significand, 0x2ff2333333333333, 0x3333333333333333));
   ASSERT(decimal128_equal(&truncated_significand, 0xaff2333333333333, 0x3333333333333333));
}


static void
test_decimal128_from_string__scientific(void)
{
   bson_decimal128_t ten;
   bson_decimal128_t ten_again;
   bson_decimal128_t one;
   bson_decimal128_t huge_exp;
   bson_decimal128_t tiny_exp;
   bson_decimal128_t fractional;
   bson_decimal128_t trailing_zeros;

   ASSERT(bson_decimal128_from_string("10e0", &ten));
   ASSERT(bson_decimal128_from_string("1e1", &ten_again));
   ASSERT(bson_decimal128_from_string("10e-1", &one));

   ASSERT(decimal128_equal(&ten, 0x3040000000000000, 0x000000000000000a));
   ASSERT(decimal128_equal(&ten_again, 0x3042000000000000, 0x0000000000000001));
   ASSERT(decimal128_equal(&one, 0x303e000000000000, 0x000000000000000a));

   ASSERT(bson_decimal128_from_string("12345678901234567e6111", &huge_exp));
   ASSERT(bson_decimal128_from_string("1e-6176", &tiny_exp));

   ASSERT(decimal128_equal(&huge_exp, 0x5ffe000000000000, 0x002bdc545d6b4b87));
   ASSERT(decimal128_equal(&tiny_exp, 0x0000000000000000, 0x0000000000000001));

   ASSERT(bson_decimal128_from_string("-100E-10", &fractional));
   ASSERT(bson_decimal128_from_string("10.50E8", &trailing_zeros));

   ASSERT(decimal128_equal(&fractional, 0xb02c000000000000, 0x0000000000000064));
   ASSERT(decimal128_equal(&trailing_zeros, 0x304c000000000000, 0x000000000000041a));


   ASSERT(bson_decimal128_from_string_w_len("10e0", 4, &ten));
   ASSERT(bson_decimal128_from_string_w_len("1e1", 3, &ten_again));
   ASSERT(bson_decimal128_from_string_w_len("10e-1", 5, &one));

   ASSERT(decimal128_equal(&ten, 0x3040000000000000, 0x000000000000000a));
   ASSERT(decimal128_equal(&ten_again, 0x3042000000000000, 0x0000000000000001));
   ASSERT(decimal128_equal(&one, 0x303e000000000000, 0x000000000000000a));

   ASSERT(bson_decimal128_from_string_w_len("12345678901234567e6111", 22, &huge_exp));
   ASSERT(bson_decimal128_from_string_w_len("1e-6176", 7, &tiny_exp));

   ASSERT(decimal128_equal(&huge_exp, 0x5ffe000000000000, 0x002bdc545d6b4b87));
   ASSERT(decimal128_equal(&tiny_exp, 0x0000000000000000, 0x0000000000000001));

   ASSERT(bson_decimal128_from_string_w_len("-100E-10", 8, &fractional));
   ASSERT(bson_decimal128_from_string_w_len("10.50E8", 7, &trailing_zeros));

   ASSERT(decimal128_equal(&fractional, 0xb02c000000000000, 0x0000000000000064));
   ASSERT(decimal128_equal(&trailing_zeros, 0x304c000000000000, 0x000000000000041a));
}


static void
test_decimal128_from_string__large(void)
{
   bson_decimal128_t large;
   bson_decimal128_t all_digits;
   bson_decimal128_t largest;
   bson_decimal128_t tiniest;
   bson_decimal128_t full_house;

   ASSERT(bson_decimal128_from_string("12345689012345789012345", &large));
   ASSERT(bson_decimal128_from_string("1234567890123456789012345678901234", &all_digits));
   ASSERT(bson_decimal128_from_string("9.999999999999999999999999999999999E+6144", &largest));
   ASSERT(bson_decimal128_from_string("9.999999999999999999999999999999999E-6143", &tiniest));
   ASSERT(bson_decimal128_from_string("5.192296858534827628530496329220095E+33", &full_house));

   ASSERT(decimal128_equal(&large, 0x304000000000029d, 0x42da3a76f9e0d979));
   ASSERT(decimal128_equal(&all_digits, 0x30403cde6fff9732, 0xde825cd07e96aff2));
   ASSERT(decimal128_equal(&largest, 0x5fffed09bead87c0, 0x378d8e63ffffffff));
   ASSERT(decimal128_equal(&tiniest, 0x0001ed09bead87c0, 0x378d8e63ffffffff));
   ASSERT(decimal128_equal(&full_house, 0x3040ffffffffffff, 0xffffffffffffffff));


   ASSERT(bson_decimal128_from_string_w_len("12345689012345789012345", -1, &large));
   ASSERT(bson_decimal128_from_string_w_len("1234567890123456789012345678901234", -1, &all_digits));
   ASSERT(bson_decimal128_from_string_w_len("9.999999999999999999999999999999999E+6144", -1, &largest));
   ASSERT(bson_decimal128_from_string_w_len("9.999999999999999999999999999999999E-6143", -1, &tiniest));
   ASSERT(bson_decimal128_from_string_w_len("5.192296858534827628530496329220095E+33", -1, &full_house));

   ASSERT(decimal128_equal(&large, 0x304000000000029d, 0x42da3a76f9e0d979));
   ASSERT(decimal128_equal(&all_digits, 0x30403cde6fff9732, 0xde825cd07e96aff2));
   ASSERT(decimal128_equal(&largest, 0x5fffed09bead87c0, 0x378d8e63ffffffff));
   ASSERT(decimal128_equal(&tiniest, 0x0001ed09bead87c0, 0x378d8e63ffffffff));
   ASSERT(decimal128_equal(&full_house, 0x3040ffffffffffff, 0xffffffffffffffff));
}


static void
test_decimal128_from_string__exponent_normalization(void)
{
   bson_decimal128_t trailing_zeros;
   bson_decimal128_t one_normalize;
   bson_decimal128_t no_normalize;
   bson_decimal128_t a_disaster;

   ASSERT(bson_decimal128_from_string("1000000000000000000000000000000000000000", &trailing_zeros));
   ASSERT(bson_decimal128_from_string("10000000000000000000000000000000000", &one_normalize));
   ASSERT(bson_decimal128_from_string("1000000000000000000000000000000000", &no_normalize));
   bson_decimal128_from_string("100000000000000000000000000000000000000000000000000000000000000000000"
                               "000000000000000000000000000000000000000000000000000000000000000000000"
                               "000000000000000000000000000000000000000000000000000000000000000000000"
                               "000000000000000000000000000000000000000000000000000000000000000000000"
                               "000000000000000000000000000000000000000000000000000000000000000000000"
                               "000000000000000000000000000000000000000000000000000000000000000000000"
                               "000000000000000000000000000000000000000000000000000000000000000000000"
                               "000000000000000000000000000000000000000000000000000000000000000000000"
                               "000000000000000000000000000000000000000000000000000000000000000000000"
                               "000000000000000000000000000000000000000000000000000000000000000000000"
                               "000000000000000000000000000000000000000000000000000000000000000000000"
                               "000000000000000000000000000000000000000000000000000000000000000000000"
                               "000000000000000000000000000000000000000000000000000000000000000000000"
                               "000000000000000000000000000000000000000000000000000000000000000000000"
                               "0000000000000000000000000000000000",
                               &a_disaster);

   ASSERT(decimal128_equal(&trailing_zeros, 0x304c314dc6448d93, 0x38c15b0a00000000));
   ASSERT(decimal128_equal(&one_normalize, 0x3042314dc6448d93, 0x38c15b0a00000000));
   ASSERT(decimal128_equal(&no_normalize, 0x3040314dc6448d93, 0x38c15b0a00000000));
   ASSERT(decimal128_equal(&a_disaster, 0x37cc314dc6448d93, 0x38c15b0a00000000));


   ASSERT(bson_decimal128_from_string_w_len("1000000000000000000000000000000000000000", -1, &trailing_zeros));
   ASSERT(bson_decimal128_from_string_w_len("10000000000000000000000000000000000", -1, &one_normalize));
   ASSERT(bson_decimal128_from_string_w_len("1000000000000000000000000000000000", -1, &no_normalize));
   bson_decimal128_from_string_w_len("100000000000000000000000000000000000000000000000000000000000000000000"
                                     "000000000000000000000000000000000000000000000000000000000000000000000"
                                     "000000000000000000000000000000000000000000000000000000000000000000000"
                                     "000000000000000000000000000000000000000000000000000000000000000000000"
                                     "000000000000000000000000000000000000000000000000000000000000000000000"
                                     "000000000000000000000000000000000000000000000000000000000000000000000"
                                     "000000000000000000000000000000000000000000000000000000000000000000000"
                                     "000000000000000000000000000000000000000000000000000000000000000000000"
                                     "000000000000000000000000000000000000000000000000000000000000000000000"
                                     "000000000000000000000000000000000000000000000000000000000000000000000"
                                     "000000000000000000000000000000000000000000000000000000000000000000000"
                                     "000000000000000000000000000000000000000000000000000000000000000000000"
                                     "000000000000000000000000000000000000000000000000000000000000000000000"
                                     "000000000000000000000000000000000000000000000000000000000000000000000"
                                     "0000000000000000000000000000000000",
                                     -1,
                                     &a_disaster);

   ASSERT(decimal128_equal(&trailing_zeros, 0x304c314dc6448d93, 0x38c15b0a00000000));
   ASSERT(decimal128_equal(&one_normalize, 0x3042314dc6448d93, 0x38c15b0a00000000));
   ASSERT(decimal128_equal(&no_normalize, 0x3040314dc6448d93, 0x38c15b0a00000000));
   ASSERT(decimal128_equal(&a_disaster, 0x37cc314dc6448d93, 0x38c15b0a00000000));
}


static void
test_decimal128_from_string__zeros(void)
{
   bson_decimal128_t zero;
   bson_decimal128_t exponent_zero;
   bson_decimal128_t large_exponent;
   bson_decimal128_t negative_zero;

   ASSERT(bson_decimal128_from_string("0", &zero));
   ASSERT(bson_decimal128_from_string("0e-611", &exponent_zero));
   ASSERT(bson_decimal128_from_string("0e+6000", &large_exponent));
   ASSERT(bson_decimal128_from_string("-0e-1", &negative_zero));

   ASSERT(decimal128_equal(&zero, 0x3040000000000000, 0x0000000000000000));
   ASSERT(decimal128_equal(&exponent_zero, 0x2b7a000000000000, 0x0000000000000000));
   ASSERT(decimal128_equal(&large_exponent, 0x5f20000000000000, 0x0000000000000000));
   ASSERT(decimal128_equal(&negative_zero, 0xb03e000000000000, 0x0000000000000000));


   ASSERT(bson_decimal128_from_string_w_len("0", 1, &zero));
   ASSERT(bson_decimal128_from_string_w_len("0e-611", -1, &exponent_zero));
   ASSERT(bson_decimal128_from_string_w_len("0e+6000", 7, &large_exponent));
   ASSERT(bson_decimal128_from_string_w_len("-0e-1", 5, &negative_zero));

   ASSERT(decimal128_equal(&zero, 0x3040000000000000, 0x0000000000000000));
   ASSERT(decimal128_equal(&exponent_zero, 0x2b7a000000000000, 0x0000000000000000));
   ASSERT(decimal128_equal(&large_exponent, 0x5f20000000000000, 0x0000000000000000));
   ASSERT(decimal128_equal(&negative_zero, 0xb03e000000000000, 0x0000000000000000));
}

static void
test_decimal128_from_string_w_len__special(void)
{
   bson_decimal128_t number;
   bson_decimal128_t number_two;
   bson_decimal128_t negative_number;

   /* These strings have more bytes than the length indicates. */
   ASSERT(bson_decimal128_from_string_w_len("12345678901234567abcd", 17, &number));
   ASSERT(bson_decimal128_from_string_w_len("989898983458abcd", 12, &number_two));
   ASSERT(bson_decimal128_from_string_w_len("-12345678901234567abcd", 18, &negative_number));

   ASSERT(decimal128_equal(&number, 0x3040000000000000, 0x002bdc545d6b4b87));
   ASSERT(decimal128_equal(&number_two, 0x3040000000000000, 0x000000e67a93c822));
   ASSERT(decimal128_equal(&negative_number, 0xb040000000000000, 0x002bdc545d6b4b87));
}

void
test_decimal128_install(TestSuite *suite)
{
   TestSuite_Add(suite, "/bson/decimal128/to_string/infinity", test_decimal128_to_string__infinity);
   TestSuite_Add(suite, "/bson/decimal128/to_string/nan", test_decimal128_to_string__nan);
   TestSuite_Add(suite, "/bson/decimal128/to_string/regular", test_decimal128_to_string__regular);
   TestSuite_Add(suite, "/bson/decimal128/to_string/scientific", test_decimal128_to_string__scientific);
   TestSuite_Add(suite, "/bson/decimal128/to_string/zero", test_decimal128_to_string__zeros);
   TestSuite_Add(suite, "/bson/decimal128/from_string/invalid", test_decimal128_from_string__invalid_inputs);
   TestSuite_Add(suite, "/bson/decimal128/from_string/nan", test_decimal128_from_string__nan);
   TestSuite_Add(suite, "/bson/decimal128/from_string/infinity", test_decimal128_from_string__infinity);
   TestSuite_Add(suite, "/bson/decimal128/from_string/basic", test_decimal128_from_string__simple);
   TestSuite_Add(suite, "/bson/decimal128/from_string/scientific", test_decimal128_from_string__scientific);
   TestSuite_Add(suite, "/bson/decimal128/from_string/large", test_decimal128_from_string__large);
   TestSuite_Add(suite,
                 "/bson/decimal128/from_string/exponent_normalization",
                 test_decimal128_from_string__exponent_normalization);
   TestSuite_Add(suite, "/bson/decimal128/from_string/zero", test_decimal128_from_string__zeros);
   TestSuite_Add(suite, "/bson/decimal128/from_string/with_length", test_decimal128_from_string_w_len__special);
}
