summaryrefslogtreecommitdiff
path: root/src/main/java/sevenUnits/utils/ObjectProduct.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/sevenUnits/utils/ObjectProduct.java')
-rw-r--r--src/main/java/sevenUnits/utils/ObjectProduct.java88
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);