package gov.nist.toolkit.testenginelogging.logrepository;

import gov.nist.toolkit.results.client.TestInstance;
import gov.nist.toolkit.testenginelogging.client.LogMapDTO;
import gov.nist.toolkit.xdsexception.client.XdsException;
import gov.nist.toolkit.xdsexception.client.XdsInternalException;
import org.apache.log4j.Logger;
import java.io.*;

/**
 * A LogRepository is a directory where log files can be stored
 * for an event.  This class creates the directory and returns
 * a File handle to the directory.  What goes into the directory
 * @author bill
 */
abstract public class LogRepository1 {

    File logDir = null;

    // must initialize logDir
    abstract public LogRepository1 getNewLogRepository(String user) throws IOException;

    abstract protected Logger getLogger();

    protected LogRepository1() {
    }

    public void logOut(TestInstance id, LogMapDTO log) throws XdsException {
        getLogger().debug("Writing log " + log.getKeys() + " to " + logDir);
        FileOutputStream fos = null;
        ObjectOutputStream out = null;
        try {
            fos = new FileOutputStream(logFile(id));
            out = new ObjectOutputStream(fos);
            out.writeObject(log);
        } catch (IOException e) {
            throw new XdsInternalException("Cannot write transaction log file", e);
        } finally {
            try {
                out.close();
            } catch (IOException e) {
                throw new XdsInternalException("Cannot write transaction log file", e);
            }
        }
    }

    public LogMapDTO logIn(TestInstance id) throws Exception {
        FileInputStream fis = null;
        ObjectInputStream in = null;
        try {
            fis = new FileInputStream(logFile(id));
            in = new ObjectInputStream(fis);
            LogMapDTO map = (LogMapDTO) in.readObject();
            getLogger().debug("restoring log " + map.getKeys() + " from " + logDir);
            return map;
        } catch (ClassNotFoundException e) {
            getLogger().debug("attempting to restore log " + "from " + logDir);
            throw new XdsInternalException("Cannot create model of type LogMapDTO - class not found", e);
        } finally {
            in.close();
        }
    }

    String logFile(TestInstance id) throws IOException {
        return logDir().toString() + File.separator + id.getId();
    }

    public File logDir() throws IOException


getLogger().debug("Writing log " + log.getKeys() + " to " + logDir);

package gov.nist.toolkit.testenginelogging.logrepository;

import gov.nist.toolkit.results.client.TestInstance;
import gov.nist.toolkit.testenginelogging.client.LogMapDTO;
import gov.nist.toolkit.xdsexception.client.XdsException;
import gov.nist.toolkit.xdsexception.client.XdsInternalException;
import org.apache.log4j.Logger;
import java.io.*;

/**
 * A LogRepository is a directory where log files can be stored
 * for an event.  This class creates the directory and returns
 * a File handle to the directory.  What goes into the directory
 * @author bill
 */
abstract public class LogRepository1 {

    File logDir = null;

    // must initialize logDir
    abstract public LogRepository1 getNewLogRepository(String user) throws IOException;

    abstract protected Logger getLogger();

    protected LogRepository1() {
    }

    public void logOut(TestInstance id, LogMapDTO log) throws XdsException {

        FileOutputStream fos = null;
        ObjectOutputStream out = null;
        try {
            fos = new FileOutputStream(logFile(id));
            out = new ObjectOutputStream(fos);
            out.writeObject(log);
        } catch (IOException e) {
            throw new XdsInternalException("Cannot write transaction log file", e);
        } finally {
            try {
                out.close();
            } catch (IOException e) {
                throw new XdsInternalException("Cannot write transaction log file", e);
            }
        }
    }

    public LogMapDTO logIn(TestInstance id) throws Exception {
        FileInputStream fis = null;
        ObjectInputStream in = null;
        try {
            fis = new FileInputStream(logFile(id));
            in = new ObjectInputStream(fis);
            LogMapDTO map = (LogMapDTO) in.readObject();
            getLogger().debug("restoring log " + map.getKeys() + " from " + logDir);
            return map;
        } catch (ClassNotFoundException e) {
            getLogger().debug("attempting to restore log " + "from " + logDir);
            throw new XdsInternalException("Cannot create model of type LogMapDTO - class not found", e);
        } finally {
            in.close();
        }
    }

    String logFile(TestInstance id) throws IOException {
        return logDir().toString() + File.separator + id.getId();
    }

    public File logDir() throws IOException {
        return logDir;
    }
}


FileOutputStream fos = null;
        ObjectOutputStream out = null;

package gov.nist.toolkit.testenginelogging.logrepository;

import gov.nist.toolkit.results.client.TestInstance;
import gov.nist.toolkit.testenginelogging.client.LogMapDTO;
import gov.nist.toolkit.xdsexception.client.XdsException;
import gov.nist.toolkit.xdsexception.client.XdsInternalException;
import org.apache.log4j.Logger;
import java.io.*;

/**
 * A LogRepository is a directory where log files can be stored
 * for an event.  This class creates the directory and returns
 * a File handle to the directory.  What goes into the directory
 * @author bill
 */
abstract public class LogRepository1 {

    File logDir = null;

    // must initialize logDir
    abstract public LogRepository1 getNewLogRepository(String user) throws IOException;

    abstract protected Logger getLogger();

    protected LogRepository1() {
    }

    public void logOut(TestInstance id, LogMapDTO log) throws XdsException {
        getLogger().debug("Writing log " + log.getKeys() + " to " + logDir);

        try {
            fos = new FileOutputStream(logFile(id));
            out = new ObjectOutputStream(fos);
            out.writeObject(log);
        } catch (IOException e) {
            throw new XdsInternalException("Cannot write transaction log file", e);
        } finally {
            try {
                out.close();
            } catch (IOException e) {
                throw new XdsInternalException("Cannot write transaction log file", e);
            }
        }
    }

    public LogMapDTO logIn(TestInstance id) throws Exception {
        FileInputStream fis = null;
        ObjectInputStream in = null;
        try {
            fis = new FileInputStream(logFile(id));
            in = new ObjectInputStream(fis);
            LogMapDTO map = (LogMapDTO) in.readObject();
            getLogger().debug("restoring log " + map.getKeys() + " from " + logDir);
            return map;
        } catch (ClassNotFoundException e) {
            getLogger().debug("attempting to restore log " + "from " + logDir);
            throw new XdsInternalException("Cannot create model of type LogMapDTO - class not found", e);
        } finally {
            in.close();
        }
    }

    String logFile(TestInstance id) throws IOException {
        return logDir().toString() + File.separator + id.getId();
    }

    public File logDir() throws IOException {
        return logDir;
    }
}


try {
            fos = new FileOutputStream(logFile(id));
            out = new ObjectOutputStream(fos);
            out.writeObject(log);
        } catch (IOException e) {
            throw new XdsInternalException("Cannot write transaction log file", e);
        } finally {
            try {
                out.close();
            } catch (IOException e) {
                throw new XdsInternalException("Cannot write transaction log file", e);
            }
        }
    }

package gov.nist.toolkit.testenginelogging.logrepository;

import gov.nist.toolkit.results.client.TestInstance;
import gov.nist.toolkit.testenginelogging.client.LogMapDTO;
import gov.nist.toolkit.xdsexception.client.XdsException;
import gov.nist.toolkit.xdsexception.client.XdsInternalException;
import org.apache.log4j.Logger;
import java.io.*;

/**
 * A LogRepository is a directory where log files can be stored
 * for an event.  This class creates the directory and returns
 * a File handle to the directory.  What goes into the directory
 * @author bill
 */
abstract public class LogRepository1 {

    File logDir = null;

    // must initialize logDir
    abstract public LogRepository1 getNewLogRepository(String user) throws IOException;

    abstract protected Logger getLogger();

    protected LogRepository1() {
    }

    public void logOut(TestInstance id, LogMapDTO log) throws XdsException {
        getLogger().debug("Writing log " + log.getKeys() + " to " + logDir);
        FileOutputStream fos = null;
        ObjectOutputStream out = null;

    public LogMapDTO logIn(TestInstance id) throws Exception {
        FileInputStream fis = null;
        ObjectInputStream in = null;
        try {
            fis = new FileInputStream(logFile(id));
            in = new ObjectInputStream(fis);
            LogMapDTO map = (LogMapDTO) in.readObject();
            getLogger().debug("restoring log " + map.getKeys() + " from " + logDir);
            return map;
        } catch (ClassNotFoundException e) {
            getLogger().debug("attempting to restore log " + "from " + logDir);
            throw new XdsInternalException("Cannot create model of type LogMapDTO - class not found", e);
        } finally {
            in.close();
        }
    }

    String logFile(TestInstance id) throws IOException {
        return logDir().toString() + File.separator + id.getId();
    }

    public File logDir() throws IOException {
        return logDir;
    }
}


"Writing log " + log.getKeys() + " to " + logDir);

package gov.nist.toolkit.testenginelogging.logrepository;

import gov.nist.toolkit.results.client.TestInstance;
import gov.nist.toolkit.testenginelogging.client.LogMapDTO;
import gov.nist.toolkit.xdsexception.client.XdsException;
import gov.nist.toolkit.xdsexception.client.XdsInternalException;
import org.apache.log4j.Logger;
import java.io.*;

/**
 * A LogRepository is a directory where log files can be stored
 * for an event.  This class creates the directory and returns
 * a File handle to the directory.  What goes into the directory
 * @author bill
 */
abstract public class LogRepository1 {

    File logDir = null;

    // must initialize logDir
    abstract public LogRepository1 getNewLogRepository(String user) throws IOException;

    abstract protected Logger getLogger();

    protected LogRepository1() {
    }

    public void logOut(TestInstance id, LogMapDTO log) throws XdsException {
        getLogger().debug(


try {
            fos = new FileOutputStream(logFile(id));
            out = new ObjectOutputStream(fos);
            out.writeObject(log);
        } catch (IOException e) {
            throw new XdsInternalException("Cannot write transaction log file", e);
        } finally {
            try {
                out.close();
            } catch (IOException e) {
                throw new XdsInternalException("Cannot write transaction log file", e);
            }
        }

    }

    public LogMapDTO logIn(TestInstance id) throws Exception {
        FileInputStream fis = null;
        ObjectInputStream in = null;
        try {
            fis = new FileInputStream(logFile(id));
            in = new ObjectInputStream(fis);
            LogMapDTO map = (LogMapDTO) in.readObject();
            getLogger().debug("restoring log " + map.getKeys() + " from " + logDir);
            return map;
        } catch (ClassNotFoundException e) {
            getLogger().debug("attempting to restore log " + "from " + logDir);
            throw new XdsInternalException("Cannot create model of type LogMapDTO - class not found", e);
        } finally {
            in.close();
        }
    }

    String logFile(TestInstance id) throws IOException {
        return logDir().toString() + File.separator + id.getId();
    }

    public File logDir() throws IOException {
        return logDir;
    }
}


@Override
    public Object doWork(Object value1, Object value2)

package org.apache.solr.client.solrj.io.eval;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import org.apache.solr.client.solrj.io.stream.expr.StreamExpression;
import org.apache.solr.client.solrj.io.stream.expr.StreamFactory;

public class SplitEvaluator extends RecursiveObjectEvaluator implements TwoValueWorker {

