com.anrisoftware.globalpom.measurement.AbstractValue.java Source code

Java tutorial

Introduction

Here is the source code for com.anrisoftware.globalpom.measurement.AbstractValue.java

Source

/*
 * Copyright 2013-2014 Erwin Mller <erwin.mueller@deventm.org>
 *
 * This file is part of globalpomutils-math.
 *
 * globalpomutils-math is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or (at your
 * option) any later version.
 *
 * globalpomutils-math is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with globalpomutils-math. If not, see <http://www.gnu.org/licenses/>.
 */
package com.anrisoftware.globalpom.measurement;

import static com.anrisoftware.globalpom.measurement.RoundToSignificantFigures.roundToDecimal;
import static com.anrisoftware.globalpom.measurement.RoundToSignificantFigures.roundToSignificant;
import static java.lang.Double.isNaN;
import static java.lang.Math.min;

import java.io.Serializable;

import javax.inject.Inject;

import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.math3.util.FastMath;

/**
 * Implements exact value's operators.
 * 
 * @author Erwin Mueller, erwin.mueller@deventm.org
 * @since 1.9
 */
@SuppressWarnings("serial")
public abstract class AbstractValue implements Value, Serializable {

    private static final double DEFAULT_DIV = 3.0;

    private static final double EPSILON = 10e-9;

    private final double value;

    private final int significant;

    private final double uncertainty;

    private final int decimal;

    private transient ValueFactory valueFactory;

    @Inject
    private ValueToString toString;

    /**
     * @see ValueFactory#create(Value, ValueFactory)
     */
    protected AbstractValue(Value value, ValueFactory valueFactory) {
        this(value.getValue(), value.getSignificant(), value.getUncertainty(), value.getDecimal(), valueFactory);
    }

    /**
     * @see ValueFactory#create(double, int, double, int, ValueFactory)
     */
    protected AbstractValue(double value, int sig, double un, int dec, ValueFactory valueFactory) {
        this.value = value;
        this.significant = sig;
        this.uncertainty = un;
        this.decimal = dec;
        this.valueFactory = valueFactory;
    }

    public void setValueFactory(ValueFactory factory) {
        this.valueFactory = factory;
    }

    public ValueFactory getValueFactory() {
        return valueFactory;
    }

    @Override
    public Value valueOf(double value) {
        return createValue(value);
    }

    @Override
    public Value valueOf(double value, int sig, double un, int dec) {
        return createValue(value, sig, un, dec);
    }

    @Override
    public double getValue() {
        return value;
    }

    @Override
    public Value getRoundedValue() {
        int sig = this.significant;
        int dec = this.decimal;
        return roundedValue(sig, dec);
    }

    @Override
    public Value roundedValue(int sig, int dec) {
        double v = this.value;
        double un = this.uncertainty;
        if (isExact()) {
            return createValue(v, sig, un, dec);
        } else {
            double value = roundToDecimal(v, sig);
            double signi = roundToSignificant(un, roundedSignificantFigure(sig));
            return createValue(value, sig, signi, dec);
        }
    }

    private int roundedSignificantFigure(int sig) {
        double v = FastMath.abs(getUncertainty());
        while (v < 0.0) {
            v *= 10;
        }
        if (v >= 1.0) {
            return sig + 1;
        } else {
            return sig;
        }
    }

    @Override
    public int getSignificant() {
        return significant;
    }

    @Override
    public double getUncertainty() {
        return uncertainty;
    }

    @Override
    public double getMinValue() {
        return minValue(DEFAULT_DIV);
    }

    @Override
    public double minValue(double deviation) {
        if (isExact()) {
            return getValue();
        } else {
            return getValue() - (deviation * getUncertainty());
        }
    }

    @Override
    public double getMaxValue() {
        return maxValue(DEFAULT_DIV);
    }

    @Override
    public double maxValue(double deviation) {
        if (isExact()) {
            return getValue();
        } else {
            return getValue() + (deviation * getUncertainty());
        }
    }

    @Override
    public boolean isExact() {
        return isNaN(uncertainty);
    }

    @Override
    public int getDecimal() {
        return decimal;
    }

