values = new ArrayList<>(others.size());
for (final LinearUnit unit : others.subList(0, others.size() - 1)) {
final LinearUnitValue remainingInUnit = remaining.convertTo(unit);
final LinearUnitValue value = getExact(unit,
Math.floor(remainingInUnit.getValueExact() + 1e-12));
values.add(value);
remaining = remaining.minus(value);
}
final LinearUnitValue lastValue = remaining
.convertTo(others.get(others.size() - 1));
values.add(lastValue);
return values;
}
/**
* Divides this value by a scalar
*
* @param divisor value to divide by
* @return multiplied value
* @since 2020-07-28
* @since v0.3.0
*/
public LinearUnitValue dividedBy(final double divisor) {
return LinearUnitValue.of(this.unit, this.value.dividedByExact(divisor));
}
/**
* Divides this value by another value
*
* @param divisor value to multiply by
* @return quotient
* @since 2020-07-28
* @since v0.3.0
*/
public LinearUnitValue dividedBy(final LinearUnitValue divisor) {
return LinearUnitValue.of(this.unit.dividedBy(divisor.unit),
this.value.dividedBy(divisor.value));
}
/**
* Returns true if this and obj represent the same value, regardless of
* whether or not they are expressed in the same unit. So (1000 m).equals(1
* km) returns true.
*
* @since 2020-07-26
* @since v0.3.0
* @see #equals(Object, boolean)
*/
@Override
public boolean equals(final Object obj) {
if (!(obj instanceof LinearUnitValue))
return false;
final LinearUnitValue other = (LinearUnitValue) obj;
return Objects.equals(this.unit.getBase(), other.unit.getBase())
&& this.unit.convertToBase(this.value)
.equals(other.unit.convertToBase(other.value));
}
/**
* Returns true if this and obj represent the same value, regardless of
* whether or not they are expressed in the same unit. So (1000 m).equals(1
* km) returns true.
*
* @param obj object to test equality with
* @param avoidFPErrors if true, this method will attempt to avoid
* floating-point errors, at the cost of not always
* being transitive.
* @return true iff this and obj are equal
*
* @since 2020-07-28
* @since v0.3.0
*/
public boolean equals(final Object obj, final boolean avoidFPErrors) {
if (!avoidFPErrors)
return this.equals(obj);
if (!(obj instanceof LinearUnitValue))
return false;
final LinearUnitValue other = (LinearUnitValue) obj;
return Objects.equals(this.unit.getBase(), other.unit.getBase())
&& DecimalComparison.equals(this.unit.convertToBase(this.value),
other.unit.convertToBase(other.value));
}
/**
* @param other another {@code LinearUnitValue}
* @return true iff this and other are within each other's uncertainty range
*
* @since 2020-07-26
* @since v0.3.0
*/
public boolean equivalent(final LinearUnitValue other) {
if (other == null
|| !Objects.equals(this.unit.getBase(), other.unit.getBase()))
return false;
final LinearUnit base = LinearUnit.valueOf(this.unit.getBase(), 1);
final LinearUnitValue thisBase = this.convertTo(base);
final LinearUnitValue otherBase = other.convertTo(base);
return thisBase.value.equivalent(otherBase.value);
}
/**
* @return the unit
* @since 2020-09-29
* @since v0.3.0
*/
public final LinearUnit getUnit() {
return this.unit;
}
/**
* @return the value
* @since 2020-09-29
* @since v0.3.0
*/
public final UncertainDouble getValue() {
return this.value;
}
/**
* @return the exact value
* @since 2020-09-07
* @since v0.3.0
*/
public final double getValueExact() {
return this.value.value();
}
@Override
public int hashCode() {
return Objects.hash(this.unit.getBase(),
this.unit.convertToBase(this.getValue()));
}
/**
* Returns the difference of this value and another, expressed in this
* value's unit
*
* @param subtrahend value to subtract
* @return difference of values
* @throws IllegalArgumentException if {@code subtrahend} has a unit that is
* not compatible for addition
* @since 2020-07-26
* @since v0.3.0
*/
public LinearUnitValue minus(final LinearUnitValue subtrahend) {
Objects.requireNonNull(subtrahend, "subtrahend may not be null");
if (!this.canConvertTo(subtrahend.unit))
throw new IllegalArgumentException(String.format(
"Incompatible units for subtraction \"%s\" and \"%s\".",
this.unit, subtrahend.unit));
final LinearUnitValue otherConverted = subtrahend.convertTo(this.unit);
return LinearUnitValue.of(this.unit,
this.value.minus(otherConverted.value));
}
/**
* Returns the sum of this value and another, expressed in this value's unit
*
* @param addend value to add
* @return sum of values
* @throws IllegalArgumentException if {@code addend} has a unit that is not
* compatible for addition
* @since 2020-07-26
* @since v0.3.0
*/
public LinearUnitValue plus(final LinearUnitValue addend) {
Objects.requireNonNull(addend, "addend may not be null");
if (!this.canConvertTo(addend.unit))
throw new IllegalArgumentException(String.format(
"Incompatible units for addition \"%s\" and \"%s\".", this.unit,
addend.unit));
final LinearUnitValue otherConverted = addend.convertTo(this.unit);
return LinearUnitValue.of(this.unit,
this.value.plus(otherConverted.value));
}
/**
* Multiplies this value by a scalar
*
* @param multiplier value to multiply by
* @return multiplied value
* @since 2020-07-28
* @since v0.3.0
*/
public LinearUnitValue times(final double multiplier) {
return LinearUnitValue.of(this.unit, this.value.timesExact(multiplier));
}
/**
* Multiplies this value by another value
*
* @param multiplier value to multiply by
* @return product
* @since 2020-07-28
* @since v0.3.0
*/
public LinearUnitValue times(final LinearUnitValue multiplier) {
return LinearUnitValue.of(this.unit.times(multiplier.unit),
this.value.times(multiplier.value));
}
/**
* Raises a value to an exponent
*
* @param exponent exponent to raise to
* @return result of exponentiation
* @since 2020-07-28
* @since v0.3.0
*/
public LinearUnitValue toExponent(final int exponent) {
return LinearUnitValue.of(this.unit.toExponent(exponent),
this.value.toExponentExact(exponent));
}
/**
* Raises this value to an exponent, rounding all dimensions to integers.
*
* @param exponent exponent to raise this value to
* @return result of exponentation
*
* @since 2024-08-22
* @since v1.0.0
* @see ObjectProduct#toExponentRounded
*/
public LinearUnitValue toExponentRounded(final double exponent) {
return LinearUnitValue.of(this.unit.toExponentRounded(exponent),
this.value.toExponentExact(exponent));
}
@Override
public String toString() {
return this.toString(!this.value.isExact(), RoundingMode.HALF_EVEN);
}
/**
* Returns a string representing the object.
* If the attached unit has a name or symbol, the string looks like "12 km".
* Otherwise, it looks like "13 unnamed unit (= 2 m/s)".
*
* If showUncertainty is true, strings like "35 � 8" are shown instead of
* single numbers.
*
* Non-exact values are rounded intelligently based on their uncertainty.
*
* @param showUncertainty whether to show the value's uncertainty
* @param roundingMode how to round numbers in this string
* @return string representing this value
*
* @since 2020-07-26
* @since v0.3.0
*/
public String toString(final boolean showUncertainty,
RoundingMode roundingMode) {
final Optional primaryName = this.unit.getPrimaryName();
final Optional symbol = this.unit.getSymbol();
final String chosenName = symbol.orElse(primaryName.orElse(null));
final UncertainDouble baseValue = this.unit.convertToBase(this.value);
// get rounded strings
// if showUncertainty is true, add brackets around the string
final String valueString = (showUncertainty ? "(" : "")
+ this.value.toString(showUncertainty, roundingMode)
+ (showUncertainty ? ")" : "");
final String baseValueString = (showUncertainty ? "(" : "")
+ baseValue.toString(showUncertainty, roundingMode)
+ (showUncertainty ? ")" : "");
// create string
if (chosenName == null)
return String.format("%s unnamed unit (= %s %s)", valueString,
baseValueString, this.unit.getBase()
.toString(unit -> unit.getSymbol().orElseThrow()));
else
return String.format("%s %s", valueString, chosenName);
}
}