/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */
package org.apache.hadoop.hive.common.type;

import java.sql.Timestamp;
import java.util.Random;
import java.util.Arrays;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;

import org.apache.hadoop.hive.serde2.io.HiveDecimalWritableV1;
import org.apache.hadoop.hive.serde2.io.HiveDecimalWritable;
import org.apache.hadoop.hive.common.type.RandomTypeUtil;
import org.apache.hadoop.hive.ql.exec.vector.expressions.StringExpr;
import org.apache.hadoop.hive.ql.util.TimestampUtils;

import org.junit.*;

import static org.junit.Assert.*;

public class TestHiveDecimal extends HiveDecimalTestBase {

  @Test
  public void testInvalidStringInput() {

    HiveDecimalV1 oldDec;
    HiveDecimalV1 resultOldDec;
    HiveDecimal dec;
    HiveDecimal resultDec;

    //---------------------------------------------------
    oldDec = HiveDecimalV1.create("-");
    Assert.assertTrue(oldDec == null);
    //---------------------------------------------------
    dec = HiveDecimal.create("-");
    Assert.assertTrue(dec == null);

    //---------------------------------------------------
    oldDec = HiveDecimalV1.create("+");
    Assert.assertTrue(oldDec == null);
    //---------------------------------------------------
    dec = HiveDecimal.create("+");
    Assert.assertTrue(dec == null);

    // Naked dot.
    //---------------------------------------------------
    oldDec = HiveDecimalV1.create(".");
    Assert.assertTrue(oldDec == null);
    //---------------------------------------------------
    dec = HiveDecimal.create(".");
    Assert.assertTrue(dec == null);

    //---------------------------------------------------
    oldDec = HiveDecimalV1.create("-.");
    Assert.assertTrue(oldDec == null);
    //---------------------------------------------------
    dec = HiveDecimal.create("-.");
    Assert.assertTrue(dec == null);

    //---------------------------------------------------
    oldDec = HiveDecimalV1.create("+.");
    Assert.assertTrue(oldDec == null);
    //---------------------------------------------------
    dec = HiveDecimal.create("+.");
    Assert.assertTrue(dec == null);

    // Naked E/e.
    //---------------------------------------------------
    oldDec = HiveDecimalV1.create("E");
    Assert.assertTrue(oldDec == null);
    //---------------------------------------------------
    dec = HiveDecimal.create("E");
    Assert.assertTrue(dec == null);

    //---------------------------------------------------
    oldDec = HiveDecimalV1.create(".E");
    Assert.assertTrue(oldDec == null);
    //---------------------------------------------------
    dec = HiveDecimal.create(".E");
    Assert.assertTrue(dec == null);

    //---------------------------------------------------
    oldDec = HiveDecimalV1.create("-E");
    Assert.assertTrue(oldDec == null);
    //---------------------------------------------------
    dec = HiveDecimal.create("-E");
    Assert.assertTrue(dec == null);

    //---------------------------------------------------
    oldDec = HiveDecimalV1.create("+E");
    Assert.assertTrue(oldDec == null);
    //---------------------------------------------------
    dec = HiveDecimal.create("+E");
    Assert.assertTrue(dec == null);
 
    //---------------------------------------------------
    oldDec = HiveDecimalV1.create("e");
    Assert.assertTrue(oldDec == null);
    //---------------------------------------------------
    dec = HiveDecimal.create("e");
    Assert.assertTrue(dec == null);

    //---------------------------------------------------
    oldDec = HiveDecimalV1.create(".e");
    Assert.assertTrue(oldDec == null);
    //---------------------------------------------------
    dec = HiveDecimal.create(".e");
    Assert.assertTrue(dec == null);

    //---------------------------------------------------
    oldDec = HiveDecimalV1.create("-e");
    Assert.assertTrue(oldDec == null);
    //---------------------------------------------------
    dec = HiveDecimal.create("-e");
    Assert.assertTrue(dec == null);

    //---------------------------------------------------
    oldDec = HiveDecimalV1.create("+e");
    Assert.assertTrue(oldDec == null);
    //---------------------------------------------------
    dec = HiveDecimal.create("+e");
    Assert.assertTrue(dec == null);

    //---------------------------------------------------
    oldDec = HiveDecimalV1.create("error");
    Assert.assertTrue(oldDec == null);
    //---------------------------------------------------
    dec = HiveDecimal.create("error");
    Assert.assertTrue(dec == null);

    //---------------------------------------------------
    oldDec = HiveDecimalV1.create("0x0");
    Assert.assertTrue(oldDec == null);
    //---------------------------------------------------
    dec = HiveDecimal.create("0x0");
    Assert.assertTrue(dec == null);

    //---------------------------------------------------
    oldDec = HiveDecimalV1.create("0e");
    Assert.assertTrue(oldDec == null);
    //---------------------------------------------------
    dec = HiveDecimal.create("0e");
    Assert.assertTrue(dec == null);

    //---------------------------------------------------
    oldDec = HiveDecimalV1.create("7e");
    Assert.assertTrue(oldDec == null);
    //---------------------------------------------------
    dec = HiveDecimal.create("7e");
    Assert.assertTrue(dec == null);

    //---------------------------------------------------
    oldDec = HiveDecimalV1.create("233e-");
    Assert.assertTrue(oldDec == null);
    //---------------------------------------------------
    dec = HiveDecimal.create("233e-");
    Assert.assertTrue(dec == null);

    //---------------------------------------------------
    oldDec = HiveDecimalV1.create("32e+");
    Assert.assertTrue(oldDec == null);
    //---------------------------------------------------
    dec = HiveDecimal.create("32e+");
    Assert.assertTrue(dec == null);

    //---------------------------------------------------
    oldDec = HiveDecimalV1.create(".0e");
    Assert.assertTrue(oldDec == null);
    //---------------------------------------------------
    dec = HiveDecimal.create(".0e");
    Assert.assertTrue(dec == null);

    //---------------------------------------------------
    oldDec = HiveDecimalV1.create(".4e");
    Assert.assertTrue(oldDec == null);
    //---------------------------------------------------
    dec = HiveDecimal.create(".4e");
    Assert.assertTrue(dec == null);
  }

  @Test
  public void testVariousCases() {

    HiveDecimalV1 oldDec;
    HiveDecimalV1 resultOldDec;
    HiveDecimal dec;
    HiveDecimal resultDec;

    BigDecimal bigDecimal = new BigDecimal("-99999999999999999999999999999999999999.99999999999999999");

    //---------------------------------------------------
    oldDec = HiveDecimalV1.create(bigDecimal);
    Assert.assertEquals("-100000000000000000000000000000000000000", oldDec.toString());
    //---------------------------------------------------
    dec = HiveDecimal.create(bigDecimal);
    Assert.assertTrue(dec == null);

    // One less integer digit...
    bigDecimal = new BigDecimal("-9999999999999999999999999999999999999.99999999999999999");

    //---------------------------------------------------
    oldDec = HiveDecimalV1.create(bigDecimal);
    Assert.assertEquals("-10000000000000000000000000000000000000", oldDec.toString());
    //---------------------------------------------------
    dec = HiveDecimal.create(bigDecimal);
    Assert.assertEquals("-10000000000000000000000000000000000000", dec.toString());

    //---------------------------------------------------
    oldDec = HiveDecimalV1.create("101");
    resultOldDec = HiveDecimalV1.enforcePrecisionScale(oldDec, 10, 0);
    Assert.assertEquals("101", resultOldDec.toString());
    //---------------------------------------------------
    dec = HiveDecimal.create("101");
    resultDec = HiveDecimal.enforcePrecisionScale(dec, 10, 0);
    Assert.assertEquals("101", resultDec.toString());

    //---------------------------------------------------
    oldDec = HiveDecimalV1.create("1");
    resultOldDec = oldDec.scaleByPowerOfTen(-99);
    Assert.assertEquals("0", resultOldDec.toString());
    //---------------------------------------------------
    dec = HiveDecimal.create("1");
    resultDec = dec.scaleByPowerOfTen(-99);
    Assert.assertEquals("0", resultDec.toString());
  }

  @Test
  public void testCreateFromBigIntegerRounding() {

    BigInteger bigInt;
    HiveDecimalV1 oldDec;
    HiveDecimal dec;

    // 1786135888657847525803324040144343378.09799306448796128931113691624
    bigInt = new BigInteger(
                        "178613588865784752580332404014434337809799306448796128931113691624");
    Assert.assertEquals("178613588865784752580332404014434337809799306448796128931113691624", bigInt.toString());
    //                   12345678901234567890123456789012345678
    //                            1         2         3
    //                                                        12345678901234567890123456789
    dec = HiveDecimal.create(bigInt, 29);
    Assert.assertEquals("1786135888657847525803324040144343378.1", dec.toString());

    // 8.090000000000000000000000000000000000000123456
    bigInt = new BigInteger(
                        "8090000000000000000000000000000000000000123456");
    //                   123456789012345678901234567890123456789012345
    //                             1         2         3         4
    Assert.assertEquals("8090000000000000000000000000000000000000123456", bigInt.toString());
    //---------------------------------------------------
    oldDec = HiveDecimalV1.create(bigInt, 45);
    Assert.assertEquals("8.09", oldDec.toString());
    //---------------------------------------------------
    dec = HiveDecimal.create(bigInt, 45);
    Assert.assertEquals("8.09", dec.toString());

    // 99999999.99999999999999999999999999999949999
    // MAX_DECIMAL 9's WITH NO ROUND (longer than 38 digits)
    bigInt = new BigInteger(
                        "9999999999999999999999999999999999999949999");
    //                   12345678901234567890123456789012345678
    //                             1         2         3
    //                   99999999.99999999999999999999999999999949999
    Assert.assertEquals("9999999999999999999999999999999999999949999", bigInt.toString());
    //---------------------------------------------------
    oldDec = HiveDecimalV1.create(bigInt, 35);
    Assert.assertEquals("99999999.999999999999999999999999999999", oldDec.toString());
    //---------------------------------------------------
    // Without the round, this conversion fails.
    dec = HiveDecimal.create(bigInt, 35);
    Assert.assertEquals("99999999.999999999999999999999999999999", dec.toString());

    // MAX_DECIMAL 9's WITH ROUND.
    bigInt = new BigInteger(
                        "9999999999999999999999999999999999999979999");
    //                   12346678.901234667890123466789012346678
    //                             1         2         3
    Assert.assertEquals("9999999999999999999999999999999999999979999", bigInt.toString());
    //---------------------------------------------------
    oldDec = HiveDecimalV1.create(bigInt, 35);
    Assert.assertEquals("100000000", oldDec.toString());
    //---------------------------------------------------
    dec = HiveDecimal.create(bigInt, 35);
    Assert.assertEquals("100000000", dec.toString());
  }

  @Test
  public void testCreateFromBigDecimal() {

    BigDecimal bigDec;
    HiveDecimalV1 oldDec;
    HiveDecimal dec;

    bigDec = new BigDecimal("0");
    Assert.assertEquals("0", bigDec.toString());
    dec = HiveDecimal.create(bigDec);
    Assert.assertEquals("0", dec.toString());

    bigDec = new BigDecimal("1");
    Assert.assertEquals("1", bigDec.toString());
    dec = HiveDecimal.create(bigDec);
    Assert.assertEquals("1", dec.toString());

    bigDec = new BigDecimal("0.999");
    Assert.assertEquals("0.999", bigDec.toString());
    dec = HiveDecimal.create(bigDec);
    Assert.assertEquals("0.999", dec.toString());

    // HiveDecimal suppresses trailing zeroes.
    bigDec = new BigDecimal("0.9990");
    Assert.assertEquals("0.9990", bigDec.toString());
    dec = HiveDecimal.create(bigDec);
    Assert.assertEquals("0.999", dec.toString());
  }

  @Test
  public void testCreateFromBigDecimalRounding() {

    BigDecimal bigDec;
    HiveDecimalV1 oldDec;
    HiveDecimal dec;

    bigDec = new BigDecimal(
                        "1786135888657847525803324040144343378.09799306448796128931113691624");
    Assert.assertEquals("1786135888657847525803324040144343378.09799306448796128931113691624", bigDec.toString());
    //                   1234567890123456789012345678901234567.8
    //                            1         2         3
    // Without the round, this conversion fails.
    dec = HiveDecimal.create(bigDec, false);
    Assert.assertTrue(dec == null);
    dec = HiveDecimal.create(bigDec, true);
    Assert.assertEquals("1786135888657847525803324040144343378.1", dec.toString());

    bigDec = new BigDecimal(
                        "8.090000000000000000000000000000000000000123456");
    //                   1.23456789012345678901234567890123456789012345
    //                             1         2         3         4
    Assert.assertEquals("8.090000000000000000000000000000000000000123456", bigDec.toString());
    //---------------------------------------------------
    HiveDecimalV1 oldDec4 = HiveDecimalV1.create(bigDec, false);
    Assert.assertTrue(oldDec4 == null);
    oldDec4 = HiveDecimalV1.create(bigDec, true);
    Assert.assertEquals("8.09", oldDec4.toString());
    //---------------------------------------------------
    // Without the round, this conversion fails.
    dec = HiveDecimal.create(bigDec, false);
    Assert.assertTrue(dec == null);
    dec = HiveDecimal.create(bigDec, true);
    Assert.assertEquals("8.09", dec.toString());

    // MAX_DECIMAL 9's WITH NO ROUND (longer than 38 digits)
    bigDec = new BigDecimal(
                        "99999999.99999999999999999999999999999949999");
    //                   12345678.901234567890123456789012345678
    //                             1         2         3
    Assert.assertEquals("99999999.99999999999999999999999999999949999", bigDec.toString());
    //---------------------------------------------------
    HiveDecimalV1 oldDec5 = HiveDecimalV1.create(bigDec, false);
    Assert.assertTrue(oldDec5 == null);
    oldDec5 = HiveDecimalV1.create(bigDec, true);
    Assert.assertEquals("99999999.999999999999999999999999999999", oldDec5.toString());
    //---------------------------------------------------
    dec = HiveDecimal.create(bigDec, false);
    Assert.assertTrue(dec == null);
    dec = HiveDecimal.create(bigDec, true);
    Assert.assertEquals("99999999.999999999999999999999999999999", dec.toString());

    // MAX_DECIMAL 9's WITH ROUND.
    bigDec = new BigDecimal(
                        "99999999.99999999999999999999999999999979999");
    //                   12346678.901234667890123466789012346678
    //                             1         2         3
    Assert.assertEquals("99999999.99999999999999999999999999999979999", bigDec.toString());
    //---------------------------------------------------
    HiveDecimalV1 oldDec6 = HiveDecimalV1.create(bigDec, false);
    Assert.assertTrue(oldDec6 == null);
    oldDec6 = HiveDecimalV1.create(bigDec, true);
    Assert.assertEquals("100000000", oldDec6.toString());
    //---------------------------------------------------
    dec = HiveDecimal.create(bigDec, false);
    Assert.assertTrue(dec == null);
    dec = HiveDecimal.create(bigDec, true);
    Assert.assertEquals("100000000", dec.toString());
  }