    @Override
    public Value add(Value addend) {
        double value = this.value + addend.getValue();
        double uncertainty = addUncertainty(addend, value);
        int sig = min(significant, addend.getSignificant());
        int dec = min(decimal, addend.getDecimal());
        return createValue(value, sig, uncertainty, dec);
    }

    @Override
    public Value plus(Value addend) {
        return add(addend);
    }

    @Override
    public Value add(double addend) {
        return add(createValue(addend));
    }

    @Override
    public Value plus(double addend) {
        return add(addend);
    }

    /**
     * Adds the uncertainty of this value and the addend.
     * 
     * @param addend
     *            the {@link Value} addend.
     * 
     * @param sum
     *            the value of the sum.
     * 
     * @return the added uncertainly or {@link Double#NaN} if the value is
     *         exact.
     */
    protected abstract double addUncertainty(Value addend, double sum);

    @Override
    public Value sub(Value subtrahend) {
        double value = this.value - subtrahend.getValue();
        double uncertainty = subUncertainty(subtrahend, value);
        int sig = min(significant, subtrahend.getSignificant());
        int dec = min(decimal, subtrahend.getDecimal());
        return createValue(value, sig, uncertainty, dec);
    }

    @Override
    public Value minus(Value subtrahend) {
        return sub(subtrahend);
    }

    @Override
    public Value sub(double subtrahend) {
        return sub(createValue(subtrahend));
    }

    @Override
    public Value minus(double subtrahend) {
        return sub(subtrahend);
    }

    /**
     * Subtracts the uncertainty of this value and the subtrahend.
     * 
     * @param subtrahend
     *            the {@link Value} subtrahend.
     * 
     * @param diff
     *            the value of the difference.
     * 
     * @return the subtracted uncertainly.
     */
    protected abstract double subUncertainty(Value subtrahend, double diff);

    @Override
    public Value mul(Value factor) {
        double value = this.value * factor.getValue();
        double uncertainty = mulUncertainty(factor, value);
        int sig = min(significant, factor.getSignificant());
        int dec = min(decimal, factor.getDecimal());
        return createValue(value, sig, uncertainty, dec);
    }

    @Override
    public Value multiply(Value factor) {
        return mul(factor);
    }

    @Override
    public Value mul(double factor) {
        return mul(createValue(factor));
    }

    @Override
    public Value multiply(double factor) {
        return mul(factor);
    }

    /**
     * Multiplies the uncertainty of this value and the factor.
     * 
     * @param factor
     *            the {@link Value} factor.
     * 
     * @param product
     *            the value of the product.
     * 
     * @return the multiplied uncertainly.
     */
    protected abstract double mulUncertainty(Value factor, double product);

    @Override
    public Value div(double divisor) {
        return div(createValue(divisor));
    }

    @Override
    public Value div(Value divisor) {
        double value = this.value / divisor.getValue();
        double uncertainty = divUncertainty(divisor, value);
        int sig = min(significant, divisor.getSignificant());
        int dec = min(decimal, divisor.getDecimal());
        return createValue(value, sig, uncertainty, dec);
    }

    @Override
    public Value divNum(double numerator) {
        return createValue(numerator).div(this);
    }

    /**
     * Divides the uncertainty of this value and the divisor.
     * 
     * @param divisor
     *            the {@link Value} divisor.
     * 
     * @param quotient
     *            the value of the quotient.
     * 
     * @return the divided uncertainly.
     */
    protected abstract double divUncertainty(Value divisor, double quotient);

    @Override
    public Value reciprocal() {
        double value = 1.0 / this.value;
        double uncertainty = reciprocalUncertainty(value);
        int sig = significant;
        int dec = decimal;
        return createValue(value, sig, uncertainty, dec);
    }

    /**
     * Calculates the uncertainty of this value as reciprocal.
     * 
     * @param value
     *            the value.
     * 
     * @return the reciprocal uncertainly.
     */
    protected abstract double reciprocalUncertainty(double value);

    @Override
    public Value log() {
        double value = FastMath.log(this.value);
        double uncertainty = logUncertainty(value);
        int sig = significant;
        int dec = decimal;
        return createValue(value, sig, uncertainty, dec);
    }

    /**
     * Logarithm the uncertainty of this value.
     * 
     * @param exponent
     *            the value of the exponent.
     * 
     * @return the logarithm uncertainly.
     */
    protected abstract double logUncertainty(double exponent);