    protected static final long serialVersionUID = 1L;

    public SplitEvaluator(StreamExpression expression, StreamFactory factory) throws IOException {
        super(expression, factory);
        if (2 != containedEvaluators.size()) {
            throw new IOException(String.format(Locale.ROOT, "Invalid expression %s - expecting exactly 2 values but found %d", expression, containedEvaluators.size()));
        }
    }

{
        if (null == value1) {
            return null;
        }
        String s = value1.toString();
        String p = value2.toString();
        String[] tokens = s.split(p, -1);
        List<String> strings = new ArrayList<>(tokens.length);
        for (String tok : tokens) {
            strings.add(tok);
        }
        return strings;
    }
}


{
        if (null == value1) {
            return null;
        }
        String s = value1.toString();
        String p = value2.toString();
        String[] tokens = s.split(p, -1);
        List<String> strings = new ArrayList<>(tokens.length);
        for (String tok : tokens) {
            strings.add(tok);
        }
        return strings;
    }

package org.apache.solr.client.solrj.io.eval;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import org.apache.solr.client.solrj.io.stream.expr.StreamExpression;
import org.apache.solr.client.solrj.io.stream.expr.StreamFactory;

public class SplitEvaluator extends RecursiveObjectEvaluator implements TwoValueWorker {

    protected static final long serialVersionUID = 1L;

    public SplitEvaluator(StreamExpression expression, StreamFactory factory) throws IOException {
        super(expression, factory);
        if (2 != containedEvaluators.size()) {
            throw new IOException(String.format(Locale.ROOT, "Invalid expression %s - expecting exactly 2 values but found %d", expression, containedEvaluators.size()));
        }
    }

    @Override
    public Object doWork(Object value1, Object value2)


        if (null == value1) {
            return null;
        }
        String s = value1.toString();

package org.apache.solr.client.solrj.io.eval;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import org.apache.solr.client.solrj.io.stream.expr.StreamExpression;
import org.apache.solr.client.solrj.io.stream.expr.StreamFactory;

public class SplitEvaluator extends RecursiveObjectEvaluator implements TwoValueWorker {

    protected static final long serialVersionUID = 1L;

    public SplitEvaluator(StreamExpression expression, StreamFactory factory) throws IOException {
        super(expression, factory);
        if (2 != containedEvaluators.size()) {
            throw new IOException(String.format(Locale.ROOT, "Invalid expression %s - expecting exactly 2 values but found %d", expression, containedEvaluators.size()));
        }
    }

    @Override
    public Object doWork(Object value1, Object value2) {

        String p = value2.toString();
        String[] tokens = s.split(p, -1);
        List<String> strings = new ArrayList<>(tokens.length);
        for (String tok : tokens) {
            strings.add(tok);
        }
        return strings;
    }
}


String p = value2.toString();
        String[] tokens = s.split(p, -1);
        List<String> strings = new ArrayList<>(tokens.length);

package org.apache.solr.client.solrj.io.eval;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import org.apache.solr.client.solrj.io.stream.expr.StreamExpression;
import org.apache.solr.client.solrj.io.stream.expr.StreamFactory;

public class SplitEvaluator extends RecursiveObjectEvaluator implements TwoValueWorker {

    protected static final long serialVersionUID = 1L;

    public SplitEvaluator(StreamExpression expression, StreamFactory factory) throws IOException {
        super(expression, factory);
        if (2 != containedEvaluators.size()) {
            throw new IOException(String.format(Locale.ROOT, "Invalid expression %s - expecting exactly 2 values but found %d", expression, containedEvaluators.size()));
        }
    }

    @Override
    public Object doWork(Object value1, Object value2) {
        if (null == value1) {
            return null;
        }
        String s = value1.toString();

        for (String tok : tokens) {
            strings.add(tok);
        }
        return strings;
    }
}


for (String tok : tokens) {
            strings.add(tok);
        }
        return strings;
    }

package org.apache.solr.client.solrj.io.eval;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import org.apache.solr.client.solrj.io.stream.expr.StreamExpression;
import org.apache.solr.client.solrj.io.stream.expr.StreamFactory;

public class SplitEvaluator extends RecursiveObjectEvaluator implements TwoValueWorker {

    protected static final long serialVersionUID = 1L;

    public SplitEvaluator(StreamExpression expression, StreamFactory factory) throws IOException {
        super(expression, factory);
        if (2 != containedEvaluators.size()) {
            throw new IOException(String.format(Locale.ROOT, "Invalid expression %s - expecting exactly 2 values but found %d", expression, containedEvaluators.size()));
        }
    }