  @Test
  public void testPrecisionScaleEnforcement() {

    HiveDecimalV1 oldDec;
    HiveDecimalV1 oldResultDec;

    HiveDecimal dec;
    HiveDecimal resultDec;

    //---------------------------------------------------
    oldDec = HiveDecimalV1.create("0.02538461538461538461538461538461538462");
    Assert.assertEquals("0.02538461538461538461538",
        HiveDecimalV1.enforcePrecisionScale(oldDec, 38, 23).toString());
    //---------------------------------------------------
    dec = HiveDecimal.create("0.02538461538461538461538461538461538462");
    Assert.assertEquals("0.02538461538461538461538",
        HiveDecimal.enforcePrecisionScale(dec, 38, 23).toString());

    //---------------------------------------------------
    oldDec = HiveDecimalV1.create("005.34000");
    Assert.assertEquals(oldDec.precision(), 3);  // 1 integer digit; 2 fraction digits.
    Assert.assertEquals(oldDec.scale(), 2);      // Trailing zeroes are suppressed.
    //---------------------------------------------------
    dec = HiveDecimal.create("005.34000");
    Assert.assertEquals(dec.precision(), 3);  // 1 integer digit; 2 fraction digits.
    Assert.assertEquals(dec.scale(), 2);      // Trailing zeroes are suppressed.

    dec = HiveDecimal.create("178613588865784752580332404014434337809799306448796128931113691624");
    Assert.assertNull(dec);

    // Rounding numbers that increase int digits
    //---------------------------------------------------
    oldDec = HiveDecimalV1.create("9.5");
    Assert.assertEquals("10",
        HiveDecimalV1.enforcePrecisionScale(oldDec, 2, 0).toString());
    Assert.assertNull(
        HiveDecimalV1.enforcePrecisionScale(oldDec, 1, 0));
    oldDec = HiveDecimalV1.create("9.4");
    Assert.assertEquals("9",
        HiveDecimalV1.enforcePrecisionScale(oldDec, 1, 0).toString());
    //---------------------------------------------------
    dec = HiveDecimal.create("9.5");
    Assert.assertEquals("10",
        HiveDecimal.enforcePrecisionScale(dec, 2, 0).toString());
    Assert.assertNull(
        HiveDecimal.enforcePrecisionScale(dec, 1, 0));
    dec = HiveDecimal.create("9.4");
    Assert.assertEquals("9",
        HiveDecimal.enforcePrecisionScale(dec, 1, 0).toString());
  }

  @Test
  public void testPrecisionScaleEnforcementEdgeCond() {

    // Since HiveDecimal now uses FastHiveDecimal which stores 16 decimal digits per long,
    // lets test edge conditions here.

    HiveDecimal fifteenFractionalNinesDec = HiveDecimal.create("0.999999999999999");
    Assert.assertNotNull(fifteenFractionalNinesDec);
    Assert.assertEquals("0.999999999999999",
        HiveDecimal.enforcePrecisionScale(fifteenFractionalNinesDec, 15, 15).toString());

    HiveDecimal sixteenFractionalNines = HiveDecimal.create("0.9999999999999999");
    Assert.assertNotNull(sixteenFractionalNines);
    Assert.assertEquals("0.9999999999999999",
        HiveDecimal.enforcePrecisionScale(sixteenFractionalNines, 16, 16).toString());

    HiveDecimal seventeenFractionalNines = HiveDecimal.create("0.99999999999999999");
    Assert.assertNotNull(seventeenFractionalNines);
    Assert.assertEquals("0.99999999999999999",
        HiveDecimal.enforcePrecisionScale(seventeenFractionalNines, 17, 17).toString());

  }

  @Test
  public void testTrailingZeroRemovalAfterEnforcement() {
    String decStr = "8.090000000000000000000000000000000000000123456";
    //                 123456789012345678901234567890123456789012345
    //                          1         2         3         4
    HiveDecimal dec = HiveDecimal.create(decStr);
    Assert.assertEquals("8.09", dec.toString());
  }

  @Test
  public void testMultiply() {

    // This multiply produces more than 38 digits --> overflow.
    //---------------------------------------------------
    HiveDecimalV1 oldDec1 = HiveDecimalV1.create("0.00001786135888657847525803");
    HiveDecimalV1 oldDec2 = HiveDecimalV1.create("3.0000123456789");
    HiveDecimalV1 oldResult = oldDec1.multiply(oldDec2);
    Assert.assertTrue(oldResult == null);
    //---------------------------------------------------
    HiveDecimal dec1 = HiveDecimal.create("0.00001786135888657847525803");
    HiveDecimal dec2 = HiveDecimal.create("3.0000123456789");
    HiveDecimal result = dec1.multiply(dec2);
    Assert.assertTrue(result == null);

    dec1 = HiveDecimal.create("178613588865784752580323232232323444.4");
    dec2 = HiveDecimal.create("178613588865784752580302323232.3");
    Assert.assertNull(dec1.multiply(dec2));   // i.e. Overflow.

    dec1 = HiveDecimal.create("47.324");
    dec2 = HiveDecimal.create("9232.309");
    Assert.assertEquals("436909.791116", dec1.multiply(dec2).toString());

    dec1 = HiveDecimal.create("3.140");
    dec2 = HiveDecimal.create("1.00");
    Assert.assertEquals("3.14", dec1.multiply(dec2).toString());

    dec1 = HiveDecimal.create("43.010");
    dec2 = HiveDecimal.create("2");
    Assert.assertEquals("86.02", dec1.multiply(dec2).toString());
  }

  @Test
  public void testMultiply2() {
    // 0.09765625BD * 0.09765625BD * 0.0125BD * 578992BD
    HiveDecimal dec1 = HiveDecimal.create("0.09765625");
    HiveDecimal dec2 = HiveDecimal.create("0.09765625");
    HiveDecimal dec3 = HiveDecimal.create("0.0125");
    HiveDecimal dec4 = HiveDecimal.create("578992");
    HiveDecimal result1 = dec1.multiply(dec2);
    Assert.assertNotNull(result1);
    HiveDecimal result2 = result1.multiply(dec3);
    Assert.assertNotNull(result2);
    HiveDecimal result = result2.multiply(dec4);
    Assert.assertNotNull(result);
    Assert.assertEquals("69.0212249755859375", result.toString());
  }

  @Test
  public void testPow() {

    HiveDecimal dec;

    dec = HiveDecimal.create("3.00001415926");
    HiveDecimal decPow2 = dec.pow(2);
    HiveDecimal decMultiplyTwice = dec.multiply(dec);
    Assert.assertEquals(decPow2, decMultiplyTwice);

    dec = HiveDecimal.create("0.000017861358882");
    dec = dec.pow(3);
    Assert.assertNull(dec);

    dec = HiveDecimal.create("3.140");
    Assert.assertEquals("9.8596", dec.pow(2).toString());
  }

  @Test
  public void testScaleByPowerOfTen() {

    HiveDecimalV1 oldDec;
    HiveDecimal dec;
    HiveDecimalV1 oldResultDec;
    HiveDecimal resultDec;

    //**********************************************************************************************

    //---------------------------------------------------
    oldDec = HiveDecimalV1.create(
        "1");
    Assert.assertEquals(0, oldDec.scale());
    oldResultDec = oldDec.scaleByPowerOfTen(2);
    Assert.assertEquals(
        "100", oldResultDec.toString());
    //---------------------------------------------------
    dec = HiveDecimal.create(
        "1");
    Assert.assertEquals(0, dec.scale());
    // resultDec = dec.scaleByPowerOfTen(2);
    // Assert.assertEquals(
    //     "100", resultDec.toString());

    //---------------------------------------------------
    oldDec = HiveDecimalV1.create(
        "0.00000000000000000000000000000000000001");
    Assert.assertEquals(38, oldDec.scale());
    oldResultDec = oldDec.scaleByPowerOfTen(2);
    Assert.assertEquals(
        "0.000000000000000000000000000000000001", oldResultDec.toString());
    //---------------------------------------------------
    dec = HiveDecimal.create(
        "0.00000000000000000000000000000000000001");
    Assert.assertEquals(38, dec.scale());
    resultDec = dec.scaleByPowerOfTen(2);
    Assert.assertEquals(
        "0.000000000000000000000000000000000001", resultDec.toString());

    //---------------------------------------------------
    oldDec = HiveDecimalV1.create(
        "0.00000000000000000000000000000000000001");
    Assert.assertEquals(38, oldDec.scale());
    oldResultDec = oldDec.scaleByPowerOfTen(38);
    Assert.assertEquals(
        "1", oldResultDec.toString());
    //---------------------------------------------------
    dec = HiveDecimal.create(
        "0.00000000000000000000000000000000000001");
    Assert.assertEquals(38, dec.scale());
    resultDec = dec.scaleByPowerOfTen(38);
    Assert.assertEquals(
        "1", resultDec.toString());

    //---------------------------------------------------
    oldDec = HiveDecimalV1.create(
        "0.00000000000000000000000000000000000001");
    Assert.assertEquals(38, oldDec.scale());
    oldResultDec = oldDec.scaleByPowerOfTen(2 * 38 - 1);
    Assert.assertEquals(
        "10000000000000000000000000000000000000", oldResultDec.toString());
    Assert.assertEquals(0, oldResultDec.scale());
    //---------------------------------------------------
    dec = HiveDecimal.create(
        "0.00000000000000000000000000000000000001");
    Assert.assertEquals(38, dec.scale());
    resultDec = dec.scaleByPowerOfTen(2 * 38 - 1);
    Assert.assertEquals(
        "10000000000000000000000000000000000000", resultDec.toString());
    Assert.assertEquals(0, resultDec.scale());

    //---------------------------------------------------
    oldDec = HiveDecimalV1.create(
        "0.00000000000000000000000000000000000001");
    Assert.assertEquals(38, oldDec.scale());
    oldResultDec = oldDec.scaleByPowerOfTen(2 * 38);
    Assert.assertTrue(oldResultDec == null);
    //---------------------------------------------------
    dec = HiveDecimal.create(
        "0.00000000000000000000000000000000000001");
    Assert.assertEquals(38, dec.scale());
    resultDec = dec.scaleByPowerOfTen(2 * 38);
    Assert.assertTrue(resultDec == null);


    //---------------------------------------------------
    oldDec = HiveDecimalV1.create(
        "0.00000000000000000000000000000000000022");
    Assert.assertEquals(38, oldDec.scale());
    oldResultDec = oldDec.scaleByPowerOfTen(38);
    Assert.assertEquals(
        "22", oldResultDec.toString());
    Assert.assertEquals(0, oldResultDec.scale());
    //---------------------------------------------------
    dec = HiveDecimal.create(
        "0.00000000000000000000000000000000000022");
    Assert.assertEquals(38, dec.scale());
    resultDec = dec.scaleByPowerOfTen(38);
    Assert.assertEquals(
        "22", resultDec.toString());
    Assert.assertEquals(0, resultDec.scale());

    //---------------------------------------------------
    oldDec = HiveDecimalV1.create("3.00001415926");
    Assert.assertEquals(11, oldDec.scale());
    oldResultDec = oldDec.scaleByPowerOfTen(2);
    Assert.assertEquals("300.001415926", oldResultDec.toString());
    Assert.assertEquals(9, oldResultDec.scale());
    oldResultDec = oldDec.scaleByPowerOfTen(5);
    Assert.assertEquals("300001.415926", oldResultDec.toString());
    Assert.assertEquals(6, oldResultDec.scale());
    oldResultDec = oldDec.scaleByPowerOfTen(18);
    Assert.assertEquals("3000014159260000000", oldResultDec.toString());
    Assert.assertEquals(0, oldResultDec.scale());
    oldResultDec = oldDec.scaleByPowerOfTen(35);
    Assert.assertEquals("300001415926000000000000000000000000", oldResultDec.toString());
    Assert.assertEquals(0, oldResultDec.scale());
    oldResultDec = oldDec.scaleByPowerOfTen(37);
    Assert.assertEquals("30000141592600000000000000000000000000", oldResultDec.toString());
    Assert.assertEquals(0, oldResultDec.scale());
    //---------------------------------------------------
    dec = HiveDecimal.create("3.00001415926");
    Assert.assertEquals(11, dec.scale());
    Assert.assertEquals(1, dec.integerDigitCount());
    resultDec = dec.scaleByPowerOfTen(2);
    Assert.assertEquals("300.001415926", resultDec.toString());
    Assert.assertEquals(9, resultDec.scale());
    Assert.assertEquals(3, resultDec.integerDigitCount());
    resultDec = dec.scaleByPowerOfTen(5);
    Assert.assertEquals("300001.415926", resultDec.toString());
    Assert.assertEquals(6, resultDec.scale());
    Assert.assertEquals(6, resultDec.integerDigitCount());
    resultDec = dec.scaleByPowerOfTen(18);
    Assert.assertEquals("3000014159260000000", resultDec.toString());
    Assert.assertEquals(0, resultDec.scale());
    Assert.assertEquals(19, resultDec.integerDigitCount());
    resultDec = dec.scaleByPowerOfTen(35);
    Assert.assertEquals("300001415926000000000000000000000000", resultDec.toString());
    Assert.assertEquals(0, resultDec.scale());
    Assert.assertEquals(36, resultDec.integerDigitCount());
    resultDec = dec.scaleByPowerOfTen(37);
    Assert.assertEquals("30000141592600000000000000000000000000", resultDec.toString());
    Assert.assertEquals(0, resultDec.scale());
    Assert.assertEquals(38, resultDec.integerDigitCount());
  }

  @Test
  public void testSingleWordDivision() {
 
    HiveDecimalV1 oldDec1;
    HiveDecimalV1 oldDec2;
    HiveDecimalV1 oldResultDec;

    HiveDecimal dec1;
    HiveDecimal dec2;
    HiveDecimal resultDec;

    //---------------------------------------------------
    oldDec1 = HiveDecimalV1.create("839293");
    oldDec2 = HiveDecimalV1.create("8");
    oldResultDec = oldDec1.divide(oldDec2);
    Assert.assertEquals("104911.625", oldResultDec.toString());
    //---------------------------------------------------
    dec1 = HiveDecimal.create("839293");
    dec2 = HiveDecimal.create("8");
    resultDec = dec1.divide(dec2);
    Assert.assertEquals("104911.625", resultDec.toString());  // UNDONE

    //---------------------------------------------------
    oldDec1 = HiveDecimalV1.create("1");
    oldDec2 = HiveDecimalV1.create("3");
    oldResultDec = oldDec1.divide(oldDec2);
    Assert.assertEquals("0.33333333333333333333333333333333333333", oldResultDec.toString());
    //---------------------------------------------------
    dec1 = HiveDecimal.create("1");
    dec2 = HiveDecimal.create("3");
    resultDec = dec1.divide(dec2);
    Assert.assertEquals("0.33333333333333333333333333333333333333", resultDec.toString());  // UNDONE

    //---------------------------------------------------
    oldDec1 = HiveDecimalV1.create("1");
    oldDec2 = HiveDecimalV1.create("9");
    oldResultDec = oldDec1.divide(oldDec2);
    Assert.assertEquals("0.11111111111111111111111111111111111111", oldResultDec.toString());
    //---------------------------------------------------
    dec1 = HiveDecimal.create("1");
    dec2 = HiveDecimal.create("9");
    resultDec = dec1.divide(dec2);
    Assert.assertEquals("0.11111111111111111111111111111111111111", resultDec.toString());  // UNDONE

    //---------------------------------------------------
    oldDec1 = HiveDecimalV1.create("22");
    oldDec2 = HiveDecimalV1.create("7");
    oldResultDec = oldDec1.divide(oldDec2);
    Assert.assertEquals("3.1428571428571428571428571428571428571", oldResultDec.toString());
    //---------------------------------------------------
    dec1 = HiveDecimal.create("22");
    dec2 = HiveDecimal.create("7");
    resultDec = dec1.divide(dec2);
    Assert.assertEquals("3.1428571428571428571428571428571428571", resultDec.toString());  // UNDONE

    //---------------------------------------------------
    oldDec1 = HiveDecimalV1.create("1");
    oldDec2 = HiveDecimalV1.create("81");
    oldResultDec = oldDec1.divide(oldDec2);
    Assert.assertEquals("0.01234567901234567901234567901234567901", oldResultDec.toString());
    //---------------------------------------------------
    dec1 = HiveDecimal.create("1");
    dec2 = HiveDecimal.create("81");
    resultDec = dec1.divide(dec2);
    Assert.assertEquals("0.01234567901234567901234567901234567901", resultDec.toString());  // UNDONE

    //---------------------------------------------------
    oldDec1 = HiveDecimalV1.create("425");
    oldDec2 = HiveDecimalV1.create("1000000000000000");
    oldResultDec = oldDec1.divide(oldDec2);
    Assert.assertEquals("0.000000000000425", oldResultDec.toString());
    //---------------------------------------------------
    dec1 = HiveDecimal.create("425");
    dec2 = HiveDecimal.create("1000000000000000");
    resultDec = dec1.divide(dec2);
    Assert.assertEquals("0.000000000000425", resultDec.toString());  // UNDONE

    //---------------------------------------------------
    oldDec1 = HiveDecimalV1.create("0.000000000088");
    oldDec2 = HiveDecimalV1.create("1000000000000000");
    oldResultDec = oldDec1.divide(oldDec2);
    Assert.assertEquals("0.000000000000000000000000088", oldResultDec.toString());
    Assert.assertEquals(27, oldResultDec.scale());
    //---------------------------------------------------
    dec1 = HiveDecimal.create("0.000000000088");
    dec2 = HiveDecimal.create("1000000000000000");
    resultDec = dec1.divide(dec2);
    Assert.assertEquals("0.000000000000000000000000088", resultDec.toString());  // UNDONE
    Assert.assertEquals(27, resultDec.scale());
   }

