io.horizondb.model.core.fields.DecimalField.java Source code

Java tutorial

Introduction

Here is the source code for io.horizondb.model.core.fields.DecimalField.java

Source

/**
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.horizondb.model.core.fields;

import io.horizondb.io.ByteReader;
import io.horizondb.io.ByteWriter;
import io.horizondb.io.encoding.VarInts;
import io.horizondb.model.core.Field;
import io.horizondb.model.schema.FieldType;

import java.io.IOException;
import java.io.PrintStream;
import java.util.TimeZone;

import org.apache.commons.lang.Validate;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;

/**
 * <code>Field</code> used to store decimal as mantissa and exponent of base 10.
 * 
 * @author Benjamin
 * 
 */
public class DecimalField extends AbstractField {

    /**
     * The exponent for a NaN double.
     */
    public static final byte NaN_EXPONENT = Byte.MIN_VALUE;

    /**
     * The mantissa for a NaN double.
     */
    public static final long NaN_MANTISSA = 0;

    /**
     * The exponent for a positive or negative infinite double.
     */
    public static final byte INFINITY_EXPONENT = Byte.MIN_VALUE;

    /**
     * The mantissa for the positive infinity.
     */
    public static final long POSITIVE_INFINITY_MANTISSA = 1;

    /**
     * The mantissa for the negative infinity.
     */
    public static final long NEGATIVE_INFINITY_MANTISSA = -1;

    /**
     * Constant used to speed up computation.
     */
    public final static double[] POW10 = new double[] { 1d, 10d, 100d, 1000d, 10000d, 100000d, 1000000d, 10000000d,
            100000000d, 1000000000d, 10000000000d, 100000000000d, 1000000000000d, 10000000000000d, 100000000000000d,
            1000000000000000d };

    /**
     * The field maximum value.
     */
    private static final Field MAX_VALUE = ImmutableField
            .of(new DecimalField(POSITIVE_INFINITY_MANTISSA, INFINITY_EXPONENT));

    /**
     * The field minimum value.
     */
    private static final Field MIN_VALUE = ImmutableField
            .of(new DecimalField(NEGATIVE_INFINITY_MANTISSA, INFINITY_EXPONENT));

    /**
     * The mantissa.
     */
    private long mantissa;

    /**
     * The exponent.
     */
    private byte exponent;

