diff options
Diffstat (limited to 'src/main/java/sevenUnits/unit/LinearUnitValue.java')
-rw-r--r-- | src/main/java/sevenUnits/unit/LinearUnitValue.java | 174 |
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); } } |