  @Test
  public void testDivide() {
    HiveDecimal dec1 = HiveDecimal.create("3.14");
    HiveDecimal dec2 = HiveDecimal.create("3");
    Assert.assertNotNull(dec1.divide(dec2));

    dec1 = HiveDecimal.create("15");
    dec2 = HiveDecimal.create("5");
    Assert.assertEquals("3", dec1.divide(dec2).toString());

    dec1 = HiveDecimal.create("3.140");
    dec2 = HiveDecimal.create("1.00");
    Assert.assertEquals("3.14", dec1.divide(dec2).toString());
  }

  @Test
  public void testPlus() {

    HiveDecimalV1 oldDec;
    HiveDecimalV1 oldDec2;
    HiveDecimalV1 oldResultDec;

    HiveDecimal dec;
    HiveDecimal dec1;
    HiveDecimal dec2;
    HiveDecimal resultDec;

    String decStr;
    String decStr2;

    dec1 = HiveDecimal.create("3.140");
    dec1.validate();
    dec2 = HiveDecimal.create("1.00");
    dec2.validate();
    resultDec = dec1.add(dec2);
    resultDec.validate();
    Assert.assertEquals("4.14", resultDec.toString());

    decStr = "3.140";
    decStr2 = "1.00";
    //---------------------------------------------------
    oldDec = HiveDecimalV1.create(decStr);
    oldDec2 = HiveDecimalV1.create(decStr2);
    oldResultDec = oldDec.add(oldDec2);
    Assert.assertEquals("4.14", oldResultDec.toString());
    //---------------------------------------------------
    dec = HiveDecimal.create(decStr);
    assertTrue(dec != null);
    dec.validate();
    dec2 = HiveDecimal.create(decStr2);
    assertTrue(dec2 != null);
    dec2.validate();
    resultDec = dec.add(dec2);
    assertTrue(resultDec != null);
    resultDec.validate();
    Assert.assertEquals("4.14", resultDec.toString());
    Assert.assertEquals(1, resultDec.integerDigitCount());

    decStr = "3.140";
    decStr2 = "1.00000008733";
    //---------------------------------------------------
    oldDec = HiveDecimalV1.create(decStr);
    oldDec2 = HiveDecimalV1.create(decStr2);
    oldResultDec = oldDec.add(oldDec2);
    Assert.assertEquals("4.14000008733", oldResultDec.toString());
    //---------------------------------------------------
    dec = HiveDecimal.create(decStr);
    assertTrue(dec != null);
    dec.validate();
    dec2 = HiveDecimal.create(decStr2);
    assertTrue(dec2 != null);
    dec2.validate();
    resultDec = dec.add(dec2);
    assertTrue(resultDec != null);
    resultDec.validate();
    Assert.assertEquals("4.14000008733", resultDec.toString());
    Assert.assertEquals(1, resultDec.integerDigitCount());

    decStr = "3.140";
    decStr2 = "1.00000000000000000008733";
    //---------------------------------------------------
    oldDec = HiveDecimalV1.create(decStr);
    oldDec2 = HiveDecimalV1.create(decStr2);
    oldResultDec = oldDec.add(oldDec2);
    Assert.assertEquals("4.14000000000000000008733", oldResultDec.toString());
    //---------------------------------------------------
    dec = HiveDecimal.create(decStr);
    assertTrue(dec != null);
    dec.validate();
    dec2 = HiveDecimal.create(decStr2);
    assertTrue(dec2 != null);
    dec2.validate();
    resultDec = dec.add(dec2);
    assertTrue(resultDec != null);
    resultDec.validate();
    Assert.assertEquals("4.14000000000000000008733", resultDec.toString());
    Assert.assertEquals(1, resultDec.integerDigitCount());

    decStr = "30000000000.140";
    decStr2 = "1.00000000000000000008733";
    //---------------------------------------------------
    oldDec = HiveDecimalV1.create(decStr);
    oldDec2 = HiveDecimalV1.create(decStr2);
    oldResultDec = oldDec.add(oldDec2);
    Assert.assertEquals("30000000001.14000000000000000008733", oldResultDec.toString());
    //---------------------------------------------------
    dec = HiveDecimal.create(decStr);
    assertTrue(dec != null);
    dec.validate();
    dec2 = HiveDecimal.create(decStr2);
    assertTrue(dec2 != null);
    dec2.validate();
    resultDec = dec.add(dec2);
    assertTrue(resultDec != null);
    resultDec.validate();
    Assert.assertEquals("30000000001.14000000000000000008733", resultDec.toString());
    // Assert.assertEquals(1, resultDec.integerDigitCount());

    decStr = "300000000000000.140";
    decStr2 = "1.00000000000000000008733";
    //---------------------------------------------------
    oldDec = HiveDecimalV1.create(decStr);
    oldDec2 = HiveDecimalV1.create(decStr2);
    oldResultDec = oldDec.add(oldDec2);
    Assert.assertEquals("300000000000001.14000000000000000008733", oldResultDec.toString());
    //---------------------------------------------------
    dec = HiveDecimal.create(decStr);
    assertTrue(dec != null);
    dec.validate();
    dec2 = HiveDecimal.create(decStr2);
    assertTrue(dec2 != null);
    dec2.validate();
    resultDec = dec.add(dec2);
    assertTrue(resultDec != null);
    resultDec.validate();
    Assert.assertEquals("300000000000001.14000000000000000008733", resultDec.toString());
    // Assert.assertEquals(1, resultDec.integerDigitCount());

    // Edge case?
    decStr = "3000000000000000.140";
    decStr2 = "1.00000000000000000008733";
    //---------------------------------------------------
    oldDec = HiveDecimalV1.create(decStr);
    oldDec2 = HiveDecimalV1.create(decStr2);
    oldResultDec = oldDec.add(oldDec2);
    Assert.assertEquals("3000000000000001.1400000000000000000873", oldResultDec.toString());
    //---------------------------------------------------
    dec = HiveDecimal.create(decStr);
    assertTrue(dec != null);
    dec.validate();
    dec2 = HiveDecimal.create(decStr2);
    assertTrue(dec2 != null);
    dec2.validate();
    resultDec = dec.add(dec2);
    assertTrue(resultDec != null);
    resultDec.validate();
    Assert.assertEquals("3000000000000001.1400000000000000000873", resultDec.toString());
    // Assert.assertEquals(1, resultDec.integerDigitCount());

    decStr = "300000000000000000000000.14";
    decStr2 = "0.0000055555555550008733";
    //---------------------------------------------------
    oldDec = HiveDecimalV1.create(decStr);
    oldDec2 = HiveDecimalV1.create(decStr2);
    oldResultDec = oldDec.add(oldDec2);
    Assert.assertEquals("300000000000000000000000.14000555555556", oldResultDec.toString());
    //---------------------------------------------------
    dec = HiveDecimal.create(decStr);
    assertTrue(dec != null);
    dec.validate();
    dec2 = HiveDecimal.create(decStr2);
    assertTrue(dec2 != null);
    dec2.validate();
    resultDec = dec.add(dec2);
    assertTrue(resultDec != null);
    resultDec.validate();
    Assert.assertEquals("300000000000000000000000.14000555555556", resultDec.toString());
    // Assert.assertEquals(1, resultDec.integerDigitCount());

    decStr = "300000000000000000000000.14";
    decStr2 = "0.000005555555555000873355";
    //---------------------------------------------------
    oldDec = HiveDecimalV1.create(decStr);
    oldDec2 = HiveDecimalV1.create(decStr2);
    oldResultDec = oldDec.add(oldDec2);
    Assert.assertEquals("300000000000000000000000.14000555555556", oldResultDec.toString());
    //---------------------------------------------------
    dec = HiveDecimal.create(decStr);
    assertTrue(dec != null);
    dec.validate();
    dec2 = HiveDecimal.create(decStr2);
    assertTrue(dec2 != null);
    dec2.validate();
    resultDec = dec.add(dec2);
    assertTrue(resultDec != null);
    resultDec.validate();
    Assert.assertEquals("300000000000000000000000.14000555555556", resultDec.toString());
    // Assert.assertEquals(1, resultDec.integerDigitCount());



    // Example from HiveDecimal.add header comments.
    decStr = "598575157855521918987423259.94094";
    decStr2 = "0.0000000000006711991169422033";
    //---------------------------------------------------
    oldDec = HiveDecimalV1.create(decStr);
    oldDec2 = HiveDecimalV1.create(decStr2);
    oldResultDec = oldDec.add(oldDec2);
    Assert.assertEquals("598575157855521918987423259.94094", oldResultDec.toString());
    //---------------------------------------------------
    dec = HiveDecimal.create(decStr);
    assertTrue(dec != null);
    dec.validate();
    dec2 = HiveDecimal.create(decStr2);
    assertTrue(dec2 != null);
    dec2.validate();
    resultDec = dec.add(dec2);
    assertTrue(resultDec != null);
    resultDec.validate();
    Assert.assertEquals("598575157855521918987423259.94094", resultDec.toString());
    Assert.assertEquals(27, resultDec.integerDigitCount());

    decStr = "598575157855521918987423259.94094";
    decStr2 = "0.5555555555556711991169422033";
    //---------------------------------------------------
    oldDec = HiveDecimalV1.create(decStr);
    oldDec2 = HiveDecimalV1.create(decStr2);
    oldResultDec = oldDec.add(oldDec2);
    Assert.assertEquals("598575157855521918987423260.49649555556", oldResultDec.toString());
    //---------------------------------------------------
    dec = HiveDecimal.create(decStr);
    assertTrue(dec != null);
    dec.validate();
    dec2 = HiveDecimal.create(decStr2);
    assertTrue(dec2 != null);
    dec2.validate();
    resultDec = dec.add(dec2);
    assertTrue(resultDec != null);
    resultDec.validate();
    Assert.assertEquals("598575157855521918987423260.49649555556", resultDec.toString());
    Assert.assertEquals(27, resultDec.integerDigitCount());

    decStr = "199999999.99995";
    decStr2 = "100000000.00005";
    //---------------------------------------------------
    oldDec = HiveDecimalV1.create(decStr);
    oldDec2 = HiveDecimalV1.create(decStr2);
    oldResultDec = oldDec.add(oldDec2);
    Assert.assertEquals("300000000", oldResultDec.toString());
    //---------------------------------------------------
    dec = HiveDecimal.create(decStr);
    assertTrue(dec != null);
    dec.validate();
    dec2 = HiveDecimal.create(decStr2);
    assertTrue(dec2 != null);
    dec2.validate();
    resultDec = dec.add(dec2);
    assertTrue(resultDec != null);
    resultDec.validate();
    Assert.assertEquals("300000000", resultDec.toString());
    Assert.assertEquals(9, resultDec.integerDigitCount());

    dec1 = HiveDecimal.create("99999999999999999999999999999999999999");
    dec1.validate();
    Assert.assertEquals(38, dec1.integerDigitCount());
    dec2 = HiveDecimal.create("1");
    dec2.validate();
    Assert.assertNull(dec1.add(dec2));
  }

  @Test
  public void testAdd() {

    HiveDecimalV1 oldDec;
    HiveDecimalV1 oldDec2;
    HiveDecimalV1 oldResultDec;

    HiveDecimal dec;
    HiveDecimal dec2;
    HiveDecimal resultDec;

    // Use the example from HIVE-13423 where the integer digits of the result exceed the
    // enforced precision/scale.
    //---------------------------------------------------
    oldDec = HiveDecimalV1.create("98765432109876543210.12345");
    oldResultDec = oldDec.add(oldDec);
    Assert.assertEquals("197530864219753086420.2469", oldResultDec.toString());
    oldResultDec = HiveDecimalV1.enforcePrecisionScale(oldResultDec, 38, 18);
    Assert.assertTrue(oldResultDec == null);
    //---------------------------------------------------
    dec = HiveDecimal.create("98765432109876543210.12345");
    assertTrue(dec != null);
    dec.validate();
    resultDec = dec.add(dec);
    assertTrue(resultDec != null);
    resultDec.validate();
    Assert.assertEquals("197530864219753086420.2469", resultDec.toString());
    // Assert.assertEquals(21, resultDec.integerDigitCount());
    resultDec = HiveDecimal.enforcePrecisionScale(resultDec, 38, 18);
    Assert.assertTrue(resultDec == null);
 
    // Make sure zero trimming doesn't extend into the integer digits.
    //---------------------------------------------------
    oldDec = HiveDecimalV1.create("199999999.99995");
    oldDec2 = HiveDecimalV1.create("100000000.00005");
    oldResultDec = oldDec.add(oldDec2);
    Assert.assertEquals("300000000", oldResultDec.toString());
    //---------------------------------------------------
    dec = HiveDecimal.create("199999999.99995");
    assertTrue(dec != null);
    dec.validate();
    dec2 = HiveDecimal.create("100000000.00005");
    assertTrue(dec2 != null);
    dec2.validate();
    resultDec = dec.add(dec2);
    assertTrue(resultDec != null);
    resultDec.validate();
    Assert.assertEquals("300000000", resultDec.toString());
    Assert.assertEquals(9, resultDec.integerDigitCount());
   }