    /**
     * Creates a new <code>DecimalField</code> instance.
     */
    public DecimalField() {

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Field newInstance() {

        return new DecimalField(this.mantissa, this.exponent);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean isZero() {
        return this.mantissa == 0 && this.exponent != NaN_EXPONENT;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Field setValueToZero() {
        this.mantissa = 0;
        this.exponent = 0;
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Field add(Field field) {

        DecimalField other = (DecimalField) field;
        add(other.mantissa, other.exponent);
        return this;
    }

    /**    
     * {@inheritDoc}
     */
    @Override
    public Field setDouble(double d) {

        int doubleExponent = exponent(d);
        long doubleMantissa = mantissa(d, doubleExponent);

        if (toDouble(doubleMantissa, doubleExponent) != d && !Double.isNaN(d)) {
            throw new TypeConversionException("the double: " + d + " cannot be stored in a field of type decimal.");
        }

        this.exponent = (byte) doubleExponent;
        this.mantissa = doubleMantissa;
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Field subtract(Field field) {

        DecimalField other = (DecimalField) field;

        if (isNaN(this.mantissa, this.exponent)) {

            add(other.mantissa, other.exponent);

        } else {

            add(-other.mantissa, other.exponent);
        }

        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void copyTo(Field field) {

        DecimalField decimal = (DecimalField) field;

        decimal.mantissa = this.mantissa;
        decimal.exponent = this.exponent;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void writeTo(ByteWriter writer) throws IOException {

        VarInts.writeLong(writer, this.mantissa);
        VarInts.writeByte(writer, this.exponent);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void readFrom(ByteReader reader) throws IOException {

        this.mantissa = VarInts.readLong(reader);
        this.exponent = reader.readByte();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int computeSerializedSize() {
        return VarInts.computeLongSize(this.mantissa) + 1;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public FieldType getType() {

        return FieldType.DECIMAL;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Field setByte(int b) {
        setDecimal(b, (byte) 0);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Field setInt(int i) {
        setDecimal(i, (byte) 0);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Field setLong(long l) {
        setDecimal(l, (byte) 0);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Field setDecimal(long mantissa, int exponent) {

        Validate.isTrue(exponent <= Byte.MAX_VALUE && exponent >= Byte.MIN_VALUE,
                "the specified exponent is not a byte value");

        this.mantissa = mantissa;
        this.exponent = (byte) exponent;
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int getByte() {
        return (byte) getDouble();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int getInt() {
        return (int) getDouble();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public long getLong() {
        return (long) getDouble();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public double getDouble() {
        return toDouble(this.mantissa, this.exponent);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public long getDecimalMantissa() {
        return this.mantissa;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public byte getDecimalExponent() {
        return this.exponent;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String toString() {
        return Long.toString(this.mantissa) + "*10E" + Integer.toString(this.exponent);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean equals(Object object) {

        if (object == this) {
            return true;
        }
        if (!(object instanceof DecimalField)) {
            return false;
        }
        Field rhs = (Field) object;

        return new EqualsBuilder().append(this.getDouble(), rhs.getDouble()).isEquals();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int hashCode() {
        return new HashCodeBuilder(-857079051, 1637524317).append(getDouble()).toHashCode();
    }

    /**    
     * {@inheritDoc}
     */
    @Override
    public int compareTo(Field other) {
        return Double.compare(getDouble(), other.getDouble());
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Field setValueFromString(TimeZone timeZone, String s) {

        int exponentIndex = s.indexOf('E');

        if (!s.contains(".") && exponentIndex >= 0) {

            this.mantissa = Long.parseLong(s.substring(0, exponentIndex));
            this.exponent = Byte.parseByte(s.substring(exponentIndex + 1));
        }

        return setDouble(Double.parseDouble(s));
    }

    /**    
     * {@inheritDoc}
     */
    @Override
    public void writePrettyPrint(PrintStream stream) {

        if (isNaN(this.mantissa, this.exponent)) {

            stream.print("NaN");
            return;
        }

        if (isPositiveInfinity(this.mantissa, this.exponent)) {
            stream.print("Infinity");
            return;
        }

        if (isNegativeInfinity(this.mantissa, this.exponent)) {
            stream.print("-Infinity");
            return;
        }

        stream.print(this.mantissa);
        stream.print(" * 10E");
        stream.print(this.exponent);
    }

    /**    
     * {@inheritDoc}
     */
    @Override
    public Field maxValue() {
        return MAX_VALUE;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Field minValue() {
        return MIN_VALUE;
    }

    /**
     * Adds the specified decimal to this field.
     * 
     * @param otherMantissa the decimal mantissa
     * @param otherExponent the decimal exponent
     */
    private void add(long otherMantissa, byte otherExponent) {

        if (isNaN(otherMantissa, otherExponent)) {

            this.mantissa = NaN_MANTISSA;
            this.exponent = NaN_EXPONENT;

            return;
        }

        if (isNaN(this.mantissa, this.exponent)) {

            this.mantissa = otherMantissa;
            this.exponent = otherExponent;

            return;
        }

        int exponentDelta = otherExponent - this.exponent;

        if (exponentDelta == 0) {

            this.mantissa += otherMantissa;

        } else if (exponentDelta > 0) {

            this.mantissa += otherMantissa * pow10(exponentDelta);

        } else {

            this.mantissa *= pow10(-exponentDelta);

            this.mantissa += otherMantissa;
            this.exponent = otherExponent;
        }

    }

    /**
     * Returns the exponent of the specified double.
     * 
     * @param d the double for which the exponent must be computed.
     * @return the exponent of the specified double.
     */
    static int exponent(double d) {

        if (Double.isNaN(d)) {
            return NaN_EXPONENT;
        }

        if (d == ((long) d)) {

            if (d == 0) {
                return 0;
            }

            int exponent = -1;
            double remaining = d;

            do {

                exponent++;
                remaining /= 10;

            } while (remaining == ((long) remaining));

            return exponent;
        }

        String value = Double.toString(d);

        return exponent(value);
    }

    /**
     * Extracts the exponent value from the specified double.
     * 
     * @param s the double as <code>String</code>
     * @return the exponent value from the specified double
     */
    private static int exponent(String s) {

        int numberOfFractionDigits = 0;

        boolean startCounting = false;

        for (int i = s.length() - 1; i >= 0; i--) {

            char c = s.charAt(i);

            if (c == '.') {

                break;
            }

            if (c != '0' || startCounting) {

                startCounting = true;

                if (c == 'E') {

                    numberOfFractionDigits = -Integer.parseInt(s.substring(i + 1));
                    startCounting = false;

                } else {

                    numberOfFractionDigits++;
                }
            }

        }

        return -numberOfFractionDigits;
    }

    /**
     * Returns the mantissa of the specified double.
     * 
     * @param d the double
     * @param exponent the exponent
     * @return the mantissa of the specified double.
     */
    static long mantissa(double d, int exponent) {

        if (Double.isNaN(d)) {
            return 0;
        }

        if (exponent > 0) {

            return (long) (d / pow10(exponent));
        }

        return (long) Math.floor((d + (0.5 / POW10[-exponent])) * POW10[-exponent]);
    }

    /**
     * Creates a new <code>DecimalField</code> instance with the specified value.
     * 
     * @param mantissa the mantissa
     * @param exponent the exponent
     */
    private DecimalField(long mantissa, byte exponent) {

        this.mantissa = mantissa;
        this.exponent = exponent;
    }

    /**
     * Returns <code>true</code> if the double corresponding to specified mantissa and exponent is
     * <code>Double.NaN</code>, <code>false</code> otherwise.
     * 
     * @param mantissa the mantissa
     * @param exponent the exponent
     * @return <code>true</code> if the double corresponding to specified mantissa and exponent is
     * <code>Double.NaN</code>, <code>false</code> otherwise.
     */
    private static boolean isNaN(long mantissa, int exponent) {

        return exponent == NaN_EXPONENT && mantissa == NaN_MANTISSA;
    }

    /**
     * Returns <code>true</code> if the double corresponding to specified mantissa and exponent is
     * <code>Double.POSITIVE_INFINITY</code>, <code>false</code> otherwise.
     * 
     * @param mantissa the mantissa
     * @param exponent the exponent
     * @return <code>true</code> if the double corresponding to specified mantissa and exponent is
     * <code>Double.POSITIVE_INFINITY</code>, <code>false</code> otherwise.
     */
    private static boolean isPositiveInfinity(long mantissa, int exponent) {

        return exponent == INFINITY_EXPONENT && mantissa == POSITIVE_INFINITY_MANTISSA;
    }

    /**
     * Returns <code>true</code> if the double corresponding to specified mantissa and exponent is
     * <code>Double.POSITIVE_INFINITY</code>, <code>false</code> otherwise.
     * 
     * @param mantissa the mantissa
     * @param exponent the exponent
     * @return <code>true</code> if the double corresponding to specified mantissa and exponent is
     * <code>Double.POSITIVE_INFINITY</code>, <code>false</code> otherwise.
     */
    private static boolean isNegativeInfinity(long mantissa, int exponent) {

        return exponent == INFINITY_EXPONENT && mantissa == NEGATIVE_INFINITY_MANTISSA;
    }

    /**
     * Returns 10 raised to the power of the specified exponent.
     * 
     * @param exponent the exponent
     * @return 10 raised to the power of the specified exponent.
     */
    private static double pow10(int exponent) {

        if (exponent < POW10.length) {

            return POW10[exponent];
        }

        return Math.pow(10, exponent);
    }

    /**
     * Converts the specified decimal into a double.
     * 
     * @param mantissa the decimal mantissa
     * @param exponent the decimal exponent
     * @return the double corresponding to the specified decimal
     */
    private static double toDouble(long mantissa, int exponent) {

        if (isNaN(mantissa, exponent)) {
            return Double.NaN;
        }

        if (isPositiveInfinity(mantissa, exponent)) {
            return Double.POSITIVE_INFINITY;
        }

        if (isNegativeInfinity(mantissa, exponent)) {
            return Double.NEGATIVE_INFINITY;
        }

        if (exponent > 0) {

            return mantissa * pow10(exponent);
        }

        return mantissa / pow10(-exponent);
    }
}