/*
 VisAD system for interactive analysis and visualization of numerical
 data.  Copyright (C) 1996 - 2017 Bill Hibbard, Curtis Rueden, Tom
 Rink, Dave Glowacki, Steve Emmerson, Tom Whittaker, Don Murray, and
 Tommy Jasmin.

 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Library General Public
 License as published by the Free Software Foundation; either
 version 2 of the License, or (at your option) any later version.

 This library 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
 Library General Public License for more details.

 You should have received a copy of the GNU Library General Public
 License along with this library; if not, write to the Free
 Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 MA 02111-1307, USA
 */

package visad;

import java.io.Serializable;

/**
 * A class that represents a scaled unit with an offset.
 * 
 * Instances are immutable.
 * 
 * @author Steven R. Emmerson
 * 
 *         This is part of Steve Emerson's Unit package that has been
 *         incorporated into VisAD.
 * 
 *         Instances are immutable.
 */
public final class LogarithmicUnit extends Unit implements Serializable {
    private static final long       serialVersionUID    = 1L;
    private static final Unit       ONE                 = new DerivedUnit();
    /**
     * The reference level.
     * 
     * @serial
     */
    private final Unit              reference;
    /**
     * The logarithmic base.
     * 
     * @serial
     */
    private final double            base;
    /**
     * The natural logarithm of the base (for computational efficiency).
     */
    private final transient double  lnBase;

    /**
     * Constructs from a reference level and a logarithmic base. The identifier
     * will be empty.
     * 
     * @param base
     *            The logarithmic base. Must be 2, {@link Math#E}, or 10.
     * @param reference
     *            The reference level.
     * @throws IllegalArgumentException
     *             if {@code base} isn't one of the allowed values.
     * @throws NullPointerException
     *             if {@code reference} is {@code null}.
     */
    private LogarithmicUnit(final double base, final Unit reference) {
        this(reference, base, "");
    }

    /**
     * Constructs from a reference level, a logarithmic base, and an identifier.
     * 
     * @param reference
     *            The reference level.
     * @param base
     *            The logarithmic base. Must be 2, {@link Math#E}, or 10.
     * @param identifier
     *            The name or abbreviation for the cloned unit. May be
     *            null or empty.
     * @throws IllegalArgumentException
     *             if {@code base} isn't one of the allowed values.
     * @throws NullPointerException
     *             if {@code reference} is {@code null}.
     */
    private LogarithmicUnit(final Unit reference, final double base,
            final String identifier) {
        super(identifier);
        if (reference == null) {
            throw new NullPointerException("Null reference level");
        }
        if (base != 2 && base != Math.E && base != 10) {
            throw new IllegalArgumentException("Invalid logarithmic base: "
                    + base);
        }
        this.reference = reference;
        this.base = base;
        lnBase = base == Math.E
                ? 1
                : Math.log(base);
    }

    static Unit getInstance(final double base, final Unit reference) {
        return new LogarithmicUnit(base, reference);
    }

    /**
     * Indicates if this instance is dimensionless. Logarithmic units are
     * dimensionless by definition.
     * 
     * @return {@code true} always.
     */
    @Override
    public boolean isDimensionless() {
        return true;
    }

    /**
     * Indicates if this instance is a unit of time.
     * 
     * @return {@code true} if and only if values in this unit are convertible
     *         with seconds.
     */
    protected boolean isTime() {
        return SI.second.isConvertible(reference);
    }

    /**
     * Clones this unit, changing the identifier.
     * 
     * @param identifier
     *            The name or abbreviation for the cloned unit. May be
     *            null or empty.
     * @return A unit equal to this unit but with the given identifier.
     */
    @Override
    protected Unit protectedClone(final String identifier) {
        return new LogarithmicUnit(reference, base, identifier);
    }

    @Override
    public Unit scale(final double scale) throws UnitException {
        return ScaledUnit.getInstance(scale, this);
    }

    @Override
    public Unit shift(final double offset) throws UnitException {
        return OffsetUnit.getInstance(offset, this);
    }

    @Override
    public Unit log(final double base) throws UnitException {
        throw new UnitException(
                "Can't form logarithmic unit from logarithmic unit: " + this);
    }