  @Test
  public void testMinus() {

    HiveDecimalV1 oldDec;
    HiveDecimalV1 oldDec2;
    HiveDecimalV1 oldResultDec;

    HiveDecimal dec;
    HiveDecimal dec1;
    HiveDecimal dec2;
    HiveDecimal resultDec;

    String decStr;
    String decStr2;

    dec1 = HiveDecimal.create("3.140");
    dec1.validate();
    dec2 = HiveDecimal.create("1.00");
    dec2.validate();
    resultDec = dec1.add(dec2);
    resultDec.validate();
    Assert.assertEquals("4.14", resultDec.toString());

    decStr = "3.140";
    decStr2 = "1.00";
    //---------------------------------------------------
    oldDec = HiveDecimalV1.create(decStr);
    oldDec2 = HiveDecimalV1.create(decStr2);
    oldResultDec = oldDec.subtract(oldDec2);
    Assert.assertEquals("2.14", oldResultDec.toString());
    //---------------------------------------------------
    dec = HiveDecimal.create(decStr);
    assertTrue(dec != null);
    dec.validate();
    dec2 = HiveDecimal.create(decStr2);
    assertTrue(dec2 != null);
    dec2.validate();
    resultDec = dec.subtract(dec2);
    assertTrue(resultDec != null);
    resultDec.validate();
    Assert.assertEquals("2.14", resultDec.toString());
    // Assert.assertEquals(1, resultDec.integerDigitCount());

    decStr = "3.140";
    decStr2 = "1.00000008733";
    //---------------------------------------------------
    oldDec = HiveDecimalV1.create(decStr);
    oldDec2 = HiveDecimalV1.create(decStr2);
    oldResultDec = oldDec.subtract(oldDec2);
    Assert.assertEquals("2.13999991267", oldResultDec.toString());
    //---------------------------------------------------
    dec = HiveDecimal.create(decStr);
    assertTrue(dec != null);
    dec.validate();
    dec2 = HiveDecimal.create(decStr2);
    assertTrue(dec2 != null);
    dec2.validate();
    resultDec = dec.subtract(dec2);
    assertTrue(resultDec != null);
    resultDec.validate();
    Assert.assertEquals("2.13999991267", resultDec.toString());
    // Assert.assertEquals(1, resultDec.integerDigitCount());

    decStr = "3.140";
    decStr2 = "1.00000000000000000008733";
    //---------------------------------------------------
    oldDec = HiveDecimalV1.create(decStr);
    oldDec2 = HiveDecimalV1.create(decStr2);
    oldResultDec = oldDec.subtract(oldDec2);
    Assert.assertEquals("2.13999999999999999991267", oldResultDec.toString());
    //---------------------------------------------------
    dec = HiveDecimal.create(decStr);
    assertTrue(dec != null);
    dec.validate();
    dec2 = HiveDecimal.create(decStr2);
    assertTrue(dec2 != null);
    dec2.validate();
    resultDec = dec.subtract(dec2);
    assertTrue(resultDec != null);
    resultDec.validate();
    Assert.assertEquals("2.13999999999999999991267", resultDec.toString());
    // Assert.assertEquals(1, resultDec.integerDigitCount());

    decStr = "30000000000.140";
    decStr2 = "1.00000000000000000008733";
    //---------------------------------------------------
    oldDec = HiveDecimalV1.create(decStr);
    oldDec2 = HiveDecimalV1.create(decStr2);
    oldResultDec = oldDec.subtract(oldDec2);
    Assert.assertEquals("29999999999.13999999999999999991267", oldResultDec.toString());
    //---------------------------------------------------
    dec = HiveDecimal.create(decStr);
    assertTrue(dec != null);
    dec.validate();
    dec2 = HiveDecimal.create(decStr2);
    assertTrue(dec2 != null);
    dec2.validate();
    resultDec = dec.subtract(dec2);
    assertTrue(resultDec != null);
    resultDec.validate();
    Assert.assertEquals("29999999999.13999999999999999991267", resultDec.toString());
    // Assert.assertEquals(1, resultDec.integerDigitCount());

    decStr = "300000000000000.140";
    decStr2 = "1.00000000000000000008733";
    //---------------------------------------------------
    oldDec = HiveDecimalV1.create(decStr);
    oldDec2 = HiveDecimalV1.create(decStr2);
    oldResultDec = oldDec.subtract(oldDec2);
    Assert.assertEquals("299999999999999.13999999999999999991267", oldResultDec.toString());
    //---------------------------------------------------
    dec = HiveDecimal.create(decStr);
    assertTrue(dec != null);
    dec.validate();
    dec2 = HiveDecimal.create(decStr2);
    assertTrue(dec2 != null);
    dec2.validate();
    resultDec = dec.subtract(dec2);
    assertTrue(resultDec != null);
    resultDec.validate();
    Assert.assertEquals("299999999999999.13999999999999999991267", resultDec.toString());
    // Assert.assertEquals(1, resultDec.integerDigitCount());

    // Edge case?
    decStr = "3000000000000000.140";
    decStr2 = "1.00000000000000000008733";
    //---------------------------------------------------
    oldDec = HiveDecimalV1.create(decStr);
    oldDec2 = HiveDecimalV1.create(decStr2);
    oldResultDec = oldDec.subtract(oldDec2);
    Assert.assertEquals("2999999999999999.1399999999999999999127", oldResultDec.toString());
    //---------------------------------------------------
    dec = HiveDecimal.create(decStr);
    assertTrue(dec != null);
    dec.validate();
    dec2 = HiveDecimal.create(decStr2);
    assertTrue(dec2 != null);
    dec2.validate();
    resultDec = dec.subtract(dec2);
    assertTrue(resultDec != null);
    resultDec.validate();
    Assert.assertEquals("2999999999999999.1399999999999999999127", resultDec.toString());
    // Assert.assertEquals(1, resultDec.integerDigitCount());

    decStr = "300000000000000000000000.14";
    decStr2 = "0.0000055555555550008733";
    //---------------------------------------------------
    oldDec = HiveDecimalV1.create(decStr);
    oldDec2 = HiveDecimalV1.create(decStr2);
    oldResultDec = oldDec.subtract(oldDec2);
    Assert.assertEquals("300000000000000000000000.13999444444444", oldResultDec.toString());
    //---------------------------------------------------
    dec = HiveDecimal.create(decStr);
    assertTrue(dec != null);
    dec.validate();
    dec2 = HiveDecimal.create(decStr2);
    assertTrue(dec2 != null);
    dec2.validate();
    resultDec = dec.subtract(dec2);
    assertTrue(resultDec != null);
    resultDec.validate();
    Assert.assertEquals("300000000000000000000000.13999444444444", resultDec.toString());
    // Assert.assertEquals(1, resultDec.integerDigitCount());

    decStr = "300000000000000000000000.14";
    decStr2 = "0.000005555555555000873355";
    //---------------------------------------------------
    oldDec = HiveDecimalV1.create(decStr);
    oldDec2 = HiveDecimalV1.create(decStr2);
    oldResultDec = oldDec.subtract(oldDec2);
    Assert.assertEquals("300000000000000000000000.13999444444444", oldResultDec.toString());
    //---------------------------------------------------
    dec = HiveDecimal.create(decStr);
    assertTrue(dec != null);
    dec.validate();
    dec2 = HiveDecimal.create(decStr2);
    assertTrue(dec2 != null);
    dec2.validate();
    resultDec = dec.subtract(dec2);
    assertTrue(resultDec != null);
    resultDec.validate();
    Assert.assertEquals("300000000000000000000000.13999444444444", resultDec.toString());
    // Assert.assertEquals(1, resultDec.integerDigitCount());

    // Example from HiveDecimal.subtract header comments.
    decStr = "598575157855521918987423259.94094";
    decStr2 = "0.0000000000006711991169422033";
    //---------------------------------------------------
    oldDec = HiveDecimalV1.create(decStr);
    oldDec2 = HiveDecimalV1.create(decStr2);
    oldResultDec = oldDec.subtract(oldDec2);
    Assert.assertEquals("598575157855521918987423259.94094", oldResultDec.toString());
    //---------------------------------------------------
    dec = HiveDecimal.create(decStr);
    assertTrue(dec != null);
    dec.validate();
    dec2 = HiveDecimal.create(decStr2);
    assertTrue(dec2 != null);
    dec2.validate();
    resultDec = dec.subtract(dec2);
    assertTrue(resultDec != null);
    resultDec.validate();
    Assert.assertEquals("598575157855521918987423259.94094", resultDec.toString());
    // Assert.assertEquals(1, resultDec.integerDigitCount());

    decStr = "598575157855521918987423259.94094";
    decStr2 = "0.5555555555556711991169422033";
    //---------------------------------------------------
    oldDec = HiveDecimalV1.create(decStr);
    oldDec2 = HiveDecimalV1.create(decStr2);
    oldResultDec = oldDec.subtract(oldDec2);
    Assert.assertEquals("598575157855521918987423259.38538444444", oldResultDec.toString());
    //---------------------------------------------------
    dec = HiveDecimal.create(decStr);
    assertTrue(dec != null);
    dec.validate();
    dec2 = HiveDecimal.create(decStr2);
    assertTrue(dec2 != null);
    dec2.validate();
    resultDec = dec.subtract(dec2);
    assertTrue(resultDec != null);
    resultDec.validate();
    Assert.assertEquals("598575157855521918987423259.38538444444", resultDec.toString());
    // Assert.assertEquals(1, resultDec.integerDigitCount());
  }

  @Test
  public void testSubtract() {
    HiveDecimal dec1 = HiveDecimal.create("3.140");
    assertTrue(dec1 != null);
    dec1.validate();
    HiveDecimal dec2 = HiveDecimal.create("1.00");
    assertTrue(dec2 != null);
    dec2.validate();
    HiveDecimal result = dec1.subtract(dec2);
    assertTrue(result != null);
    result.validate();
    Assert.assertEquals("2.14", result.toString());

    dec1 = HiveDecimal.create("0.00001786135888657847525803");
    assertTrue(dec1 != null);
    dec1.validate();
    dec2 = HiveDecimal.create("3.0000123456789");
    assertTrue(dec2 != null);
    dec2.validate();
    result = dec1.subtract(dec2);
    assertTrue(result != null);
    result.validate();
    Assert.assertEquals("-2.99999448432001342152474197", result.toString());
  }

  @Test
  public void testPosMod() {
    HiveDecimal hd1 = HiveDecimal.create("-100.91");
    assertTrue(hd1 != null);
    hd1.validate();
    HiveDecimal hd2 = HiveDecimal.create("9.8");
    assertTrue(hd2 != null);
    hd2.validate();
    HiveDecimal dec = hd1.remainder(hd2).add(hd2).remainder(hd2);
    assertTrue(dec != null);
    dec.validate();
    Assert.assertEquals("6.89", dec.toString());
  }

  @Test
  public void testHashCode() {
      Assert.assertEquals(HiveDecimal.create("9").hashCode(), HiveDecimal.create("9.00").hashCode());
      Assert.assertEquals(HiveDecimal.create("0").hashCode(), HiveDecimal.create("0.00").hashCode());
  }

  @Test
  public void testException() {
    HiveDecimal dec = HiveDecimal.create("3.1415.926");
    Assert.assertNull(dec);
    dec = HiveDecimal.create("3abc43");
    Assert.assertNull(dec);
  }

  @Test
  public void testBinaryConversion() {
    Random r = new Random(2399);
    for (String decString : specialDecimalStrings) {
      doTestBinaryConversion(decString, r);
    }
  }

  private void doTestBinaryConversion(String num, Random r) {
    int scale = r.nextInt(HiveDecimal.MAX_SCALE);
    HiveDecimal dec = HiveDecimal.create(num);
    if (dec == null) {
      return;
    }
    byte[] d = dec.setScale(scale).unscaledValue().toByteArray();
    HiveDecimal roundedDec = dec.setScale(scale, HiveDecimal.ROUND_HALF_UP);
    Assert.assertEquals(roundedDec, HiveDecimal.create(new BigInteger(d), scale));
  }

//------------------------------------------------------------------------------------------------

  @Test
  public void testDecimalsWithOneOne() {
    doTestDecimalsWithPrecisionScale(decimal_1_1_txt, 1, 1);
  }

  @Test
  public void testDecimalsWithKv7Keys() {
    doTestDecimalsWithPrecisionScale(kv7_txt_keys, 38, 18);
  }

  public void doTestDecimalsWithPrecisionScale(String[] decStrings, int precision, int scale) {

    HiveDecimalV1 oldSum = HiveDecimalV1.create(0);
    HiveDecimalWritable sum = new HiveDecimalWritable(0);

    for (int i = 0; i < decStrings.length; i++) {

      String string = decStrings[i];

      HiveDecimalV1 oldDec = HiveDecimalV1.create(string);

      HiveDecimalV1 resultOldDec;
      if (oldDec == null) {
        resultOldDec = null;
      } else {
        resultOldDec = HiveDecimalV1.enforcePrecisionScale(oldDec, precision, scale);
      }

      HiveDecimal dec = HiveDecimal.create(string);

      if (oldDec == null) {
        Assert.assertTrue(dec == null);
        continue;
      }
      HiveDecimal resultDec = HiveDecimal.enforcePrecisionScale(dec, precision, scale);
      if (resultOldDec == null) {
        Assert.assertTrue(resultDec == null);
        continue;
      }

      Assert.assertEquals(resultOldDec.toString(), resultDec.toString());
      Assert.assertEquals(resultOldDec.toFormatString(scale), resultDec.toFormatString(scale));

      oldSum = oldSum.add(resultOldDec);
      sum.mutateAdd(resultDec);
    }

    Assert.assertEquals(oldSum.toString(), sum.toString());
  }

//------------------------------------------------------------------------------------------------

  @Test
  public void testDecimalsWithOneOneWritable() {
    doTestDecimalsWithPrecisionScaleWritable(decimal_1_1_txt, 1, 1);
  }

  @Test
  public void testDecimalsWithKv7KeysWritable() {
    doTestDecimalsWithPrecisionScaleWritable(kv7_txt_keys, 38, 18);
  }

  public void doTestDecimalsWithPrecisionScaleWritable(String[] decStrings, int precision, int scale) {

    HiveDecimalV1 oldSum = HiveDecimalV1.create(0);
    HiveDecimalWritable sum = new HiveDecimalWritable(0);

    for (int i = 0; i < decStrings.length; i++) {
      String string = decStrings[i];

      HiveDecimalV1 oldDec = HiveDecimalV1.create(string);
      HiveDecimalV1 resultOldDec;
      if (oldDec == null) {
        resultOldDec = null;
      } else {
        resultOldDec = HiveDecimalV1.enforcePrecisionScale(oldDec, precision, scale);
      }

      HiveDecimalWritable decWritable = new HiveDecimalWritable(string);
      if (oldDec == null) {
        Assert.assertTrue(!decWritable.isSet());
        continue;
      }
      decWritable.mutateEnforcePrecisionScale(precision, scale);;
      if (resultOldDec == null) {
        Assert.assertTrue(!decWritable.isSet());
        continue;
      }

      Assert.assertEquals(resultOldDec.toString(), decWritable.toString());
      Assert.assertEquals(resultOldDec.toFormatString(scale), decWritable.toFormatString(scale));

      oldSum = oldSum.add(resultOldDec);
      sum.mutateAdd(decWritable);
    }

    Assert.assertEquals(oldSum.toString(), sum.toString());
  }

  //------------------------------------------------------------------------------------------------

  @Test
  public void testSort() {
    doTestSort(decimal_1_1_txt);
  }

  @Test
  public void testSortSpecial() {
    doTestSort(specialDecimalStrings);
  }

  @Test
  public void testSortRandom() {
    Random r = new Random(14434);
    for (BigDecimalFlavor bigDecimalFlavor : BigDecimalFlavor.values()) {
      doTestSortRandom(r, standardAlphabet, bigDecimalFlavor);
    }
    for (BigDecimalFlavor bigDecimalFlavor : BigDecimalFlavor.values()) {
      for (String sparseAlphabet : sparseAlphabets) {
        doTestSortRandom(r, sparseAlphabet, bigDecimalFlavor);
      }
    }
  }

  public void doTestSortRandom(Random r, String digitAlphabet, BigDecimalFlavor bigDecimalFlavor) {
    String[] randomStrings = new String[POUND_FACTOR];

    for (int i = 0; i < POUND_FACTOR; i++) {
      BigDecimal bigDecimal = randHiveBigDecimal(r, digitAlphabet, bigDecimalFlavor);

      randomStrings[i] = bigDecimal.toString();
    }

    doTestSort(randomStrings);
  }

  public void doTestSort(String[] decStrings) {

    HiveDecimalV1[] oldDecSortArray = new HiveDecimalV1[decStrings.length];
    HiveDecimal[] decSortArray = new HiveDecimal[decStrings.length];

    int count = 0;
    for (int i = 0; i < decStrings.length; i++) {
      String string = decStrings[i];

      HiveDecimalV1 oldDec = HiveDecimalV1.create(string);
      if (oldDec == null) {
        continue;
      }
      if (isTenPowerBug(oldDec.toString())) {
        continue;
      }
      oldDecSortArray[count] = oldDec;

      HiveDecimal dec = HiveDecimal.create(string);
      if (dec == null) {
        Assert.fail();
      }
      decSortArray[count] = dec;
      count++;
    }

    oldDecSortArray = Arrays.copyOf(oldDecSortArray,  count);
    decSortArray = Arrays.copyOf(decSortArray, count);

    Arrays.sort(oldDecSortArray);
    Arrays.sort(decSortArray);

    for (int i = 0; i < count; i++) {
      String oldDecString = oldDecSortArray[i].toString();
      String decString = decSortArray[i].toString();

      if (!oldDecString.equals(decString)) {
        Assert.fail();
      }
    }
  }

