diff options
Diffstat (limited to 'src/main/java/sevenUnits/utils/ObjectProduct.java')
-rw-r--r-- | src/main/java/sevenUnits/utils/ObjectProduct.java | 88 |
1 files changed, 69 insertions, 19 deletions
diff --git a/src/main/java/sevenUnits/utils/ObjectProduct.java b/src/main/java/sevenUnits/utils/ObjectProduct.java index 5a29d79..23fe41c 100644 --- a/src/main/java/sevenUnits/utils/ObjectProduct.java +++ b/src/main/java/sevenUnits/utils/ObjectProduct.java @@ -1,5 +1,5 @@ /** - * Copyright (C) 2018 Adrien Hopkins + * Copyright (C) 2018, 2021, 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 @@ -29,17 +29,26 @@ import java.util.function.Function; /** * An immutable product of multiple objects of a type, such as base units. The * objects can be multiplied and exponentiated. - * + * * @author Adrien Hopkins + * @param <T> type of object that is being multiplied * @since 2019-10-16 + * @since v0.3.0 */ public class ObjectProduct<T> implements Nameable { /** + * If {@link #toExponentRounded}'s rounding changes exponents by more than + * this value, a warning will be printed to standard error. + */ + private static final double ROUND_WARN_THRESHOLD = 1e-12; + + /** * Returns an empty ObjectProduct of a certain type - * + * * @param <T> type of objects that can be multiplied * @return empty product * @since 2019-10-16 + * @since v0.3.0 */ public static final <T> ObjectProduct<T> empty() { return new ObjectProduct<>(new HashMap<>()); @@ -47,11 +56,12 @@ public class ObjectProduct<T> implements Nameable { /** * Gets an {@code ObjectProduct} from an object-to-integer mapping - * + * * @param <T> type of object in product * @param map map mapping objects to exponents * @return object product * @since 2019-10-16 + * @since v0.3.0 */ public static final <T> ObjectProduct<T> fromExponentMapping( final Map<T, Integer> map) { @@ -61,10 +71,12 @@ public class ObjectProduct<T> implements Nameable { /** * Gets an ObjectProduct that has one of the inputted argument, and nothing * else. - * + * * @param object object that will be in the product + * @param <T> type of object contained in returned ObjectProduct * @return product * @since 2019-10-16 + * @since v0.3.0 * @throws NullPointerException if object is null */ public static final <T> ObjectProduct<T> oneOf(final T object) { @@ -77,21 +89,21 @@ public class ObjectProduct<T> implements Nameable { /** * The objects that make up the product, mapped to their exponents. This map * treats zero as null, and is immutable. - * + * * @since 2019-10-16 + * @since v0.3.0 */ final Map<T, Integer> exponents; - /** - * The object's name and symbol - */ + /** The object's name and symbol */ private final NameSymbol nameSymbol; /** * Creates a {@code ObjectProduct} without a name/symbol. - * + * * @param exponents objects that make up this product * @since 2019-10-16 + * @since v0.3.0 */ ObjectProduct(final Map<T, Integer> exponents) { this(exponents, NameSymbol.EMPTY); @@ -99,10 +111,11 @@ public class ObjectProduct<T> implements Nameable { /** * Creates the {@code ObjectProduct}. - * + * * @param exponents objects that make up this product * @param nameSymbol name and symbol of object product * @since 2019-10-16 + * @since v0.3.0 */ ObjectProduct(final Map<T, Integer> exponents, NameSymbol nameSymbol) { this.exponents = Collections.unmodifiableMap( @@ -117,6 +130,7 @@ public class ObjectProduct<T> implements Nameable { * @param other other product * @return quotient of two products * @since 2019-10-16 + * @since v0.3.0 * @throws NullPointerException if other is null */ public ObjectProduct<T> dividedBy(final ObjectProduct<T> other) { @@ -150,6 +164,7 @@ public class ObjectProduct<T> implements Nameable { /** * @return immutable map mapping objects to exponents * @since 2019-10-16 + * @since v0.3.0 */ public Map<T, Integer> exponentMap() { return this.exponents; @@ -177,7 +192,7 @@ public class ObjectProduct<T> implements Nameable { /** * Gets the exponent for a specific dimension. - * + * * @param dimension dimension to check * @return exponent for that dimension * @since 2018-12-12 @@ -201,10 +216,11 @@ public class ObjectProduct<T> implements Nameable { * @return true if this product is a single object, i.e. it has one exponent * of one and no other nonzero exponents * @since 2019-10-16 + * @since v0.3.0 */ public boolean isSingleObject() { - int oneCount = 0; - boolean twoOrMore = false; // has exponents of 2 or more + var oneCount = 0; + var twoOrMore = false; // has exponents of 2 or more for (final T b : this.getBaseSet()) { if (this.getExponent(b) == 1) { oneCount++; @@ -221,6 +237,7 @@ public class ObjectProduct<T> implements Nameable { * @param other other product * @return product of two products * @since 2019-10-16 + * @since v0.3.0 * @throws NullPointerException if other is null */ public ObjectProduct<T> times(final ObjectProduct<T> other) { @@ -242,10 +259,11 @@ public class ObjectProduct<T> implements Nameable { /** * Returns this product, but to an exponent - * + * * @param exponent exponent * @return result of exponentiation * @since 2019-10-16 + * @since v0.3.0 */ public ObjectProduct<T> toExponent(final int exponent) { final Map<T, Integer> map = new HashMap<>(this.exponents); @@ -256,12 +274,41 @@ public class ObjectProduct<T> implements Nameable { } /** + * Returns this product to an exponent, where every dimension is rounded to + * the nearest integer. + * + * This function will send a warning (via standard error) if the rounding + * significantly changes the value. + * + * @param exponent exponent to raise this product to + * @return result of exponentiation + * + * @since 2024-08-22 + * @since v0.3.0 + */ + public ObjectProduct<T> toExponentRounded(final double exponent) { + final Map<T, Integer> map = new HashMap<>(this.exponents); + for (final T key : this.exponents.keySet()) { + final var newExponent = this.getExponent(key) * exponent; + if (Math.abs( + newExponent - Math.round(newExponent)) > ROUND_WARN_THRESHOLD) { + System.err.printf( + "Exponent Rounding Warning: Dimension exponents must be integers, so %d ^ %g = %g was rounded to %d.\n", + this.getExponent(key), exponent, newExponent, + Math.round(newExponent)); + } + map.put(key, (int) Math.round(newExponent)); + } + return new ObjectProduct<>(map); + } + + /** * Converts this product to a string using the objects' * {@link Object#toString()} method (or {@link Nameable#getShortName} if * available). If objects have a long toString representation, it is * recommended to use {@link #toString(Function)} instead to shorten the * returned string. - * + * * <p> * {@inheritDoc} */ @@ -275,10 +322,11 @@ public class ObjectProduct<T> implements Nameable { /** * Converts this product to a string. The objects that make up this product * are represented by {@code objectToString} - * + * * @param objectToString function to convert objects to strings * @return string representation of product * @since 2019-10-16 + * @since v0.3.0 */ public String toString(final Function<T, String> objectToString) { final List<String> positiveStringComponents = new ArrayList<>(); @@ -298,18 +346,20 @@ public class ObjectProduct<T> implements Nameable { } } - final String positiveString = positiveStringComponents.isEmpty() ? "1" + final var positiveString = positiveStringComponents.isEmpty() ? "1" : String.join(" * ", positiveStringComponents); - final String negativeString = negativeStringComponents.isEmpty() ? "" + final var negativeString = negativeStringComponents.isEmpty() ? "" : " / " + String.join(" * ", negativeStringComponents); return positiveString + negativeString; } /** + * @param nameSymbol name to add to this product * @return named version of this {@code ObjectProduct}, using data from * {@code nameSymbol} * @since 2021-12-15 + * @since v0.3.0 */ public ObjectProduct<T> withName(NameSymbol nameSymbol) { return new ObjectProduct<>(this.exponents, nameSymbol); |