    /**
     * Raises this unit to a power. Only certain powers are meaningful.
     * 
     * @param power
     *            The power to raise this unit by. The only meaningful values
     *            are {@code 0} and {@code 1}.
     * @return The result of raising this unit to the given power.
     * @throws IllegalArgumentException
     *             if {@code power} isn't {@code 0} or {@code 1}.
     */
    @Override
    public Unit pow(final int power) {
        Unit result;
        if (power == 0) {
            result = ONE;
        }
        else if (power == 1) {
            result = this;
        }
        else {
            throw new IllegalArgumentException("Invalid power: " + power);
        }
        return result;
    }

    /**
     * Raises this unit to a power. Only certain powers are meaningful.
     * 
     * @param power
     *            The power to raise this unit by. The only meaningful values
     *            are 0 and 1.
     * @return The result of raising this unit to the given power.
     */
    @Override
    public Unit pow(final double power) {
        if (power != 0 || power != 1) {
            throw new IllegalArgumentException("Invalid power: " + power);
        }
        return pow((int) Math.round(power));
    }

    /**
     * Returns the N-th root of this unit. Only the 1st root is meaningful.
     * 
     * @param root
     *            The root to take. Must be {@code 1}.
     * @return This instance.
     * @throws IllegalArgumentException
     *             if {@code root} isn't {@code 1}.
     */
    @Override
    public Unit root(final int root) throws IllegalArgumentException {
        if (root != 1) {
            throw new IllegalArgumentException("Invalid root: " + root);
        }
        return this;
    }

    /**
     * Return the definition of this unit as a string.
     * 
     * @return The definition of this unit (e.g., {@code "lg(re 0.001 W)"}) for
     *         a Bel unit with a milliwatt reference level.
     */
    @Override
    public String getDefinition() {
        final StringBuilder buf = new StringBuilder(40);
        if (base == 2) {
            buf.append("lb");
        }
        else if (base == Math.E) {
            buf.append("ln");
        }
        else if (base == 10) {
            buf.append("lg");
        }
        else {
            throw new AssertionError("Invalid base: " + base);
        }
        buf.append("(re ");
        buf.append(reference.toString());
        buf.append(")");
        return buf.toString();
    }

    /**
     * Multiply this unit by another unit. Only certain other units are
     * meaningful.
     * 
     * @param that
     *            The unit with which to multiply this unit. Must be
     *            dimensionless.
     * @return A unit equal to this instance multiplied by the given unit.
     * @throws IllegalArgumentException
     *             if {@code that} isn't dimensionless.
     * @throws UnitException
     *             Can't multiply units.
     */
    @Override
    public Unit multiply(final Unit that) throws UnitException {
        if (!that.isDimensionless()) {
            throw new IllegalArgumentException("Not dimensionless: " + that);
        }
        final Unit result;
        if (that.equals(ONE)) {
            result = this;
        }
        else if (that instanceof LogarithmicUnit) {
            throw new UnitException("Can't multiply by: " + that);
        }
        else {
            result = that.multiply(this);
        }
        return result;
    }

    /**
     * Divide this unit by another unit. Only certain other units are
     * meaningful.
     * 
     * @param that
     *            The unit to divide into this unit. Must be dimensionless.
     * @return A unit equal to this instance divided by the given unit.
     * @exception UnitException
     *                Can't divide units.
     */
    @Override
    public Unit divide(final Unit that) throws UnitException {
        if (!that.isDimensionless()) {
            throw new IllegalArgumentException("Not dimensionless: " + that);
        }
        final Unit result;
        if (that.equals(ONE)) {
            result = this;
        }
        else if (that instanceof LogarithmicUnit) {
            throw new UnitException("Can't divide by: " + that);
        }
        else {
            result = that.divideInto(this);
        }
        return result;
    }

    /**
     * Divide this unit into another unit. This operation isn't meaningful.
     * 
     * @param that
     *            The unit to be divide by this unit.
     * @return Never
     * @throws UnitException
     *             if this method is called.
     */
    @Override
    protected Unit divideInto(final Unit that) throws UnitException {
        throw new UnitException("Can't divide into: " + that);
    }

    /**
     * Convert values to this unit from another unit.
     * 
     * @param values
     *            The values to be converted.
     * @param that
     *            The unit of values.
     * @return The converted values in units of this unit.
     * require: The units are convertible.
     * promise: Neither unit has been modified.
     * @throws UnitException
     *             The units are not convertible.
     */
    @Override
    public double[] toThis(final double[] values, final Unit that)
            throws UnitException {
        return toThis(values, that, true);
    }