  //------------------------------------------------------------------------------------------------

  @Test
  public void testRandomCreateFromBigDecimal() {
    Random r = new Random(14434);
    for (BigDecimalFlavor bigDecimalFlavor : BigDecimalFlavor.values()) {
      doTestRandomCreateFromBigDecimal(r, standardAlphabet, bigDecimalFlavor);
    }
    for (BigDecimalFlavor bigDecimalFlavor : BigDecimalFlavor.values()) {
      for (String sparseAlphabet : sparseAlphabets) {
        doTestRandomCreateFromBigDecimal(r, sparseAlphabet, bigDecimalFlavor);
      }
    }
  }

  private void doTestRandomCreateFromBigDecimal(Random r, String digitAlphabet, BigDecimalFlavor bigDecimalFlavor) {

    for (int i = 0; i < POUND_FACTOR; i++) {
      BigDecimal bigDecimal = randHiveBigDecimal(r, digitAlphabet, bigDecimalFlavor);

      doTestCreateFromBigDecimal(bigDecimal);
    }
  }

  @Test
  public void testCreateFromBigDecimalSpecial() {
    for (BigDecimal bigDecimal : specialBigDecimals) {
      doTestCreateFromBigDecimal(bigDecimal);
    }
  }

  private void doTestCreateFromBigDecimal(BigDecimal bigDecimal) {

    HiveDecimalV1 oldDec = HiveDecimalV1.create(bigDecimal);
    if (oldDec != null && isTenPowerBug(oldDec.toString())) {
      return;
    }
    HiveDecimal dec = HiveDecimal.create(bigDecimal);
    if (oldDec == null) {
      assertTrue(dec == null);
      return;
    }
    assertTrue(dec != null);
    dec.validate();

    Assert.assertEquals(oldDec.toString(), dec.toString());
  }

  //------------------------------------------------------------------------------------------------

  @Test
  public void testRandomCreateFromBigDecimalNoRound() {
    Random r = new Random(14434);
    for (BigDecimalFlavor bigDecimalFlavor : BigDecimalFlavor.values()) {
      doTestRandomCreateFromBigDecimalNoRound(r, standardAlphabet, bigDecimalFlavor);
    }
    for (BigDecimalFlavor bigDecimalFlavor : BigDecimalFlavor.values()) {
      for (String sparseAlphabet : sparseAlphabets) {
        doTestRandomCreateFromBigDecimalNoRound(r, sparseAlphabet, bigDecimalFlavor);
      }
    }
  }

  private void doTestRandomCreateFromBigDecimalNoRound(Random r, String digitAlphabet, BigDecimalFlavor bigDecimalFlavor) {

    for (int i = 0; i < POUND_FACTOR; i++) {
      BigDecimal bigDecimal = randHiveBigDecimal(r, digitAlphabet, bigDecimalFlavor);

      doTestCreateFromBigDecimalNoRound(bigDecimal);
    }
  }

  @Test
  public void testCreateFromBigDecimalNoRoundSpecial() {
    for (BigDecimal bigDecimal : specialBigDecimals) {
      doTestCreateFromBigDecimalNoRound(bigDecimal);
    }
  }

  private void doTestCreateFromBigDecimalNoRound(BigDecimal bigDecimal) {

    HiveDecimalV1 oldDec = HiveDecimalV1.create(bigDecimal, /* allowRounding */ false);
    HiveDecimal dec = HiveDecimal.create(bigDecimal, /* allowRounding */ false);
    if (oldDec == null) {
      assertTrue(dec == null);
      return;
    }
    if (dec == null) {
      Assert.fail();
    }
    dec.validate();

    Assert.assertEquals(oldDec.toString(), dec.toString());

  }

  //------------------------------------------------------------------------------------------------

  @Test
  public void testCreateFromBigDecimalNegativeScaleSpecial() {
    Random r = new Random(223965);
    for (BigDecimal bigDecimal : specialBigDecimals) {
      int negativeScale = -(0 + r.nextInt(38 + 1));
      bigDecimal = bigDecimal.setScale(negativeScale, BigDecimal.ROUND_HALF_UP);
      doTestCreateFromBigDecimalNegativeScale(bigDecimal);
    }
  }

  private void doTestCreateFromBigDecimalNegativeScale(BigDecimal bigDecimal) {

    HiveDecimalV1 oldDec = HiveDecimalV1.create(bigDecimal);
    HiveDecimal dec = HiveDecimal.create(bigDecimal);
    if (oldDec == null) {
      assertTrue(dec == null);
      return;
    }
    assertTrue(dec != null);
    dec.validate();

    Assert.assertEquals(oldDec.toString(), dec.toString());
  }

  //------------------------------------------------------------------------------------------------

  @Test
  public void testRandomCreateFromBigInteger() {
    doTestRandomCreateFromBigInteger(standardAlphabet);
  }

  @Test
  public void testRandomCreateFromBigIntegerSparse() {
    for (String digitAlphabet : sparseAlphabets) {
      doTestRandomCreateFromBigInteger(digitAlphabet);
    }
  }

  private void doTestRandomCreateFromBigInteger(String digitAlphabet) {

    Random r = new Random(11241);
    for (int i = 0; i < POUND_FACTOR; i++) {
      BigInteger bigInteger = randHiveBigInteger(r, digitAlphabet);

      doTestCreateFromBigInteger(bigInteger);
    }
  }

  @Test
  public void testCreateFromBigIntegerSpecial() {
    for (BigDecimal bigDecimal : specialBigDecimals) {
      doTestCreateFromBigInteger(bigDecimal.unscaledValue());
    }
  }

  private void doTestCreateFromBigInteger(BigInteger bigInteger) {

    HiveDecimalV1 oldDec = HiveDecimalV1.create(bigInteger);
    HiveDecimal dec = HiveDecimal.create(bigInteger);
    if (oldDec == null) {
      assertTrue(dec == null);
      return;
    }
    assertTrue(dec != null);
    dec.validate();

    Assert.assertEquals(oldDec.toString(), dec.toString());

  }

  //------------------------------------------------------------------------------------------------

  @Test
  public void testRandomCreateFromBigIntegerScale() {
    doTestRandomCreateFromBigIntegerScale(standardAlphabet, false);
  }

  @Test
  public void testRandomCreateFromBigIntegerScaleFractionsOnly() {
    doTestRandomCreateFromBigIntegerScale(standardAlphabet, true);
  }

  @Test
  public void testRandomCreateFromBigIntegerScaleSparse() {
    for (String digitAlphabet : sparseAlphabets) {
      doTestRandomCreateFromBigIntegerScale(digitAlphabet, false);
    }
  }

  private void doTestRandomCreateFromBigIntegerScale(String digitAlphabet, boolean fractionsOnly) {

    Random r = new Random(4448);
    for (int i = 0; i < POUND_FACTOR; i++) {
      BigInteger bigInteger = randHiveBigInteger(r, digitAlphabet);

      int scale;
      if (fractionsOnly) {
        scale = 1 + r.nextInt(38);
      } else {
        scale = 0 + r.nextInt(38 + 1);
      }

      doTestCreateFromBigIntegerScale(bigInteger, scale);
    }
  }

  @Test
  public void testCreateFromBigIntegerScaleSpecial() {
    for (BigDecimal bigDecimal : specialBigDecimals) {
      doTestCreateFromBigIntegerScale(bigDecimal.unscaledValue(), bigDecimal.scale());
    }
  }

  private void doTestCreateFromBigIntegerScale(BigInteger bigInteger, int scale) {

    HiveDecimalV1 oldDec = HiveDecimalV1.create(bigInteger, scale);
    HiveDecimal dec = HiveDecimal.create(bigInteger, scale);
    if (oldDec == null) {
      assertTrue(dec == null);
      return;
    }
    assertTrue(dec != null);
    dec.validate();

    Assert.assertEquals(oldDec.toString(), dec.toString());

  }

  //------------------------------------------------------------------------------------------------

  @Test
  public void testRandomSetFromDouble() {
    Random r = new Random(14434);
    for (BigDecimalFlavor bigDecimalFlavor : BigDecimalFlavor.values()) {
      doTestRandomSetFromDouble(r, standardAlphabet, bigDecimalFlavor);
    }
    for (BigDecimalFlavor bigDecimalFlavor : BigDecimalFlavor.values()) {
      for (String sparseAlphabet : sparseAlphabets) {
        doTestRandomSetFromDouble(r, sparseAlphabet, bigDecimalFlavor);
      }
    }
  }

  private void doTestRandomSetFromDouble(Random r, String digitAlphabet, BigDecimalFlavor bigDecimalFlavor) {

    for (int i = 0; i < POUND_FACTOR; i++) {
      BigDecimal bigDecimal = randHiveBigDecimal(r, digitAlphabet, bigDecimalFlavor);

      doTestSetFromDouble(bigDecimal.doubleValue());
    }
  }

  private void doTestRandomSetFromDouble() {

    Random r = new Random(94762);
    for (int i = 0; i < POUND_FACTOR; i++) {
      double randomDouble = r.nextDouble();
 
      doTestSetFromDouble(randomDouble);
    }
  }

  @Test
  public void testSetFromDoubleSpecial() {

    for (String specialString : specialDecimalStrings) {
      double specialDouble = Double.valueOf(specialString);
      doTestSetFromDouble(specialDouble);
    }
  }

  private void doTestSetFromDouble(double doubleValue) {

    HiveDecimalV1 oldDec = HiveDecimalV1.create(Double.toString(doubleValue));
    if (oldDec == null) {
      return;
    }
    HiveDecimal dec = HiveDecimal.create(doubleValue);
    if (dec == null) {
      Assert.fail();
    }
    dec.validate();
    if (!oldDec.toString().equals(dec.toString())) {
      BigDecimal bigDecimal = new BigDecimal(dec.toString());
      for (int i = 16; i < 18;i++) {
        BigDecimal trial = bigDecimal.setScale(i, HiveDecimal.ROUND_HALF_UP);
      }
    }
  }

  //------------------------------------------------------------------------------------------------

  @Test
  public void testRandomCreateFromString() {
    Random r = new Random(1221);
    for (BigDecimalFlavor bigDecimalFlavor : BigDecimalFlavor.values()) {
      doTestRandomCreateFromString(r, standardAlphabet, bigDecimalFlavor);
    }
    for (BigDecimalFlavor bigDecimalFlavor : BigDecimalFlavor.values()) {
      for (String sparseAlphabet : sparseAlphabets) {
        doTestRandomCreateFromString(r, sparseAlphabet, bigDecimalFlavor);
      }
    }
  }

  private void doTestRandomCreateFromString(Random r, String digitAlphabet, BigDecimalFlavor bigDecimalFlavor) {

    for (int i = 0; i < POUND_FACTOR; i++) {
      BigDecimal bigDecimal = randHiveBigDecimal(r, digitAlphabet, bigDecimalFlavor);

      doTestCreateFromString(bigDecimal);
    }
  }

  @Test
  public void testCreateFromStringSpecial() {
    for (BigDecimal bigDecimal : specialBigDecimals) {
      doTestCreateFromString(bigDecimal);
    }
  }

  private void doTestCreateFromString(BigDecimal bigDecimal) {

    String decString = bigDecimal.toPlainString();

    HiveDecimalV1 oldDec = HiveDecimalV1.create(decString);
    if (oldDec != null && isTenPowerBug(oldDec.toString())) {
      return;
    }
    HiveDecimal dec = HiveDecimal.create(decString);
    if (oldDec == null) {
      assertTrue(dec == null);
      return;
    }
    assertTrue(dec != null);
    dec.validate();

    Assert.assertEquals(oldDec.toString(), dec.toString());
  }

  //------------------------------------------------------------------------------------------------

  @Test
  public void testRandomCreateFromStringPadded() {
    Random r = new Random(9774);
    for (BigDecimalFlavor bigDecimalFlavor : BigDecimalFlavor.values()) {
      doTestRandomCreateFromStringPadded(r, standardAlphabet, bigDecimalFlavor);
    }
    for (BigDecimalFlavor bigDecimalFlavor : BigDecimalFlavor.values()) {
      for (String sparseAlphabet : sparseAlphabets) {
        doTestRandomCreateFromStringPadded(r, sparseAlphabet, bigDecimalFlavor);
      }
    }
  }

  private void doTestRandomCreateFromStringPadded(Random r, String digitAlphabet, BigDecimalFlavor bigDecimalFlavor) {

    for (int i = 0; i < POUND_FACTOR; i++) {
      BigDecimal bigDecimal = randHiveBigDecimal(r, digitAlphabet, bigDecimalFlavor);

      doTestCreateFromStringPadded(bigDecimal);
    }
  }

  @Test
  public void testCreateFromStringPaddedSpecial() {
    for (BigDecimal bigDecimal : specialBigDecimals) {
      doTestCreateFromStringPadded(bigDecimal);
    }
  }

  private void doTestCreateFromStringPadded(BigDecimal bigDecimal) {

    String decString = bigDecimal.toPlainString();
    String decString1 = " " + decString;
    String decString2 = decString + " ";
    String decString3 = " " + decString + " ";
    String decString4 = "  " + decString;
    String decString5 = decString + "  ";
    String decString6 = "  " + decString + "  ";

    HiveDecimalV1 oldDec;
    HiveDecimal dec;

    oldDec = HiveDecimalV1.create(decString);
    if (oldDec != null && isTenPowerBug(oldDec.toString())) {
      return;
    }

    dec = HiveDecimal.create(decString1, true);
    if (oldDec == null) {
      assertTrue(dec == null);
    } else {
      assertTrue(dec != null);
      dec.validate();

      Assert.assertEquals(oldDec.toString(), dec.toString());
    }

    dec = HiveDecimal.create(decString2, true);
    if (oldDec == null) {
      assertTrue(dec == null);
    } else {
      assertTrue(dec != null);
      dec.validate();

      Assert.assertEquals(oldDec.toString(), dec.toString());
    }

    dec = HiveDecimal.create(decString3, true);
    if (oldDec == null) {
      assertTrue(dec == null);
    } else {
      assertTrue(dec != null);
      dec.validate();

      Assert.assertEquals(oldDec.toString(), dec.toString());
    }

    dec = HiveDecimal.create(decString4, true);
    if (oldDec == null) {
      assertTrue(dec == null);
    } else {
      assertTrue(dec != null);
      dec.validate();

      Assert.assertEquals(oldDec.toString(), dec.toString());
    }

    dec = HiveDecimal.create(decString5, true);
    if (oldDec == null) {
      assertTrue(dec == null);
    } else {
      assertTrue(dec != null);
      dec.validate();

      Assert.assertEquals(oldDec.toString(), dec.toString());
    }

    dec = HiveDecimal.create(decString6, true);
    if (oldDec == null) {
      assertTrue(dec == null);
    } else {
      assertTrue(dec != null);
      dec.validate();

      Assert.assertEquals(oldDec.toString(), dec.toString());
    }
  }

  //------------------------------------------------------------------------------------------------

  @Test
  public void testRandomCreateFromStringExponent() {
    Random r = new Random(297111);
    for (BigDecimalFlavor bigDecimalFlavor : BigDecimalFlavor.values()) {
      doTestRandomCreateFromStringPadded(r, standardAlphabet, bigDecimalFlavor);
    }
    for (BigDecimalFlavor bigDecimalFlavor : BigDecimalFlavor.values()) {
      for (String sparseAlphabet : sparseAlphabets) {
        doTestRandomCreateFromStringPadded(r, sparseAlphabet, bigDecimalFlavor);
      }
    }
  }

  private void doTestRandomCreateFromStringExponent(Random r, String digitAlphabet, BigDecimalFlavor bigDecimalFlavor) {

    for (int i = 0; i < POUND_FACTOR; i++) {
      BigDecimal bigDecimal = randHiveBigDecimal(r, digitAlphabet, bigDecimalFlavor);

      doTestCreateFromStringExponent(bigDecimal);
    }
  }