    @Override
    public Value exp() {
        double value = FastMath.exp(this.value);
        double uncertainty = expUncertainty(value);
        int sig = significant;
        int dec = decimal;
        return createValue(value, sig, uncertainty, dec);
    }

    /**
     * Power to the basis of e the uncertainty of this value.
     * 
     * @param power
     *            the value of the power.
     * 
     * @return the power uncertainly.
     */
    protected abstract double expUncertainty(double power);

    @Override
    public Value abs() {
        double value = FastMath.abs(this.value);
        double uncertainty = absUncertainty(value);
        int sig = significant;
        int dec = decimal;
        return createValue(value, sig, uncertainty, dec);
    }

    /**
     * Absolute uncertainty of this value.
     * 
     * @param value
     *            the absolute value.
     * 
     * @return the uncertainly.
     */
    protected abstract double absUncertainty(double value);

    /**
     * Create a new value.
     * 
     * @param value
     *            the value.
     * 
     * @param sig
     *            the significant figures of the value.
     * 
     * @param un
     *            the uncertainty or {@link Double#NaN} for an exact number.
     * 
     * @param dec
     *            the least significant decimal places.
     * 
     * @return the {@link Value}.
     */
    protected Value createValue(double value, int sig, double un, int dec) {
        return valueFactory.create(value, sig, un, dec, valueFactory);
    }

    /**
     * Create a new exact value.
     * 
     * @param value
     *            the value.
     * 
     * @return the exact {@link Value}.
     */
    protected abstract Value createValue(double value);

    @Override
    public int compareTo(Object o) {
        if (o instanceof Value) {
            return compare((Value) o);
        }
        if (o instanceof Number) {
            return compare((Number) o);
        }
        throw new ClassCastException();
    }

    @Override
    public int compare(Value value) {
        return compare(value, DEFAULT_DIV);
    }

    @Override
    public int compare(Value value, double dev) {
        if (equals(value, dev)) {
            return 0;
        }
        Value delta = this.sub(value).getRoundedValue();
        return delta.isExact() ? compareExact(delta) : compareDelta(delta, dev);
    }

    private int compareExact(Value delta) {
        double d = delta.getValue();
        if (d < 0.0) {
            return -1;
        } else {
            return 1;
        }
    }

    private int compareDelta(Value delta, double dev) {
        double deltavalue = delta.getValue();
        double deltaunc = delta.getUncertainty();
        double d = deltavalue - (deltaunc * dev);
        if (d < 0.0) {
            return -1;
        } else {
            return 1;
        }
    }

    @Override
    public int compare(Number value) {
        return compare(value, DEFAULT_DIV);
    }

    @Override
    public int compare(Number value, double dev) {
        return compare(createValue(value.doubleValue()), dev);
    }

    @Override
    public boolean equals(Object obj) {
        return equals(obj, DEFAULT_DIV);
    }

    @Override
    public boolean equals(Object obj, double dev) {
        if (obj == null) {
            return false;
        }
        if (obj == this) {
            return true;
        }
        Value rhs = asValue(obj);
        return rhs == null ? false : rhs.isExact() ? equalExact(this, rhs) : equalsUncertain(rhs, dev);
    }

    private Value asValue(Object obj) {
        if (obj instanceof Value) {
            return (Value) obj;
        } else if (obj instanceof Number) {
            return createValue(((Number) obj).doubleValue());
        }
        return null;
    }

    private boolean equalsUncertain(Value rhs, double dev) {
        Value delta = this.sub(rhs).getRoundedValue();
        double deltavalue = FastMath.abs(delta.getValue());
        double deltaunc = delta.getUncertainty();
        return deltavalue - (deltaunc * dev) <= 0.0;
    }

    private boolean equalExact(Value lhs, Value rhs) {
        return FastMath.abs(lhs.getValue() - rhs.getValue()) < EPSILON;
    }

    @Override
    public int hashCode() {
        return new Double(value).hashCode();
    }

    @Override
    public String toString() {
        StringBuffer buff = new StringBuffer();
        toString.format(buff, this);
        return new ToStringBuilder(this).append(buff.toString()).build();
    }

}