    /**
     * Convert values to this unit from another unit.
     * 
     * @param values
     *            The values to be converted.
     * @param that
     *            The unit of values.
     * @return The converted values in units of this unit.
     * require: The units are convertible.
     * promise: Neither unit has been modified.
     * @throws UnitException
     *             The units are not convertible.
     */
    @Override
    public float[] toThis(final float[] values, final Unit that)
            throws UnitException {
        return toThis(values, that, true);
    }

    /**
     * Convert values to this unit from another unit.
     * 
     * @param values
     *            The values to be converted.
     * @param that
     *            The unit of values.
     * @param copy
     *            if false and that equals this, return
     *            values, else return a new array
     * @return The converted values in units of this unit.
     * require: The units are convertible.
     * promise: Neither unit has been modified.
     * @throws UnitException
     *             The units are not convertible.
     */
    @Override
    public double[] toThis(final double[] values, final Unit that,
            final boolean copy) throws UnitException {
        double[] newValues;
        if (equals(that) || that instanceof PromiscuousUnit) {
            newValues = (copy)
                    ? (double[]) values.clone()
                    : values;
        }
        else {
            newValues = that.toThat(values, reference, copy);
            for (int i = 0; i < newValues.length; ++i) {
                newValues[i] = Math.log(newValues[i]) / lnBase;
            }
        }
        return newValues;
    }

    /**
     * Convert values to this unit from another unit.
     * 
     * @param values
     *            The values to be converted.
     * @param that
     *            The unit of values.
     * @param copy
     *            if false, convert values in place.
     * @return The converted values in units of this unit.
     * require: The units are convertible.
     * promise: Neither unit has been modified.
     * @throws UnitException
     *             The units are not convertible.
     */
    @Override
    public float[] toThis(final float[] values, final Unit that,
            final boolean copy) throws UnitException {
        float[] newValues;
        if (equals(that) || that instanceof PromiscuousUnit) {
            newValues = (copy)
                    ? (float[]) values.clone()
                    : values;
        }
        else {
            newValues = that.toThat(values, reference, copy);
            for (int i = 0; i < newValues.length; ++i) {
                newValues[i] = (float) (Math.log(newValues[i]) / lnBase);
            }
        }
        return newValues;
    }

    /**
     * Convert values from this unit to another unit.
     * 
     * @param values
     *            The values to be converted in units of this unit.
     * @param that
     *            The unit to which to convert the values.
     * @return The converted values.
     * require: The units are convertible.
     * promise: Neither unit has been modified.
     * @throws UnitException
     *             The units are not convertible.
     */
    @Override
    public double[] toThat(final double values[], final Unit that)
            throws UnitException {
        return toThat(values, that, true);
    }

    /**
     * Convert values from this unit to another unit.
     * 
     * @param values
     *            The values to be converted in units of this unit.
     * @param that
     *            The unit to which to convert the values.
     * @return The converted values.
     * require: The units are convertible.
     * promise: Neither unit has been modified.
     * @throws UnitException
     *             The units are not convertible.
     */
    @Override
    public float[] toThat(final float values[], final Unit that)
            throws UnitException {
        return toThat(values, that, true);
    }

    /**
     * Convert values from this unit to another unit.
     * 
     * @param values
     *            The values to be converted in units of this unit.
     * @param that
     *            The unit to which to convert the values.
     * @param copy
     *            if false and that equals this, return a
     *            values, else return a new array
     * @return The converted values.
     * require: The units are convertible.
     * promise: Neither unit has been modified.
     * @throws UnitException
     *             The units are not convertible.
     */
    @Override
    public double[] toThat(final double values[], final Unit that,
            final boolean copy) throws UnitException {
        double[] newValues = (copy)
                ? (double[]) values.clone()
                : values;
        if (!(equals(that) || that instanceof PromiscuousUnit)) {
            for (int i = 0; i < newValues.length; ++i) {
                newValues[i] = Math.exp(newValues[i] * lnBase);
            }
            newValues = that.toThis(newValues, reference, copy);
        }
        return newValues;
    }

    /**
     * Convert values from this unit to another unit.
     * 
     * @param values
     *            The values to be converted in units of this unit.
     * @param that
     *            The unit to which to convert the values.
     * @param copy
     *            if false and that equals this, return a
     *            values, else return a new array
     * @return The converted values.
     * require: The units are convertible.
     * promise: Neither unit has been modified.
     * @throws UnitException
     *             The units are not convertible.
     */
    @Override
    public float[] toThat(final float values[], final Unit that,
            final boolean copy) throws UnitException {
        float[] newValues = (copy)
                ? (float[]) values.clone()
                : values;
        if (!(equals(that) || that instanceof PromiscuousUnit)) {
            for (int i = 0; i < newValues.length; ++i) {
                newValues[i] = (float) Math.exp(newValues[i] * lnBase);
            }
            newValues = that.toThis(newValues, reference, copy);
        }
        return newValues;
    }