  @Test
  public void testCreateFromStringExponentSpecial() {
    for (BigDecimal bigDecimal : specialBigDecimals) {
      doTestCreateFromStringExponent(bigDecimal);
    }
  }

  private void doTestCreateFromStringExponent(BigDecimal bigDecimal) {

    // Use toString which will have exponents instead of toPlainString.
    String decString = bigDecimal.toString();

    HiveDecimalV1 oldDec = HiveDecimalV1.create(decString);
    HiveDecimal dec = HiveDecimal.create(decString);
    if (oldDec == null) {
      assertTrue(dec == null);
      return;
    }
    assertTrue(dec != null);
    dec.validate();

    Assert.assertEquals(oldDec.toString(), dec.toString());
  }

  //------------------------------------------------------------------------------------------------

  @Test
  public void testRandomLongValue() {
    Random r = new Random(73293);
    for (BigDecimalFlavor bigDecimalFlavor : BigDecimalFlavor.values()) {
      doTestRandomLongValue(r, standardAlphabet, bigDecimalFlavor);
    }
    for (BigDecimalFlavor bigDecimalFlavor : BigDecimalFlavor.values()) {
      for (String sparseAlphabet : sparseAlphabets) {
        doTestRandomLongValue(r, sparseAlphabet, bigDecimalFlavor);
      }
    }
  }

  private void doTestRandomLongValue(Random r, String digitAlphabet, BigDecimalFlavor bigDecimalFlavor) {

    for (int i = 0; i < POUND_FACTOR; i++) {
      BigDecimal bigDecimal = randHiveBigDecimal(r, digitAlphabet, bigDecimalFlavor);

      doTestLongValue(bigDecimal);
    }
  }

  @Test
  public void testLongValueSpecial() {
    for (BigDecimal bigDecimal : specialBigDecimals) {
      doTestLongValue(bigDecimal);
    }
  }

  private void doTestLongValue(BigDecimal bigDecimal) {

    HiveDecimalV1 oldDec = HiveDecimalV1.create(bigDecimal);
    if (oldDec != null && isTenPowerBug(oldDec.toString())) {
      return;
    }
    HiveDecimal dec = HiveDecimal.create(bigDecimal);
    if (oldDec == null) {
      assertTrue(dec == null);
      return;
    }
    assertTrue(dec != null);
    dec.validate();

    BigDecimal bigDecimalOldDec = oldDec.bigDecimalValue();
    BigDecimal bigDecimalDec = dec.bigDecimalValue();
    Assert.assertEquals(bigDecimalOldDec, bigDecimalDec);

    BigDecimal bigDecimalFloor = bigDecimalDec.setScale(0, BigDecimal.ROUND_DOWN);
    long longValueBigDecimalFloor = bigDecimalFloor.longValue();
    boolean isLongExpected =
        bigDecimalFloor.equals(bigDecimalDec.valueOf(longValueBigDecimalFloor));

    boolean decIsLong = dec.isLong();
    long oldDecLong = oldDec.longValue();
    long decLong = dec.longValue();
    if (isLongExpected != decIsLong) {
      Assert.fail();
    }

    if (decIsLong) {
      if (oldDecLong != decLong) {
        Assert.fail();
      }
    }
  }

  //------------------------------------------------------------------------------------------------

  @Test
  public void testRandomIntValue() {
    Random r = new Random(98333);
    for (BigDecimalFlavor bigDecimalFlavor : BigDecimalFlavor.values()) {
      doTestRandomIntValue(r, standardAlphabet, bigDecimalFlavor);
    }
    for (BigDecimalFlavor bigDecimalFlavor : BigDecimalFlavor.values()) {
      for (String sparseAlphabet : sparseAlphabets) {
        doTestRandomIntValue(r, sparseAlphabet, bigDecimalFlavor);
      }
    }
  }

  private void doTestRandomIntValue(Random r, String digitAlphabet, BigDecimalFlavor bigDecimalFlavor) {

    for (int i = 0; i < POUND_FACTOR; i++) {
      BigDecimal bigDecimal = randHiveBigDecimal(r, digitAlphabet, bigDecimalFlavor);

      doTestIntValue(bigDecimal);
    }
  }

  @Test
  public void testIntValueSpecial() {
    for (BigDecimal bigDecimal : specialBigDecimals) {
      doTestIntValue(bigDecimal);
    }
  }

  private void doTestIntValue(BigDecimal bigDecimal) {

    HiveDecimalV1 oldDec = HiveDecimalV1.create(bigDecimal);
    if (oldDec != null && isTenPowerBug(oldDec.toString())) {
      return;
    }
    HiveDecimal dec = HiveDecimal.create(bigDecimal);
    if (oldDec == null) {
      assertTrue(dec == null);
      return;
    }
    assertTrue(dec != null);
    dec.validate();

    BigDecimal bigDecimalOldDec = oldDec.bigDecimalValue();
    BigDecimal bigDecimalDec = dec.bigDecimalValue();
    Assert.assertEquals(bigDecimalOldDec, bigDecimalDec);

    BigDecimal bigDecimalFloor = bigDecimalDec.setScale(0, BigDecimal.ROUND_DOWN);
    int intValueBigDecimalFloor = bigDecimalFloor.intValue();
    boolean isIntExpected =
        bigDecimalFloor.equals(bigDecimalDec.valueOf(intValueBigDecimalFloor));

    boolean decIsInt = dec.isInt();
    int oldDecInt = oldDec.intValue();
    int decInt = dec.intValue();
    if (isIntExpected != decIsInt) {
      Assert.fail();
    }

    if (decIsInt) {
      if (oldDecInt != decInt) {
        Assert.fail();
      }
    }
  }

  //------------------------------------------------------------------------------------------------

  @Test
  public void testRandomShortValue() {
    Random r = new Random(15);
    for (BigDecimalFlavor bigDecimalFlavor : BigDecimalFlavor.values()) {
      doTestRandomShortValue(r, standardAlphabet, bigDecimalFlavor);
    }
    for (BigDecimalFlavor bigDecimalFlavor : BigDecimalFlavor.values()) {
      for (String sparseAlphabet : sparseAlphabets) {
        doTestRandomShortValue(r, sparseAlphabet, bigDecimalFlavor);
      }
    }
  }

  private void doTestRandomShortValue(Random r, String digitAlphabet, BigDecimalFlavor bigDecimalFlavor) {
    for (int i = 0; i < POUND_FACTOR; i++) {
      BigDecimal bigDecimal = randHiveBigDecimal(r, digitAlphabet, bigDecimalFlavor);

      doTestShortValue(bigDecimal);
    }
  }

  @Test
  public void testShortValueSpecial() {
    for (BigDecimal bigDecimal : specialBigDecimals) {
      doTestShortValue(bigDecimal);
    }
  }

  private void doTestShortValue(BigDecimal bigDecimal) {

    HiveDecimalV1 oldDec = HiveDecimalV1.create(bigDecimal);
    if (oldDec != null && isTenPowerBug(oldDec.toString())) {
      return;
    }
    HiveDecimal dec = HiveDecimal.create(bigDecimal);
    if (oldDec == null) {
      assertTrue(dec == null);
      return;
    }
    assertTrue(dec != null);
    dec.validate();

    BigDecimal bigDecimalOldDec = oldDec.bigDecimalValue();
    BigDecimal bigDecimalDec = dec.bigDecimalValue();
    Assert.assertEquals(bigDecimalOldDec, bigDecimalDec);

    BigDecimal bigDecimalFloor = bigDecimalDec.setScale(0, BigDecimal.ROUND_DOWN);
    short shortValueBigDecimalFloor = bigDecimalFloor.shortValue();
    boolean isShortExpected =
        bigDecimalFloor.equals(bigDecimalDec.valueOf(shortValueBigDecimalFloor));

    boolean decIsShort = dec.isShort();
    short oldDecShort = oldDec.shortValue();
    short decShort = dec.shortValue();
    if (isShortExpected != decIsShort) {
      Assert.fail();
    }

    if (decIsShort) {
      if (oldDecShort != decShort) {
        Assert.fail();
      }
    }
  }

  //------------------------------------------------------------------------------------------------

  @Test
  public void testRandomByteValue() {
    Random r = new Random(9292);
    for (BigDecimalFlavor bigDecimalFlavor : BigDecimalFlavor.values()) {
      doTestRandomByteValue(r, standardAlphabet, bigDecimalFlavor);
    }
    for (BigDecimalFlavor bigDecimalFlavor : BigDecimalFlavor.values()) {
      for (String sparseAlphabet : sparseAlphabets) {
        doTestRandomByteValue(r, sparseAlphabet, bigDecimalFlavor);
      }
    }
  }

  private void doTestRandomByteValue(Random r, String digitAlphabet, BigDecimalFlavor bigDecimalFlavor) {

    for (int i = 0; i < POUND_FACTOR; i++) {
      BigDecimal bigDecimal = randHiveBigDecimal(r, digitAlphabet, bigDecimalFlavor);

      doTestByteValue(bigDecimal);
    }
  }

  @Test
  public void testByteValueSpecial() {
    for (BigDecimal bigDecimal : specialBigDecimals) {
      doTestByteValue(bigDecimal);
    }
  }

  private void doTestByteValue(BigDecimal bigDecimal) {

    HiveDecimalV1 oldDec = HiveDecimalV1.create(bigDecimal);
    if (oldDec != null && isTenPowerBug(oldDec.toString())) {
      return;
    }
    HiveDecimal dec = HiveDecimal.create(bigDecimal);
    if (oldDec == null) {
      assertTrue(dec == null);
      return;
    }
    assertTrue(dec != null);
    dec.validate();

    BigDecimal bigDecimalOldDec = oldDec.bigDecimalValue();
    BigDecimal bigDecimalDec = dec.bigDecimalValue();
    Assert.assertEquals(bigDecimalOldDec, bigDecimalDec);

    BigDecimal bigDecimalFloor = bigDecimalDec.setScale(0, BigDecimal.ROUND_DOWN);
    byte byteValueBigDecimalFloor = bigDecimalFloor.byteValue();
    boolean isByteExpected =
        bigDecimalFloor.equals(bigDecimalDec.valueOf(byteValueBigDecimalFloor));

    boolean decIsByte = dec.isByte();
    byte oldDecByte = oldDec.byteValue();
    byte decByte = dec.byteValue();
    if (isByteExpected != decIsByte) {
      Assert.fail();
    }

    if (decIsByte) {
      if (oldDecByte != decByte) {
        Assert.fail();
      }
    }
  }

  //------------------------------------------------------------------------------------------------

  @Test
  public void testRandomTimestamp() {
    Random r = new Random(5476);
    for (BigDecimalFlavor bigDecimalFlavor : BigDecimalFlavor.values()) {
      doTestRandomTimestamp(r, standardAlphabet, bigDecimalFlavor);
    }
    for (BigDecimalFlavor bigDecimalFlavor : BigDecimalFlavor.values()) {
      for (String sparseAlphabet : sparseAlphabets) {
        doTestRandomTimestamp(r, sparseAlphabet, bigDecimalFlavor);
      }
    }
  }

  private void doTestRandomTimestamp(Random r, String digitAlphabet, BigDecimalFlavor bigDecimalFlavor) {
    for (int i = 0; i < POUND_FACTOR; i++) {
      BigDecimal bigDecimal = randHiveBigDecimal(r, digitAlphabet, bigDecimalFlavor);

      doTestTimestamp(bigDecimal);
    }
  }

  @Test
  public void testTimestampSpecial() {
    for (BigDecimal bigDecimal : specialBigDecimals) {
      doTestTimestamp(bigDecimal);
    }
  }

  private void doTestTimestamp(BigDecimal bigDecimal) {

    HiveDecimalV1 oldDec = HiveDecimalV1.create(bigDecimal);
    if (oldDec != null && isTenPowerBug(oldDec.toString())) {
      return;
    }
    HiveDecimal dec = HiveDecimal.create(bigDecimal);
    if (oldDec == null) {
      assertTrue(dec == null);
      return;
    }
    assertTrue(dec != null);
    dec.validate();

    Timestamp timestampOldDec = TimestampUtils.decimalToTimestamp(oldDec);
    Timestamp timestampDec = TimestampUtils.decimalToTimestamp(dec);
    if (timestampOldDec == null) {
      Assert.assertTrue(timestampDec == null);
      return;
    }
    if (timestampDec == null) {
      return;
    }
    Assert.assertEquals(timestampOldDec, timestampDec);
  }

  //------------------------------------------------------------------------------------------------

  @Test
  public void testRandomBigIntegerBytes() {
    Random r = new Random(1050);
    for (BigDecimalFlavor bigDecimalFlavor : BigDecimalFlavor.values()) {
      doTestRandomBigIntegerBytes(r, standardAlphabet, bigDecimalFlavor);
    }
    for (BigDecimalFlavor bigDecimalFlavor : BigDecimalFlavor.values()) {
      for (String sparseAlphabet : sparseAlphabets) {
        doTestRandomBigIntegerBytes(r, sparseAlphabet, bigDecimalFlavor);
      }
    }
  }

  private void doTestRandomBigIntegerBytes(Random r, String digitAlphabet, BigDecimalFlavor bigDecimalFlavor) {
    for (int i = 0; i < POUND_FACTOR; i++) {
      BigDecimal bigDecimal = randHiveBigDecimal(r, digitAlphabet, bigDecimalFlavor);

      doTestBigIntegerBytes(bigDecimal);
    }
  }

  @Test
  public void testBigIntegerBytesSpecial() {
    Random r = new Random(1050);
    for (BigDecimal bigDecimal : specialBigDecimals) {
      int negativeScale = -(0 + r.nextInt(38 + 1));
      bigDecimal = bigDecimal.setScale(negativeScale, BigDecimal.ROUND_HALF_UP);
      doTestBigIntegerBytes(bigDecimal);
    }
  }

  private void doTestBigIntegerBytes(BigDecimal bigDecimal) {

    HiveDecimalV1 oldDec = HiveDecimalV1.create(bigDecimal);
    if (oldDec != null && isTenPowerBug(oldDec.toString())) {
      return;
    }
    if (oldDec == null) {
      return;
    }
    HiveDecimal dec = HiveDecimal.create(bigDecimal);
    assertTrue(dec != null);
    dec.validate();

    //---------------------------------------------------
    BigInteger oldBigInteger = oldDec.unscaledValue();
    int oldScale = oldDec.scale();
    //---------------------------------------------------

    BigInteger bigInteger = dec.unscaledValue();
    int scale = dec.scale();

    long[] scratchLongs = new long[HiveDecimal.SCRATCH_LONGS_LEN];
    byte[] scratchBuffer = new byte[HiveDecimal.SCRATCH_BUFFER_LEN_BIG_INTEGER_BYTES];

    int which = 0;
    try {
      which = 1;
      int byteLength = dec.bigIntegerBytes(scratchLongs, scratchBuffer);
      byte[] bytes = null;
      if (byteLength == 0) {
        Assert.fail();
      } else {
        bytes = Arrays.copyOf(scratchBuffer, byteLength);
      }

      which = 2;
      byte[] bytesExpected = bigInteger.toByteArray();
      String bytesExpectedString = displayBytes(bytesExpected, 0, bytesExpected.length);

      if (!StringExpr.equal(bytes, 0, bytes.length, bytesExpected, 0, bytesExpected.length)) {
        fail();
      }

      which = 3;
      HiveDecimal createFromBigIntegerBytesDec =
          HiveDecimal.createFromBigIntegerBytesAndScale(
              bytes, 0, bytes.length, scale);
      if (!createFromBigIntegerBytesDec.equals(dec)) {
        fail();
      }

    } catch (Exception e) {
      fail();
    }
  }

  //------------------------------------------------------------------------------------------------

