summaryrefslogtreecommitdiff
path: root/src/main/java/sevenUnits/unit/LinearUnitValue.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/sevenUnits/unit/LinearUnitValue.java')
-rw-r--r--src/main/java/sevenUnits/unit/LinearUnitValue.java174
1 files changed, 127 insertions, 47 deletions
diff --git a/src/main/java/sevenUnits/unit/LinearUnitValue.java b/src/main/java/sevenUnits/unit/LinearUnitValue.java
index 3a9428b..ce60e3b 100644
--- a/src/main/java/sevenUnits/unit/LinearUnitValue.java
+++ b/src/main/java/sevenUnits/unit/LinearUnitValue.java
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2019 Adrien Hopkins
+ * Copyright (C) 2019, 2021, 2022, 2024, 2025 Adrien Hopkins
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@@ -17,33 +17,38 @@
package sevenUnits.unit;
import java.math.RoundingMode;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Objects;
-import java.util.Optional;
import sevenUnits.utils.DecimalComparison;
+import sevenUnits.utils.ObjectProduct;
import sevenUnits.utils.UncertainDouble;
/**
* A possibly uncertain value expressed in a linear unit.
- *
+ *
* Unless otherwise indicated, all methods in this class throw a
* {@code NullPointerException} when an argument is null.
- *
+ *
* @author Adrien Hopkins
* @since 2020-07-26
+ * @since v0.3.0
*/
public final class LinearUnitValue {
+ /** The value 1 as a LinearUnitValue. */
public static final LinearUnitValue ONE = getExact(Metric.ONE, 1);
/**
* Gets an exact {@code LinearUnitValue}
- *
+ *
* @param unit unit to express with
* @param value value to express
* @return exact {@code LinearUnitValue} instance
* @since 2020-07-26
+ * @since v0.3.0
*/
- public static final LinearUnitValue getExact(final LinearUnit unit,
+ public static LinearUnitValue getExact(final LinearUnit unit,
final double value) {
return new LinearUnitValue(
Objects.requireNonNull(unit, "unit must not be null"),
@@ -52,14 +57,14 @@ public final class LinearUnitValue {
/**
* Gets an uncertain {@code LinearUnitValue}
- *
- * @param unit unit to express with
- * @param value value to express
- * @param uncertainty absolute uncertainty of value
+ *
+ * @param unit unit to express with
+ * @param value value to express
* @return uncertain {@code LinearUnitValue} instance
* @since 2020-07-26
+ * @since v0.3.0
*/
- public static final LinearUnitValue of(final LinearUnit unit,
+ public static LinearUnitValue of(final LinearUnit unit,
final UncertainDouble value) {
return new LinearUnitValue(
Objects.requireNonNull(unit, "unit must not be null"),
@@ -74,6 +79,7 @@ public final class LinearUnitValue {
* @param unit unit to express as
* @param value value to express
* @since 2020-07-26
+ * @since v0.3.0
*/
private LinearUnitValue(final LinearUnit unit, final UncertainDouble value) {
this.unit = unit;
@@ -84,8 +90,9 @@ public final class LinearUnitValue {
* @return this value as a {@code UnitValue}. All uncertainty information is
* removed from the returned value.
* @since 2020-08-04
+ * @since v0.3.0
*/
- public final UnitValue asUnitValue() {
+ public UnitValue asUnitValue() {
return UnitValue.of(this.unit, this.value.value());
}
@@ -93,29 +100,68 @@ public final class LinearUnitValue {
* @param other a {@code LinearUnit}
* @return true iff this value can be represented with {@code other}.
* @since 2020-07-26
+ * @since v0.3.0
*/
- public final boolean canConvertTo(final LinearUnit other) {
+ public boolean canConvertTo(final LinearUnit other) {
return this.unit.canConvertTo(other);
}
/**
* Returns a LinearUnitValue that represents the same value expressed in a
* different unit
- *
+ *
* @param other new unit to express value in
* @return value expressed in {@code other}
* @since 2020-07-26
+ * @since v0.3.0
*/
- public final LinearUnitValue convertTo(final LinearUnit other) {
+ public LinearUnitValue convertTo(final LinearUnit other) {
return LinearUnitValue.of(other, this.unit.convertTo(other, this.value));
}
/**
+ * Convert a LinearUnitValue to a sum of multiple units, where all but the
+ * last have exact integer values.
+ *
+ * @param others units to convert to
+ * @return terms of the sum
+ * @throws IllegalArgumentException if no units are provided or units
+ * provided have incompatible bases
+ * @since 2024-08-15
+ * @since v1.0.0
+ */
+ public List<LinearUnitValue> convertToMultiple(
+ final List<LinearUnit> others) {
+ if (others.size() < 1)
+ throw new IllegalArgumentException("Must have at least one unit");
+ for (final LinearUnit unit : others) {
+ if (!Objects.equals(this.unit.getBase(), unit.getBase()))
+ throw new IllegalArgumentException(
+ "All provided units must have the same base as the value.");
+ }
+
+ var remaining = this;
+ final List<LinearUnitValue> values = new ArrayList<>(others.size());
+ for (final LinearUnit unit : others.subList(0, others.size() - 1)) {
+ final var remainingInUnit = remaining.convertTo(unit);
+ final var value = getExact(unit,
+ Math.floor(remainingInUnit.getValueExact() + 1e-12));
+ values.add(value);
+ remaining = remaining.minus(value);
+ }
+
+ final var 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));
@@ -123,10 +169,11 @@ public final class LinearUnitValue {
/**
* 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),
@@ -137,15 +184,16 @@ public final class LinearUnitValue {
* 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;
+ final var other = (LinearUnitValue) obj;
return Objects.equals(this.unit.getBase(), other.unit.getBase())
&& this.unit.convertToBase(this.value)
.equals(other.unit.convertToBase(other.value));
@@ -155,18 +203,22 @@ public final class LinearUnitValue {
* 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.
- * <p>
- * If avoidFPErrors is true, this method will attempt to avoid floating-point
- * errors, at the cost of not always being transitive.
- *
+ *
+ * @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;
+ final var other = (LinearUnitValue) obj;
return Objects.equals(this.unit.getBase(), other.unit.getBase())
&& DecimalComparison.equals(this.unit.convertToBase(this.value),
other.unit.convertToBase(other.value));
@@ -175,16 +227,17 @@ public final class LinearUnitValue {
/**
* @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);
+ final var base = LinearUnit.valueOf(this.unit.getBase(), 1);
+ final var thisBase = this.convertTo(base);
+ final var otherBase = other.convertTo(base);
return thisBase.value.equivalent(otherBase.value);
}
@@ -192,24 +245,27 @@ public final class LinearUnitValue {
/**
* @return the unit
* @since 2020-09-29
+ * @since v0.3.0
*/
- public final LinearUnit getUnit() {
+ public LinearUnit getUnit() {
return this.unit;
}
/**
* @return the value
* @since 2020-09-29
+ * @since v0.3.0
*/
- public final UncertainDouble getValue() {
+ public UncertainDouble getValue() {
return this.value;
}
/**
* @return the exact value
* @since 2020-09-07
+ * @since v0.3.0
*/
- public final double getValueExact() {
+ public double getValueExact() {
return this.value.value();
}
@@ -222,12 +278,13 @@ public final class LinearUnitValue {
/**
* 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");
@@ -237,19 +294,20 @@ public final class LinearUnitValue {
"Incompatible units for subtraction \"%s\" and \"%s\".",
this.unit, subtrahend.unit));
- final LinearUnitValue otherConverted = subtrahend.convertTo(this.unit);
+ final var 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");
@@ -259,17 +317,18 @@ public final class LinearUnitValue {
"Incompatible units for addition \"%s\" and \"%s\".", this.unit,
addend.unit));
- final LinearUnitValue otherConverted = addend.convertTo(this.unit);
+ final var 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));
@@ -277,10 +336,11 @@ public final class LinearUnitValue {
/**
* 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),
@@ -289,16 +349,32 @@ public final class LinearUnitValue {
/**
* 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);
@@ -313,23 +389,28 @@ public final class LinearUnitValue {
* single numbers.
* <p>
* 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<String> primaryName = this.unit.getPrimaryName();
- final Optional<String> symbol = this.unit.getSymbol();
- final String chosenName = symbol.orElse(primaryName.orElse(null));
+ final var primaryName = this.unit.getPrimaryName();
+ final var symbol = this.unit.getSymbol();
+ final var chosenName = symbol.orElse(primaryName.orElse(null));
- final UncertainDouble baseValue = this.unit.convertToBase(this.value);
+ final var baseValue = this.unit.convertToBase(this.value);
// get rounded strings
// if showUncertainty is true, add brackets around the string
- final String valueString = (showUncertainty ? "(" : "")
+ final var valueString = (showUncertainty ? "(" : "")
+ this.value.toString(showUncertainty, roundingMode)
+ (showUncertainty ? ")" : "");
- final String baseValueString = (showUncertainty ? "(" : "")
+ final var baseValueString = (showUncertainty ? "(" : "")
+ baseValue.toString(showUncertainty, roundingMode)
+ (showUncertainty ? ")" : "");
@@ -338,7 +419,6 @@ public final class LinearUnitValue {
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);
+ return String.format("%s %s", valueString, chosenName);
}
}