    /**
     * Gets the absolute unit of this unit. An interval in the underlying
     * physical quantity has the same numeric value in an absolute unit of a
     * unit as in the unit itself -- but an absolute unit is always referenced
     * to the physical origin of the underlying physical quantity. For example,
     * the absolute unit corresponding to degrees celsius is degrees kelvin --
     * and calling this method on a degrees celsius unit obtains a degrees
     * kelvin unit.
     * 
     * @return The absolute unit corresponding to this unit.
     */
    @Override
    public Unit getAbsoluteUnit() {
        return reference.getAbsoluteUnit();
    }

    /**
     * Indicate whether this unit is convertible with another unit. If one unit
     * is convertible with another, then the toThis(...)/ and
     * toThat(...) methods will not throw a UnitException. Unit A
     * is convertible with unit B if and only if unit B is convertible with unit
     * A; hence, calling-order is irrelevant.
     * 
     * @param unit
     *            The other unit.
     * @return True if and only if this unit is convertible with the other unit.
     */
    @Override
    public boolean isConvertible(final Unit unit) {
        return reference.isConvertible(unit);
    }

    private static void myAssert(final boolean bool) {
        if (!bool) {
            throw new AssertionError();
        }
    }

    /**
     * Test this class.
     * 
     * @param args
     *            Arguments (ignored).
     * @exception UnitException
     *                A problem occurred.
     */
    public static void main(final String[] args) throws UnitException {
        final BaseUnit meter = SI.meter;
        final ScaledUnit micron = new ScaledUnit(1e-6, meter);
        final Unit cubicMicron = micron.pow(3);
        final LogarithmicUnit Bz = new LogarithmicUnit(10, cubicMicron);
        myAssert(Bz.isDimensionless());
        myAssert(Bz.equals(Bz));
        myAssert(Bz.getAbsoluteUnit().equals(cubicMicron));
        myAssert(!Bz.equals(cubicMicron));
        myAssert(!Bz.equals(micron));
        myAssert(!Bz.equals(meter));
        try {
            Bz.multiply(meter);
            myAssert(false);
        }
        catch (final Exception e) {
        }
        try {
            Bz.divide(meter);
            myAssert(false);
        }
        catch (final Exception e) {
        }
        try {
            Bz.pow(2);
            myAssert(false);
        }
        catch (final Exception e) {
        }
        double value = Bz.toThat(0, Bz.getAbsoluteUnit());
        myAssert(0.9 < value && value < 1.1);
        value = Bz.toThat(1, Bz.getAbsoluteUnit());
        myAssert(9 < value && value < 11);
        value = Bz.toThis(1, Bz.getAbsoluteUnit());
        myAssert(-0.1 < value && value < 0.1);
        value = Bz.toThis(10, Bz.getAbsoluteUnit());
        myAssert(0.9 < value && value < 1.1);
        final String string = Bz.toString();
        myAssert(string.equals("lg(re 9.999999999999999E-19 m3)"));
        System.out.println("Done");
    }

    /**
     * Indicates if this instance equals a unit.
     * 
     * @param unit
     *            The unit.
     * @return true if and only if this instance equals the unit.
     */
    @Override
    public boolean equals(final Unit unit) {
        if (this == unit) {
            return true;
        }
        if (!(unit instanceof LogarithmicUnit)) {
            return false;
        }
        final LogarithmicUnit that = (LogarithmicUnit) unit;
        return base == that.base && reference.equals(that.reference);
    }

    /**
     * Returns the hash code of this instance. {@link Object#hashCode()} should
     * be overridden whenever {@link Object#equals(Object)} is.
     * 
     * @return The hash code of this instance.
     */
    @Override
    public int hashCode() {
        if (hashCode == 0) {
            hashCode = reference.hashCode() ^ Double.valueOf(base).hashCode();
        }
        return hashCode;
    }

    @Override
    public DerivedUnit getDerivedUnit() {
        return reference.getDerivedUnit();
    }
}

 

Ads help maintain this website.