  @Test
  public void testRandomToFormatString() {
    Random r = new Random(1051);
    for (BigDecimalFlavor bigDecimalFlavor : BigDecimalFlavor.values()) {
      doTestRandomToFormatString(r, standardAlphabet, bigDecimalFlavor);
    }
    for (BigDecimalFlavor bigDecimalFlavor : BigDecimalFlavor.values()) {
      for (String sparseAlphabet : sparseAlphabets) {
        doTestRandomToFormatString(r, sparseAlphabet, bigDecimalFlavor);
      }
    }
  }

  private void doTestRandomToFormatString(Random r, String digitAlphabet, BigDecimalFlavor bigDecimalFlavor) {
    for (int i = 0; i < POUND_FACTOR; i++) {
      BigDecimal bigDecimal = randHiveBigDecimal(r, digitAlphabet, bigDecimalFlavor);

      doTestToFormatString(r, bigDecimal);
    }
  }

  @Test
  public void testToFormatStringSpecial() {
    Random r = new Random(1050);
    for (BigDecimal bigDecimal : specialBigDecimals) {
      doTestToFormatString(r, bigDecimal);
    }
  }

  private void doTestToFormatString(Random r, BigDecimal bigDecimal) {

    HiveDecimalV1 oldDec = HiveDecimalV1.create(bigDecimal);
    if (oldDec != null && isTenPowerBug(oldDec.toString())) {
      return;
    }
    HiveDecimal dec;
    if (oldDec == null) {
      dec = HiveDecimal.create(bigDecimal);
      if (dec != null) {
        Assert.fail();
      }
      return;
    } else {
      dec = HiveDecimal.create(bigDecimal);
      if (dec == null) {
        if (isTenPowerBug(oldDec.toString())) {
          return;
        }
        Assert.fail();
      }
    }
    dec.validate();

    // UNDONE: Does this random range need to go as high as 38?
    int formatScale = 0 + r.nextInt(38);

    String oldDecFormatString = oldDec.toFormatString(formatScale);
    String decFormatString;
    if (oldDecFormatString == null) {
      decFormatString = dec.toFormatString(formatScale);
      if (decFormatString != null) {
        Assert.fail();
      }
      return;
    } else {
      decFormatString = dec.toFormatString(formatScale);
      if (decFormatString == null) {
        if (isTenPowerBug(oldDecFormatString)) {
          return;
        }
        Assert.fail();
      }
    }

    if (!oldDecFormatString.equals(decFormatString)) {
      fail();
    }
  }

  //------------------------------------------------------------------------------------------------

  @Test
  public void testRandomScaleByPowerOfTen() {
    Random r = new Random(1052);
    for (BigDecimalFlavor bigDecimalFlavor : BigDecimalFlavor.values()) {
      doTestRandomScaleByPowerOfTen(r, standardAlphabet, bigDecimalFlavor);
    }
    for (BigDecimalFlavor bigDecimalFlavor : BigDecimalFlavor.values()) {
      for (String sparseAlphabet : sparseAlphabets) {
        doTestRandomScaleByPowerOfTen(r, sparseAlphabet, bigDecimalFlavor);
      }
    }
  }

  private void doTestRandomScaleByPowerOfTen(Random r, String digitAlphabet, BigDecimalFlavor bigDecimalFlavor) {
    for (int i = 0; i < POUND_FACTOR; i++) {
      BigDecimal bigDecimal = randHiveBigDecimal(r, digitAlphabet, bigDecimalFlavor);

      doTestScaleByPowerOfTen(r, bigDecimal);
    }
  }

  @Test
  public void testScaleByPowerOfTenSpecial() {
    Random r = new Random(1050);
    for (BigDecimal bigDecimal : specialBigDecimals) {
      doTestScaleByPowerOfTen(r, bigDecimal);
    }
  }

  private void doTestScaleByPowerOfTen(Random r, BigDecimal bigDecimal) {

    HiveDecimalV1 oldDec = HiveDecimalV1.create(bigDecimal);
    if (oldDec != null && isTenPowerBug(oldDec.toString())) {
      return;
    }
    HiveDecimal dec = HiveDecimal.create(bigDecimal);
    if (oldDec == null) {
      assertTrue(dec == null);
      return;
    }
    assertTrue(dec != null);
    dec.validate();

    HiveDecimalV1 oldPowerDec;
    HiveDecimal powerDec;

    for (int power = -(2 * HiveDecimal.MAX_SCALE + 1);
         power <= 2 * HiveDecimal.MAX_SCALE + 1;
         power++) {

      oldPowerDec = oldDec.scaleByPowerOfTen(power);
      boolean isEqual;
      if (oldPowerDec == null) {
        powerDec = dec.scaleByPowerOfTen(power);
        if (powerDec != null) {
          Assert.fail();
        }
        return;
      } else {
        String oldPowerDecString = oldPowerDec.toString();
        powerDec = dec.scaleByPowerOfTen(power);
        if (powerDec == null) {
          if (isTenPowerBug(oldPowerDec.toString())) {
            return;
          }
          Assert.fail();
          continue;
        }
        powerDec.validate();
        String powerDecString = powerDec.toString();
        isEqual = oldPowerDecString.equals(powerDecString);
        if (!isEqual) {
          if (oldPowerDecString.equals("0.00000000000000000000000000000000000001") ||
              oldPowerDecString.equals("-0.00000000000000000000000000000000000001")) {
            continue;
          }
          Assert.fail();
        }
      }
    }
  }

  //------------------------------------------------------------------------------------------------

  @Test
  public void testRandomWriteReadFields() throws Exception {
    Random r = new Random(1052);
    for (BigDecimalFlavor bigDecimalFlavor : BigDecimalFlavor.values()) {
      doTestRandomWriteReadFields(r, standardAlphabet, bigDecimalFlavor);
    }
    for (BigDecimalFlavor bigDecimalFlavor : BigDecimalFlavor.values()) {
      for (String sparseAlphabet : sparseAlphabets) {
        doTestRandomWriteReadFields(r, sparseAlphabet, bigDecimalFlavor);
      }
    }
  }

  private void doTestRandomWriteReadFields(Random r, String digitAlphabet, BigDecimalFlavor bigDecimalFlavor) throws Exception {
    for (int i = 0; i < POUND_FACTOR; i++) {
      BigDecimal bigDecimal = randHiveBigDecimal(r, digitAlphabet, bigDecimalFlavor);

      doTestWriteReadFields(r, bigDecimal);
    }
  }

  @Test
  public void testWriteReadFieldsSpecial() throws Exception {
    Random r = new Random(1050);
    for (BigDecimal bigDecimal : specialBigDecimals) {
      doTestWriteReadFields(r, bigDecimal);
    }
  }

  private void doTestWriteReadFields(Random r, BigDecimal bigDecimal) throws IOException {

    HiveDecimalV1 oldDec = HiveDecimalV1.create(bigDecimal);
    if (oldDec != null && isTenPowerBug(oldDec.toString())) {
      return;
    }
    HiveDecimal dec = HiveDecimal.create(bigDecimal);
    if (oldDec == null) {
      assertTrue(dec == null);
      return;
    }
    assertTrue(dec != null);
    dec.validate();

    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    DataOutputStream out = new DataOutputStream(baos);

    HiveDecimalWritable decimalWritableOut = new HiveDecimalWritable(dec);
    decimalWritableOut.write(out);

    byte[] valueBytes = baos.toByteArray();

    ByteArrayInputStream bais = new ByteArrayInputStream(valueBytes);
    DataInputStream in = new DataInputStream(bais);
    HiveDecimalWritable decimalWritableIn = new HiveDecimalWritable();
    decimalWritableIn.readFields(in);

    Assert.assertEquals(dec, decimalWritableIn.getHiveDecimal());
  }

  //------------------------------------------------------------------------------------------------

  @Test
  public void testRandomBigIntegerBytesScaled() throws Exception {
    Random r = new Random(1052);
    for (BigDecimalFlavor bigDecimalFlavor : BigDecimalFlavor.values()) {
      doTestRandomBigIntegerBytesScaled(r, standardAlphabet, bigDecimalFlavor);
    }
    for (BigDecimalFlavor bigDecimalFlavor : BigDecimalFlavor.values()) {
      for (String sparseAlphabet : sparseAlphabets) {
        doTestRandomBigIntegerBytesScaled(r, sparseAlphabet, bigDecimalFlavor);
      }
    }
  }

  private void doTestRandomBigIntegerBytesScaled(Random r, String digitAlphabet, BigDecimalFlavor bigDecimalFlavor) throws Exception {
    for (int i = 0; i < POUND_FACTOR; i++) {
      BigDecimal bigDecimal = randHiveBigDecimal(r, digitAlphabet, bigDecimalFlavor);

      doTestBigIntegerBytesScaled(r, bigDecimal);
    }
  }

  @Test
  public void testBigIntegerBytesScaledSpecial() throws Exception {
    Random r = new Random(1050);
    for (BigDecimal bigDecimal : specialBigDecimals) {
      doTestBigIntegerBytesScaled(r, bigDecimal);
    }
  }

  private void doTestBigIntegerBytesScaled(Random r, BigDecimal bigDecimal) throws IOException {

    HiveDecimalV1 oldDec = HiveDecimalV1.create(bigDecimal);
    if (oldDec != null && isTenPowerBug(oldDec.toString())) {
      return;
    }
    HiveDecimal dec = HiveDecimal.create(bigDecimal);
    if (oldDec == null) {
      assertTrue(dec == null);
      return;
    }
    assertTrue(dec != null);
    dec.validate();

    int scale = oldDec.scale();
    int newScale;
    if (scale == HiveDecimal.MAX_SCALE) {
      newScale = scale;
    } else {
      newScale = scale + r.nextInt(HiveDecimal.MAX_SCALE - scale);
    }

    HiveDecimalV1 oldDecScaled = oldDec.setScale(newScale);
    HiveDecimalWritableV1 oldDecScaledWritable = new HiveDecimalWritableV1(oldDecScaled);

    byte[] bytesExpected = oldDecScaledWritable.getInternalStorage();

    byte[] bytes = dec.bigIntegerBytesScaled(newScale);

    if (!StringExpr.equal(bytes, 0, bytes.length, bytesExpected, 0, bytesExpected.length)) {
      Assert.fail();
    }

    HiveDecimalWritableV1 oldDecWritableRetrieve = new HiveDecimalWritableV1(bytesExpected, newScale);
    HiveDecimalV1 oldDecRetrieve = oldDecWritableRetrieve.getHiveDecimal();
    Assert.assertTrue(oldDecRetrieve != null);

    HiveDecimal decRetrieve = HiveDecimal.createFromBigIntegerBytesAndScale(bytes, newScale);
    Assert.assertTrue(decRetrieve != null);

    Assert.assertEquals(oldDecRetrieve.toString(), decRetrieve.toString());
  }

  //------------------------------------------------------------------------------------------------

  @Test
  public void testRandomRoundFloor() {
    Random r = new Random(1052);
    for (BigDecimalFlavor bigDecimalFlavor : BigDecimalFlavor.values()) {
      doTestRandomRound(r, standardAlphabet, bigDecimalFlavor, HiveDecimal.ROUND_FLOOR);
    }
    for (BigDecimalFlavor bigDecimalFlavor : BigDecimalFlavor.values()) {
      for (String sparseAlphabet : sparseAlphabets) {
        doTestRandomRound(r, sparseAlphabet, bigDecimalFlavor, HiveDecimal.ROUND_FLOOR);
      }
    }
  }

  private void doTestRandomRound(Random r, String digitAlphabet, BigDecimalFlavor bigDecimalFlavor, int roundingMode) {

    for (int i = 0; i < POUND_FACTOR; i++) {
      BigDecimal bigDecimal = randHiveBigDecimal(r, digitAlphabet, bigDecimalFlavor);

      doTestRound(r, bigDecimal, roundingMode);
    }
  }

  @Test
  public void testRoundFloorSpecial() {
    Random r = new Random(1050);
    for (BigDecimal bigDecimal : specialBigDecimals) {
      doTestRound(r, bigDecimal, HiveDecimal.ROUND_FLOOR);
    }
  }

  // Used by all flavors.
  private void doTestRound(Random r, BigDecimal bigDecimal, int roundingMode) {

    // Temporarily....
    bigDecimal = bigDecimal.abs();

    HiveDecimalV1 oldDec = HiveDecimalV1.create(bigDecimal);
    if (oldDec != null && isTenPowerBug(oldDec.toString())) {
      return;
    }
    HiveDecimal dec = HiveDecimal.create(bigDecimal);
    if (oldDec == null) {
      assertTrue(dec == null);
      return;
    }
    assertTrue(dec != null);
    dec.validate();

    HiveDecimalV1 oldScaledDec;
    HiveDecimal scaledDec;

    for (int newScale = -(2 * HiveDecimal.MAX_SCALE + 1);
         newScale <= 2 * HiveDecimal.MAX_SCALE + 1;
         newScale++) {

      oldScaledDec = oldDec.setScale(newScale, roundingMode);
      boolean isEqual;
      if (oldScaledDec == null) {
        scaledDec = dec.setScale(newScale, roundingMode);
        if (scaledDec != null) {
          Assert.fail();
        }
        return;
      } else {
        scaledDec = dec.setScale(newScale, roundingMode);
        if (scaledDec == null) {
          if (isTenPowerBug(oldScaledDec.toString())) {
            continue;
          }
          Assert.fail();
        }
        scaledDec.validate();
        isEqual = oldScaledDec.toString().equals(scaledDec.toString());
        if (!isEqual) {
          Assert.fail();
        }
      }
    }
  }

  //------------------------------------------------------------------------------------------------

  @Test
  public void testRandomRoundCeiling() {
    Random r = new Random(1053);
    for (BigDecimalFlavor bigDecimalFlavor : BigDecimalFlavor.values()) {
      doTestRandomRound(r, standardAlphabet, bigDecimalFlavor, HiveDecimal.ROUND_CEILING);
    }
    for (BigDecimalFlavor bigDecimalFlavor : BigDecimalFlavor.values()) {
      for (String sparseAlphabet : sparseAlphabets) {
        doTestRandomRound(r, sparseAlphabet, bigDecimalFlavor, HiveDecimal.ROUND_CEILING);
      }
    }
  }

  @Test
  public void testRoundCeilingSpecial() {
    Random r = new Random(1050);
    for (BigDecimal bigDecimal : specialBigDecimals) {
      doTestRound(r, bigDecimal, HiveDecimal.ROUND_CEILING);
    }
  }

  //------------------------------------------------------------------------------------------------

  @Test
  public void testRandomRoundHalfUp() {
    Random r = new Random(1053);
    for (BigDecimalFlavor bigDecimalFlavor : BigDecimalFlavor.values()) {
      doTestRandomRound(r, standardAlphabet, bigDecimalFlavor, HiveDecimal.ROUND_HALF_UP);
    }
    for (BigDecimalFlavor bigDecimalFlavor : BigDecimalFlavor.values()) {
      for (String sparseAlphabet : sparseAlphabets) {
        doTestRandomRound(r, sparseAlphabet, bigDecimalFlavor, HiveDecimal.ROUND_HALF_UP);
      }
    }
  }

  @Test
  public void testRoundHalfUpSpecial() {
    Random r = new Random(1050);
    for (BigDecimal bigDecimal : specialBigDecimals) {
      doTestRound(r, bigDecimal, HiveDecimal.ROUND_HALF_UP);
    }
  }

  //------------------------------------------------------------------------------------------------

  @Test
  public void testRandomRoundHalfEven() {
    Random r = new Random(1053);
    for (BigDecimalFlavor bigDecimalFlavor : BigDecimalFlavor.values()) {
      doTestRandomRound(r, standardAlphabet, bigDecimalFlavor, HiveDecimal.ROUND_HALF_EVEN);
    }
    for (BigDecimalFlavor bigDecimalFlavor : BigDecimalFlavor.values()) {
      for (String sparseAlphabet : sparseAlphabets) {
        doTestRandomRound(r, sparseAlphabet, bigDecimalFlavor, HiveDecimal.ROUND_HALF_EVEN);
      }
    }
  }