    @Override
    public Object doWork(Object value1, Object value2) {
        if (null == value1) {
            return null;
        }
        String s = value1.toString();
        String p = value2.toString();
        String[] tokens = s.split(p, -1);
        List<String> strings = new ArrayList<>(tokens.length);


package org.apache.solr.client.solrj.io.eval;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import org.apache.solr.client.solrj.io.stream.expr.StreamExpression;
import org.apache.solr.client.solrj.io.stream.expr.StreamFactory;

public class SplitEvaluator extends RecursiveObjectEvaluator implements TwoValueWorker {

    protected static final long serialVersionUID = 1L;

    public SplitEvaluator(StreamExpression expression, StreamFactory factory) throws IOException {
        super(expression, factory);
        if (2 != containedEvaluators.size()) {
            throw new IOException(String.format(Locale.ROOT, "Invalid expression %s - expecting exactly 2 values but found %d", expression, containedEvaluators.size()));
        }
    }

    @Override
    public Object doWork(Object value1, Object value2) {
        if (null == value1) {
            return null;
        }
        String s = value1.toString();
        String p = value2.toString();
        String[] tokens = s.split(p, -1);
        List<String> strings = new ArrayList<>(tokens.length);
        for (String tok : tokens) {
            strings.add(tok);
        }
        re


if (2 != containedEvaluators.size()) {
            throw new IOException(String.format(Locale.ROOT, "Invalid expression %s - expecting exactly 2 values but found %d", expression, containedEvaluators.size()));
        }

package org.apache.solr.client.solrj.io.eval;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import org.apache.solr.client.solrj.io.stream.expr.StreamExpression;
import org.apache.solr.client.solrj.io.stream.expr.StreamFactory;

public class SplitEvaluator extends RecursiveObjectEvaluator implements TwoValueWorker {

    protected static final long serialVersionUID = 1L;

    public SplitEvaluator(StreamExpression expression, StreamFactory factory) throws IOException {
        super(expression, factory);

    }

    @Override
    public Object doWork(Object value1, Object value2) {
        if (null == value1) {
            return null;
        }
        String s = value1.toString();
        String p = value2.toString();
        String[] tokens = s.split(p, -1);
        List<String> strings = new ArrayList<>(tokens.length);
        for (String tok : tokens) {
            strings.add(tok);
        }
        return strings;
    }
}


@Override
    public double doubleValue()

package org.bson.types;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.HashSet;
import java.util.Set;
import static java.math.MathContext.DECIMAL128;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;

/**
 * A binary integer decimal representation of a 128-bit decimal value, supporting 34 decimal digits of significand and an exponent range
 * of -6143 to +6144.
 *
 * @since 3.4
 * @see BSON Decimal128
 * specification
 * @see binary integer decimal
 * @see decimal128 floating-point format
 * @see 754-2008 - IEEE Standard for Floating-Point Arithmetic
 */
public final class Decimal128 extends Number implements Comparable<Decimal128> {

    private static final long serialVersionUID = 4570973266503637887L;

    private static final long INFINITY_MASK = 0x7800000000000000L;

    private static final long NaN_MASK = 0x7c00000000000000L;

    private static final long SIGN_BIT_MASK = 1L << 63;

    private static final int MIN_EXPONENT = -6176;

    private static final int MAX_EXPONENT = 6111;

    private static final int EXPONENT_OFFSET = 6176;

    private static final int MAX_BIT_LENGTH = 113;

    private static final BigInteger BIG_INT_TEN = new BigInteger("10");

    private static final BigInteger BIG_INT_ONE = new BigInteger("1");

    private static final BigInteger BIG_INT_ZERO = new BigInteger("0");

    private static final Set<String> NaN_STRINGS = new HashSet<String>(singletonList("nan"));

    private static final Set<String> NEGATIVE_NaN_STRINGS = new HashSet<String>(singletonList("-nan"));

    private static final Set<String> POSITIVE_INFINITY_STRINGS = new HashSet<String>(asList("inf", "+inf", "infinity", "+infinity"));

    private static final Set<String> NEGATIVE_INFINITY_STRINGS = new HashSet<String>(asList("-inf", "-infinity"));

    /**
     * A constant holding the positive infinity of type {@code Decimal128}.  It is equal to the value return by
     * {@code Decimal128.valueOf("Infinity")}.
     */
    public static final Decimal128 POSITIVE_INFINITY = fromIEEE754BIDEncoding(INFINITY_MASK, 0);

    /**
     * A constant holding the negative infinity of type {@code Decimal128}.  It is equal to the value return by
     * {@code Decimal128.valueOf("-Infinity")}.
     */
    public static final Decimal128 NEGATIVE_INFINITY = fromIEEE754BIDEncoding(INFINITY_MASK | SIGN_BIT_MASK, 0);

    /**
     * A constant holding a negative Not-a-Number (-NaN) value of type {@code Decimal128}.  It is equal to the value return by
     * {@code Decimal128.valueOf("-NaN")}.
     */
    public static final Decimal128 NEGATIVE_NaN = fromIEEE754BIDEncoding(NaN_MASK | SIGN_BIT_MASK, 0);

    /**
     * A constant holding a Not-a-Number (NaN) value of type {@code Decimal128}.  It is equal to the value return by
     * {@code Decimal128.valueOf("NaN")}.
     */
    public static final Decimal128 NaN = fromIEEE754BIDEncoding(NaN_MASK, 0);

    /**
     * A constant holding a postive zero value of type {@code Decimal128}.  It is equal to the value return by
     * {@code Decimal128.valueOf("0")}.
     */
    public static final Decimal128 POSITIVE_ZERO = fromIEEE754BIDEncoding(0x3040000000000000L, 0x0000000000000000L);

    /**
     * A constant holding a negative zero value of type {@code Decimal128}.  It is equal to the value return by
     * {@code Decimal128.valueOf("-0")}.
     */
    public static final Decimal128 NEGATIVE_ZERO = fromIEEE754BIDEncoding(0xb040000000000000L, 0x0000000000000000L);

    private final long high;

    private final long low;

    /**
     * Returns a Decimal128 value representing the given String.
     *
     * @param value the Decimal128 value represented as a String
     * @return the Decimal128 value representing the given String
     * @throws NumberFormatException if the value is out of the Decimal128 range
     * @see
     *
     *     From-String Specification
     */
    public static Decimal128 parse(final String value) {
        String lowerCasedValue = value.toLowerCase();
        if (NaN_STRINGS.contains(lowerCasedValue)) {
            return NaN;
        }
        if (NEGATIVE_NaN_STRINGS.contains(lowerCasedValue)) {
            return NEGATIVE_NaN;
        }
        if (POSITIVE_INFINITY_STRINGS.contains(lowerCasedValue)) {
            return POSITIVE_INFINITY;
        }
        if (NEGATIVE_INFINITY_STRINGS.contains(lowerCasedValue)) {
            return NEGATIVE_INFINITY;
        }
        return new Decimal128(new BigDecimal(value), value.charAt(0) == '-');
    }

    /**
     * Create an instance with the given high and low order bits representing this Decimal128 as an IEEE 754-2008 128-bit decimal
     * floating point using the BID encoding scheme.
     *
     * @param high the high-order 64 bits
     * @param low  the low-order 64 bits
     * @return the Decimal128 value representing the given high and low order bits
     */
    public static Decimal128 fromIEEE754BIDEncoding(final long high, final long low) {
        return new Decimal128(high, low);
    }

    /**
     * Constructs a Decimal128 value representing the given long.
     *
     * @param value the Decimal128 value represented as a long
     */
    public Decimal128(final long value) {
        this(new BigDecimal(value, DECIMAL128));
    }

    /**
     * Constructs a Decimal128 value representing the given BigDecimal.
     *
     * @param value the Decimal128 value represented as a BigDecimal
     * @throws NumberFormatException if the value is out of the Decimal128 range
     */
    public Decimal128(final BigDecimal value) {
        this(value, value.signum() == -1);
    }

    private Decimal128(final long high, final long low) {
        this.high = high;
        this.low = low;
    }

    // isNegative is necessary to detect -0, which can't be represented with a BigDecimal
    private Decimal128(final BigDecimal initialValue, final boolean isNegative) {
        long localHigh = 0;
        long localLow = 0;
        BigDecimal value = clampAndRound(initialValue);
        long exponent = -value.scale();
        if ((exponent < MIN_EXPONENT) || (exponent > MAX_EXPONENT)) {
            throw new AssertionError("Exponent is out of range for Decimal128 encoding: " + exponent);
        }
        if (value.unscaledValue().bitLength() > MAX_BIT_LENGTH) {
            throw new AssertionError("Unscaled roundedValue is out of range for Decimal128 encoding:" + value.unscaledValue());
        }
        BigInteger significand = value.unscaledValue().abs();
        int bitLength = significand.bitLength();
        for (int i = 0; i < Math.min(64, bitLength); i++) {
            if (significand.testBit(i)) {
                localLow |= 1L << i;
            }
        }
        for (int i = 64; i < bitLength; i++) {
            if (significand.testBit(i)) {
                localHigh |= 1L << (i - 64);
            }
        }
        long biasedExponent = exponent + EXPONENT_OFFSET;
        localHigh |= biasedExponent << 49;
        if (value.signum() == -1 || isNegative) {
            localHigh |= SIGN_BIT_MASK;
        }
        high = localHigh;
        low = localLow;
    }

    private BigDecimal clampAndRound(final BigDecimal initialValue) {
        BigDecimal value;
        if (-initialValue.scale() > MAX_EXPONENT) {
            int diff = -initialValue.scale() - MAX_EXPONENT;
            if (initialValue.unscaledValue().equals(BIG_INT_ZERO)) {
                value = new BigDecimal(initialValue.unscaledValue(), -MAX_EXPONENT);
            } else if (diff + initialValue.precision() > 34) {
                throw new NumberFormatException("Exponent is out of range for Decimal128 encoding of " + initialValue);
            } else {
                BigInteger multiplier = BIG_INT_TEN.pow(diff);
                value = new BigDecimal(initialValue.unscaledValue().multiply(multiplier), initialValue.scale() + diff);
            }
        } else if (-initialValue.scale() < MIN_EXPONENT) {
            // Increasing a very negative exponent may require decreasing precision, which is rounding
            // Only round exactly (by removing precision that is all zeroes).  An exception is thrown if the rounding would be inexact:
            int diff = initialValue.scale() + MIN_EXPONENT;
            int undiscardedPrecision = ensureExactRounding(initialValue, diff);
            BigInteger divisor = undiscardedPrecision == 0 ? BIG_INT_ONE : BIG_INT_TEN.pow(diff);
            value = new BigDecimal(initialValue.unscaledValue().divide(divisor), initialValue.scale() - diff);
        } else {
            value = initialValue.round(DECIMAL128);
            int extraPrecision = initialValue.precision() - value.precision();
            if (extraPrecision > 0) {
                // Again, only round exactly
                ensureExactRounding(initialValue, extraPrecision);
            }
        }
        return value;
    }

    private int ensureExactRounding(final BigDecimal initialValue, final int extraPrecision) {
        String significand = initialValue.unscaledValue().abs().toString();
        int undiscardedPrecision = Math.max(0, significand.length() - extraPrecision);
        for (int i = undiscardedPrecision; i < significand.length(); i++) {
            if (significand.charAt(i) != '0') {
                throw new NumberFormatException("Conversion to Decimal128 would require inexact rounding of " + initialValue);
            }
        }
        return undiscardedPrecision;
    }

    /**
     * Gets the high-order 64 bits of the IEEE 754-2008 128-bit decimal floating point encoding for this Decimal128, using the BID encoding
     * scheme.
     *
     * @return the high-order 64 bits of this Decimal128
     */
    public long getHigh() {
        return high;
    }

    /**
     * Gets the low-order 64 bits of the IEEE 754-2008 128-bit decimal floating point encoding for this Decimal128, using the BID encoding
     * scheme.
     *
     * @return the low-order 64 bits of this Decimal128
     */
    public long getLow() {
        return low;
    }

    /**
     * Gets a BigDecimal that is equivalent to this Decimal128.
     *
     * @return a BigDecimal that is equivalent to this Decimal128
     * @throws ArithmeticException if the Decimal128 value is NaN, Infinity, -Infinity, or -0, none of which can be represented as a
     * BigDecimal
     */
    public BigDecimal bigDecimalValue() {
        if (isNaN()) {
            throw new ArithmeticException("NaN can not be converted to a BigDecimal");
        }
        if (isInfinite()) {
            throw new ArithmeticException("Infinity can not be converted to a BigDecimal");
        }
        BigDecimal bigDecimal = bigDecimalValueNoNegativeZeroCheck();
        // If the BigDecimal is 0, but the Decimal128 is negative, that means we have -0.
        if (isNegative() && bigDecimal.signum() == 0) {
            throw new ArithmeticException("Negative zero can not be converted to a BigDecimal");
        }
        return bigDecimal;
    }

    // Make sure that the argument comes from a call to bigDecimalValueNoNegativeZeroCheck on this instance
    private boolean hasDifferentSign(final BigDecimal bigDecimal) {
        return isNegative() && bigDecimal.signum() == 0;
    }

    private boolean isZero(final BigDecimal bigDecimal) {
        return !isNaN() && !isInfinite() && bigDecimal.compareTo(BigDecimal.ZERO) == 0;
    }

    private BigDecimal bigDecimalValueNoNegativeZeroCheck() {
        int scale = -getExponent();
        if (twoHighestCombinationBitsAreSet()) {
            return BigDecimal.valueOf(0, scale);
        }
        return new BigDecimal(new BigInteger(isNegative() ? -1 : 1, getBytes()), scale);
    }

    // May have leading zeros.  Strip them before considering making this method public
    private byte[] getBytes() {
        byte[] bytes = new byte[15];
        long mask = 0x00000000000000ff;
        for (int i = 14; i >= 7; i--) {
            bytes[i] = (byte) ((low & mask) >>> ((14 - i) << 3));
            mask = mask << 8;
        }
        mask = 0x00000000000000ff;
        for (int i = 6; i >= 1; i--) {
            bytes[i] = (byte) ((high & mask) >>> ((6 - i) << 3));
            mask = mask << 8;
        }
        mask = 0x0001000000000000L;
        bytes[0] = (byte) ((high & mask) >>> 48);
        return bytes;
    }

    private int getExponent() {
        if (twoHighestCombinationBitsAreSet()) {
            return (int) ((high & 0x1fffe00000000000L) >>> 47) - EXPONENT_OFFSET;
        } else {
            return (int) ((high & 0x7fff800000000000L) >>> 49) - EXPONENT_OFFSET;
        }
    }

    private boolean twoHighestCombinationBitsAreSet() {
        return (high & 3L << 61) == 3L << 61;
    }

    /**
     * Returns true if this Decimal128 is negative.
     *
     * @return true if this Decimal128 is negative
     */
    public boolean isNegative() {
        return (high & SIGN_BIT_MASK) == SIGN_BIT_MASK;
    }

    /**
     * Returns true if this Decimal128 is infinite.
     *
     * @return true if this Decimal128 is infinite
     */
    public boolean isInfinite() {
        return (high & INFINITY_MASK) == INFINITY_MASK;
    }

    /**
     * Returns true if this Decimal128 is finite.
     *
     * @return true if this Decimal128 is finite
     */
    public boolean isFinite() {
        return !isInfinite();
    }

    /**
     * Returns true if this Decimal128 is Not-A-Number (NaN).
     *
     * @return true if this Decimal128 is Not-A-Number
     */
    public boolean isNaN() {
        return (high & NaN_MASK) == NaN_MASK;
    }

    @Override
    public int compareTo(final Decimal128 o) {
        if (isNaN()) {
            return o.isNaN() ? 0 : 1;
        }
        if (isInfinite()) {
            if (isNegative()) {
                if (o.isInfinite() && o.isNegative()) {
                    return 0;
                } else {
                    return -1;
                }
            } else {
                if (o.isNaN()) {
                    return -1;
                } else if (o.isInfinite() && !o.isNegative()) {
                    return 0;
                } else {
                    return 1;
                }
            }
        }
        BigDecimal bigDecimal = bigDecimalValueNoNegativeZeroCheck();
        BigDecimal otherBigDecimal = o.bigDecimalValueNoNegativeZeroCheck();
        if (isZero(bigDecimal) && o.isZero(otherBigDecimal)) {
            if (hasDifferentSign(bigDecimal)) {
                if (o.hasDifferentSign(otherBigDecimal)) {
                    return 0;
                } else {
                    return -1;
                }
            } else if (o.hasDifferentSign(otherBigDecimal)) {
                return 1;
            }
        }
        if (o.isNaN()) {
            return -1;
        } else if (o.isInfinite()) {
            if (o.isNegative()) {
                return 1;
            } else {
                return -1;
            }
        } else {
            return bigDecimal.compareTo(otherBigDecimal);
        }
    }

    /**
     * Converts this {@code Decimal128} to a {@code int}. This conversion is analogous to the narrowing primitive conversion from
     * {@code double} to {@code int} as defined in The Java™ Language Specification: any fractional part of this
     * {@code Decimal128} will be discarded, and if the resulting integral value is too big to fit in a {@code int}, only the
     * low-order 32 bits are returned. Note that this conversion can lose information about the overall magnitude and precision of this
     * {@code Decimal128} value as well as return a result with the opposite sign. Note that {@code #NEGATIVE_ZERO} is converted to
     * {@code 0}.
     *
     * @return this {@code Decimal128} converted to a {@code int}.
     * @since 3.10
     */
    @Override
    public int intValue() {
        return (int) doubleValue();
    }

    /**
     * Converts this {@code Decimal128} to a {@code long}. This conversion is analogous to the narrowing primitive conversion from
     * {@code double} to {@code long} as defined in The Java™ Language Specification: any fractional part of this
     * {@code Decimal128} will be discarded, and if the resulting integral value is too big to fit in a {@code long}, only the
     * low-order 64 bits are returned. Note that this conversion can lose information about the overall magnitude and precision of this
     * {@code Decimal128} value as well as return a result with the opposite sign. Note that {@code #NEGATIVE_ZERO} is converted to
     * {@code 0L}.
     *
     * @return this {@code Decimal128} converted to a {@code long}.
     * @since 3.10
     */
    @Override
    public long longValue() {
        return (long) doubleValue();
    }

    /**
     * Converts this {@code Decimal128} to a {@code float}. This conversion is similar to the narrowing primitive conversion from
     * {@code double} to {@code float} as defined in The Java™ Language Specification: if this {@code Decimal128} has
     * too great a magnitude to represent as a {@code float}, it will be converted to {@link Float#NEGATIVE_INFINITY} or
     * {@link Float#POSITIVE_INFINITY} as appropriate.  Note that even when the return value is finite, this conversion can lose
     * information about the precision of the {@code Decimal128} value. Note that {@code #NEGATIVE_ZERO} is converted to {@code 0.0f}.
     *
     * @return this {@code Decimal128} converted to a {@code float}.
     * @since 3.10
     */
    @Override
    public float floatValue() {
        return (float) doubleValue();
    }

    /**
     * Converts this {@code Decimal128} to a {@code double}. This conversion is similar to the narrowing primitive conversion from
     * {@code double} to {@code float} as defined in The Java™ Language Specification: if this {@code Decimal128} has
     * too great a magnitude to represent as a {@code double}, it will be converted to {@link Double#NEGATIVE_INFINITY} or
     * {@link Double#POSITIVE_INFINITY} as appropriate.  Note that even when the return value is finite, this conversion can lose
     * information about the precision of the {@code Decimal128} value. Note that {@code #NEGATIVE_ZERO} is converted to {@code 0.0d}.
     *
     * @return this {@code Decimal128} converted to a {@code double}.
     * @since 3.10
     */

{
        if (isNaN()) {
            return Double.NaN;
        }
        if (isInfinite()) {
            if (isNegative()) {
                return Double.NEGATIVE_INFINITY;
            } else {
                return Double.POSITIVE_INFINITY;
            }
        }
        BigDecimal bigDecimal = bigDecimalValueNoNegativeZeroCheck();
        if (hasDifferentSign(bigDecimal)) {
            return -0.0d;
        }
        return bigDecimal.doubleValue();
    }

    /**
     * Returns true if the encoded representation of this instance is the same as the encoded representation of {@code o}.
     *

     * One consequence is that, whereas {@code Double.NaN != Double.NaN},
     * {@code new Decimal128("NaN").equals(new Decimal128("NaN")} returns true.
     *

     *

     * Another consequence is that, as with BigDecimal, {@code new Decimal128("1.0").equals(new Decimal128("1.00")} returns false,
     * because the precision is not the same and therefore the representation is not the same.
     *

     *
     * @param o the object to compare for equality
     * @return true if the instances are equal
     */
    @Override
    public boolean equals(final Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        Decimal128 that = (Decimal128) o;
        if (high != that.high) {
            return false;
        }
        if (low != that.low) {
            return false;
        }
        return true;
    }

    @Override
    public int hashCode() {
        int result = (int) (low ^ (low >>> 32));
        result = 31 * result + (int) (high ^ (high >>> 32));
        return result;
    }

    /**
     * Returns the String representation of the Decimal128 value.
     *
     * @return the String representation
     * @see
     *     To-String Sprecification
     */
    @Override
    public String toString() {
        if (isNaN()) {
            return "NaN";
        }
        if (isInfinite()) {
            if (isNegative()) {
                return "-Infinity";
            } else {
                return "Infinity";
            }
        }
        return toStringWithBigDecimal();
    }

    private String toStringWithBigDecimal() {
        StringBuilder buffer = new StringBuilder();
        BigDecimal bigDecimal = bigDecimalValueNoNegativeZeroCheck();
        String significand = bigDecimal.unscaledValue().abs().toString();
        if (isNegative()) {
            buffer.append('-');
        }
        int exponent = -bigDecimal.scale();
        int adjustedExponent = exponent + (significand.length() - 1);
        if (exponent <= 0 && adjustedExponent >= -6) {
            if (exponent == 0) {
                buffer.append(significand);
            } else {
                int pad = -exponent - significand.length();
                if (pad >= 0) {
                    buffer.append('0');
                    buffer.append('.');
                    for (int i = 0; i < pad; i++) {
                        buffer.append('0');
                    }
                    buffer.append(significand, 0, significand.length());
                } else {
                    buffer.append(significand, 0, -pad);
                    buffer.append('.');
                    buffer.append(significand, -pad, -pad - exponent);
                }
            }
        } else {
            buffer.append(significand.charAt(0));
            if (significand.length() > 1) {
                buffer.append('.');
                buffer.append(significand, 1, significand.length());
            }
            buffer.append('E');
            if (adjustedExponent > 0) {
                buffer.append('+');
            }
            buffer.append(adjustedExponent);
        }
        return buffer.toString();
    }
}


{
        if (twoHighestCombinationBitsAreSet()) {
            return (int) ((high & 0x1fffe00000000000L) >>> 47) - EXPONENT_OFFSET;
        } else {
            return (int) ((high & 0x7fff800000000000L) >>> 49) - EXPONENT_OFFSET;
        }
    }

package org.bson.types;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.HashSet;
import java.util.Set;
import static java.math.MathContext.DECIMAL128;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;

/**
 * A binary integer decimal representation of a 128-bit decimal value, supporting 34 decimal digits of significand and an exponent range
 * of -6143 to +6144.
 *
 * @since 3.4
 * @see BSON Decimal128
 * specification
 * @see binary integer decimal
 * @see decimal128 floating-point format
 * @see 754-2008 - IEEE Standard for Floating-Point Arithmetic
 */
public final class Decimal128 extends Number implements Comparable<Decimal128> {

    private static final long serialVersionUID = 4570973266503637887L;

    private static final long INFINITY_MASK = 0x7800000000000000L;

    private static final long NaN_MASK = 0x7c00000000000000L;

    private static final long SIGN_BIT_MASK = 1L << 63;

    private static final int MIN_EXPONENT = -6176;

    private static final int MAX_EXPONENT = 6111;

    private static final int EXPONENT_OFFSET = 6176;

    private static final int MAX_BIT_LENGTH = 113;

    private static final BigInteger BIG_INT_TEN = new BigInteger("10");

    private static final BigInteger BIG_INT_ONE = new BigInteger("1");

    private static final BigInteger BIG_INT_ZERO = new BigInteger("0");

    private static final Set<String> NaN_STRINGS = new HashSet<String>(singletonList("nan"));

    private static final Set<String> NEGATIVE_NaN_STRINGS = new HashSet<String>(singletonList("-nan"));

    private static final Set<String> POSITIVE_INFINITY_STRINGS = new HashSet<String>(asList("inf", "+inf", "infinity", "+infinity"));

    private static final Set<String> NEGATIVE_INFINITY_STRINGS = new HashSet<String>(asList("-inf", "-infinity"));

    /**
     * A constant holding the positive infinity of type {@code Decimal128}.  It is equal to the value return by
     * {@code Decimal128.valueOf("Infinity")}.
     */
    public static final Decimal128 POSITIVE_INFINITY = fromIEEE754BIDEncoding(INFINITY_MASK, 0);

    /**
     * A constant holding the negative infinity of type {@code Decimal128}.  It is equal to the value return by
     * {@code Decimal128.valueOf("-Infinity")}.
     */
    public static final Decimal128 NEGATIVE_INFINITY = fromIEEE754BIDEncoding(INFINITY_MASK | SIGN_BIT_MASK, 0);

    /**
     * A constant holding a negative Not-a-Number (-NaN) value of type {@code Decimal128}.  It is equal to the value return by
     * {@code Decimal128.valueOf("-NaN")}.
     */
    public static final Decimal128 NEGATIVE_NaN = fromIEEE754BIDEncoding(NaN_MASK | SIGN_BIT_MASK, 0);

    /**
     * A constant holding a Not-a-Number (NaN) value of type {@code Decimal128}.  It is equal to the value return by
     * {@code Decimal128.valueOf("NaN")}.
     */
    public static final Decimal128 NaN = fromIEEE754BIDEncoding(NaN_MASK, 0);

    /**
     * A constant holding a postive zero value of type {@code Decimal128}.  It is equal to the value return by
     * {@code Decimal128.valueOf("0")}.
     */
    public static final Decimal128 POSITIVE_ZERO = fromIEEE754BIDEncoding(0x3040000000000000L, 0x0000000000000000L);

    /**
     * A constant holding a negative zero value of type {@code Decimal128}.  It is equal to the value return by
     * {@code Decimal128.valueOf("-0")}.
     */
    public static final Decimal128 NEGATIVE_ZERO = fromIEEE754BIDEncoding(0xb040000000000000L, 0x0000000000000000L);

    private final long high;

    private final long low;

    /**
     * Returns a Decimal128 value representing the given String.
     *
     * @param value the Decimal128 value represented as a String
     * @return the Decimal128 value representing the given String
     * @throws NumberFormatException if the value is out of the Decimal128 range
     * @see
     *
     *     From-String Specification
     */
    public static Decimal128 parse(final String value) {
        String lowerCasedValue = value.toLowerCase();
        if (NaN_STRINGS.contains(lowerCasedValue)) {
            return NaN;
        }
        if (NEGATIVE_NaN_STRINGS.contains(lowerCasedValue)) {
            return NEGATIVE_NaN;
        }
        if (POSITIVE_INFINITY_STRINGS.contains(lowerCasedValue)) {
            return POSITIVE_INFINITY;
        }
        if (NEGATIVE_INFINITY_STRINGS.contains(lowerCasedValue)) {
            return NEGATIVE_INFINITY;
        }
        return new Decimal128(new BigDecimal(value), value.charAt(0) == '-');
    }

    /**
     * Create an instance with the given high and low order bits representing this Decimal128 as an IEEE 754-2008 128-bit decimal
     * floating point using the BID encoding scheme.
     *
     * @param high the high-order 64 bits
     * @param low  the low-order 64 bits
     * @return the Decimal128 value representing the given high and low order bits
     */
    public static Decimal128 fromIEEE754BIDEncoding(final long high, final long low) {
        return new Decimal128(high, low);
    }

    /**
     * Constructs a Decimal128 value representing the given long.
     *
     * @param value the Decimal128 value represented as a long
     */
    public Decimal128(final long value) {
        this(new BigDecimal(value, DECIMAL128));
    }

    /**
     * Constructs a Decimal128 value representing the given BigDecimal.
     *
     * @param value the Decimal128 value represented as a BigDecimal
     * @throws NumberFormatException if the value is out of the Decimal128 range
     */
    public Decimal128(final BigDecimal value) {
        this(value, value.signum() == -1);
    }

    private Decimal128(final long high, final long low) {
        this.high = high;
        this.low = low;
    }

    // isNegative is necessary to detect -0, which can't be represented with a BigDecimal
    private Decimal128(final BigDecimal initialValue, final boolean isNegative) {
        long localHigh = 0;
        long localLow = 0;
        BigDecimal value = clampAndRound(initialValue);
        long exponent = -value.scale();
        if ((exponent < MIN_EXPONENT) || (exponent > MAX_EXPONENT)) {
            throw new AssertionError("Exponent is out of range for Decimal128 encoding: " + exponent);
        }
        if (value.unscaledValue().bitLength() > MAX_BIT_LENGTH) {
            throw new AssertionError("Unscaled roundedValue is out of range for Decimal128 encoding:" + value.unscaledValue());
        }
        BigInteger significand = value.unscaledValue().abs();
        int bitLength = significand.bitLength();
        for (int i = 0; i < Math.min(64, bitLength); i++) {
            if (significand.testBit(i)) {
                localLow |= 1L << i;
            }
        }
        for (int i = 64; i < bitLength; i++) {
            if (significand.testBit(i)) {
                localHigh |= 1L << (i - 64);
            }
        }
        long biasedExponent = exponent + EXPONENT_OFFSET;
        localHigh |= biasedExponent << 49;
        if (value.signum() == -1 || isNegative) {
            localHigh |= SIGN_BIT_MASK;
        }
        high = localHigh;
        low = localLow;
    }

    private BigDecimal clampAndRound(final BigDecimal initialValue) {
        BigDecimal value;
        if (-initialValue.scale() > MAX_EXPONENT) {
            int diff = -initialValue.scale() - MAX_EXPONENT;
            if (initialValue.unscaledValue().equals(BIG_INT_ZERO)) {
                value = new BigDecimal(initialValue.unscaledValue(), -MAX_EXPONENT);
            } else if (diff + initialValue.precision() > 34) {
                throw new NumberFormatException("Exponent is out of range for Decimal128 encoding of " + initialValue);
            } else {
                BigInteger multiplier = BIG_INT_TEN.pow(diff);
                value = new BigDecimal(initialValue.unscaledValue().multiply(multiplier), initialValue.scale() + diff);
            }
        } else if (-initialValue.scale() < MIN_EXPONENT) {
            // Increasing a very negative exponent may require decreasing precision, which is rounding
            // Only round exactly (by removing precision that is all zeroes).  An exception is thrown if the rounding would be inexact:
            int diff = initialValue.scale() + MIN_EXPONENT;
            int undiscardedPrecision = ensureExactRounding(initialValue, diff);
            BigInteger divisor = undiscardedPrecision == 0 ? BIG_INT_ONE : BIG_INT_TEN.pow(diff);
            value = new BigDecimal(initialValue.unscaledValue().divide(divisor), initialValue.scale() - diff);
        } else {
            value = initialValue.round(DECIMAL128);
            int extraPrecision = initialValue.precision() - value.precision();
            if (extraPrecision > 0) {
                // Again, only round exactly
                ensureExactRounding(initialValue, extraPrecision);
            }
        }
        return value;
    }

    private int ensureExactRounding(final BigDecimal initialValue, final int extraPrecision) {
        String significand = initialValue.unscaledValue().abs().toString();
        int undiscardedPrecision = Math.max(0, significand.length() - extraPrecision);
        for (int i = undiscardedPrecision; i < significand.length(); i++) {
            if (significand.charAt(i) != '0') {
                throw new NumberFormatException("Conversion to Decimal128 would require inexact rounding of " + initialValue);
            }
        }
        return undiscardedPrecision;
    }

    /**
     * Gets the high-order 64 bits of the IEEE 754-2008 128-bit decimal floating point encoding for this Decimal128, using the BID encoding
     * scheme.
     *
     * @return the high-order 64 bits of this Decimal128
     */
    public long getHigh() {
        return high;
    }

    /**
     * Gets the low-order 64 bits of the IEEE 754-2008 128-bit decimal floating point encoding for this Decimal128, using the BID encoding
     * scheme.
     *
     * @return the low-order 64 bits of this Decimal128
     */
    public long getLow() {
        return low;
    }

    /**
     * Gets a BigDecimal that is equivalent to this Decimal128.
     *
     * @return a BigDecimal that is equivalent to this Decimal128
     * @throws ArithmeticException if the Decimal128 value is NaN, Infinity, -Infinity, or -0, none of which can be represented as a
     * BigDecimal
     */
    public BigDecimal bigDecimalValue() {
        if (isNaN()) {
            throw new ArithmeticException("NaN can not be converted to a BigDecimal");
        }
        if (isInfinite()) {
            throw new ArithmeticException("Infinity can not be converted to a BigDecimal");
        }
        BigDecimal bigDecimal = bigDecimalValueNoNegativeZeroCheck();
        // If the BigDecimal is 0, but the Decimal128 is negative, that means we have -0.
        if (isNegative() && bigDecimal.signum() == 0) {
            throw new ArithmeticException("Negative zero can not be converted to a BigDecimal");
        }
        return bigDecimal;
    }

    // Make sure that the argument comes from a call to bigDecimalValueNoNegativeZeroCheck on this instance
    private boolean hasDifferentSign(final BigDecimal bigDecimal) {
        return isNegative() && bigDecimal.signum() == 0;
    }

    private boolean isZero(final BigDecimal bigDecimal) {
        return !isNaN() && !isInfinite() && bigDecimal.compareTo(BigDecimal.ZERO) == 0;
    }

    private BigDecimal bigDecimalValueNoNegativeZeroCheck() {
        int scale = -getExponent();
        if (twoHighestCombinationBitsAreSet()) {
            return BigDecimal.valueOf(0, scale);
        }
        return new BigDecimal(new BigInteger(isNegative() ? -1 : 1, getBytes()), scale);
    }

    // May have leading zeros.  Strip them before considering making this method public
    private byte[] getBytes() {
        byte[] bytes = new byte[15];
        long mask = 0x00000000000000ff;
        for (int i = 14; i >= 7; i--) {
            bytes[i] = (byte) ((low & mask) >>> ((14 - i) << 3));
            mask = mask << 8;
        }
        mask = 0x00000000000000ff;
        for (int i = 6; i >= 1; i--) {
            bytes[i] = (byte) ((high & mask) >>> ((6 - i) << 3));
            mask = mask << 8;
        }
        mask = 0x0001000000000000L;
        bytes[0] = (byte) ((high & mask) >>> 48);
        return bytes;
    }

    private int getExponent()

private boolean twoHighestCombinationBitsAreSet() {
        return (high & 3L << 61) == 3L << 61;
    }

    /**
     * Returns true if this Decimal128 is negative.
     *
     * @return true if this Decimal128 is negative
     */
    public boolean isNegative() {
        return (high & SIGN_BIT_MASK) == SIGN_BIT_MASK;
    }

    /**
     * Returns true if this Decimal128 is infinite.
     *
     * @return true if this Decimal128 is infinite
     */
    public boolean isInfinite() {
        return (high & INFINITY_MASK) == INFINITY_MASK;
    }

    /**
     * Returns true if this Decimal128 is finite.
     *
     * @return true if this Decimal128 is finite
     */
    public boolean isFinite() {
        return !isInfinite();
    }

    /**
     * Returns true if this Decimal128 is Not-A-Number (NaN).
     *
     * @return true if this Decimal128 is Not-A-Number
     */
    public boolean isNaN() {
        return (high & NaN_MASK) == NaN_MASK;
    }

    @Override
    public int compareTo(final Decimal128 o) {
        if (isNaN()) {
            return o.isNaN() ? 0 : 1;
        }
        if (isInfinite()) {
            if (isNegative()) {
                if (o.isInfinite() && o.isNegative()) {
                    return 0;
                } else {
                    return -1;
                }
            } else {
                if (o.isNaN()) {
                    return -1;
                } else if (o.isInfinite() && !o.isNegative()) {
                    return 0;
                } else {
                    return 1;
                }
            }
        }
        BigDecimal bigDecimal = bigDecimalValueNoNegativeZeroCheck();
        BigDecimal otherBigDecimal = o.bigDecimalValueNoNegativeZeroCheck();
        if (isZero(bigDecimal) && o.isZero(otherBigDecimal)) {
            if (hasDifferentSign(bigDecimal)) {
                if (o.hasDifferentSign(otherBigDecimal)) {
                    return 0;
                } else {
                    return -1;
                }
            } else if (o.hasDifferentSign(otherBigDecimal)) {
                return 1;
            }
        }
        if (o.isNaN()) {
            return -1;
        } else if (o.isInfinite()) {
            if (o.isNegative()) {
                return 1;
            } else {
                return -1;
            }
        } else {
            return bigDecimal.compareTo(otherBigDecimal);
        }
    }

    /**
     * Converts this {@code Decimal128} to a {@code int}. This conversion is analogous to the narrowing primitive conversion from
     * {@code double} to {@code int} as defined in The Java™ Language Specification: any fractional part of this
     * {@code Decimal128} will be discarded, and if the resulting integral value is too big to fit in a {@code int}, only the
     * low-order 32 bits are returned. Note that this conversion can lose information about the overall magnitude and precision of this
     * {@code Decimal128} value as well as return a result with the opposite sign. Note that {@code #NEGATIVE_ZERO} is converted to
     * {@code 0}.
     *
     * @return this {@code Decimal128} converted to a {@code int}.
     * @since 3.10
     */
    @Override
    public int intValue() {
        return (int) doubleValue();
    }

    /**
     * Converts this {@code Decimal128} to a {@code long}. This conversion is analogous to the narrowing primitive conversion from
     * {@code double} to {@code long} as defined in The Java™ Language Specification: any fractional part of this
     * {@code Decimal128} will be discarded, and if the resulting integral value is too big to fit in a {@code long}, only the
     * low-order 64 bits are returned. Note that this conversion can lose information about the overall magnitude and precision of this
     * {@code Decimal128} value as well as return a result with the opposite sign. Note that {@code #NEGATIVE_ZERO} is converted to
     * {@code 0L}.
     *
     * @return this {@code Decimal128} converted to a {@code long}.
     * @since 3.10
     */
    @Override
    public long longValue() {
        return (long) doubleValue();
    }

    /**
     * Converts this {@code Decimal128} to a {@code float}. This conversion is similar to the narrowing primitive conversion from
     * {@code double} to {@code float} as defined in The Java™ Language Specification: if this {@code Decimal128} has
     * too great a magnitude to represent as a {@code float}, it will be converted to {@link Float#NEGATIVE_INFINITY} or
     * {@link Float#POSITIVE_INFINITY} as appropriate.  Note that even when the return value is finite, this conversion can lose
     * information about the precision of the {@code Decimal128} value. Note that {@code #NEGATIVE_ZERO} is converted to {@code 0.0f}.
     *
     * @return this {@code Decimal128} converted to a {@code float}.
     * @since 3.10
     */
    @Override
    public float floatValue() {
        return (float) doubleValue();
    }

    /**
     * Converts this {@code Decimal128} to a {@code double}. This conversion is similar to the narrowing primitive conversion from
     * {@code double} to {@code float} as defined in The Java™ Language Specification: if this {@code Decimal128} has
     * too great a magnitude to represent as a {@code double}, it will be converted to {@link Double#NEGATIVE_INFINITY} or
     * {@link Double#POSITIVE_INFINITY} as appropriate.  Note that even when the return value is finite, this conversion can lose
     * information about the precision of the {@code Decimal128} value. Note that {@code #NEGATIVE_ZERO} is converted to {@code 0.0d}.
     *
     * @return this {@code Decimal128} converted to a {@code double}.
     * @since 3.10
     */
    @Override
    public double doubleValue() {
        if (isNaN()) {
            return Double.NaN;
        }
        if (isInfinite()) {
            if (isNegative()) {
                return Double.NEGATIVE_INFINITY;
            } else {
                return Double.POSITIVE_INFINITY;
            }
        }
        BigDecimal bigDecimal = bigDecimalValueNoNegativeZeroCheck();
        if (hasDifferentSign(bigDecimal)) {
            return -0.0d;
        }
        return bigDecimal.doubleValue();
    }

    /**
     * Returns true if the encoded representation of this instance is the same as the encoded representation of {@code o}.
     *

     * One consequence is that, whereas {@code Double.NaN != Double.NaN},
     * {@code new Decimal128("NaN").equals(new Decimal128("NaN")} returns true.
     *

     *

     * Another consequence is that, as with BigDecimal, {@code new Decimal128("1.0").equals(new Decimal128("1.00")} returns false,
     * because the precision is not the same and therefore the representation is not the same.
     *

     *
     * @param o the object to compare for equality
     * @return true if the instances are equal
     */
    @Override
    public boolean equals(final Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        Decimal128 that = (Decimal128) o;
        if (high != that.high) {
            return false;
        }
        if (low != that.low) {
            return false;
        }
        return true;
    }

    @Override
    public int hashCode() {
        int result = (int) (low ^ (low >>> 32));
        result = 31 * result + (int) (high ^ (high >>> 32));
        return result;
    }

    /**
     * Returns the String representation of the Decimal128 value.
     *
     * @return the String representation
     * @see
     *     To-String Sprecification
     */
    @Override
    public String toString() {
        if (isNaN()) {
            return "NaN";
        }
        if (isInfinite()) {
            if (isNegative()) {
                return "-Infinity";
            } else {
                return "Infinity";
            }
        }
        return toStringWithBigDecimal();
    }

    private String toStringWithBigDecimal() {
        StringBuilder buffer = new StringBuilder();
        BigDecimal bigDecimal = bigDecimalValueNoNegativeZeroCheck();
        String significand = bigDecimal.unscaledValue().abs().toString();
        if (isNegative()) {
            buffer.append('-');
        }
        int exponent = -bigDecimal.scale();
        int adjustedExponent = exponent + (significand.length() - 1);
        if (exponent <= 0 && adjustedExponent >= -6) {
            if (exponent == 0) {
                buffer.append(significand);
            } else {
                int pad = -exponent - significand.length();
                if (pad >= 0) {
                    buffer.append('0');
                    buffer.append('.');
                    for (int i = 0; i < pad; i++) {
                        buffer.append('0');
                    }
                    buffer.append(significand, 0, significand.length());
                } else {
                    buffer.append(significand, 0, -pad);
                    buffer.append('.');
                    buffer.append(significand, -pad, -pad - exponent);
                }
            }
        } else {
            buffer.append(significand.charAt(0));
            if (significand.length() > 1) {
                buffer.append('.');
                buffer.append(significand, 1, significand.length());
            }
            buffer.append('E');
            if (adjustedExponent > 0) {
                buffer.append('+');
            }
            buffer.append(adjustedExponent);
        }
        return buffer.toString();
    }
}


package org.bson.types;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.HashSet;
import java.util.Set;
import static java.math.MathContext.DECIMAL128;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;

/**
 * A binary integer decimal representation of a 128-bit decimal value, supporting 34 decimal digits of significand and an exponent range
 * of -6143 to +6144.
 *
 * @since 3.4
 * @see BSON Decimal128
 * specification
 * @see binary integer decimal
 * @see decimal128 floating-point format
 * @see 754-2008 - IEEE Standard for Floating-Point Arithmetic
 */
public final class Decimal128 extends Number implements Comparable<Decimal128> {

    private static final long serialVersionUID = 4570973266503637887L;

    private static final long INFINITY_MASK = 0x7800000000000000L;

    private static final long NaN_MASK = 0x7c00000000000000L;

    private static final long SIGN_BIT_MASK = 1L << 63;

    private static final int MIN_EXPONENT = -6176;

    private static final int MAX_EXPONENT = 6111;

    private static final int EXPONENT_OFFSET = 6176;

    private static final int MAX_BIT_LENGTH = 113;

    private static final BigInteger BIG_INT_TEN = new BigInteger("10");

    private static final BigInteger BIG_INT_ONE = new BigInteger("1");

    private static final BigInteger BIG_INT_ZERO = new BigInteger("0");

    private static final Set<String> NaN_STRINGS = new HashSet<String>(singletonList("nan"));

    private static final Set<String> NEGATIVE_NaN_STRINGS = new HashSet<String>(singletonList("-nan"));

    private static final Set<String> POSITIVE_INFINITY_STRINGS = new HashSet<String>(asList("inf", "+inf", "infinity", "+infinity"));

    private static final Set<String> NEGATIVE_INFINITY_STRINGS = new HashSet<String>(asList("-inf", "-infinity"));

    /**
     * A constant holding the positive infinity of type {@code Decimal128}.  It is equal to the value return by
     * {@code Decimal128.valueOf("Infinity")}.
     */
    public static final Decimal128 POSITIVE_INFINITY = fromIEEE754BIDEncoding(INFINITY_MASK, 0);

    /**
     * A constant holding the negative infinity of type {@code Decimal128}.  It is equal to the value return by
     * {@code Decimal128.valueOf("-Infinity")}.
     */
    public static final Decimal128 NEGATIVE_INFINITY = fromIEEE754BIDEncoding(INFINITY_MASK | SIGN_BIT_MASK, 0);

    /**
     * A constant holding a negative Not-a-Number (-NaN) value of type {@code Decimal128}.  It is equal to the value return by
     * {@code Decimal128.valueOf("-NaN")}.
     */
    public static final Decimal128 NEGATIVE_NaN = fromIEEE754BIDEncoding(NaN_MASK | SIGN_BIT_MASK, 0);

    /**
     * A constant holding a Not-a-Number (NaN) value of type {@code Decimal128}.  It is equal to the value return by
     * {@code Decimal128.valueOf("NaN")}.
     */
    public static final Decimal128 NaN = fromIEEE754BIDEncoding(NaN_MASK, 0);

    /**
     * A constant holding a postive zero value of type {@code Decimal128}.  It is equal to the value return by
     * {@code Decimal128.valueOf("0")}.
     */
    public static final Decimal128 POSITIVE_ZERO = fromIEEE754BIDEncoding(0x3040000000000000L, 0x0000000000000000L);

    /**
     * A constant holding a negative zero value of type {@code Decimal128}.  It is equal to the value return by
     * {@code Decimal128.valueOf("-0")}.
     */
    public static final Decimal128 NEGATIVE_ZERO = fromIEEE754BIDEncoding(0xb040000000000000L, 0x0000000000000000L);

    private final long high;

    private final long low;

    /**
     * Returns a Decimal128 value representing the given String.
     *
     * @param value the Decimal128 value represented as a String
     * @return the Decimal128 value representing the given String
     * @throws NumberFormatException if the value is out of the Decimal128 range
     * @see
     *
     *     From-String Specification
     */
    public static Decimal128 parse(final String value) {
        String lowerCasedValue = value.toLowerCase();
        if (NaN_STRINGS.contains(lowerCasedValue)) {
            return NaN;
        }
        if (NEGATIVE_NaN_STRINGS.contains(lowerCasedValue)) {
            return NEGATIVE_NaN;
        }
        if (POSITIVE_INFINITY_STRINGS.contains(lowerCasedValue)) {
            return POSITIVE_INFINITY;
        }
        if (NEGATIVE_INFINITY_STRINGS.contains(lowerCasedValue)) {
            return NEGATIVE_INFINITY;
        }
        return new Decimal128(new BigDecimal(value), value.charAt(0) == '-');
    }

    /**
     * Create an instance with the given high and low order bits representing this Decimal128 as an IEEE 754-2008 128-bit decimal
     * floating point using the BID encoding scheme.
     *
     * @param high the high-order 64 bits
     * @param low  the low-order 64 bits
     * @return the Decimal128 value representing the given high and low order bits
     */
    public static Decimal128 fromIEEE754BIDEncoding(final long high, final long low) {
        return new Decimal128(high, low);
    }

    /**
     * Constructs a Decimal128 value representing the given long.
     *
     * @param value the Decimal128 value represented as a long
     */
    public Decimal128(final long value) {
        this(new BigDecimal(value, DECIMAL128));
    }

    /**
     * Constructs a Decimal128 value representing the given BigDecimal.
     *
     * @param value the Decimal128 value represented as a BigDecimal
     * @throws NumberFormatException if the value is out of the Decimal128 range
     */
    public Decimal128(final BigDecimal value) {
        this(value, value.signum() == -1);
    }

    private Decimal128(final long high, final long low) {
        this.high = high;
        this.low = low;
    }

    // isNegative is necessary to detect -0, which can't be represented with a BigDecimal
    private Decimal128(final BigDecimal initialValue, final boolean isNegative) {
        long localHigh = 0;
        long localLow = 0;
        BigDecimal value = clampAndRound(initialValue);
        long exponent = -value.scale();
        if ((exponent < MIN_EXPONENT) || (exponent > MAX_EXPONENT)) {
            throw new AssertionError("Exponent is out of range for Decimal128 encoding: " + exponent);
        }
        if (value.unscaledValue().bitLength() > MAX_BIT_LENGTH) {
            throw new AssertionError("Unscaled roundedValue is out of range for Decimal128 encoding:" + value.unscaledValue());
        }
        BigInteger significand = value.unscaledValue().abs();
        int bitLength = significand.bitLength();
        for (int i = 0; i < Math.min(64, bitLength); i++) {
            if (significand.testBit(i)) {
                localLow |= 1L << i;
            }
        }
        for (int i = 64; i < bitLength; i++) {
            if (significand.testBit(i)) {
                localHigh |= 1L << (i - 64);
            }
        }
        long biasedExponent = exponent + EXPONENT_OFFSET;
        localHigh |= biasedExponent << 49;
        if (value.signum() == -1 || isNegative) {
            localHigh |= SIGN_BIT_MASK;
        }
        high = localHigh;
        low = localLow;
    }

    private BigDecimal clampAndRound(final BigDecimal initialValue) {
        BigDecimal value;
        if (-initialValue.scale() > MAX_EXPONENT) {
            int diff = -initialValue.scale() - MAX_EXPONENT;
            if (initialValue.unscaledValue().equals(BIG_INT_ZERO)) {
                value = new BigDecimal(initialValue.unscaledValue(), -MAX_EXPONENT);
            } else if (diff + initialValue.precision() > 34) {
                throw new NumberFormatException("Exponent is out of range for Decimal128 encoding of " + initialValue);
            } else {
                BigInteger multiplier = BIG_INT_TEN.pow(diff);
                value = new BigDecimal(initialValue.unscaledValue().multiply(multiplier), initialValue.scale() + diff);
            }
        } else if (-initialValue.scale() < MIN_EXPONENT) {
            // Increasing a very negative exponent may require decreasing precision, which is rounding
            // Only round exactly (by removing precision that is all zeroes).  An exception is thrown if the rounding would be inexact:
            int diff = initialValue.scale() + MIN_EXPONENT;
            int undiscardedPrecision = ensureExactRounding(initialValue, diff);
            BigInteger divisor = undiscardedPrecision == 0 ? BIG_INT_ONE : BIG_INT_TEN.pow(diff);
            value = new BigDecimal(initialValue.unscaledValue().divide(divisor), initialValue.scale() - diff);
        } else {
            value = initialValue.round(DECIMAL128);
            int extraPrecision = initialValue.precision() - value.precision();
            if (extraPrecision > 0) {
                // Again, only round exactly
                ensureExactRounding(initialValue, extraPrecision);
            }
        }
        return value;
    }

    private int ensureExactRounding(final BigDecimal initialValue, final int extraPrecision) {
        String significand = initialValue.unscaledValue().abs().toString();
        int undiscardedPrecision = Math.max(0, significand.length() - extraPrecision);
        for (int i = undiscardedPrecision; i < significand.length(); i++) {
            if (significand.charAt(i) != '0') {
                throw new NumberFormatException("Conversion to Decimal128 would require inexact rounding of " + initialValue);
            }
        }
        return undiscardedPrecision;
    }

    /**
     * Gets the high-order 64 bits of the IEEE 754-2008 128-bit decimal floating point encoding for this Decimal128, using the BID encoding
     * scheme.
     *
     * @return the high-order 64 bits of this Decimal128
     */
    public long getHigh() {
        return high;
    }

    /**
     * Gets the low-order 64 bits of the IEEE 754-2008 128-bit decimal floating point encoding for this Decimal128, using the BID encoding
     * scheme.
     *
     * @return the low-order 64 bits of this Decimal128
     */
    public long getLow() {
        return low;
    }

    /**
     * Gets a BigDecimal that is equivalent to this Decimal128.
     *
     * @return a BigDecimal that is equivalent to this Decimal128
     * @throws ArithmeticException if the Decimal128 value is NaN, Infinity, -Infinity, or -0, none of which can be represented as a
     * BigDecimal
     */
    public BigDecimal bigDecimalValue() {
        if (isNaN()) {
            throw new ArithmeticException("NaN can not be converted to a BigDecimal");
        }
        if (isInfinite()) {
            throw new ArithmeticException("Infinity can not be converted to a BigDecimal");
        }
        BigDecimal bigDecimal = bigDecimalValueNoNegativeZeroCheck();
        // If the BigDecimal is 0, but the Decimal128 is negative, that means we have -0.
        if (isNegative() && bigDecimal.signum() == 0) {
            throw new ArithmeticException("Negative zero can not be converted to a BigDecimal");
        }
        return bigDecimal;
    }

    // Make sure that the argument comes from a call to bigDecimalValueNoNegativeZeroCheck on this instance
    private boolean hasDifferentSign(final BigDecimal bigDecimal) {
        return isNegative() && bigDecimal.signum() == 0;
    }

    private boolean isZero(final BigDecimal bigDecimal) {
        return !isNaN() && !isInfinite() && bigDecimal.compareTo(BigDecimal.ZERO) == 0;
    }

    private BigDecimal bigDecimalValueNoNegativeZeroCheck() {
        int scale = -getExponent();
        if (twoHighestCombinationBitsAreSet()) {
            return BigDecimal.valueOf(0, scale);
        }
        return new BigDecimal(new BigInteger(isNegative() ? -1 : 1, getBytes()), scale);
    }

    // May have leading zeros.  Strip them before considering making this method public
    private byte[] getBytes() {
        byte[] bytes = new byte[15];
        long mask = 0x00000000000000ff;
        for (int i = 14; i >= 7; i--) {
            bytes[i] = (byte) ((low & mask) >>> ((14 - i) << 3));
            mask = mask << 8;
        }
        mask = 0x00000000000000ff;
        for (int i = 6; i >= 1; i--) {
            bytes[i] = (byte) ((high & mask) >>> ((6 - i) << 3));
            mask = mask << 8;
        }
        mask = 0x0001000000000000L;
        bytes[0] = (byte) ((high & mask) >>> 48);
        return bytes;
    }

    private int getExponent() {

if (twoHighestCombinationBitsAreSet()) {
            return (int) ((high & 0x1fffe00000000000L) >>> 47) - EXPONENT_OFFSET;
        } else {
            return (int) ((high & 0x7fff800000000000L) >>> 49) - EXPONENT_OFFSET;
        }
    }

    private boolean twoHighestCombinationBitsAreSet() {
        return (high & 3L << 61) == 3L << 61;
    }

    /**
     * Returns true if this Decimal128 is negative.
     *
     * @return true if this Decimal128 is negative
     */
    public boolean isNegative() {
        return (high & SIGN_BIT_MASK) == SIGN_BIT_MASK;
    }

    /**
     * Returns true if this Decimal128 is infinite.
     *
     * @return true if this Decimal128 is infinite
     */
    public boolean isInfinite() {
        return (high & INFINITY_MASK) == INFINITY_MASK;
    }

    /**
     * Returns true if this Decimal128 is finite.
     *
     * @return true if this Decimal128 is finite
     */
    public boolean isFinite() {
        return !isInfinite();
    }

    /**
     * Returns true if this Decimal128 is Not-A-Number (NaN).
     *
     * @return true if this Decimal128 is Not-A-Number
     */
    public boolean isNaN() {
        return (high & NaN_MASK) == NaN_MASK;
    }

    @Override
    public int compareTo(final Decimal128 o) {
        if (isNaN()) {
            return o.isNaN() ? 0 : 1;
        }
        if (isInfinite()) {
            if (isNegative()) {
                if (o.isInfinite() && o.isNegative()) {
                    return 0;
                } else {
                    return -1;
                }
            } else {
                if (o.isNaN()) {
                    return -1;
                } else if (o.isInfinite() && !o.isNegative()) {
                    return 0;
                } else {
                    return 1;
                }
            }
        }
        BigDecimal bigDecimal = bigDecimalValueNoNegativeZeroCheck();
        BigDecimal otherBigDecimal = o.bigDecimalValueNoNegativeZeroCheck();
        if (isZero(bigDecimal) && o.isZero(otherBigDecimal)) {
            if (hasDifferentSign(bigDecimal)) {
                if (o.hasDifferentSign(otherBigDecimal)) {
                    return 0;
                } else {
                    return -1;
                }
            } else if (o.hasDifferentSign(otherBigDecimal)) {
                return 1;
            }
        }
        if (o.isNaN()) {
            return -1;
        } else if (o.isInfinite()) {
            if (o.isNegative()) {
                return 1;
            } else {
                return -1;
            }
        } else {
            return bigDecimal.compareTo(otherBigDecimal);
        }
    }

    /**
     * Converts this {@code Decimal128} to a {@code int}. This conversion is analogous to the narrowing primitive conversion from
     * {@code double} to {@code int} as defined in The Java™ Language Specification: any fractional part of this
     * {@code Decimal128} will be discarded, and if the resulting integral value is too big to fit in a {@code int}, only the
     * low-order 32 bits are returned. Note that this conversion can lose information about the overall magnitude and precision of this
     * {@code Decimal128} value as well as return a result with the opposite sign. Note that {@code #NEGATIVE_ZERO} is converted to
     * {@code 0}.
     *
     * @return this {@code Decimal128} converted to a {@code int}.
     * @since 3.10
     */
    @Override
    public int intValue() {
        return (int) doubleValue();
    }

    /**
     * Converts this {@code Decimal128} to a {@code long}. This conversion is analogous to the narrowing primitive conversion from
     * {@code double} to {@code long} as defined in The Java™ Language Specification: any fractional part of this
     * {@code Decimal128} will be discarded, and if the resulting integral value is too big to fit in a {@code long}, only the
     * low-order 64 bits are returned. Note that this conversion can lose information about the overall magnitude and precision of this
     * {@code Decimal128} value as well as return a result with the opposite sign. Note that {@code #NEGATIVE_ZERO} is converted to
     * {@code 0L}.
     *
     * @return this {@code Decimal128} converted to a {@code long}.
     * @since 3.10
     */
    @Override
    public long longValue() {
        return (long) doubleValue();
    }

    /**
     * Converts this {@code Decimal128} to a {@code float}. This conversion is similar to the narrowing primitive conversion from
     * {@code double} to {@code float} as defined in The Java™ Language Specification: if this {@code Decimal128} has
     * too great a magnitude to represent as a {@code float}, it will be converted to {@link Float#NEGATIVE_INFINITY} or
     * {@link Float#POSITIVE_INFINITY} as appropriate.  Note that even when the return value is finite, this conversion can lose
     * information about the precision of the {@code Decimal128} value. Note that {@code #NEGATIVE_ZERO} is converted to {@code 0.0f}.
     *
     * @return this {@code Decimal128} converted to a {@code float}.
     * @since 3.10
     */
    @Override
    public float floatValue() {
        return (float) doubleValue();
    }

    /**
     * Converts this {@code Decimal128} to a {@code double}. This conversion is similar to the narrowing primitive conversion from
     * {@code double} to {@code float} as defined in The Java™ Language Specification: if this {@code Decimal128} has
     * too great a magnitude to represent as a {@code double}, it will be converted to {@link Double#NEGATIVE_INFINITY} or
     * {@link Double#POSITIVE_INFINITY} as appropriate.  Note that even when the return value is finite, this conversion can lose
     * information about the precision of the {@code Decimal128} value. Note that {@code #NEGATIVE_ZERO} is converted to {@code 0.0d}.
     *
     * @return this {@code Decimal128} converted to a {@code double}.
     * @since 3.10
     */
    @Override
    public double doubleValue() {
        if (isNaN()) {
            return Double.NaN;
        }
        if (isInfinite()) {
            if (isNegative()) {
                return Double.NEGATIVE_INFINITY;
            } else {
                return Double.POSITIVE_INFINITY;
            }
        }
        BigDecimal bigDecimal = bigDecimalValueNoNegativeZeroCheck();
        if (hasDifferentSign(bigDecimal)) {
            return -0.0d;
        }
        return bigDecimal.doubleValue();
    }

    /**
     * Returns true if the encoded representation of this instance is the same as the encoded representation of {@code o}.
     *

     * One consequence is that, whereas {@code Double.NaN != Double.NaN},
     * {@code new Decimal128("NaN").equals(new Decimal128("NaN")} returns true.
     *

     *

     * Another consequence is that, as with BigDecimal, {@code new Decimal128("1.0").equals(new Decimal128("1.00")} returns false,
     * because the precision is not the same and therefore the representation is not the same.
     *

     *
     * @param o the object to compare for equality
     * @return true if the instances are equal
     */
    @Override
    public boolean equals(final Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        Decimal128 that = (Decimal128) o;
        if (high != that.high) {
            return false;
        }
        if (low != that.low) {
            return false;
        }
        return true;
    }

    @Override
    public int hashCode() {
        int result = (int) (low ^ (low >>> 32));
        result = 31 * result + (int) (high ^ (high >>> 32));
        return result;
    }

    /**
     * Returns the String representation of the Decimal128 value.
     *
     * @return the String representation
     * @see
     *     To-String Sprecification
     */
    @Override
    public String toString() {
        if (isNaN()) {
            return "NaN";
        }
        if (isInfinite()) {
            if (isNegative()) {
                return "-Infinity";
            } else {
                return "Infinity";
            }
        }
        return toStringWithBigDecimal();
    }

    private String toStringWithBigDecimal() {
        StringBuilder buffer = new StringBuilder();
        BigDecimal bigDecimal = bigDecimalValueNoNegativeZeroCheck();
        String significand = bigDecimal.unscaledValue().abs().toString();
        if (isNegative()) {
            buffer.append('-');
        }
        int exponent = -bigDecimal.scale();
        int adjustedExponent = exponent + (significand.length() - 1);
        if (exponent <= 0 && adjustedExponent >= -6) {
            if (exponent == 0) {
                buffer.append(significand);
            } else {
                int pad = -exponent - significand.length();
                if (pad >= 0) {
                    buffer.append('0');
                    buffer.append('.');
                    for (int i = 0; i < pad; i++) {
                        buffer.append('0');
                    }
                    buffer.append(significand, 0, significand.length());
                } else {
                    buffer.append(significand, 0, -pad);
                    buffer.append('.');
                    buffer.append(significand, -pad, -pad - exponent);
                }
            }
        } else {
            buffer.append(significand.charAt(0));
            if (significand.length() > 1) {
                buffer.append('.');
                buffer.append(significand, 1, significand.length());
            }
            buffer.append('E');
            if (adjustedExponent > 0) {
                buffer.append('+');
            }
            buffer.append(adjustedExponent);
        }
        return buffer.toString();
    }
}


if (twoHighestCombinationBitsAreSet()) {
            return (int) ((high & 0x1fffe00000000000L) >>> 47) - EXPONENT_OFFSET;
        } else {
            return (int) ((high & 0x7fff800000000000L) >>> 49) - EXPONENT_OFFSET;
        }

}

    private boolean twoHighestCombinationBitsAreSet() {
        return (high & 3L << 61) == 3L << 61;
    }

    /**
     * Returns true if this Decimal128 is negative.
     *
     * @return true if this Decimal128 is negative
     */
    public boolean isNegative() {
        return (high & SIGN_BIT_MASK) == SIGN_BIT_MASK;
    }

    /**
     * Returns true if this Decimal128 is infinite.
     *
     * @return true if this Decimal128 is infinite
     */
    public boolean isInfinite() {
        return (high & INFINITY_MASK) == INFINITY_MASK;
    }

    /**
     * Returns true if this Decimal128 is finite.
     *
     * @return true if this Decimal128 is finite
     */
    public boolean isFinite() {
        return !isInfinite();
    }

    /**
     * Returns true if this Decimal128 is Not-A-Number (NaN).
     *
     * @return true if this Decimal128 is Not-A-Number
     */
    public boolean isNaN() {
        return (high & NaN_MASK) == NaN_MASK;
    }

    @Override
    public int compareTo(final Decimal128 o) {
        if (isNaN()) {
            return o.isNaN() ? 0 : 1;
        }
        if (isInfinite()) {
            if (isNegative()) {
                if (o.isInfinite() && o.isNegative()) {
                    return 0;
                } else {
                    return -1;
                }
            } else {
                if (o.isNaN()) {
                    return -1;
                } else if (o.isInfinite() && !o.isNegative()) {
                    return 0;
                } else {
                    return 1;
                }
            }
        }
        BigDecimal bigDecimal = bigDecimalValueNoNegativeZeroCheck();
        BigDecimal otherBigDecimal = o.bigDecimalValueNoNegativeZeroCheck();
        if (isZero(bigDecimal) && o.isZero(otherBigDecimal)) {
            if (hasDifferentSign(bigDecimal)) {
                if (o.hasDifferentSign(otherBigDecimal)) {
                    return 0;
                } else {
                    return -1;
                }
            } else if (o.hasDifferentSign(otherBigDecimal)) {
                return 1;
            }
        }
        if (o.isNaN()) {
            return -1;
        } else if (o.isInfinite()) {
            if (o.isNegative()) {
                return 1;
            } else {
                return -1;
            }
        } else {
            return bigDecimal.compareTo(otherBigDecimal);
        }
    }

    /**
     * Converts this {@code Decimal128} to a {@code int}. This conversion is analogous to the narrowing primitive conversion from
     * {@code double} to {@code int} as defined in The Java™ Language Specification: any fractional part of this
     * {@code Decimal128} will be discarded, and if the resulting integral value is too big to fit in a {@code int}, only the
     * low-order 32 bits are returned. Note that this conversion can lose information about the overall magnitude and precision of this
     * {@code Decimal128} value as well as return a result with the opposite sign. Note that {@code #NEGATIVE_ZERO} is converted to
     * {@code 0}.
     *
     * @return this {@code Decimal128} converted to a {@code int}.
     * @since 3.10
     */
    @Override
    public int intValue() {
        return (int) doubleValue();
    }

    /**
     * Converts this {@code Decimal128} to a {@code long}. This conversion is analogous to the narrowing primitive conversion from
     * {@code double} to {@code long} as defined in The Java™ Language Specification: any fractional part of this
     * {@code Decimal128} will be discarded, and if the resulting integral value is too big to fit in a {@code long}, only the
     * low-order 64 bits are returned. Note that this conversion can lose information about the overall magnitude and precision of this
     * {@code Decimal128} value as well as return a result with the opposite sign. Note that {@code #NEGATIVE_ZERO} is converted to
     * {@code 0L}.
     *
     * @return this {@code Decimal128} converted to a {@code long}.
     * @since 3.10
     */
    @Override
    public long longValue() {
        return (long) doubleValue();
    }

    /**
     * Converts this {@code Decimal128} to a {@code float}. This conversion is similar to the narrowing primitive conversion from
     * {@code double} to {@code float} as defined in The Java™ Language Specification: if this {@code Decimal128} has
     * too great a magnitude to represent as a {@code float}, it will be converted to {@link Float#NEGATIVE_INFINITY} or
     * {@link Float#POSITIVE_INFINITY} as appropriate.  Note that even when the return value is finite, this conversion can lose
     * information about the precision of the {@code Decimal128} value. Note that {@code #NEGATIVE_ZERO} is converted to {@code 0.0f}.
     *
     * @return this {@code Decimal128} converted to a {@code float}.
     * @since 3.10
     */
    @Override
    public float floatValue() {
        return (float) doubleValue();
    }

    /**
     * Converts this {@code Decimal128} to a {@code double}. This conversion is similar to the narrowing primitive conversion from
     * {@code double} to {@code float} as defined in The Java™ Language Specification: if this {@code Decimal128} has
     * too great a magnitude to represent as a {@code double}, it will be converted to {@link Double#NEGATIVE_INFINITY} or
     * {@link Double#POSITIVE_INFINITY} as appropriate.  Note that even when the return value is finite, this conversion can lose
     * information about the precision of the {@code Decimal128} value. Note that {@code #NEGATIVE_ZERO} is converted to {@code 0.0d}.
     *
     * @return this {@code Decimal128} converted to a {@code double}.
     * @since 3.10
     */
    @Override
    public double doubleValue() {
        if (isNaN()) {
            return Double.NaN;
        }
        if (isInfinite()) {
            if (isNegative()) {
                return Double.NEGATIVE_INFINITY;
            } else {
                return Double.POSITIVE_INFINITY;
            }
        }
        BigDecimal bigDecimal = bigDecimalValueNoNegativeZeroCheck();
        if (hasDifferentSign(bigDecimal)) {
            return -0.0d;
        }
        return bigDecimal.doubleValue();
    }

    /**
     * Returns true if the encoded representation of this instance is the same as the encoded representation of {@code o}.
     *

     * One consequence is that, whereas {@code Double.NaN != Double.NaN},
     * {@code new Decimal128("NaN").equals(new Decimal128("NaN")} returns true.
     *

     *

     * Another consequence is that, as with BigDecimal, {@code new Decimal128("1.0").equals(new Decimal128("1.00")} returns false,
     * because the precision is not the same and therefore the representation is not the same.
     *

     *
     * @param o the object to compare for equality
     * @return true if the instances are equal
     */
    @Override
    public boolean equals(final Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        Decimal128 that = (Decimal128) o;
        if (high != that.high) {
            return false;
        }
        if (low != that.low) {
            return false;
        }
        return true;
    }

    @Override
    public int hashCode() {
        int result = (int) (low ^ (low >>> 32));
        result = 31 * result + (int) (high ^ (high >>> 32));
        return result;
    }

    /**
     * Returns the String representation of the Decimal128 value.
     *
     * @return the String representation
     * @see
     *     To-String Sprecification
     */
    @Override
    public String toString() {
        if (isNaN()) {
            return "NaN";
        }
        if (isInfinite()) {
            if (isNegative()) {
                return "-Infinity";
            } else {
                return "Infinity";
            }
        }
        return toStringWithBigDecimal();
    }

    private String toStringWithBigDecimal() {
        StringBuilder buffer = new StringBuilder();
        BigDecimal bigDecimal = bigDecimalValueNoNegativeZeroCheck();
        String significand = bigDecimal.unscaledValue().abs().toString();
        if (isNegative()) {
            buffer.append('-');
        }
        int exponent = -bigDecimal.scale();
        int adjustedExponent = exponent + (significand.length() - 1);
        if (exponent <= 0 && adjustedExponent >= -6) {
            if (exponent == 0) {
                buffer.append(significand);
            } else {
                int pad = -exponent - significand.length();
                if (pad >= 0) {
                    buffer.append('0');
                    buffer.append('.');
                    for (int i = 0; i < pad; i++) {
                        buffer.append('0');
                    }
                    buffer.append(significand, 0, significand.length());
                } else {
                    buffer.append(significand, 0, -pad);
                    buffer.append('.');
                    buffer.append(significand, -pad, -pad - exponent);
                }
            }
        } else {
            buffer.append(significand.charAt(0));
            if (significand.length() > 1) {
                buffer.append('.');
                buffer.append(significand, 1, significand.length());
            }
            buffer.append('E');
            if (adjustedExponent > 0) {
                buffer.append('+');
            }
            buffer.append(adjustedExponent);
        }
        return buffer.toString();
    }
}