/*
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
* <code>null</code> 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
* <code>null</code> 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 <code>values</code>.
* @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 <code>values</code>.
* @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 <code>values</code>.
* @param copy
* if false and <code>that</code> equals this, return
* <code>values</code>, 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 <code>values</code>.
* @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 <code>that</code> equals this, return a
* <code>values</code>, 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 <code>that</code> equals this, return a
* <code>values</code>, 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 <code>toThis(...)</code>/ and
* <code>toThat(...)</code> 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 <code>true</code> 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();
}
}