  @Test
  public void testRoundHalfEvenSpecial() {
    Random r = new Random(1050);
    for (BigDecimal bigDecimal : specialBigDecimals) {
      doTestRound(r, bigDecimal, HiveDecimal.ROUND_HALF_EVEN);
    }
  }

  //------------------------------------------------------------------------------------------------

  @Test
  public void testRandomCompareTo() {
    Random r = new Random(1054);
    for (BigDecimalFlavor bigDecimalFlavor : BigDecimalFlavor.values()) {
      doTestRandomCompareTo(r, standardAlphabet, bigDecimalFlavor);
    }
    for (BigDecimalFlavor bigDecimalFlavor : BigDecimalFlavor.values()) {
      for (String sparseAlphabet : sparseAlphabets) {
        doTestRandomCompareTo(r, sparseAlphabet, bigDecimalFlavor);
      }
    }
  }

  private void doTestRandomCompareTo(Random r, String digitAlphabet, BigDecimalFlavor bigDecimalFlavor) {

    for (BigDecimalPairFlavor bigDecimalPairFlavor : BigDecimalPairFlavor.values()) {
      for (int i = 0; i < POUND_FACTOR; i++) {
        BigDecimal[] pair = randHiveBigDecimalPair(r, digitAlphabet, bigDecimalFlavor, bigDecimalPairFlavor);

        doTestCompareTo(r, pair[0], pair[1]);
      }
    }
  }

  @Test
  public void testCompareToSpecial() {
    Random r = new Random(1050);
    for (BigDecimal bigDecimal : specialBigDecimals) {
      for (BigDecimal bigDecimal2 : specialBigDecimals) {
        doTestCompareTo(r, bigDecimal, bigDecimal2);
      }
    }
  }

  private void doTestCompareTo(Random r, BigDecimal bigDecimal, BigDecimal bigDecimal2) {

    HiveDecimalV1 oldDec = HiveDecimalV1.create(bigDecimal);
    if (oldDec != null && isTenPowerBug(oldDec.toString())) {
      return;
    }
    HiveDecimal dec = HiveDecimal.create(bigDecimal);
    if (oldDec == null) {
      assertTrue(dec == null);
      return;
    }
    assertTrue(dec != null);
    dec.validate();

    HiveDecimalV1 oldDec2 = HiveDecimalV1.create(bigDecimal2);
    if (oldDec2 != null && isTenPowerBug(oldDec2.toString())) {
      return;
    }
    HiveDecimal dec2 = HiveDecimal.create(bigDecimal2);
    if (oldDec2 == null) {
      assertTrue(dec2 == null);
      return;
    }
    assertTrue(dec2 != null);
    dec.validate();

    // Verify.
    Assert.assertEquals(oldDec.toString(), dec.toString());
    Assert.assertEquals(oldDec2.toString(), dec2.toString());

    int oldCompareTo;
    int compareTo;

    // Same object.
    oldCompareTo = oldDec.compareTo(oldDec);
    Assert.assertEquals(0, oldCompareTo);
    compareTo = dec.compareTo(dec);
    Assert.assertEquals(0, compareTo);

    // Two objects.
    oldCompareTo = oldDec.compareTo(oldDec2);
    compareTo = dec.compareTo(dec2);
    Assert.assertEquals(oldCompareTo, compareTo);

    int oldCompareToReverse = oldDec2.compareTo(oldDec);
    int compareToReverse = dec2.compareTo(dec);
    Assert.assertEquals(oldCompareToReverse, compareToReverse);
  }

  //------------------------------------------------------------------------------------------------

  @Test
  public void testRandomAdd() {
    Random r = new Random(1055);
    for (BigDecimalFlavor bigDecimalFlavor : BigDecimalFlavor.values()) {
      doTestRandomAdd(r, standardAlphabet, bigDecimalFlavor);
    }
    for (BigDecimalFlavor bigDecimalFlavor : BigDecimalFlavor.values()) {
      for (String sparseAlphabet : sparseAlphabets) {
        doTestRandomAdd(r, sparseAlphabet, bigDecimalFlavor);
      }
    }
  }

  private void doTestRandomAdd(Random r, String digitAlphabet, BigDecimalFlavor bigDecimalFlavor) {

    for (BigDecimalPairFlavor bigDecimalPairFlavor : BigDecimalPairFlavor.values()) {
      for (int i = 0; i < POUND_FACTOR; i++) {
        BigDecimal[] pair = randHiveBigDecimalPair(r, digitAlphabet, bigDecimalFlavor, bigDecimalPairFlavor);
  
        doTestAdd(r, pair[0], pair[1]);
      }
    }
  }

  @Test
  public void testAddSpecial() {
    Random r = new Random(1050);
    for (BigDecimal bigDecimal : specialBigDecimals) {
      for (BigDecimal bigDecimal2 : specialBigDecimals) {
        doTestAdd(r, bigDecimal, bigDecimal2);
      }
    }
  }

  private void doTestAdd(Random r, BigDecimal bigDecimal, BigDecimal bigDecimal2) {

    HiveDecimalV1 oldDec = HiveDecimalV1.create(bigDecimal);
    if (oldDec != null && isTenPowerBug(oldDec.toString())) {
      return;
    }
    HiveDecimal dec = HiveDecimal.create(bigDecimal);
    if (oldDec == null) {
      assertTrue(dec == null);
      return;
    }
    assertTrue(dec != null);
    dec.validate();

    HiveDecimalV1 oldDec2 = HiveDecimalV1.create(bigDecimal2);
    if (oldDec2 != null && isTenPowerBug(oldDec2.toString())) {
      return;
    }
    HiveDecimal dec2 = HiveDecimal.create(bigDecimal2);
    if (oldDec2 == null) {
      assertTrue(dec2 == null);
      return;
    }
    assertTrue(dec2 != null);
    dec.validate();

    // Verify.
    Assert.assertEquals(oldDec.toString(), dec.toString());
    Assert.assertEquals(oldDec2.toString(), dec2.toString());

    // Add to self.
    HiveDecimalV1 oldAddDec;
    HiveDecimal addDec;

    oldAddDec = oldDec.add(oldDec);
    boolean isEqual;
    if (oldAddDec == null) {
      addDec = dec.add(dec);
      assertTrue(addDec == null);
      return;
    } else {
      addDec = dec.add(dec);
      if (addDec == null) {
        if (isTenPowerBug(oldAddDec.toString())) {
          return;
        }
        Assert.fail();
      }
      addDec.validate();
      isEqual = oldAddDec.toString().equals(addDec.toString());
      if (!isEqual) {
        Assert.fail();
      }
    }

    // Add two decimals.
    oldAddDec = oldDec.add(oldDec2);
    if (oldAddDec == null) {
      addDec = dec.add(dec2);
      assertTrue(addDec == null);
      return;
    } else {
      addDec = dec.add(dec2);
      if (addDec == null) {
        if (isTenPowerBug(oldAddDec.toString())) {
          return;
        }
        Assert.fail();
      }
      addDec.validate();
      isEqual = oldAddDec.toString().equals(addDec.toString());
      if (!isEqual) {
        Assert.fail();
      }
    }

    // Add negative self.

    oldAddDec = oldDec.add(oldDec.negate());
    if (oldAddDec == null) {
      addDec = dec.add(dec.negate());
      assertTrue(addDec == null);
      return;
    } else {
      addDec = dec.add(dec.negate());
      if (addDec == null) {
        if (isTenPowerBug(oldAddDec.toString())) {
          return;
        }
        Assert.fail();
      }
      addDec.validate();
      isEqual = oldAddDec.toString().equals(addDec.toString());
      if (!isEqual) {
        Assert.fail();
      }
    }
  }

  //------------------------------------------------------------------------------------------------

  @Test
  public void testRandomSubtract() {
    Random r = new Random(1055);
    for (BigDecimalFlavor bigDecimalFlavor : BigDecimalFlavor.values()) {
      doTestRandomSubtract(r, standardAlphabet, bigDecimalFlavor);
    }
    for (BigDecimalFlavor bigDecimalFlavor : BigDecimalFlavor.values()) {
      for (String sparseAlphabet : sparseAlphabets) {
        doTestRandomSubtract(r, sparseAlphabet, bigDecimalFlavor);
      }
    }
  }

  private void doTestRandomSubtract(Random r, String digitAlphabet, BigDecimalFlavor bigDecimalFlavor) {

    for (BigDecimalPairFlavor bigDecimalPairFlavor : BigDecimalPairFlavor.values()) {
      for (int i = 0; i < POUND_FACTOR; i++) {
        BigDecimal[] pair = randHiveBigDecimalPair(r, digitAlphabet, bigDecimalFlavor, bigDecimalPairFlavor);

        doTestSubtract(r, pair[0], pair[1]);
      }
    }
  }

  @Test
  public void testSubtractSpecial() {
    Random r = new Random(1050);
    for (BigDecimal bigDecimal : specialBigDecimals) {
      for (BigDecimal bigDecimal2 : specialBigDecimals) {
        doTestSubtract(r, bigDecimal, bigDecimal2);
      }
    }
  }

  private void doTestSubtract(Random r, BigDecimal bigDecimal, BigDecimal bigDecimal2) {

    HiveDecimalV1 oldDec = HiveDecimalV1.create(bigDecimal);
    if (oldDec != null && isTenPowerBug(oldDec.toString())) {
      return;
    }
    HiveDecimal dec = HiveDecimal.create(bigDecimal);
    if (oldDec == null) {
      assertTrue(dec == null);
      return;
    }
    assertTrue(dec != null);
    dec.validate();

    HiveDecimalV1 oldDec2 = HiveDecimalV1.create(bigDecimal2);
    if (oldDec2 != null && isTenPowerBug(oldDec2.toString())) {
      return;
    }
    HiveDecimal dec2 = HiveDecimal.create(bigDecimal2);
    if (oldDec2 == null) {
      assertTrue(dec2 == null);
      return;
    }
    assertTrue(dec2 != null);
    dec.validate();

    // Verify.
    Assert.assertEquals(oldDec.toString(), dec.toString());
    Assert.assertEquals(oldDec2.toString(), dec2.toString());

    // Subtract from self.
    HiveDecimalV1 oldSubtractDec;
    HiveDecimal subtractDec;

    oldSubtractDec = oldDec.subtract(oldDec);
    Assert.assertEquals(0, oldSubtractDec.signum());
    subtractDec = dec.subtract(dec);
    Assert.assertEquals(0, subtractDec.signum());

    boolean isEqual;
    oldSubtractDec = oldDec.subtract(oldDec2);
    if (oldSubtractDec == null) {
      subtractDec = dec.subtract(dec2);
      assertTrue(subtractDec == null);
      return;
    } else {
      subtractDec = dec.subtract(dec2);
      if (subtractDec == null) {
        if (isTenPowerBug(oldSubtractDec.toString())) {
          return;
        }
        Assert.fail();
      }
      subtractDec.validate();
      isEqual = oldSubtractDec.toString().equals(subtractDec.toString());
      if (!isEqual) {
        Assert.fail();
      }
    }
  }

  //------------------------------------------------------------------------------------------------

  @Test
  public void testRandomMultiply() {
    Random r = new Random(1056);
    for (BigDecimalFlavor bigDecimalFlavor : BigDecimalFlavor.values()) {
      doTestRandomMultiply(r, standardAlphabet, bigDecimalFlavor);
    }
    for (BigDecimalFlavor bigDecimalFlavor : BigDecimalFlavor.values()) {
      for (String sparseAlphabet : sparseAlphabets) {
        doTestRandomMultiply(r, sparseAlphabet, bigDecimalFlavor);
      }
    }
  }

  private void doTestRandomMultiply(Random r, String digitAlphabet, BigDecimalFlavor bigDecimalFlavor) {

    for (BigDecimalPairFlavor bigDecimalPairFlavor : BigDecimalPairFlavor.values()) {
      for (int i = 0; i < POUND_FACTOR; i++) {
        BigDecimal[] pair = randHiveBigDecimalPair(r, digitAlphabet, bigDecimalFlavor, bigDecimalPairFlavor);

        doTestMultiply(r, pair[0], pair[1]);
      }
    }
  }

  @Test
  public void testMultiplySpecial() {
    Random r = new Random(1050);
    for (BigDecimal bigDecimal : specialBigDecimals) {
      for (BigDecimal bigDecimal2 : specialBigDecimals) {
        doTestMultiply(r, bigDecimal, bigDecimal2);
      }
    }
  }

  private void doTestMultiply(Random r, BigDecimal bigDecimal, BigDecimal bigDecimal2) {

    HiveDecimalV1 oldDec = HiveDecimalV1.create(bigDecimal);
    if (oldDec != null && isTenPowerBug(oldDec.toString())) {
      return;
    }
    HiveDecimal dec = HiveDecimal.create(bigDecimal);
    if (oldDec == null) {
      assertTrue(dec == null);
      return;
    }
    assertTrue(dec != null);
    dec.validate();

    HiveDecimalV1 oldDec2 = HiveDecimalV1.create(bigDecimal2);
    if (oldDec2 != null && isTenPowerBug(oldDec2.toString())) {
      return;
    }
    HiveDecimal dec2 = HiveDecimal.create(bigDecimal2);
    if (oldDec2 == null) {
      assertTrue(dec2 == null);
      return;
    }
    assertTrue(dec2 != null);
    dec.validate();

    // Verify.
    Assert.assertEquals(oldDec.toString(), dec.toString());
    Assert.assertEquals(oldDec2.toString(), dec2.toString());

    // Multiply by self.
    BigDecimal bigDecimalMultiply = bigDecimal.multiply(bigDecimal);
    BigDecimal bigDecimalMultiplyAbs = bigDecimalMultiply.abs();
    String bigDecimalMultiplyAbsString = bigDecimalMultiplyAbs.toString();
    int digits = bigDecimalMultiplyAbsString.indexOf('.') != -1 ? bigDecimalMultiplyAbsString.length() - 1: bigDecimalMultiplyAbsString.length();
    HiveDecimalV1 oldMultiplyDec;
    HiveDecimal multiplyDec;

    oldMultiplyDec = oldDec.multiply(oldDec);
    boolean isEqual;
    if (oldMultiplyDec == null) {
      multiplyDec = dec.multiply(dec);
      if (multiplyDec != null) {
        Assert.fail();
      }
      return;
    } else {
      multiplyDec = dec.multiply(dec);
      if (multiplyDec == null) {
        if (isTenPowerBug(oldMultiplyDec.toString())) {
          return;
        }
        Assert.fail();
      }
      multiplyDec.validate();
      isEqual = oldMultiplyDec.toString().equals(multiplyDec.toString());
      if (!isEqual) {
        Assert.fail();
      }
    }

    bigDecimalMultiply = bigDecimal.multiply(bigDecimal2);
    bigDecimalMultiplyAbs = bigDecimalMultiply.abs();
    bigDecimalMultiplyAbsString = bigDecimalMultiplyAbs.toString();
    digits = bigDecimalMultiplyAbsString.indexOf('.') != -1 ? bigDecimalMultiplyAbsString.length() - 1: bigDecimalMultiplyAbsString.length();
    oldMultiplyDec = oldDec.multiply(oldDec2);
    if (oldMultiplyDec == null) {
      multiplyDec = dec.multiply(dec2);
      if (multiplyDec != null) {
        Assert.fail();
      }
      return;
    } else {
      multiplyDec = dec.multiply(dec2);
      if (multiplyDec == null) {
        if (isTenPowerBug(oldMultiplyDec.toString())) {
          return;
        }
        Assert.fail();
      }
      multiplyDec.validate();
      isEqual = oldMultiplyDec.toString().equals(multiplyDec.toString());
      if (!isEqual) {
        Assert.fail();
      }
    }
  }

  public static String displayBytes(byte[] bytes, int start, int length) {
    StringBuilder sb = new StringBuilder();
    for (int i = start; i < start + length; i++) {
      sb.append(String.format("\\%03d", (int) (bytes[i] & 0xff)));
    }
    return sb.toString();
  }
}
