/*
 * JScience - Java(TM) Tools and Libraries for the Advancement of Sciences.
 * Copyright (C) 2006 - JScience (http://jscience.org/)
 * All rights reserved.
 * 
 * Permission to use, copy, modify, and distribute this software is
 * freely granted, provided that this notice is preserved.
 */
package org.jscience.physics.amount;

import java.io.Serializable;

import javolution.context.ObjectFactory;
import javolution.lang.Immutable;
import javolution.lang.MathLib;
import javolution.text.Text;
import javolution.util.FastComparator;
import javolution.util.FastMap;
import javolution.xml.XMLFormat;
import javolution.xml.stream.XMLStreamException;

import org.jscience.mathematics.structure.Field;

import javax.measure.converter.ConversionException;
import javax.measure.converter.RationalConverter;
import javax.measure.converter.UnitConverter;
import javax.measure.quantity.Dimensionless;
import javax.measure.quantity.Quantity;
import javax.measure.unit.Unit;
import javax.measure.Measurable;
import javax.realtime.MemoryArea;

/**
 * 

This class represents a determinate or estimated amount for which * operations such as addition, subtraction, multiplication and division * can be performed (it implements the {@link Field} interface).

* *

The nature of an amount can be deduced from its parameterization * (compile time) or its {@link #getUnit() unit} (run time). * Its precision is given by its {@link #getAbsoluteError() error}.

* *

Amounts can be {@link #isExact() exact}, in which case they can be * expressed as an exact long integer in the amount unit. * The framework tries to keep amount exact as much as possible. * For example:[code] * Amount m = Amount.valueOf(33, FOOT).divide(11).times(2); * System.out.println(m); * System.out.println(m.isExact() ? "exact" : "inexact"); * System.out.println(m.getExactValue()); * > 6 ft * > exact * > 6[/code] *

* *

Errors (including numeric errors) are calculated using numeric intervals. * It is possible to resolve systems of linear equations involving * {@link org.jscience.mathematics.vector.Matrix matrices}, even if the * system is close to singularity; in which case the error associated with * some (or all) components of the solution may be large.

* *

By default, non-exact amounts are shown using the plus/minus * character ('±') (see {@link AmountFormat}). For example, * "(2.0 ± 0.001) km/s" represents a velocity of * 2 km/s with an absolute error of ± 1 m/s. Exact amount use an * integer notation (no decimal point, e.g. "2000 m").

* *

Operations between different amounts may or may not be authorized * based upon the current {@link org.jscience.physics.model.PhysicalModel * PhysicalModel}. For example, adding Amount<Length> and * Amount<Duration> is not allowed by the * {@link org.jscience.physics.model.StandardModel StandardModel}, * but is authorized with the {@link * org.jscience.physics.model.RelativisticModel RelativisticModel}.

* * @author Jean-Marie Dautelle * @version 4.0, June 4, 2007 * @see * Wikipedia: Measuring */ public final class Amount implements Measurable, Field>, Serializable, Immutable { //////////////////////////////////////////////////////////////////////////////// // Note: In the future, Amount might be abstract (with more measure types) // // We don't provide public constructors, factory methods should be used.// //////////////////////////////////////////////////////////////////////////////// /** * Holds a dimensionless measure of zero (exact). */ public static final Amount ZERO = new Amount(); static { ZERO._unit = Unit.ONE; ZERO._isExact = true; ZERO._exactValue = 0L; ZERO._minimum = 0; ZERO._maximum = 0; } /** * Holds a dimensionless measure of one (exact). */ public static final Amount ONE = new Amount(); static { ONE._unit = Unit.ONE; ONE._isExact = true; ONE._exactValue = 1L; ONE._minimum = 1.0; ONE._maximum = 1.0; } /** * Holds the default XML representation for measures. * This representation consists of a value, * an unit and an optional error attribute * when the measure is not exact. * The unit attribute determinates the measurement type. For example:
     * <Amount value="12" unit="µA"/>
* represents an electric current measurement. */ @SuppressWarnings("unchecked") protected static final XMLFormat XML = new XMLFormat( Amount.class) { @Override public Amount newInstance(Class cls, InputElement xml) throws XMLStreamException { Unit unit = Unit.valueOf(xml.getAttribute("unit")); Amount m = Amount.newInstance(unit); if (xml.getAttribute("error") == null) // Exact. return m.setExact(xml.getAttribute("value", 0L)); m._isExact = false; double estimatedValue = xml.getAttribute("value", 0.0); double error = xml.getAttribute("error", 0.0); m._minimum = estimatedValue - error; m._maximum = estimatedValue + error; return m; } @Override public void read(javolution.xml.XMLFormat.InputElement arg0, Amount arg1) throws XMLStreamException { // Nothing to do. } @Override public void write(Amount m, OutputElement xml) throws XMLStreamException { if (m._isExact) { xml.setAttribute("value", m._exactValue); } else { xml.setAttribute("value", m.getEstimatedValue()); xml.setAttribute("error", m.getAbsoluteError()); } xml.setAttribute("unit", m._unit.toString()); } }; /** * Returns the exact measure corresponding to the value stated in the * specified unit. * * @param value the exact value stated in the specified unit. * @param unit the unit in which the value is stated. * @return the corresponding measure object. */ public static Amount valueOf(long value, Unit unit) { Amount m = Amount.newInstance(unit); return m.setExact(value); } /** * Returns the measure corresponding to an approximate value * (double) stated in the specified unit; * the precision of the measure is assumed to be the * double precision (64 bits IEEE 754 format). * * @param value the estimated value (± LSB) stated in the specified unit. * @param unit the unit in which the value is stated. * @return the corresponding measure object. */ public static Amount valueOf(double value, Unit unit) { Amount m = Amount.newInstance(unit); m._isExact = false; double valInc = value * INCREMENT; double valDec = value * DECREMENT; m._minimum = (value < 0) ? valInc : valDec; m._maximum = (value < 0) ? valDec : valInc; return m; } /** * Returns the measure corresponding to the specified approximate value * and measurement error, both stated in the specified unit. * * @param value the estimated amount (± error) stated in the specified unit. * @param error the measurement error (absolute). * @param unit the unit in which the amount and the error are stated. * @return the corresponding measure object. * @throws IllegalArgumentException if error < 0.0 */ public static Amount valueOf(double value, double error, Unit unit) { if (error < 0) throw new IllegalArgumentException("error: " + error + " is negative"); Amount m = Amount.newInstance(unit); double min = value - error; double max = value + error; m._isExact = false; m._minimum = (min < 0) ? min * INCREMENT : min * DECREMENT; m._maximum = (max < 0) ? max * DECREMENT : max * INCREMENT; return m; } /** * Returns the measure corresponding to the specified interval stated * in the specified unit. * * @param minimum the lower bound for the measure value. * @param maximum the upper bound for the measure value. * @param unit the unit for both the minimum and maximum values. * @return the corresponding measure object. * @throws IllegalArgumentException if minimum > maximum */ public static Amount rangeOf(double minimum, double maximum, Unit unit) { if (minimum > maximum) throw new IllegalArgumentException("minimum: " + minimum + " greater than maximum: " + maximum); Amount m = Amount.newInstance(unit); m._isExact = false; m._minimum = (minimum < 0) ? minimum * INCREMENT : minimum * DECREMENT; m._maximum = (maximum < 0) ? maximum * DECREMENT : maximum * INCREMENT; return m; } /** * Returns the measure represented by the specified character sequence. * * @param csq the character sequence. * @return AmountFormat.getInstance().parse(csq) */ public static Amount valueOf(CharSequence csq) { return AmountFormat.getInstance().parse(csq); } /** * Indicates if this measure is exact. */ private boolean _isExact; /** * Holds the exact value (when exact) stated in this measure unit. */ private long _exactValue; /** * Holds the minimum value stated in this measure unit. * For inexact measures: _minimum < _maximum */ private double _minimum; /** * Holds the maximum value stated in this measure unit. * For inexact measures: _maximum > _minimum */ private double _maximum; /** * Holds this measure unit. */ private Unit _unit; /** * Indicates if this measure amount is exact. An exact amount is * guarantee exact only when stated in this measure unit * (e.g. this.longValue()); stating the amount * in any other unit may introduce conversion errors. * * @return true if this measure is exact; * false otherwise. */ public boolean isExact() { return _isExact; } /** * Returns the unit in which the {@link #getEstimatedValue() * estimated value} and {@link #getAbsoluteError() absolute error} * are stated. * * @return the measure unit. */ public Unit getUnit() { return _unit; } /** * Returns the exact value for this measure stated in this measure * {@link #getUnit unit}. * * @return the exact measure value (long) stated * in this measure's {@link #getUnit unit} * @throws AmountException if this measure is not {@link #isExact()} */ public long getExactValue() throws AmountException { if (!_isExact) throw new AmountException( "Inexact measures don't have exact values"); return _exactValue; } /** * Returns the estimated value for this measure stated in this measure * {@link #getUnit unit}. * * @return the median value of the measure interval. */ public double getEstimatedValue() { return (_isExact) ? _exactValue : (_minimum + _maximum) * 0.5; } /** * Returns the lower bound interval value for this measure stated in * this measure unit. * * @return the minimum value. */ public double getMinimumValue() { return _minimum; } /** * Returns the upper bound interval value for this measure stated in * this measure unit. * * @return the maximum value. */ public double getMaximumValue() { return _maximum; } /** * Returns the value by which the{@link #getEstimatedValue() estimated * value} may differ from the true value (all stated in base units). * * @return the absolute error stated in base units. */ public double getAbsoluteError() { return MathLib.abs(_maximum - _minimum) * 0.5; } /** * Returns the percentage by which the estimated amount may differ * from the true amount. * * @return the relative error. */ public double getRelativeError() { return _isExact ? 0 : (_maximum - _minimum) / (_minimum + _maximum); } /** * Returns the measure equivalent to this measure but stated in the * specified unit. The returned measure may not be exact even if this * measure is exact due to conversion errors. * * @param unit the unit of the measure to be returned. * @return a measure equivalent to this measure but stated in the * specified unit. * @throws ConversionException if the current model does not allows for * conversion to the specified unit. */ @SuppressWarnings("unchecked") public Amount to(Unit unit) { if ((_unit == unit) || this._unit.equals(unit)) return (Amount) this; UnitConverter cvtr = Amount.converterOf(_unit, unit); if (cvtr == UnitConverter.IDENTITY) { // No conversion necessary. Amount result = Amount.copyOf(this); result._unit = unit; return result; } if (cvtr instanceof RationalConverter) { // Exact conversion. RationalConverter rc = (RationalConverter) cvtr; Amount result = this.times(rc.getDividend()).divide(rc.getDivisor()); result._unit = unit; return result; } Amount result = Amount.newInstance(unit); double min = cvtr.convert(_minimum); double max = cvtr.convert(_maximum); result._isExact = false; result._minimum = (min < 0) ? min * INCREMENT : min * DECREMENT; result._maximum = (max < 0) ? max * DECREMENT : max * INCREMENT; return result; } /** * Returns the opposite of this measure. * * @return -this. */ public Amount opposite() { Amount m = Amount.newInstance(_unit); if ((_isExact) && (_exactValue != Long.MAX_VALUE)) return m.setExact(-_exactValue); m._isExact = false; m._minimum = -_maximum; m._maximum = -_minimum; return m; } /** * Returns the sum of this measure with the one specified. * * @param that the measure to be added. * @return this + that. * @throws ConversionException if the current model does not allows for * these quantities to be added. */ @SuppressWarnings("unchecked") public Amount plus(Amount that) throws ConversionException { final Amount thatToUnit = that.to(_unit); Amount m = Amount.newInstance(_unit); if (this._isExact && thatToUnit._isExact) { long sumLong = this._exactValue + thatToUnit._exactValue; double sumDouble = ((double) this._exactValue) + ((double) thatToUnit._exactValue); if (sumLong == sumDouble) return m.setExact(sumLong); } double min = this._minimum + thatToUnit._minimum; double max = this._maximum + thatToUnit._maximum; m._isExact = false; m._minimum = (min < 0) ? min * INCREMENT : min * DECREMENT; m._maximum = (max < 0) ? max * DECREMENT : max * INCREMENT; return m; } /** * Returns the difference of this measure with the one specified. * * @param that the measure to be subtracted. * @return this - that. * @throws ConversionException if the current model does not allows for * these quantities to be subtracted. */ @SuppressWarnings("unchecked") public Amount minus(Amount that) throws ConversionException { final Amount thatToUnit = that.to(_unit); Amount m = Amount.newInstance(_unit); if (this._isExact && thatToUnit._isExact) { long diffLong = this._exactValue - thatToUnit._exactValue; double diffDouble = ((double) this._exactValue) - ((double) thatToUnit._exactValue); if (diffLong == diffDouble) return m.setExact(diffLong); } double min = this._minimum - thatToUnit._maximum; double max = this._maximum - thatToUnit._minimum; m._isExact = false; m._minimum = (min < 0) ? min * INCREMENT : min * DECREMENT; m._maximum = (max < 0) ? max * DECREMENT : max * INCREMENT; return m; } /** * Returns this measure scaled by the specified exact factor * (dimensionless). * * @param factor the scaling factor. * @return this · factor. */ public Amount times(long factor) { Amount m = Amount.newInstance(_unit); if (this._isExact) { long productLong = _exactValue * factor; double productDouble = ((double) _exactValue) * factor; if (productLong == productDouble) return m.setExact(productLong); } m._isExact = false; m._minimum = (factor > 0) ? _minimum * factor : _maximum * factor; m._maximum = (factor > 0) ? _maximum * factor : _minimum * factor; return m; } /** * Returns this measure scaled by the specified approximate factor * (dimensionless). * * @param factor the scaling factor. * @return this · factor. */ public Amount times(double factor) { Amount m = Amount.newInstance(_unit); double min = (factor > 0) ? _minimum * factor : _maximum * factor; double max = (factor > 0) ? _maximum * factor : _minimum * factor; m._isExact = false; m._minimum = (min < 0) ? min * INCREMENT : min * DECREMENT; m._maximum = (max < 0) ? max * DECREMENT : max * INCREMENT; return m; } /** * Returns the product of this measure with the one specified. * * @param that the measure multiplier. * @return this · that. */ @SuppressWarnings("unchecked") public Amount times(Amount that) { Unit unit = Amount.productOf(this._unit, that._unit); if (that._isExact) { Amount m = this.times(that._exactValue); m._unit = unit; return m; } Amount m = Amount.newInstance(unit); double min, max; if (_minimum >= 0) { if (that._minimum >= 0) { min = _minimum * that._minimum; max = _maximum * that._maximum; } else if (that._maximum < 0) { min = _maximum * that._minimum; max = _minimum * that._maximum; } else { min = _maximum * that._minimum; max = _maximum * that._maximum; } } else if (_maximum < 0) { if (that._minimum >= 0) { min = _minimum * that._maximum; max = _maximum * that._minimum; } else if (that._maximum < 0) { min = _maximum * that._maximum; max = _minimum * that._minimum; } else { min = _minimum * that._maximum; max = _minimum * that._minimum; } } else { if (that._minimum >= 0) { min = _minimum * that._maximum; max = _maximum * that._maximum; } else if (that._maximum < 0) { min = _maximum * that._minimum; max = _minimum * that._minimum; } else { // Both around zero. min = MathLib.min(_minimum * that._maximum, _maximum * that._minimum); max = MathLib.max(_minimum * that._minimum, _maximum * that._maximum); } } m._isExact = false; m._minimum = (min < 0) ? min * INCREMENT : min * DECREMENT; m._maximum = (max < 0) ? max * DECREMENT : max * INCREMENT; return m; } /** * Returns the multiplicative inverse of this measure. * If this measure is possibly zero, then the result is unbounded * (]-infinity, +infinity[). * * @return 1 / this. */ public Amount inverse() { Amount m = newInstance(Amount.inverseOf(_unit)); if ((_isExact) && (_exactValue == 1L)) { // Only one exact inverse: one m.setExact(1L); return m; } m._isExact = false; if ((_minimum <= 0) && (_maximum >= 0)) { // Encompass zero. m._minimum = Double.NEGATIVE_INFINITY; m._maximum = Double.POSITIVE_INFINITY; return m; } double min = 1.0 / _maximum; double max = 1.0 / _minimum; m._minimum = (min < 0) ? min * INCREMENT : min * DECREMENT; m._maximum = (max < 0) ? max * DECREMENT : max * INCREMENT; return m; } /** * Returns this measure divided by the specified exact divisor * (dimensionless). * * @param divisor the exact divisor. * @return this / divisor. * @throws ArithmeticException if this measure is exact and the * specified divisor is zero. */ public Amount divide(long divisor) { Amount m = Amount.newInstance(_unit); if (this._isExact) { long quotientLong = _exactValue / divisor; double quotientDouble = ((double) _exactValue) / divisor; if (quotientLong == quotientDouble) return m.setExact(quotientLong); } double min = (divisor > 0) ? _minimum / divisor : _maximum / divisor; double max = (divisor > 0) ? _maximum / divisor : _minimum / divisor; m._isExact = false; m._minimum = (min < 0) ? min * INCREMENT : min * DECREMENT; m._maximum = (max < 0) ? max * DECREMENT : max * INCREMENT; return m; } /** * Returns this measure divided by the specified approximate divisor * (dimensionless). * * @param divisor the approximated divisor. * @return this / divisor. */ public Amount divide(double divisor) { Amount m = Amount.newInstance(_unit); double min = (divisor > 0) ? _minimum / divisor : _maximum / divisor; double max = (divisor > 0) ? _maximum / divisor : _minimum / divisor; m._isExact = false; m._minimum = (min < 0) ? min * INCREMENT : min * DECREMENT; m._maximum = (max < 0) ? max * DECREMENT : max * INCREMENT; return m; } /** * Returns this measure divided by the one specified. * * @param that the measure divisor. * @return this / that. */ @SuppressWarnings("unchecked") public Amount divide(Amount that) { if (that._isExact) { Amount m = this.divide(that._exactValue); m._unit = Amount.productOf(this._unit, Amount .inverseOf(that._unit)); return m; } return this.times(that.inverse()); } /** * Returns the absolute value of this measure. * * @return |this|. */ public Amount abs() { return (_isExact) ? ((_exactValue < 0) ? this.opposite() : this) : (_minimum >= -_maximum) ? this : this.opposite(); } /** * Returns the square root of this measure. * * @return sqrt(this) * */ public Amount sqrt() { Amount m = Amount.newInstance(_unit.root(2)); if (this._isExact) { double sqrtDouble = MathLib.sqrt(_exactValue); long sqrtLong = (long) sqrtDouble; if (sqrtLong * sqrtLong == _exactValue) return m.setExact(sqrtLong); } double min = MathLib.sqrt(_minimum); double max = MathLib.sqrt(_maximum); m._isExact = false; m._minimum = (min < 0) ? min * INCREMENT : min * DECREMENT; m._maximum = (max < 0) ? max * DECREMENT : max * INCREMENT; return m; } /** * Returns the given root of this measure. * * @param n the root's order (n != 0). * @return the result of taking the given root of this quantity. * @throws ArithmeticException if n == 0. */ public Amount root(int n) { if (n == 0) throw new ArithmeticException("Root's order of zero"); if (n < 0) return this.root(-n).inverse(); if (n == 2) return this.sqrt(); Amount m = Amount.newInstance(_unit.root(n)); if (this._isExact) { double rootDouble = MathLib.pow(_exactValue, 1.0 / n); long rootLong = (long) rootDouble; long thisLong = rootLong; for (int i = 1; i < n; i++) { thisLong *= rootLong; } if (thisLong == _exactValue) return m.setExact(rootLong); } double min = MathLib.pow(_minimum, 1.0 / n); double max = MathLib.pow(_maximum, 1.0 / n); m._isExact = false; m._minimum = (min < 0) ? min * INCREMENT : min * DECREMENT; m._maximum = (max < 0) ? max * DECREMENT : max * INCREMENT; return m; } /** * Returns this measure raised at the specified exponent. * * @param exp the exponent. * @return thisexp */ public Amount pow(int exp) { if (exp < 0) return this.pow(-exp).inverse(); if (exp == 0) return ONE; Amount pow2 = this; Amount result = null; while (exp >= 1) { // Iteration. if ((exp & 1) == 1) { result = (result == null) ? pow2 : result.times(pow2); } pow2 = pow2.times(pow2); exp >>>= 1; } return result; } /** * Compares this measure with the specified measurable object. * * @param that the measure to compare with. * @return a negative integer, zero, or a positive integer as this measure * is less than, equal to, or greater than that measurable. * @throws ConversionException if the current model does not allows for * these measure to be compared. */ @SuppressWarnings("unchecked") public int compareTo(Measurable that) { double thatValue = that.doubleValue(_unit); return Double.compare(this.getEstimatedValue(), thatValue); } /** * Compares this measure against the specified object for strict * equality (same value interval and same units). * * @param that the object to compare with. * @return true if this measure is identical to that * measure; false otherwise. */ public boolean equals(Object that) { if (this == that) return true; if (!(that instanceof Amount)) return false; Amount m = (Amount) that; if (!_unit.equals(m._unit)) return false; if (_isExact != m._isExact) return false; if (_isExact && (this._exactValue == m._exactValue)) return true; if (_minimum != m._minimum) return false; if (_maximum != m._maximum) return false; return true; } /** * Returns the hash code for this measure. * * @return the hash code value. */ public int hashCode() { int h = Float.floatToIntBits((float) _minimum); h += ~(h << 9); h ^= (h >>> 14); h += (h << 4); return h ^ (h >>> 10); } /** * Indicates if this measure approximates that measure. * Measures are considered approximately equals if their value intervals * overlaps. It should be noted that less accurate measurements are * more likely to be approximately equals. It is therefore recommended * to ensure that the measurement error is not too large before testing * for approximate equality. * * @return this ≅ that */ @SuppressWarnings("unchecked") public boolean approximates(Amount that) { Amount thatToUnit = that.to(_unit); return (this._maximum >= thatToUnit._minimum) && (thatToUnit._maximum >= this._minimum); } /** * Indicates if this measure is ordered before that measure * (independently of the measure unit). * * @return this.compareTo(that) < 0. */ public boolean isLessThan(Amount that) { return this.compareTo(that) < 0; } /** * Indicates if this measure is ordered after that measure * (independently of the measure unit). * * @return this.compareTo(that) > 0. */ public boolean isGreaterThan(Amount that) { return this.compareTo(that) > 0; } /** * Compares this measure with that measure ignoring the sign. * * @return |this| > |that| */ public boolean isLargerThan(Amount that) { return this.abs().isGreaterThan(that.abs()); } /** * Returns the text representation of this measure. * * @return AmountFormat.getInstance().format(this) */ public Text toText() { return AmountFormat.getInstance().format(this); } /** * Returns the text representation of this amount as a * java.lang.String. * * @return toText().toString() */ public final String toString() { return toText().toString(); } // Implements Quantity. public double doubleValue(Unit unit) { return ((_unit == unit) || _unit.equals(unit)) ? this .getEstimatedValue() : this.to(unit).getEstimatedValue(); } // Implements Quantity. public final long longValue(Unit unit) { if (!_unit.equals(unit)) return this.to(unit).longValue(unit); if (_isExact) return _exactValue; double doubleValue = this.getEstimatedValue(); if ((doubleValue >= Long.MIN_VALUE) && (doubleValue <= Long.MAX_VALUE)) return Math.round(doubleValue); throw new ArithmeticException(doubleValue + " " + _unit + " cannot be represented as long"); } /////////////////// // Lookup tables // /////////////////// static final FastMap, FastMap, Unit>> MULT_LOOKUP = new FastMap, FastMap, Unit>>( "UNITS_MULT_LOOKUP").setKeyComparator(FastComparator.DIRECT); static final FastMap, Unit> INV_LOOKUP = new FastMap, Unit>( "UNITS_INV_LOOKUP").setKeyComparator(FastComparator.DIRECT); static final FastMap, FastMap, UnitConverter>> CVTR_LOOKUP = new FastMap, FastMap, UnitConverter>>( "UNITS_CVTR_LOOKUP").setKeyComparator(FastComparator.DIRECT); private static Unit productOf(Unit left, Unit right) { FastMap, Unit> leftTable = MULT_LOOKUP.get(left); if (leftTable == null) return calculateProductOf(left, right); Unit result = leftTable.get(right); if (result == null) return calculateProductOf(left, right); return result; } private static synchronized Unit calculateProductOf(final Unit left, final Unit right) { MemoryArea memoryArea = MemoryArea.getMemoryArea(MULT_LOOKUP); memoryArea.executeInArea(new Runnable() { public void run() { FastMap, Unit> leftTable = MULT_LOOKUP.get(left); if (leftTable == null) { leftTable = new FastMap, Unit>().setKeyComparator( FastComparator.DIRECT); MULT_LOOKUP.put(left, leftTable); } Unit result = leftTable.get(right); if (result == null) { result = left.times(right); leftTable.put(right, result); } } }); return MULT_LOOKUP.get(left).get(right); } private static Unit inverseOf(Unit unit) { Unit inverse = INV_LOOKUP.get(unit); if (inverse == null) return calculateInverseOf(unit); return inverse; } private static synchronized Unit calculateInverseOf(final Unit unit) { MemoryArea memoryArea = MemoryArea.getMemoryArea(INV_LOOKUP); memoryArea.executeInArea(new Runnable() { public void run() { Unit inverse = INV_LOOKUP.get(unit); if (inverse == null) { inverse = unit.inverse(); INV_LOOKUP.put(unit, inverse); } } }); return INV_LOOKUP.get(unit); } private static UnitConverter converterOf(Unit left, Unit right) { FastMap, UnitConverter> leftTable = CVTR_LOOKUP.get(left); if (leftTable == null) return calculateConverterOf(left, right); UnitConverter result = leftTable.get(right); if (result == null) return calculateConverterOf(left, right); return result; } private static synchronized UnitConverter calculateConverterOf(final Unit left, final Unit right) { MemoryArea memoryArea = MemoryArea.getMemoryArea(CVTR_LOOKUP); memoryArea.executeInArea(new Runnable() { public void run() { FastMap, UnitConverter> leftTable = CVTR_LOOKUP.get(left); if (leftTable == null) { leftTable = new FastMap, UnitConverter>() .setKeyComparator(FastComparator.DIRECT); synchronized (CVTR_LOOKUP) { CVTR_LOOKUP.put(left, leftTable); } } UnitConverter result = leftTable.get(right); if (result == null) { result = left.getConverterTo(right); synchronized (leftTable) { leftTable.put(right, result); } } } }); return CVTR_LOOKUP.get(left).get(right); } public Amount copy() { Amount estimate = Amount.newInstance(_unit); estimate._isExact = _isExact; estimate._exactValue = _exactValue; estimate._minimum = _minimum; estimate._maximum = _maximum; return estimate; } ////////////////////// // Factory Creation // ////////////////////// @SuppressWarnings("unchecked") private static Amount newInstance(Unit unit) { Amount measure = FACTORY.object(); measure._unit = (Unit)unit; return measure; } @SuppressWarnings("unchecked") private static Amount copyOf(Amount original) { Amount measure = FACTORY.object(); measure._exactValue = original._exactValue; measure._isExact = original._isExact; measure._maximum = original._maximum; measure._minimum = original._minimum; measure._unit = original._unit; return measure; } @SuppressWarnings("unchecked") private static final ObjectFactory FACTORY = new ObjectFactory() { @Override protected Amount create() { return new Amount(); } }; private Amount() { } private Amount setExact(long exactValue) { _isExact = true; _exactValue = exactValue; double doubleValue = exactValue; if (doubleValue == exactValue) { _minimum = doubleValue; _maximum = doubleValue; } else { double valInc = exactValue * INCREMENT; double valDec = exactValue * DECREMENT; _minimum = (_exactValue < 0) ? valInc : valDec; _maximum = (_exactValue < 0) ? valDec : valInc; } return this; } static final double DOUBLE_RELATIVE_ERROR = MathLib.pow(2, -53); static final double DECREMENT = (1.0 - DOUBLE_RELATIVE_ERROR); static final double INCREMENT = (1.0 + DOUBLE_RELATIVE_ERROR); private static final long serialVersionUID = 1L; }

Ads help maintain this website.