summaryrefslogtreecommitdiff
path: root/src/main/java/sevenUnits/utils
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/sevenUnits/utils')
-rw-r--r--src/main/java/sevenUnits/utils/ConditionalExistenceCollections.java80
-rw-r--r--src/main/java/sevenUnits/utils/DecimalComparison.java55
-rw-r--r--src/main/java/sevenUnits/utils/ExpressionParser.java217
-rw-r--r--src/main/java/sevenUnits/utils/NameSymbol.java102
-rw-r--r--src/main/java/sevenUnits/utils/Nameable.java13
-rw-r--r--src/main/java/sevenUnits/utils/ObjectProduct.java88
-rw-r--r--src/main/java/sevenUnits/utils/SemanticVersionNumber.java168
-rw-r--r--src/main/java/sevenUnits/utils/UncertainDouble.java251
-rw-r--r--src/main/java/sevenUnits/utils/package-info.java4
9 files changed, 548 insertions, 430 deletions
diff --git a/src/main/java/sevenUnits/utils/ConditionalExistenceCollections.java b/src/main/java/sevenUnits/utils/ConditionalExistenceCollections.java
index b71a4e0..6244ee6 100644
--- a/src/main/java/sevenUnits/utils/ConditionalExistenceCollections.java
+++ b/src/main/java/sevenUnits/utils/ConditionalExistenceCollections.java
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2019 Adrien Hopkins
+ * Copyright (C) 2019, 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
@@ -49,18 +49,19 @@ import java.util.function.Predicate;
* Other than that, <i>the only difference between the provided collections and
* the returned collections are that elements don't exist if they don't pass the
* provided condition</i>.
- *
- *
+ *
+ *
* @author Adrien Hopkins
* @since 2019-10-17
+ * @since v0.3.0
*/
-// TODO add conditional existence Lists and Sorted/Navigable Sets/Maps
public final class ConditionalExistenceCollections {
/**
* Elements in this collection only exist if they meet a condition.
- *
+ *
* @author Adrien Hopkins
* @since 2019-10-17
+ * @since v0.3.0
* @param <E> type of element in collection
*/
static final class ConditionalExistenceCollection<E>
@@ -70,10 +71,11 @@ public final class ConditionalExistenceCollections {
/**
* Creates the {@code ConditionalExistenceCollection}.
- *
+ *
* @param collection
* @param existenceCondition
* @since 2019-10-17
+ * @since v0.3.0
*/
private ConditionalExistenceCollection(final Collection<E> collection,
final Predicate<E> existenceCondition) {
@@ -101,7 +103,7 @@ public final class ConditionalExistenceCollections {
// instance of E
// therefore this cast will always work
@SuppressWarnings("unchecked")
- final E e = (E) o;
+ final var e = (E) o;
return this.existenceCondition.test(e);
}
@@ -116,7 +118,7 @@ public final class ConditionalExistenceCollections {
public boolean remove(final Object o) {
// remove() must be first in the && statement, otherwise it may not
// execute
- final boolean containedObject = this.contains(o);
+ final var containedObject = this.contains(o);
return this.collection.remove(o) && containedObject;
}
@@ -147,9 +149,10 @@ public final class ConditionalExistenceCollections {
/**
* Elements in this wrapper iterator only exist if they pass a condition.
- *
+ *
* @author Adrien Hopkins
* @since 2019-10-17
+ * @since v0.3.0
* @param <E> type of elements in iterator
*/
static final class ConditionalExistenceIterator<E> implements Iterator<E> {
@@ -160,10 +163,11 @@ public final class ConditionalExistenceCollections {
/**
* Creates the {@code ConditionalExistenceIterator}.
- *
+ *
* @param iterator
* @param condition
* @since 2019-10-17
+ * @since v0.3.0
*/
private ConditionalExistenceIterator(final Iterator<E> iterator,
final Predicate<E> condition) {
@@ -174,8 +178,9 @@ public final class ConditionalExistenceCollections {
/**
* Gets the next element, and sets nextElement and hasNext accordingly.
- *
+ *
* @since 2019-10-17
+ * @since v0.3.0
*/
private void getAndSetNextElement() {
do {
@@ -197,11 +202,11 @@ public final class ConditionalExistenceCollections {
@Override
public E next() {
if (this.hasNext()) {
- final E next = this.nextElement;
+ final var next = this.nextElement;
this.getAndSetNextElement();
return next;
- } else
- throw new NoSuchElementException();
+ }
+ throw new NoSuchElementException();
}
@Override
@@ -212,9 +217,10 @@ public final class ConditionalExistenceCollections {
/**
* Mappings in this map only exist if the entry passes some condition.
- *
+ *
* @author Adrien Hopkins
* @since 2019-10-17
+ * @since v0.3.0
* @param <K> key type
* @param <V> value type
*/
@@ -224,10 +230,11 @@ public final class ConditionalExistenceCollections {
/**
* Creates the {@code ConditionalExistenceMap}.
- *
+ *
* @param map
* @param entryExistenceCondition
* @since 2019-10-17
+ * @since v0.3.0
*/
private ConditionalExistenceMap(final Map<K, V> map,
final Predicate<Entry<K, V>> entryExistenceCondition) {
@@ -243,10 +250,10 @@ public final class ConditionalExistenceCollections {
// only instances of K have mappings in the backing map
// since we know that key is a valid key, it must be an instance of K
@SuppressWarnings("unchecked")
- final K keyAsK = (K) key;
+ final var keyAsK = (K) key;
// get and test entry
- final V value = this.map.get(key);
+ final var value = this.map.get(key);
final Entry<K, V> entry = new SimpleEntry<>(keyAsK, value);
return this.entryExistenceCondition.test(entry);
}
@@ -262,7 +269,7 @@ public final class ConditionalExistenceCollections {
return this.containsKey(key) ? this.map.get(key) : null;
}
- private final Entry<K, V> getEntry(K key) {
+ private Entry<K, V> getEntry(K key) {
return new Entry<>() {
@Override
public K getKey() {
@@ -289,7 +296,7 @@ public final class ConditionalExistenceCollections {
@Override
public V put(final K key, final V value) {
- final V oldValue = this.map.put(key, value);
+ final var oldValue = this.map.put(key, value);
// get and test entry
final Entry<K, V> entry = new SimpleEntry<>(key, oldValue);
@@ -298,7 +305,7 @@ public final class ConditionalExistenceCollections {
@Override
public V remove(final Object key) {
- final V oldValue = this.map.remove(key);
+ final var oldValue = this.map.remove(key);
return this.containsKey(key) ? oldValue : null;
}
@@ -311,9 +318,10 @@ public final class ConditionalExistenceCollections {
/**
* Elements in this set only exist if a certain condition is true.
- *
+ *
* @author Adrien Hopkins
* @since 2019-10-17
+ * @since v0.3.0
* @param <E> type of element in set
*/
static final class ConditionalExistenceSet<E> extends AbstractSet<E> {
@@ -322,10 +330,11 @@ public final class ConditionalExistenceCollections {
/**
* Creates the {@code ConditionalNonexistenceSet}.
- *
+ *
* @param set set to use
* @param existenceCondition condition where element exists
* @since 2019-10-17
+ * @since v0.3.0
*/
private ConditionalExistenceSet(final Set<E> set,
final Predicate<E> existenceCondition) {
@@ -359,7 +368,7 @@ public final class ConditionalExistenceCollections {
// of E
// therefore this cast will always work
@SuppressWarnings("unchecked")
- final E e = (E) o;
+ final var e = (E) o;
return this.existenceCondition.test(e);
}
@@ -374,7 +383,7 @@ public final class ConditionalExistenceCollections {
public boolean remove(final Object o) {
// remove() must be first in the && statement, otherwise it may not
// execute
- final boolean containedObject = this.contains(o);
+ final var containedObject = this.contains(o);
return this.set.remove(o) && containedObject;
}
@@ -405,14 +414,15 @@ public final class ConditionalExistenceCollections {
/**
* Elements in the returned wrapper collection are ignored if they don't pass
* a condition.
- *
+ *
* @param <E> type of elements in collection
* @param collection collection to wrap
* @param existenceCondition elements only exist if this returns true
* @return wrapper collection
* @since 2019-10-17
+ * @since v0.3.0
*/
- public static final <E> Collection<E> conditionalExistenceCollection(
+ public static <E> Collection<E> conditionalExistenceCollection(
final Collection<E> collection,
final Predicate<E> existenceCondition) {
return new ConditionalExistenceCollection<>(collection,
@@ -422,14 +432,15 @@ public final class ConditionalExistenceCollections {
/**
* Elements in the returned wrapper iterator are ignored if they don't pass a
* condition.
- *
+ *
* @param <E> type of elements in iterator
* @param iterator iterator to wrap
* @param existenceCondition elements only exist if this returns true
* @return wrapper iterator
* @since 2019-10-17
+ * @since v0.3.0
*/
- public static final <E> Iterator<E> conditionalExistenceIterator(
+ public static <E> Iterator<E> conditionalExistenceIterator(
final Iterator<E> iterator, final Predicate<E> existenceCondition) {
return new ConditionalExistenceIterator<>(iterator, existenceCondition);
}
@@ -437,16 +448,16 @@ public final class ConditionalExistenceCollections {
/**
* Mappings in the returned wrapper map are ignored if the corresponding
* entry doesn't pass a condition
- *
+ *
* @param <K> type of key in map
* @param <V> type of value in map
* @param map map to wrap
* @param entryExistenceCondition mappings only exist if this returns true
* @return wrapper map
* @since 2019-10-17
+ * @since v0.3.0
*/
- public static final <K, V> Map<K, V> conditionalExistenceMap(
- final Map<K, V> map,
+ public static <K, V> Map<K, V> conditionalExistenceMap(final Map<K, V> map,
final Predicate<Entry<K, V>> entryExistenceCondition) {
return new ConditionalExistenceMap<>(map, entryExistenceCondition);
}
@@ -454,14 +465,15 @@ public final class ConditionalExistenceCollections {
/**
* Elements in the returned wrapper set are ignored if they don't pass a
* condition.
- *
+ *
* @param <E> type of elements in set
* @param set set to wrap
* @param existenceCondition elements only exist if this returns true
* @return wrapper set
* @since 2019-10-17
+ * @since v0.3.0
*/
- public static final <E> Set<E> conditionalExistenceSet(final Set<E> set,
+ public static <E> Set<E> conditionalExistenceSet(final Set<E> set,
final Predicate<E> existenceCondition) {
return new ConditionalExistenceSet<>(set, existenceCondition);
}
diff --git a/src/main/java/sevenUnits/utils/DecimalComparison.java b/src/main/java/sevenUnits/utils/DecimalComparison.java
index 0515b6b..1366fd3 100644
--- a/src/main/java/sevenUnits/utils/DecimalComparison.java
+++ b/src/main/java/sevenUnits/utils/DecimalComparison.java
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2019 Adrien Hopkins
+ * Copyright (C) 2019, 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
@@ -20,7 +20,7 @@ import java.math.BigDecimal;
/**
* A class that contains methods to compare float and double values.
- *
+ *
* @author Adrien Hopkins
* @since 2019-03-18
* @since v0.2.0
@@ -29,7 +29,7 @@ public final class DecimalComparison {
/**
* The value used for double comparison. If two double values are within this
* value multiplied by the larger value, they are considered equal.
- *
+ *
* @since 2019-03-18
* @since v0.2.0
*/
@@ -38,7 +38,7 @@ public final class DecimalComparison {
/**
* The value used for float comparison. If two float values are within this
* value multiplied by the larger value, they are considered equal.
- *
+ *
* @since 2019-03-18
* @since v0.2.0
*/
@@ -63,21 +63,20 @@ public final class DecimalComparison {
* <li>Use {@link BigDecimal} instead of {@code double} (this will make a
* violation of transitivity 100% impossible)
* </ol>
- *
+ *
* @param a first value to test
* @param b second value to test
* @return whether they are equal
* @since 2019-03-18
* @since v0.2.0
- * @see #hashCode(double)
*/
- public static final boolean equals(final double a, final double b) {
+ public static boolean equals(final double a, final double b) {
return DecimalComparison.equals(a, b, DOUBLE_EPSILON);
}
/**
* Tests for double equality using a custom epsilon value.
- *
+ *
* <p>
* <strong>WARNING: </strong>this method is not technically transitive. If a
* and b are off by slightly less than {@code epsilon * max(abs(a), abs(b))},
@@ -94,7 +93,7 @@ public final class DecimalComparison {
* <li>Use {@link BigDecimal} instead of {@code double} (this will make a
* violation of transitivity 100% impossible)
* </ol>
- *
+ *
* @param a first value to test
* @param b second value to test
* @param epsilon allowed difference
@@ -102,14 +101,14 @@ public final class DecimalComparison {
* @since 2019-03-18
* @since v0.2.0
*/
- public static final boolean equals(final double a, final double b,
+ public static boolean equals(final double a, final double b,
final double epsilon) {
return Math.abs(a - b) <= epsilon * Math.max(Math.abs(a), Math.abs(b));
}
/**
* Tests for equality of float values using {@link #FLOAT_EPSILON}.
- *
+ *
* <p>
* <strong>WARNING: </strong>this method is not technically transitive. If a
* and b are off by slightly less than {@code epsilon * max(abs(a), abs(b))},
@@ -126,20 +125,20 @@ public final class DecimalComparison {
* <li>Use {@link BigDecimal} instead of {@code float} (this will make a
* violation of transitivity 100% impossible)
* </ol>
- *
+ *
* @param a first value to test
* @param b second value to test
* @return whether they are equal
* @since 2019-03-18
* @since v0.2.0
*/
- public static final boolean equals(final float a, final float b) {
+ public static boolean equals(final float a, final float b) {
return DecimalComparison.equals(a, b, FLOAT_EPSILON);
}
/**
* Tests for float equality using a custom epsilon value.
- *
+ *
* <p>
* <strong>WARNING: </strong>this method is not technically transitive. If a
* and b are off by slightly less than {@code epsilon * max(abs(a), abs(b))},
@@ -156,7 +155,7 @@ public final class DecimalComparison {
* <li>Use {@link BigDecimal} instead of {@code float} (this will make a
* violation of transitivity 100% impossible)
* </ol>
- *
+ *
* @param a first value to test
* @param b second value to test
* @param epsilon allowed difference
@@ -164,7 +163,7 @@ public final class DecimalComparison {
* @since 2019-03-18
* @since v0.2.0
*/
- public static final boolean equals(final float a, final float b,
+ public static boolean equals(final float a, final float b,
final float epsilon) {
return Math.abs(a - b) <= epsilon * Math.max(Math.abs(a), Math.abs(b));
}
@@ -189,14 +188,14 @@ public final class DecimalComparison {
* <li>Use {@link BigDecimal} instead of {@code double} (this will make a
* violation of transitivity 100% impossible)
* </ol>
- *
+ *
* @param a first value to test
* @param b second value to test
* @return whether they are equal
* @since 2020-09-07
- * @see #hashCode(double)
+ * @since v0.3.0
*/
- public static final boolean equals(final UncertainDouble a,
+ public static boolean equals(final UncertainDouble a,
final UncertainDouble b) {
return DecimalComparison.equals(a.value(), b.value())
&& DecimalComparison.equals(a.uncertainty(), b.uncertainty());
@@ -204,7 +203,7 @@ public final class DecimalComparison {
/**
* Tests for {@code UncertainDouble} equality using a custom epsilon value.
- *
+ *
* <p>
* <strong>WARNING: </strong>this method is not technically transitive. If a
* and b are off by slightly less than {@code epsilon * max(abs(a), abs(b))},
@@ -221,7 +220,7 @@ public final class DecimalComparison {
* <li>Use {@link BigDecimal} instead of {@code double} (this will make a
* violation of transitivity 100% impossible)
* </ol>
- *
+ *
* @param a first value to test
* @param b second value to test
* @param epsilon allowed difference
@@ -229,25 +228,13 @@ public final class DecimalComparison {
* @since 2019-03-18
* @since v0.2.0
*/
- public static final boolean equals(final UncertainDouble a,
+ public static boolean equals(final UncertainDouble a,
final UncertainDouble b, final double epsilon) {
return DecimalComparison.equals(a.value(), b.value(), epsilon)
&& DecimalComparison.equals(a.uncertainty(), b.uncertainty(),
epsilon);
}
- /**
- * Takes the hash code of doubles. Values that are equal according to
- * {@link #equals(double, double)} will have the same hash code.
- *
- * @param d double to hash
- * @return hash code of double
- * @since 2019-10-16
- */
- public static final int hash(final double d) {
- return Float.hashCode((float) d);
- }
-
// You may NOT get any DecimalComparison instances
private DecimalComparison() {
throw new AssertionError();
diff --git a/src/main/java/sevenUnits/utils/ExpressionParser.java b/src/main/java/sevenUnits/utils/ExpressionParser.java
index e248ff0..03c763c 100644
--- a/src/main/java/sevenUnits/utils/ExpressionParser.java
+++ b/src/main/java/sevenUnits/utils/ExpressionParser.java
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2019, 2024 Adrien Hopkins
+ * Copyright (C) 2019, 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
@@ -31,7 +31,7 @@ import java.util.function.UnaryOperator;
/**
* An object that can parse expressions with unary or binary operators.
- *
+ *
* @author Adrien Hopkins
* @param <T> type of object that exists in parsed expressions
* @since 2019-03-14
@@ -40,7 +40,7 @@ import java.util.function.UnaryOperator;
public final class ExpressionParser<T> {
/**
* A builder that can create {@code ExpressionParser<T>} instances.
- *
+ *
* @author Adrien Hopkins
* @param <T> type of object that exists in parsed expressions
* @since 2019-03-17
@@ -51,7 +51,7 @@ public final class ExpressionParser<T> {
* A function that obtains a parseable object from a string. For example,
* an integer {@code ExpressionParser} would use
* {@code Integer::parseInt}.
- *
+ *
* @since 2019-03-14
* @since v0.2.0
*/
@@ -59,7 +59,7 @@ public final class ExpressionParser<T> {
/**
* The function of the space as an operator (like 3 x y)
- *
+ *
* @since 2019-03-22
* @since v0.2.0
*/
@@ -68,7 +68,7 @@ public final class ExpressionParser<T> {
/**
* A map mapping operator strings to operator functions, for unary
* operators.
- *
+ *
* @since 2019-03-14
* @since v0.2.0
*/
@@ -77,7 +77,7 @@ public final class ExpressionParser<T> {
/**
* A map mapping operator strings to operator functions, for binary
* operators.
- *
+ *
* @since 2019-03-14
* @since v0.2.0
*/
@@ -85,14 +85,15 @@ public final class ExpressionParser<T> {
/**
* A map mapping operator strings to numeric functions.
- *
+ *
* @since 2024-03-23
+ * @since v0.5.0
*/
private final Map<String, PriorityBiFunction<T, UncertainDouble, T>> numericOperators;
/**
* Creates the {@code Builder}.
- *
+ *
* @param objectObtainer a function that can turn strings into objects of
* the type handled by the parser.
* @throws NullPointerException if {@code objectObtainer} is null
@@ -109,7 +110,7 @@ public final class ExpressionParser<T> {
/**
* Adds a binary operator to the builder.
- *
+ *
* @param text text used to reference the operator, like '+'
* @param operator operator to add
* @param priority operator's priority, which determines which operators
@@ -142,7 +143,7 @@ public final class ExpressionParser<T> {
/**
* Adds a two-argument operator where the second operator is a number.
* This is used for operations like vector scaling and exponentation.
- *
+ *
* @param text text used to reference the operator, like '^'
* @param operator operator to add
* @param priority operator's priority, which determines which operators
@@ -172,7 +173,7 @@ public final class ExpressionParser<T> {
/**
* Adds a function for spaces. You must use the text of an existing binary
* operator.
- *
+ *
* @param operator text of operator to use
* @return this builder
* @since 2019-03-22
@@ -191,7 +192,7 @@ public final class ExpressionParser<T> {
/**
* Adds a unary operator to the builder.
- *
+ *
* @param text text used to reference the operator, like '-'
* @param operator operator to add
* @param priority operator's priority, which determines which operators
@@ -235,18 +236,18 @@ public final class ExpressionParser<T> {
/**
* A binary operator with a priority field that determines which operators
* apply first.
- *
+ *
* @author Adrien Hopkins
* @param <T> type of operand and result
* @since 2019-03-17
* @since v0.2.0
*/
- private static abstract class PriorityBinaryOperator<T>
- implements BinaryOperator<T>, Comparable<PriorityBinaryOperator<T>> {
+ private static abstract class PriorityBiFunction<T, U, R> implements
+ BiFunction<T, U, R>, Comparable<PriorityBiFunction<T, U, R>> {
/**
* The operator's priority. Higher-priority operators are applied before
* lower-priority operators
- *
+ *
* @since 2019-03-17
* @since v0.2.0
*/
@@ -254,33 +255,32 @@ public final class ExpressionParser<T> {
/**
* Creates the {@code PriorityBinaryOperator}.
- *
+ *
* @param priority operator's priority
* @since 2019-03-17
* @since v0.2.0
*/
- public PriorityBinaryOperator(final int priority) {
+ public PriorityBiFunction(final int priority) {
this.priority = priority;
}
/**
* Compares this object to another by priority.
- *
+ *
* <p>
* {@inheritDoc}
* </p>
- *
+ *
* @since 2019-03-17
* @since v0.2.0
*/
@Override
- public int compareTo(final PriorityBinaryOperator<T> o) {
+ public int compareTo(final PriorityBiFunction<T, U, R> o) {
if (this.priority < o.priority)
return -1;
- else if (this.priority > o.priority)
+ if (this.priority > o.priority)
return 1;
- else
- return 0;
+ return 0;
}
/**
@@ -296,18 +296,18 @@ public final class ExpressionParser<T> {
/**
* A binary operator with a priority field that determines which operators
* apply first.
- *
+ *
* @author Adrien Hopkins
* @param <T> type of operand and result
* @since 2019-03-17
* @since v0.2.0
*/
- private static abstract class PriorityBiFunction<T, U, R> implements
- BiFunction<T, U, R>, Comparable<PriorityBiFunction<T, U, R>> {
+ private static abstract class PriorityBinaryOperator<T>
+ implements BinaryOperator<T>, Comparable<PriorityBinaryOperator<T>> {
/**
* The operator's priority. Higher-priority operators are applied before
* lower-priority operators
- *
+ *
* @since 2019-03-17
* @since v0.2.0
*/
@@ -315,33 +315,32 @@ public final class ExpressionParser<T> {
/**
* Creates the {@code PriorityBinaryOperator}.
- *
+ *
* @param priority operator's priority
* @since 2019-03-17
* @since v0.2.0
*/
- public PriorityBiFunction(final int priority) {
+ public PriorityBinaryOperator(final int priority) {
this.priority = priority;
}
/**
* Compares this object to another by priority.
- *
+ *
* <p>
* {@inheritDoc}
* </p>
- *
+ *
* @since 2019-03-17
* @since v0.2.0
*/
@Override
- public int compareTo(final PriorityBiFunction<T, U, R> o) {
+ public int compareTo(final PriorityBinaryOperator<T> o) {
if (this.priority < o.priority)
return -1;
- else if (this.priority > o.priority)
+ if (this.priority > o.priority)
return 1;
- else
- return 0;
+ return 0;
}
/**
@@ -357,7 +356,7 @@ public final class ExpressionParser<T> {
/**
* A unary operator with a priority field that determines which operators
* apply first.
- *
+ *
* @author Adrien Hopkins
* @param <T> type of operand and result
* @since 2019-03-17
@@ -368,7 +367,7 @@ public final class ExpressionParser<T> {
/**
* The operator's priority. Higher-priority operators are applied before
* lower-priority operators
- *
+ *
* @since 2019-03-17
* @since v0.2.0
*/
@@ -376,7 +375,7 @@ public final class ExpressionParser<T> {
/**
* Creates the {@code PriorityUnaryOperator}.
- *
+ *
* @param priority operator's priority
* @since 2019-03-17
* @since v0.2.0
@@ -387,11 +386,11 @@ public final class ExpressionParser<T> {
/**
* Compares this object to another by priority.
- *
+ *
* <p>
* {@inheritDoc}
* </p>
- *
+ *
* @since 2019-03-17
* @since v0.2.0
*/
@@ -399,10 +398,9 @@ public final class ExpressionParser<T> {
public int compareTo(final PriorityUnaryOperator<T> o) {
if (this.priority < o.priority)
return -1;
- else if (this.priority > o.priority)
+ if (this.priority > o.priority)
return 1;
- else
- return 0;
+ return 0;
}
/**
@@ -417,18 +415,18 @@ public final class ExpressionParser<T> {
/**
* The types of tokens that are available.
- *
+ *
* @author Adrien Hopkins
* @since 2019-03-14
* @since v0.2.0
*/
- private static enum TokenType {
+ private enum TokenType {
OBJECT, UNARY_OPERATOR, BINARY_OPERATOR, NUMERIC_OPERATOR;
}
/**
* The opening bracket.
- *
+ *
* @since 2019-03-22
* @since v0.2.0
*/
@@ -436,7 +434,7 @@ public final class ExpressionParser<T> {
/**
* The closing bracket.
- *
+ *
* @since 2019-03-22
* @since v0.2.0
*/
@@ -444,7 +442,7 @@ public final class ExpressionParser<T> {
/**
* Finds the other bracket in a pair of brackets, given the position of one.
- *
+ *
* @param string string that contains brackets
* @param bracketPosition position of first bracket
* @return position of matching bracket
@@ -456,7 +454,7 @@ public final class ExpressionParser<T> {
final int bracketPosition) {
Objects.requireNonNull(string, "string must not be null.");
- final char openingBracket = string.charAt(bracketPosition);
+ final var openingBracket = string.charAt(bracketPosition);
// figure out what closing bracket to look for
final char closingBracket;
@@ -477,12 +475,12 @@ public final class ExpressionParser<T> {
// level of brackets. every opening bracket increments this; every closing
// bracket decrements it
- int bracketLevel = 0;
+ var bracketLevel = 0;
// iterate over the string to find the closing bracket
- for (int currentPosition = bracketPosition; currentPosition < string
+ for (var currentPosition = bracketPosition; currentPosition < string
.length(); currentPosition++) {
- final char currentCharacter = string.charAt(currentPosition);
+ final var currentCharacter = string.charAt(currentPosition);
if (currentCharacter == openingBracket) {
bracketLevel++;
@@ -499,7 +497,7 @@ public final class ExpressionParser<T> {
/**
* A function that obtains a parseable object from a string. For example, an
* integer {@code ExpressionParser} would use {@code Integer::parseInt}.
- *
+ *
* @since 2019-03-14
* @since v0.2.0
*/
@@ -507,7 +505,7 @@ public final class ExpressionParser<T> {
/**
* A map mapping operator strings to operator functions, for unary operators.
- *
+ *
* @since 2019-03-14
* @since v0.2.0
*/
@@ -516,7 +514,7 @@ public final class ExpressionParser<T> {
/**
* A map mapping operator strings to operator functions, for binary
* operators.
- *
+ *
* @since 2019-03-14
* @since v0.2.0
*/
@@ -524,14 +522,15 @@ public final class ExpressionParser<T> {
/**
* A map mapping operator strings to numeric functions.
- *
+ *
* @since 2024-03-23
+ * @since v0.5.0
*/
private final Map<String, PriorityBiFunction<T, UncertainDouble, T>> numericOperators;
/**
* The operator for space, or null if spaces have no function.
- *
+ *
* @since 2019-03-22
* @since v0.2.0
*/
@@ -539,7 +538,7 @@ public final class ExpressionParser<T> {
/**
* Creates the {@code ExpressionParser}.
- *
+ *
* @param objectObtainer function to get objects from strings
* @param unaryOperators unary operators available to the parser
* @param binaryOperators binary operators available to the parser
@@ -568,7 +567,7 @@ public final class ExpressionParser<T> {
* {@code 2 * (3 + 4)}<br>
* becomes<br>
* {@code 2 3 4 + *}.
- *
+ *
* @param expression expression
* @return expression in RPN
* @throws IllegalArgumentException if expression is invalid (e.g.
@@ -582,13 +581,13 @@ public final class ExpressionParser<T> {
final List<String> components = new ArrayList<>();
// the part of the expression remaining to parse
- String partialExpression = expression;
+ var partialExpression = expression;
// find and deal with brackets
while (partialExpression.indexOf(OPENING_BRACKET) != -1) {
- final int openingBracketPosition = partialExpression
+ final var openingBracketPosition = partialExpression
.indexOf(OPENING_BRACKET);
- final int closingBracketPosition = findBracketPair(partialExpression,
+ final var closingBracketPosition = findBracketPair(partialExpression,
openingBracketPosition);
// check for function
@@ -596,7 +595,7 @@ public final class ExpressionParser<T> {
&& partialExpression.charAt(openingBracketPosition - 1) != ' ') {
// function like sin(2) or tempF(32)
// find the position of the last space
- int spacePosition = openingBracketPosition;
+ var spacePosition = openingBracketPosition;
while (spacePosition >= 0
&& partialExpression.charAt(spacePosition) != ' ') {
spacePosition--;
@@ -607,8 +606,6 @@ public final class ExpressionParser<T> {
.substring(0, spacePosition + 1).split(" ")));
components.add(partialExpression.substring(spacePosition + 1,
closingBracketPosition + 1));
- partialExpression = partialExpression
- .substring(closingBracketPosition + 1);
} else {
// normal brackets like (1 + 2) * (3 / 5)
components.addAll(Arrays.asList(partialExpression
@@ -616,9 +613,9 @@ public final class ExpressionParser<T> {
components.add(this.convertExpressionToReversePolish(
partialExpression.substring(openingBracketPosition + 1,
closingBracketPosition)));
- partialExpression = partialExpression
- .substring(closingBracketPosition + 1);
}
+ partialExpression = partialExpression
+ .substring(closingBracketPosition + 1);
}
// add everything else
@@ -631,7 +628,7 @@ public final class ExpressionParser<T> {
// deal with space multiplication (x y)
if (this.spaceOperator != null) {
- for (int i = 0; i < components.size() - 1; i++) {
+ for (var i = 0; i < components.size() - 1; i++) {
if (this.getTokenType(components.get(i)) == TokenType.OBJECT && this
.getTokenType(components.get(i + 1)) == TokenType.OBJECT) {
components.add(++i, this.spaceOperator);
@@ -641,7 +638,7 @@ public final class ExpressionParser<T> {
// turn the expression into reverse Polish
while (true) {
- final int highestPriorityOperatorPosition = this
+ final var highestPriorityOperatorPosition = this
.findHighestPriorityOperatorPosition(components);
if (highestPriorityOperatorPosition == -1) {
break;
@@ -656,9 +653,9 @@ public final class ExpressionParser<T> {
if (components.size() < 2)
throw new IllegalArgumentException(
"Invalid expression \"" + expression + "\"");
- final String unaryOperator = components
+ final var unaryOperator = components
.remove(highestPriorityOperatorPosition);
- final String operand = components
+ final var operand = components
.remove(highestPriorityOperatorPosition);
components.add(highestPriorityOperatorPosition,
operand + " " + unaryOperator);
@@ -668,14 +665,14 @@ public final class ExpressionParser<T> {
if (components.size() < 3)
throw new IllegalArgumentException(
"Invalid expression \"" + expression + "\"");
- final String binaryOperator = components
+ final var binaryOperator = components
.remove(highestPriorityOperatorPosition);
- final String operand1 = components
+ final var operand1 = components
.remove(highestPriorityOperatorPosition - 1);
- final String operand2 = components
+ final var operand2 = components
.remove(highestPriorityOperatorPosition - 1);
components.add(highestPriorityOperatorPosition - 1,
- operand2 + " " + operand1 + " " + binaryOperator);
+ operand1 + " " + operand2 + " " + binaryOperator);
break;
default:
throw new AssertionError("Expected operator, found non-operator.");
@@ -684,20 +681,15 @@ public final class ExpressionParser<T> {
// join all of the components together, then ensure there is only one
// space in a row
- String expressionRPN = String.join(" ", components).replaceAll(" +", " ");
-
- while (expressionRPN.charAt(0) == ' ') {
- expressionRPN = expressionRPN.substring(1);
- }
- while (expressionRPN.charAt(expressionRPN.length() - 1) == ' ') {
- expressionRPN = expressionRPN.substring(0, expressionRPN.length() - 1);
- }
- return expressionRPN;
+ if (components.size() != 1)
+ throw new IllegalArgumentException(
+ "Invalid expression \"" + expression + "\".");
+ return components.get(0).replaceAll(" +", " ").trim();
}
/**
* Finds the position of the highest-priority operator in a list
- *
+ *
* @param components components to test
* @param blacklist positions of operators that should be ignored
* @return position of highest priority, or -1 if the list contains no
@@ -710,19 +702,19 @@ public final class ExpressionParser<T> {
final List<String> components) {
Objects.requireNonNull(components, "components must not be null.");
// find highest priority
- int maxPriority = Integer.MIN_VALUE;
- int maxPriorityPosition = -1;
+ var maxPriority = Integer.MIN_VALUE;
+ var maxPriorityPosition = -1;
// go over components one by one
// if it is an operator, test its priority to see if it's max
// if it is, update maxPriority and maxPriorityPosition
- for (int i = 0; i < components.size(); i++) {
+ for (var i = 0; i < components.size(); i++) {
switch (this.getTokenType(components.get(i))) {
case UNARY_OPERATOR:
- final PriorityUnaryOperator<T> unaryOperator = this.unaryOperators
+ final var unaryOperator = this.unaryOperators
.get(components.get(i));
- final int unaryPriority = unaryOperator.getPriority();
+ final var unaryPriority = unaryOperator.getPriority();
if (unaryPriority > maxPriority) {
maxPriority = unaryPriority;
@@ -730,9 +722,9 @@ public final class ExpressionParser<T> {
}
break;
case BINARY_OPERATOR:
- final PriorityBinaryOperator<T> binaryOperator = this.binaryOperators
+ final var binaryOperator = this.binaryOperators
.get(components.get(i));
- final int binaryPriority = binaryOperator.getPriority();
+ final var binaryPriority = binaryOperator.getPriority();
if (binaryPriority > maxPriority) {
maxPriority = binaryPriority;
@@ -740,9 +732,9 @@ public final class ExpressionParser<T> {
}
break;
case NUMERIC_OPERATOR:
- final PriorityBiFunction<T, UncertainDouble, T> numericOperator = this.numericOperators
+ final var numericOperator = this.numericOperators
.get(components.get(i));
- final int numericPriority = numericOperator.getPriority();
+ final var numericPriority = numericOperator.getPriority();
if (numericPriority > maxPriority) {
maxPriority = numericPriority;
@@ -760,7 +752,7 @@ public final class ExpressionParser<T> {
/**
* Determines whether an inputted string is an object or an operator
- *
+ *
* @param token string to input
* @return type of token it is
* @throws NullPointerException if {@code expression} is null
@@ -772,17 +764,16 @@ public final class ExpressionParser<T> {
if (this.unaryOperators.containsKey(token))
return TokenType.UNARY_OPERATOR;
- else if (this.binaryOperators.containsKey(token))
+ if (this.binaryOperators.containsKey(token))
return TokenType.BINARY_OPERATOR;
- else if (this.numericOperators.containsKey(token))
+ if (this.numericOperators.containsKey(token))
return TokenType.NUMERIC_OPERATOR;
- else
- return TokenType.OBJECT;
+ return TokenType.OBJECT;
}
/**
* Parses an expression.
- *
+ *
* @param expression expression to parse
* @return result
* @throws NullPointerException if {@code expression} is null
@@ -796,7 +787,7 @@ public final class ExpressionParser<T> {
/**
* Parses an expression expressed in reverse Polish notation.
- *
+ *
* @param expression expression to parse
* @return result
* @throws NullPointerException if {@code expression} is null
@@ -821,12 +812,12 @@ public final class ExpressionParser<T> {
item, stack.size()));
// get two arguments and operator, then apply!
- final T o1 = stack.pop();
- final T o2 = stack.pop();
+ final var o1 = stack.pop();
+ final var o2 = stack.pop();
final BinaryOperator<T> binaryOperator = this.binaryOperators
.get(item);
- stack.push(binaryOperator.apply(o1, o2));
+ stack.push(binaryOperator.apply(o2, o1));
break;
case NUMERIC_OPERATOR:
@@ -835,8 +826,8 @@ public final class ExpressionParser<T> {
"Attempted to call binary operator %s with insufficient arguments.",
item));
- final T ot = stack.pop();
- final UncertainDouble on = doubleStack.pop();
+ final var ot = stack.pop();
+ final var on = doubleStack.pop();
final BiFunction<T, UncertainDouble, T> op = this.numericOperators
.get(item);
stack.push(op.apply(ot, on));
@@ -850,14 +841,14 @@ public final class ExpressionParser<T> {
// that's the only way to tell if an expression is a number or not.
try {
stack.push(this.objectObtainer.apply(item));
- } catch (Exception e) {
+ } catch (final Exception e) {
try {
doubleStack.push(UncertainDouble.fromString(item));
- } catch (IllegalArgumentException e2) {
+ } catch (final IllegalArgumentException e2) {
try {
doubleStack.push(
UncertainDouble.of(Double.parseDouble(item), 0));
- } catch (NumberFormatException e3) {
+ } catch (final NumberFormatException e3) {
throw e;
}
}
@@ -871,7 +862,7 @@ public final class ExpressionParser<T> {
item, stack.size()));
// get one argument and operator, then apply!
- final T o = stack.pop();
+ final var o = stack.pop();
final UnaryOperator<T> unaryOperator = this.unaryOperators
.get(item);
@@ -889,7 +880,7 @@ public final class ExpressionParser<T> {
if (stack.size() > 1)
throw new IllegalStateException(
"Computation ended up with more than one answer.");
- else if (stack.size() == 0)
+ if (stack.size() == 0)
throw new IllegalStateException(
"Computation ended up without an answer.");
return stack.pop();
diff --git a/src/main/java/sevenUnits/utils/NameSymbol.java b/src/main/java/sevenUnits/utils/NameSymbol.java
index 49c44fa..089978b 100644
--- a/src/main/java/sevenUnits/utils/NameSymbol.java
+++ b/src/main/java/sevenUnits/utils/NameSymbol.java
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2019 Adrien Hopkins
+ * Copyright (C) 2019, 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
@@ -19,34 +19,35 @@ package sevenUnits.utils;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
-import java.util.Iterator;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
/**
* A class that can be used to specify names and a symbol for a unit.
- *
+ *
* @author Adrien Hopkins
* @since 2019-10-21
+ * @since v0.3.0
*/
public final class NameSymbol {
+ /** The {@code NameSymbol} with all fields empty. */
public static final NameSymbol EMPTY = new NameSymbol(Optional.empty(),
Optional.empty(), new HashSet<>());
/**
* Creates a {@code NameSymbol}, ensuring that if primaryName is null and
* otherNames is not empty, one name is moved from otherNames to primaryName
- *
+ *
* Ensure that otherNames is not a copy of the inputted argument.
*/
- private static final NameSymbol create(final String name,
- final String symbol, final Set<String> otherNames) {
+ private static NameSymbol create(final String name, final String symbol,
+ final Set<String> otherNames) {
final Optional<String> primaryName;
if (name == null && !otherNames.isEmpty()) {
// get primary name and remove it from savedNames
- final Iterator<String> it = otherNames.iterator();
+ final var it = otherNames.iterator();
assert it.hasNext();
primaryName = Optional.of(it.next());
otherNames.remove(primaryName.get());
@@ -61,14 +62,15 @@ public final class NameSymbol {
/**
* Gets a {@code NameSymbol} with a primary name, a symbol and no other
* names.
- *
+ *
* @param name name to use
* @param symbol symbol to use
* @return NameSymbol instance
* @since 2019-10-21
+ * @since v0.3.0
* @throws NullPointerException if name or symbol is null
*/
- public static final NameSymbol of(final String name, final String symbol) {
+ public static NameSymbol of(final String name, final String symbol) {
return new NameSymbol(Optional.of(name), Optional.of(symbol),
new HashSet<>());
}
@@ -76,15 +78,16 @@ public final class NameSymbol {
/**
* Gets a {@code NameSymbol} with a primary name, a symbol and additional
* names.
- *
+ *
* @param name name to use
* @param symbol symbol to use
* @param otherNames other names to use
* @return NameSymbol instance
* @since 2019-10-21
+ * @since v0.3.0
* @throws NullPointerException if any argument is null
*/
- public static final NameSymbol of(final String name, final String symbol,
+ public static NameSymbol of(final String name, final String symbol,
final Set<String> otherNames) {
return new NameSymbol(Optional.of(name), Optional.of(symbol),
new HashSet<>(Objects.requireNonNull(otherNames,
@@ -94,12 +97,13 @@ public final class NameSymbol {
/**
* h * Gets a {@code NameSymbol} with a primary name, a symbol and additional
* names.
- *
+ *
* @param name name to use
* @param symbol symbol to use
* @param otherNames other names to use
* @return NameSymbol instance
* @since 2019-10-21
+ * @since v0.3.0
* @throws NullPointerException if any argument is null
*/
public static final NameSymbol of(final String name, final String symbol,
@@ -112,13 +116,14 @@ public final class NameSymbol {
/**
* Gets a {@code NameSymbol} with a primary name, no symbol, and no other
* names.
- *
+ *
* @param name name to use
* @return NameSymbol instance
* @since 2019-10-21
+ * @since v0.3.0
* @throws NullPointerException if name is null
*/
- public static final NameSymbol ofName(final String name) {
+ public static NameSymbol ofName(final String name) {
return new NameSymbol(Optional.of(name), Optional.empty(),
new HashSet<>());
}
@@ -133,15 +138,16 @@ public final class NameSymbol {
* If {@code name} is null and {@code otherNames} is not empty, a primary
* name will be picked from {@code otherNames}. This name will not appear in
* getOtherNames().
- *
+ *
* @param name name to use
* @param symbol symbol to use
* @param otherNames other names to use
* @return NameSymbol instance
* @since 2019-11-26
+ * @since v0.3.0
*/
- public static final NameSymbol ofNullable(final String name,
- final String symbol, final Set<String> otherNames) {
+ public static NameSymbol ofNullable(final String name, final String symbol,
+ final Set<String> otherNames) {
return NameSymbol.create(name, symbol,
otherNames == null ? new HashSet<>() : new HashSet<>(otherNames));
}
@@ -156,12 +162,13 @@ public final class NameSymbol {
* If {@code name} is null and {@code otherNames} is not empty, a primary
* name will be picked from {@code otherNames}. This name will not appear in
* getOtherNames().
- *
+ *
* @param name name to use
* @param symbol symbol to use
* @param otherNames other names to use
* @return NameSymbol instance
* @since 2019-11-26
+ * @since v0.3.0
*/
public static final NameSymbol ofNullable(final String name,
final String symbol, final String... otherNames) {
@@ -171,13 +178,14 @@ public final class NameSymbol {
/**
* Gets a {@code NameSymbol} with a symbol and no names.
- *
+ *
* @param symbol symbol to use
* @return NameSymbol instance
* @since 2019-10-21
+ * @since v0.3.0
* @throws NullPointerException if symbol is null
*/
- public static final NameSymbol ofSymbol(final String symbol) {
+ public static NameSymbol ofSymbol(final String symbol) {
return new NameSymbol(Optional.empty(), Optional.of(symbol),
new HashSet<>());
}
@@ -189,21 +197,26 @@ public final class NameSymbol {
/**
* Creates the {@code NameSymbol}.
- *
+ *
* @param primaryName primary name of unit
* @param symbol symbol used to represent unit
* @param otherNames other names and/or spellings, should be a mutable copy
* of the argument
* @since 2019-10-21
+ * @since v0.3.0
*/
- private NameSymbol(final Optional<String> primaryName,
- final Optional<String> symbol, final Set<String> otherNames) {
+ NameSymbol(final Optional<String> primaryName, final Optional<String> symbol,
+ final Set<String> otherNames) {
this.primaryName = primaryName;
this.symbol = symbol;
- otherNames.remove(null);
- this.otherNames = Collections.unmodifiableSet(otherNames);
+ if (otherNames != null) {
+ otherNames.remove(null);
+ this.otherNames = Collections.unmodifiableSet(otherNames);
+ } else {
+ this.otherNames = Set.of();
+ }
- if (this.primaryName.isEmpty()) {
+ if (this.primaryName == null || this.primaryName.isEmpty()) {
assert this.otherNames.isEmpty();
}
}
@@ -214,7 +227,7 @@ public final class NameSymbol {
return true;
if (!(obj instanceof NameSymbol))
return false;
- final NameSymbol other = (NameSymbol) obj;
+ final var other = (NameSymbol) obj;
if (this.otherNames == null) {
if (other.otherNames != null)
return false;
@@ -236,31 +249,34 @@ public final class NameSymbol {
/**
* @return otherNames
* @since 2019-10-21
+ * @since v0.3.0
*/
- public final Set<String> getOtherNames() {
+ public Set<String> getOtherNames() {
return this.otherNames;
}
/**
* @return primaryName
* @since 2019-10-21
+ * @since v0.3.0
*/
- public final Optional<String> getPrimaryName() {
+ public Optional<String> getPrimaryName() {
return this.primaryName;
}
/**
* @return symbol
* @since 2019-10-21
+ * @since v0.3.0
*/
- public final Optional<String> getSymbol() {
+ public Optional<String> getSymbol() {
return this.symbol;
}
@Override
public int hashCode() {
- final int prime = 31;
- int result = 1;
+ final var prime = 31;
+ var result = 1;
result = prime * result
+ (this.otherNames == null ? 0 : this.otherNames.hashCode());
result = prime * result
@@ -270,10 +286,8 @@ public final class NameSymbol {
return result;
}
- /**
- * @return true iff this {@code NameSymbol} contains no names or symbols.
- */
- public final boolean isEmpty() {
+ /** @return true iff this {@code NameSymbol} contains no names or symbols. */
+ public boolean isEmpty() {
// if primaryName is empty, otherNames must also be empty
return this.primaryName.isEmpty() && this.symbol.isEmpty();
}
@@ -282,11 +296,10 @@ public final class NameSymbol {
public String toString() {
if (this.isEmpty())
return "NameSymbol.EMPTY";
- else if (this.primaryName.isPresent() && this.symbol.isPresent())
+ if (this.primaryName.isPresent() && this.symbol.isPresent())
return this.primaryName.orElseThrow() + " ("
+ this.symbol.orElseThrow() + ")";
- else
- return this.primaryName.orElseGet(this.symbol::orElseThrow);
+ return this.primaryName.orElseGet(this.symbol::orElseThrow);
}
/**
@@ -294,16 +307,19 @@ public final class NameSymbol {
* extra name. If this {@code NameSymbol} has a primary name, the provided
* name will become an other name, otherwise it will become the primary name.
*
- * @since v0.4.0
+ * @param name additional name to add
+ * @return copy of this NameSymbol with the additional name
+ *
* @since 2022-04-19
+ * @since v0.4.0
*/
- public final NameSymbol withExtraName(String name) {
+ public NameSymbol withExtraName(String name) {
if (this.primaryName.isPresent()) {
final var otherNames = new HashSet<>(this.otherNames);
otherNames.add(name);
return NameSymbol.ofNullable(this.primaryName.orElse(null),
this.symbol.orElse(null), otherNames);
- } else
- return NameSymbol.ofNullable(name, this.symbol.orElse(null));
+ }
+ return NameSymbol.ofNullable(name, this.symbol.orElse(null));
}
} \ No newline at end of file
diff --git a/src/main/java/sevenUnits/utils/Nameable.java b/src/main/java/sevenUnits/utils/Nameable.java
index 3959a64..166de55 100644
--- a/src/main/java/sevenUnits/utils/Nameable.java
+++ b/src/main/java/sevenUnits/utils/Nameable.java
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2020 Adrien Hopkins
+ * Copyright (C) 2020, 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
@@ -24,15 +24,17 @@ import java.util.Set;
* and symbol data should be immutable.
*
* @since 2020-09-07
+ * @since v0.3.0
*/
public interface Nameable {
/**
* @return a name for the object - if there's a primary name, it's that,
* otherwise the symbol, otherwise "Unnamed"
* @since 2022-02-26
+ * @since v0.4.0
*/
default String getName() {
- final NameSymbol ns = this.getNameSymbol();
+ final var ns = this.getNameSymbol();
return ns.getPrimaryName().or(ns::getSymbol).orElse("Unnamed");
}
@@ -40,12 +42,14 @@ public interface Nameable {
* @return a {@code NameSymbol} that contains this object's primary name,
* symbol and other names
* @since 2020-09-07
+ * @since v0.3.0
*/
NameSymbol getNameSymbol();
/**
* @return set of alternate names
* @since 2020-09-07
+ * @since v0.3.0
*/
default Set<String> getOtherNames() {
return this.getNameSymbol().getOtherNames();
@@ -54,6 +58,7 @@ public interface Nameable {
/**
* @return preferred name of object
* @since 2020-09-07
+ * @since v0.3.0
*/
default Optional<String> getPrimaryName() {
return this.getNameSymbol().getPrimaryName();
@@ -63,15 +68,17 @@ public interface Nameable {
* @return a short name for the object - if there's a symbol, it's that,
* otherwise the symbol, otherwise "Unnamed"
* @since 2022-02-26
+ * @since v0.4.0
*/
default String getShortName() {
- final NameSymbol ns = this.getNameSymbol();
+ final var ns = this.getNameSymbol();
return ns.getSymbol().or(ns::getPrimaryName).orElse("Unnamed");
}
/**
* @return short symbol representing object
* @since 2020-09-07
+ * @since v0.3.0
*/
default Optional<String> getSymbol() {
return this.getNameSymbol().getSymbol();
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);
diff --git a/src/main/java/sevenUnits/utils/SemanticVersionNumber.java b/src/main/java/sevenUnits/utils/SemanticVersionNumber.java
index fc47baa..4bb7ce5 100644
--- a/src/main/java/sevenUnits/utils/SemanticVersionNumber.java
+++ b/src/main/java/sevenUnits/utils/SemanticVersionNumber.java
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2022 Adrien Hopkins
+ * Copyright (C) 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
@@ -22,7 +22,6 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
-import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
@@ -39,8 +38,8 @@ import java.util.regex.Pattern;
* are made
* </ol>
*
- * @since v0.4.0
* @since 2022-02-19
+ * @since v0.4.0
*/
public final class SemanticVersionNumber
implements Comparable<SemanticVersionNumber> {
@@ -52,8 +51,8 @@ public final class SemanticVersionNumber
* throw NullPointerExceptions, everything else throws
* IllegalArgumentException.
*
- * @since v0.4.0
* @since 2022-02-19
+ * @since v0.4.0
*/
public static final class Builder {
private final int major;
@@ -69,8 +68,8 @@ public final class SemanticVersionNumber
* @param major major version number of final version
* @param minor minor version number of final version
* @param patch patch version number of final version
- * @since v0.4.0
* @since 2022-02-19
+ * @since v0.4.0
*/
private Builder(int major, int minor, int patch) {
this.major = major;
@@ -82,8 +81,8 @@ public final class SemanticVersionNumber
/**
* @return version number created by this builder
- * @since v0.4.0
* @since 2022-02-19
+ * @since v0.4.0
*/
public SemanticVersionNumber build() {
return new SemanticVersionNumber(this.major, this.minor, this.patch,
@@ -95,8 +94,8 @@ public final class SemanticVersionNumber
*
* @param identifiers build metadata
* @return this builder
- * @since v0.4.0
* @since 2022-02-19
+ * @since v0.4.0
*/
public Builder buildMetadata(List<String> identifiers) {
Objects.requireNonNull(identifiers, "identifiers may not be null");
@@ -115,8 +114,8 @@ public final class SemanticVersionNumber
*
* @param identifiers build metadata
* @return this builder
- * @since v0.4.0
* @since 2022-02-19
+ * @since v0.4.0
*/
public Builder buildMetadata(String... identifiers) {
Objects.requireNonNull(identifiers, "identifiers may not be null");
@@ -136,7 +135,7 @@ public final class SemanticVersionNumber
return true;
if (!(obj instanceof Builder))
return false;
- final Builder other = (Builder) obj;
+ final var other = (Builder) obj;
return Objects.equals(this.buildMetadata, other.buildMetadata)
&& this.major == other.major && this.minor == other.minor
&& this.patch == other.patch && Objects.equals(
@@ -154,8 +153,8 @@ public final class SemanticVersionNumber
*
* @param identifiers pre-release identifier(s) to add
* @return this builder
- * @since v0.4.0
* @since 2022-02-19
+ * @since v0.4.0
*/
public Builder preRelease(int... identifiers) {
Objects.requireNonNull(identifiers, "identifiers may not be null");
@@ -173,8 +172,8 @@ public final class SemanticVersionNumber
*
* @param identifiers pre-release identifier(s) to add
* @return this builder
- * @since v0.4.0
* @since 2022-02-19
+ * @since v0.4.0
*/
public Builder preRelease(List<String> identifiers) {
Objects.requireNonNull(identifiers, "identifiers may not be null");
@@ -193,8 +192,8 @@ public final class SemanticVersionNumber
*
* @param identifiers pre-release identifier(s) to add
* @return this builder
- * @since v0.4.0
* @since 2022-02-19
+ * @since v0.4.0
*/
public Builder preRelease(String... identifiers) {
Objects.requireNonNull(identifiers, "identifiers may not be null");
@@ -214,8 +213,8 @@ public final class SemanticVersionNumber
* @param identifier1 first identifier
* @param identifier2 second identifier
* @return this builder
- * @since v0.4.0
* @since 2022-02-19
+ * @since v0.4.0
*/
public Builder preRelease(String identifier1, int identifier2) {
Objects.requireNonNull(identifier1, "identifier1 may not be null");
@@ -249,12 +248,11 @@ public final class SemanticVersionNumber
public int compare(SemanticVersionNumber o1, SemanticVersionNumber o2) {
Objects.requireNonNull(o1, "o1 may not be null");
Objects.requireNonNull(o2, "o2 may not be null");
- final int naturalComparison = o1.compareTo(o2);
+ final var naturalComparison = o1.compareTo(o2);
if (naturalComparison == 0)
return SemanticVersionNumber.compareIdentifiers(o1.buildMetadata,
o2.buildMetadata);
- else
- return naturalComparison;
+ return naturalComparison;
};
};
@@ -280,11 +278,11 @@ public final class SemanticVersionNumber
* @param patch patch version number of final version
* @return version number builder
* @throws IllegalArgumentException if any argument is negative
- * @since v0.4.0
* @since 2022-02-19
+ * @since v0.4.0
*/
- public static final SemanticVersionNumber.Builder builder(int major,
- int minor, int patch) {
+ public static SemanticVersionNumber.Builder builder(int major, int minor,
+ int patch) {
if (major < 0)
throw new IllegalArgumentException(
"Major version must be non-negative.");
@@ -304,60 +302,57 @@ public final class SemanticVersionNumber
* @param b second list
* @return result of comparison as in a comparator
* @see Comparator
- * @since v0.4.0
* @since 2022-02-20
+ * @since v0.4.0
*/
- private static final int compareIdentifiers(List<String> a, List<String> b) {
+ private static int compareIdentifiers(List<String> a, List<String> b) {
// test pre-release size
- final int aSize = a.size();
- final int bSize = b.size();
+ final var aSize = a.size();
+ final var bSize = b.size();
// no identifiers is greater than any identifiers
if (aSize != 0 && bSize == 0)
return -1;
- else if (aSize == 0 && bSize != 0)
+ if (aSize == 0 && bSize != 0)
return 1;
// test identifiers one by one
- for (int i = 0; i < Math.min(aSize, bSize); i++) {
- final String aElement = a.get(i);
- final String bElement = b.get(i);
+ for (var i = 0; i < Math.min(aSize, bSize); i++) {
+ final var aElement = a.get(i);
+ final var bElement = b.get(i);
if (NUMERIC_IDENTIFER.matcher(aElement).matches()) {
- if (NUMERIC_IDENTIFER.matcher(bElement).matches()) {
- // both are numbers, compare them
- final int aNumber = Integer.parseInt(aElement);
- final int bNumber = Integer.parseInt(bElement);
-
- if (aNumber < bNumber)
- return -1;
- else if (aNumber > bNumber)
- return 1;
- } else
+ if (!NUMERIC_IDENTIFER.matcher(bElement).matches())
// aElement is a number and bElement is not a number
// by the rules, a goes before b
return -1;
- } else {
- if (NUMERIC_IDENTIFER.matcher(bElement).matches())
- // aElement is not a number but bElement is
- // by the rules, a goes after b
+ // both are numbers, compare them
+ final var aNumber = Integer.parseInt(aElement);
+ final var bNumber = Integer.parseInt(bElement);
+
+ if (aNumber < bNumber)
+ return -1;
+ if (aNumber > bNumber)
return 1;
- else {
- // both are not numbers, compare them
- final int comparison = aElement.compareTo(bElement);
- if (comparison != 0)
- return comparison;
- }
+ } else if (NUMERIC_IDENTIFER.matcher(bElement).matches())
+ // aElement is not a number but bElement is
+ // by the rules, a goes after b
+ return 1;
+ else {
+ // both are not numbers, compare them
+ final var comparison = aElement.compareTo(bElement);
+ if (comparison != 0)
+ return comparison;
}
}
-
- // we just tested the stuff that's in common, maybe someone has more
if (aSize < bSize)
return -1;
- else if (aSize > bSize)
+ if (aSize > bSize)
return 1;
- else
- return 0;
+ return 0;
+
+ // we just tested the stuff that's in common, maybe someone has more
+
}
/**
@@ -365,23 +360,23 @@ public final class SemanticVersionNumber
*
* @param versionString string to parse
* @return {@code SemanticVersionNumber} instance
- * @since v0.4.0
* @since 2022-02-19
- * @see {@link #toString}
+ * @since v0.4.0
+ * @see #toString
*/
- public static final SemanticVersionNumber fromString(String versionString) {
+ public static SemanticVersionNumber fromString(String versionString) {
// parse & validate version string
Objects.requireNonNull(versionString, "versionString may not be null");
- final Matcher m = VERSION_NUMBER.matcher(versionString);
+ final var m = VERSION_NUMBER.matcher(versionString);
if (!m.matches())
throw new IllegalArgumentException(
String.format("Provided string \"%s\" is not a version number",
versionString));
// main parts
- final int major = Integer.parseInt(m.group(1));
- final int minor = Integer.parseInt(m.group(2));
- final int patch = Integer.parseInt(m.group(3));
+ final var major = Integer.parseInt(m.group(1));
+ final var minor = Integer.parseInt(m.group(2));
+ final var patch = Integer.parseInt(m.group(3));
// pre release
final List<String> preRelease;
@@ -406,13 +401,13 @@ public final class SemanticVersionNumber
/**
* Tests whether a string is a valid Semantic Version string
- *
+ *
* @param versionString string to test
* @return true iff string is valid
- * @since v0.4.0
* @since 2022-02-19
+ * @since v0.4.0
*/
- public static final boolean isValidVersionString(String versionString) {
+ public static boolean isValidVersionString(String versionString) {
return VERSION_NUMBER.matcher(versionString).matches();
}
@@ -429,10 +424,10 @@ public final class SemanticVersionNumber
* @throws IllegalArgumentException if any argument is negative or if the
* preReleaseType is null, empty or not
* alphanumeric (0-9, A-Z, a-z, - only)
- * @since v0.4.0
* @since 2022-02-19
+ * @since v0.4.0
*/
- public static final SemanticVersionNumber preRelease(int major, int minor,
+ public static SemanticVersionNumber preRelease(int major, int minor,
int patch, String preReleaseType, int preReleaseNumber) {
if (major < 0)
throw new IllegalArgumentException(
@@ -467,10 +462,10 @@ public final class SemanticVersionNumber
* @param patch patch version number
* @return {@code SemanticVersionNumber} instance
* @throws IllegalArgumentException if any argument is negative
- * @since v0.4.0
* @since 2022-02-19
+ * @since v0.4.0
*/
- public static final SemanticVersionNumber stableVersion(int major, int minor,
+ public static SemanticVersionNumber stableVersion(int major, int minor,
int patch) {
if (major < 0)
throw new IllegalArgumentException(
@@ -500,8 +495,8 @@ public final class SemanticVersionNumber
* @param patch patch version number
* @param preReleaseIdentifiers pre-release version data
* @param buildMetadata build metadata
- * @since v0.4.0
* @since 2022-02-19
+ * @since v0.4.0
*/
private SemanticVersionNumber(int major, int minor, int patch,
List<String> preReleaseIdentifiers, List<String> buildMetadata) {
@@ -514,8 +509,8 @@ public final class SemanticVersionNumber
/**
* @return build metadata (empty if there is none)
- * @since v0.4.0
* @since 2022-02-19
+ * @since v0.4.0
*/
public List<String> buildMetadata() {
return Collections.unmodifiableList(this.buildMetadata);
@@ -536,17 +531,17 @@ public final class SemanticVersionNumber
// test the three big numbers in order first
if (this.major < o.major)
return -1;
- else if (this.major > o.major)
+ if (this.major > o.major)
return 1;
if (this.minor < o.minor)
return -1;
- else if (this.minor > o.minor)
+ if (this.minor > o.minor)
return 1;
if (this.patch < o.patch)
return -1;
- else if (this.patch > o.patch)
+ if (this.patch > o.patch)
return 1;
// now we just compare pre-release identifiers
@@ -569,7 +564,7 @@ public final class SemanticVersionNumber
* </ul>
* If this function returns <b>false</b>, you may have to change your code to
* upgrade it to {@code other}
- *
+ *
* <p>
* Two version numbers that are identical (ignoring build metadata) are
* always compatible. Different version numbers are compatible as long as:
@@ -585,8 +580,8 @@ public final class SemanticVersionNumber
* @param other version to compare with
* @return true if you can definitely upgrade to {@code other} without
* changing code
- * @since v0.4.0
* @since 2022-02-20
+ * @since v0.4.0
*/
public boolean compatibleWith(SemanticVersionNumber other) {
Objects.requireNonNull(other, "other may not be null");
@@ -601,17 +596,14 @@ public final class SemanticVersionNumber
return true;
if (!(obj instanceof SemanticVersionNumber))
return false;
- final SemanticVersionNumber other = (SemanticVersionNumber) obj;
+ final var other = (SemanticVersionNumber) obj;
if (this.buildMetadata == null) {
if (other.buildMetadata != null)
return false;
} else if (!this.buildMetadata.equals(other.buildMetadata))
return false;
- if (this.major != other.major)
- return false;
- if (this.minor != other.minor)
- return false;
- if (this.patch != other.patch)
+ if ((this.major != other.major) || (this.minor != other.minor)
+ || (this.patch != other.patch))
return false;
if (this.preReleaseIdentifiers == null) {
if (other.preReleaseIdentifiers != null)
@@ -624,8 +616,8 @@ public final class SemanticVersionNumber
@Override
public int hashCode() {
- final int prime = 31;
- int result = 1;
+ final var prime = 31;
+ var result = 1;
result = prime * result
+ (this.buildMetadata == null ? 0 : this.buildMetadata.hashCode());
result = prime * result + this.major;
@@ -639,8 +631,8 @@ public final class SemanticVersionNumber
/**
* @return true iff this version is stable (major version > 0 and not a
* pre-release)
- * @since v0.4.0
* @since 2022-02-19
+ * @since v0.4.0
*/
public boolean isStable() {
return this.major > 0 && this.preReleaseIdentifiers.isEmpty();
@@ -649,8 +641,8 @@ public final class SemanticVersionNumber
/**
* @return the MAJOR version number, incremented when you make backwards
* incompatible API changes
- * @since v0.4.0
* @since 2022-02-19
+ * @since v0.4.0
*/
public int majorVersion() {
return this.major;
@@ -659,8 +651,8 @@ public final class SemanticVersionNumber
/**
* @return the MINOR version number, incremented when you add backwards
* compatible functionality
- * @since v0.4.0
* @since 2022-02-19
+ * @since v0.4.0
*/
public int minorVersion() {
return this.minor;
@@ -669,8 +661,8 @@ public final class SemanticVersionNumber
/**
* @return the PATCH version number, incremented when you make backwards
* compatible bug fixes
- * @since v0.4.0
* @since 2022-02-19
+ * @since v0.4.0
*/
public int patchVersion() {
return this.patch;
@@ -679,8 +671,8 @@ public final class SemanticVersionNumber
/**
* @return identifiers describing this pre-release (empty if not a
* pre-release)
- * @since v0.4.0
* @since 2022-02-19
+ * @since v0.4.0
*/
public List<String> preReleaseIdentifiers() {
return Collections.unmodifiableList(this.preReleaseIdentifiers);
@@ -697,13 +689,13 @@ public final class SemanticVersionNumber
* For example, the version with major number 3, minor number 2, patch number
* 1, pre-release identifiers "alpha" and "1" and build metadata "2022-02-19"
* has a string representation "3.2.1-alpha.1+2022-02-19".
- *
+ *
* @since v0.4.0
* @see <a href="https://semver.org">The official SemVer specification</a>
*/
@Override
public String toString() {
- String versionString = String.format("%d.%d.%d", this.major, this.minor,
+ var versionString = String.format("%d.%d.%d", this.major, this.minor,
this.patch);
if (!this.preReleaseIdentifiers.isEmpty()) {
versionString += "-" + String.join(".", this.preReleaseIdentifiers);
diff --git a/src/main/java/sevenUnits/utils/UncertainDouble.java b/src/main/java/sevenUnits/utils/UncertainDouble.java
index 66d8103..24ada20 100644
--- a/src/main/java/sevenUnits/utils/UncertainDouble.java
+++ b/src/main/java/sevenUnits/utils/UncertainDouble.java
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2020 Adrien Hopkins
+ * Copyright (C) 2020-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
@@ -19,7 +19,6 @@ package sevenUnits.utils;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Objects;
-import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
@@ -29,56 +28,59 @@ import java.util.regex.Pattern;
* arguments is null.
*
* @since 2020-09-07
+ * @since v0.3.0
*/
public final class UncertainDouble implements Comparable<UncertainDouble> {
- /**
- * The exact value 0
- */
+ /** The exact value 0 */
public static final UncertainDouble ZERO = UncertainDouble.of(0, 0);
static final String NUMBER_REGEX = "(\\d+(?:[\\.,]\\d+))";
- /**
- * A regular expression that can recognize toString forms
- */
+ /** A regular expression that can recognize toString forms */
static final Pattern TO_STRING = Pattern.compile(NUMBER_REGEX
- // optional "� [number]"
- + "(?:\\s*(?:�|\\+-)\\s*" + NUMBER_REGEX + ")?");
+ // optional "± [number]"
+ + "(?:\\s*(?:±|\\+-)\\s*" + NUMBER_REGEX + ")?");
/**
* Gets an UncertainDouble from a double string. The uncertainty of the
* double will be one of the lowest decimal place of the number. For example,
- * "12345.678" will become 12345.678 � 0.001.
- *
+ * "12345.678" will become 12345.678 ± 0.001.
+ *
+ * @param s string to parse
+ * @return parsed {@code UncertainDouble}
+ *
* @throws NumberFormatException if the argument is not a number
*
* @since 2022-04-18
+ * @since v0.4.0
*/
- public static final UncertainDouble fromRoundedString(String s) {
- final BigDecimal value = new BigDecimal(s);
- final double uncertainty = Math.pow(10, -value.scale());
+ public static UncertainDouble fromRoundedString(String s) {
+ final var value = new BigDecimal(s);
+ final var uncertainty = Math.pow(10, -value.scale());
return UncertainDouble.of(value.doubleValue(), uncertainty);
}
/**
- * Parses a string in the form of {@link UncertainDouble#toString(boolean)}
- * and returns the corresponding {@code UncertainDouble} instance.
+ * Parses a string in the form of
+ * {@link UncertainDouble#toString(boolean, RoundingMode)} and returns the
+ * corresponding {@code UncertainDouble} instance.
* <p>
* This method allows some alternative forms of the string representation,
- * such as using "+-" instead of "�".
- *
+ * such as using "+-" instead of "±".
+ *
* @param s string to parse
* @return {@code UncertainDouble} instance
* @throws IllegalArgumentException if the string is invalid
* @since 2020-09-07
+ * @since v0.3.0
*/
- public static final UncertainDouble fromString(String s) {
+ public static UncertainDouble fromString(String s) {
Objects.requireNonNull(s, "s may not be null");
- final Matcher matcher = TO_STRING.matcher(s);
+ final var matcher = TO_STRING.matcher(s);
if (!matcher.matches())
throw new IllegalArgumentException(
- "Could not parse stirng \"" + s + "\".");
+ "Could not parse string \"" + s + "\".");
double value, uncertainty;
try {
@@ -88,7 +90,7 @@ public final class UncertainDouble implements Comparable<UncertainDouble> {
"String " + s + " not in correct format.");
}
- final String uncertaintyString = matcher.group(2);
+ final var uncertaintyString = matcher.group(2);
if (uncertaintyString == null) {
uncertainty = 0;
} else {
@@ -106,20 +108,32 @@ public final class UncertainDouble implements Comparable<UncertainDouble> {
/**
* Gets an {@code UncertainDouble} from its value and <b>absolute</b>
* uncertainty.
- *
+ *
+ * @param value double's value
+ * @param uncertainty double's uncertainty (non-negative)
+ * @return {@code UncertainDouble} instance with these parameters
+ *
* @since 2020-09-07
+ * @since v0.3.0
*/
- public static final UncertainDouble of(double value, double uncertainty) {
+ public static UncertainDouble of(double value, double uncertainty) {
return new UncertainDouble(value, uncertainty);
}
/**
* Gets an {@code UncertainDouble} from its value and <b>relative</b>
* uncertainty.
- *
+ *
+ * @param value double's value
+ * @param relativeUncertainty double's uncertainty (non-negative); the
+ * absolute uncertainty is equal to this value
+ * multiplied by {@code relativeUncertainty}
+ * @return {@code UncertainDouble} instance with these parameters
+ *
* @since 2020-09-07
+ * @since v0.3.0
*/
- public static final UncertainDouble ofRelative(double value,
+ public static UncertainDouble ofRelative(double value,
double relativeUncertainty) {
return new UncertainDouble(value, value * relativeUncertainty);
}
@@ -132,6 +146,7 @@ public final class UncertainDouble implements Comparable<UncertainDouble> {
* @param value
* @param uncertainty
* @since 2020-09-07
+ * @since v0.3.0
*/
private UncertainDouble(double value, double uncertainty) {
this.value = value;
@@ -153,16 +168,20 @@ public final class UncertainDouble implements Comparable<UncertainDouble> {
* {@code false}.
*/
@Override
- public final int compareTo(UncertainDouble o) {
+ public int compareTo(UncertainDouble o) {
return Double.compare(this.value, o.value);
}
/**
* Returns the quotient of {@code this} and {@code other}.
- *
+ *
+ * @param other number to divide by
+ * @return quotient
+ *
* @since 2020-09-07
+ * @since v0.3.0
*/
- public final UncertainDouble dividedBy(UncertainDouble other) {
+ public UncertainDouble dividedBy(UncertainDouble other) {
Objects.requireNonNull(other, "other may not be null");
return UncertainDouble.ofRelative(this.value / other.value, Math
.hypot(this.relativeUncertainty(), other.relativeUncertainty()));
@@ -170,23 +189,26 @@ public final class UncertainDouble implements Comparable<UncertainDouble> {
/**
* Returns the quotient of {@code this} and the exact value {@code other}.
- *
+ *
+ * @param other number to divide by
+ * @return quotient
+ *
* @since 2020-09-07
+ * @since v0.3.0
*/
- public final UncertainDouble dividedByExact(double other) {
+ public UncertainDouble dividedByExact(double other) {
return UncertainDouble.of(this.value / other, this.uncertainty / other);
}
@Override
- public final boolean equals(Object obj) {
+ public boolean equals(Object obj) {
if (this == obj)
return true;
if (!(obj instanceof UncertainDouble))
return false;
- final UncertainDouble other = (UncertainDouble) obj;
- if (Double.compare(this.value, other.value) != 0)
- return false;
- if (Double.compare(this.uncertainty, other.uncertainty) != 0)
+ final var other = (UncertainDouble) obj;
+ if ((Double.compare(this.value, other.value) != 0)
+ || (Double.compare(this.uncertainty, other.uncertainty) != 0))
return false;
return true;
}
@@ -196,8 +218,9 @@ public final class UncertainDouble implements Comparable<UncertainDouble> {
* @return true iff this and {@code other} are within each other's
* uncertainty range.
* @since 2020-09-07
+ * @since v0.3.0
*/
- public final boolean equivalent(UncertainDouble other) {
+ public boolean equivalent(UncertainDouble other) {
Objects.requireNonNull(other, "other may not be null");
return Math.abs(this.value - other.value) <= Math.min(this.uncertainty,
other.uncertainty);
@@ -205,10 +228,11 @@ public final class UncertainDouble implements Comparable<UncertainDouble> {
/**
* Gets the preferred scale for rounding a value for toString.
- *
+ *
* @since 2020-09-07
+ * @since v0.3.0
*/
- private final int getDisplayScale() {
+ private int getDisplayScale() {
// round based on uncertainty
// if uncertainty starts with 1 (ignoring zeroes and the decimal
// point), rounds
@@ -216,24 +240,23 @@ public final class UncertainDouble implements Comparable<UncertainDouble> {
// otherwise, rounds so that uncertainty has 1 significant digits.
// the value is rounded to the same number of decimal places as the
// uncertainty.
- final BigDecimal bigUncertainty = BigDecimal.valueOf(this.uncertainty);
+ final var bigUncertainty = BigDecimal.valueOf(this.uncertainty);
// the scale that will give the uncertainty two decimal places
- final int twoDecimalPlacesScale = bigUncertainty.scale()
+ final var twoDecimalPlacesScale = bigUncertainty.scale()
- bigUncertainty.precision() + 2;
- final BigDecimal roundedUncertainty = bigUncertainty
+ final var roundedUncertainty = bigUncertainty
.setScale(twoDecimalPlacesScale, RoundingMode.HALF_EVEN);
if (roundedUncertainty.unscaledValue().intValue() >= 20)
return twoDecimalPlacesScale - 1; // one decimal place
- else
- return twoDecimalPlacesScale;
+ return twoDecimalPlacesScale;
}
@Override
- public final int hashCode() {
- final int prime = 31;
- int result = 1;
+ public int hashCode() {
+ final var prime = 31;
+ var result = 1;
result = prime * result + Double.hashCode(this.value);
result = prime * result + Double.hashCode(this.uncertainty);
return result;
@@ -241,19 +264,24 @@ public final class UncertainDouble implements Comparable<UncertainDouble> {
/**
* @return true iff the value has no uncertainty
- *
+ *
* @since 2020-09-07
+ * @since v0.3.0
*/
- public final boolean isExact() {
+ public boolean isExact() {
return this.uncertainty == 0;
}
/**
* Returns the difference of {@code this} and {@code other}.
- *
+ *
+ * @param other number to subtract
+ * @return result of subtraction
+ *
* @since 2020-09-07
+ * @since v0.3.0
*/
- public final UncertainDouble minus(UncertainDouble other) {
+ public UncertainDouble minus(UncertainDouble other) {
Objects.requireNonNull(other, "other may not be null");
return UncertainDouble.of(this.value - other.value,
Math.hypot(this.uncertainty, other.uncertainty));
@@ -261,19 +289,27 @@ public final class UncertainDouble implements Comparable<UncertainDouble> {
/**
* Returns the difference of {@code this} and the exact value {@code other}.
- *
+ *
+ * @param other number to subtract
+ * @return result of subtraction
+ *
* @since 2020-09-07
+ * @since v0.3.0
*/
- public final UncertainDouble minusExact(double other) {
+ public UncertainDouble minusExact(double other) {
return UncertainDouble.of(this.value - other, this.uncertainty);
}
/**
* Returns the sum of {@code this} and {@code other}.
- *
+ *
+ * @param other number to add
+ * @return result of addition
+ *
* @since 2020-09-07
+ * @since v0.3.0
*/
- public final UncertainDouble plus(UncertainDouble other) {
+ public UncertainDouble plus(UncertainDouble other) {
Objects.requireNonNull(other, "other may not be null");
return UncertainDouble.of(this.value + other.value,
Math.hypot(this.uncertainty, other.uncertainty));
@@ -281,27 +317,36 @@ public final class UncertainDouble implements Comparable<UncertainDouble> {
/**
* Returns the sum of {@code this} and the exact value {@code other}.
- *
+ *
+ * @param other number to add
+ * @return result of addition
+ *
* @since 2020-09-07
+ * @since v0.3.0
*/
- public final UncertainDouble plusExact(double other) {
+ public UncertainDouble plusExact(double other) {
return UncertainDouble.of(this.value + other, this.uncertainty);
}
/**
* @return relative uncertainty
* @since 2020-09-07
+ * @since v0.3.0
*/
- public final double relativeUncertainty() {
+ public double relativeUncertainty() {
return this.uncertainty / this.value;
}
/**
* Returns the product of {@code this} and {@code other}.
- *
+ *
+ * @param other number to multiply
+ * @return product
+ *
* @since 2020-09-07
+ * @since v0.3.0
*/
- public final UncertainDouble times(UncertainDouble other) {
+ public UncertainDouble times(UncertainDouble other) {
Objects.requireNonNull(other, "other may not be null");
return UncertainDouble.ofRelative(this.value * other.value, Math
.hypot(this.relativeUncertainty(), other.relativeUncertainty()));
@@ -309,23 +354,31 @@ public final class UncertainDouble implements Comparable<UncertainDouble> {
/**
* Returns the product of {@code this} and the exact value {@code other}.
- *
+ *
+ * @param other number to multiply
+ * @return product
+ *
* @since 2020-09-07
+ * @since v0.3.0
*/
- public final UncertainDouble timesExact(double other) {
+ public UncertainDouble timesExact(double other) {
return UncertainDouble.of(this.value * other, this.uncertainty * other);
}
/**
* Returns the result of {@code this} raised to the exponent {@code other}.
- *
+ *
+ * @param other exponent
+ * @return result of exponentation
+ *
* @since 2020-09-07
+ * @since v0.3.0
*/
- public final UncertainDouble toExponent(UncertainDouble other) {
+ public UncertainDouble toExponent(UncertainDouble other) {
Objects.requireNonNull(other, "other may not be null");
- final double result = Math.pow(this.value, other.value);
- final double relativeUncertainty = Math.hypot(
+ final var result = Math.pow(this.value, other.value);
+ final var relativeUncertainty = Math.hypot(
other.value * this.relativeUncertainty(),
Math.log(this.value) * other.uncertainty);
@@ -335,10 +388,14 @@ public final class UncertainDouble implements Comparable<UncertainDouble> {
/**
* Returns the result of {@code this} raised the exact exponent
* {@code other}.
- *
+ *
+ * @param other exponent
+ * @return result of exponentation
+ *
* @since 2020-09-07
+ * @since v0.3.0
*/
- public final UncertainDouble toExponentExact(double other) {
+ public UncertainDouble toExponentExact(double other) {
return UncertainDouble.ofRelative(Math.pow(this.value, other),
this.relativeUncertainty() * other);
}
@@ -346,23 +403,24 @@ public final class UncertainDouble implements Comparable<UncertainDouble> {
/**
* Returns a string representation of this {@code UncertainDouble}.
* <p>
- * This method returns the same value as {@link #toString(boolean)}, but
- * {@code showUncertainty} is true if and only if the uncertainty is
- * non-zero.
- *
+ * This method returns the same value as
+ * {@link #toString(boolean, RoundingMode)}, but {@code showUncertainty} is
+ * true if and only if the uncertainty is non-zero.
+ *
* <p>
* Examples:
- *
+ *
* <pre>
* UncertainDouble.of(3.27, 0.22).toString() = "3.3 � 0.2"
* UncertainDouble.of(3.27, 0.13).toString() = "3.27 � 0.13"
* UncertainDouble.of(-5.01, 0).toString() = "-5.01"
* </pre>
- *
+ *
* @since 2020-09-07
+ * @since v0.3.0
*/
@Override
- public final String toString() {
+ public String toString() {
return this.toString(!this.isExact(), RoundingMode.HALF_EVEN);
}
@@ -370,7 +428,7 @@ public final class UncertainDouble implements Comparable<UncertainDouble> {
* Returns a string representation of this {@code UncertainDouble}.
* <p>
* If {@code showUncertainty} is true, the string will be of the form "VALUE
- * � UNCERTAINTY", and if it is false the string will be of the form "VALUE"
+ * ± UNCERTAINTY", and if it is false the string will be of the form "VALUE"
* <p>
* VALUE represents a string representation of this {@code UncertainDouble}'s
* value. If the uncertainty is non-zero, the string will be rounded to the
@@ -382,20 +440,24 @@ public final class UncertainDouble implements Comparable<UncertainDouble> {
* digits otherwise it will be rounded to one significant digit.
* <p>
* Examples:
- *
+ *
* <pre>
* UncertainDouble.of(3.27, 0.22).toString(false) = "3.3"
- * UncertainDouble.of(3.27, 0.22).toString(true) = "3.3 � 0.2"
+ * UncertainDouble.of(3.27, 0.22).toString(true) = "3.3 ± 0.2"
* UncertainDouble.of(3.27, 0.13).toString(false) = "3.27"
- * UncertainDouble.of(3.27, 0.13).toString(true) = "3.27 � 0.13"
+ * UncertainDouble.of(3.27, 0.13).toString(true) = "3.27 ± 0.13"
* UncertainDouble.of(-5.01, 0).toString(false) = "-5.01"
- * UncertainDouble.of(-5.01, 0).toString(true) = "-5.01 � 0.0"
+ * UncertainDouble.of(-5.01, 0).toString(true) = "-5.01 ± 0.0"
* </pre>
- *
+ *
+ * @param showUncertainty uncertainty is only shown if this parameter is true
+ * @param roundingMode how to round values
+ * @return string representation of this {@code UncertainDouble}
+ *
* @since 2020-09-07
+ * @since v0.3.0
*/
- public final String toString(boolean showUncertainty,
- RoundingMode roundingMode) {
+ public String toString(boolean showUncertainty, RoundingMode roundingMode) {
String valueString, uncertaintyString;
// generate the string representation of value and uncertainty
@@ -405,36 +467,37 @@ public final class UncertainDouble implements Comparable<UncertainDouble> {
} else {
// round the value and uncertainty according to getDisplayScale()
- final BigDecimal bigValue = BigDecimal.valueOf(this.value);
- final BigDecimal bigUncertainty = BigDecimal.valueOf(this.uncertainty);
+ final var bigValue = BigDecimal.valueOf(this.value);
+ final var bigUncertainty = BigDecimal.valueOf(this.uncertainty);
- final int displayScale = this.getDisplayScale();
- final BigDecimal roundedUncertainty = bigUncertainty
- .setScale(displayScale, roundingMode);
- final BigDecimal roundedValue = bigValue.setScale(displayScale,
+ final var displayScale = this.getDisplayScale();
+ final var roundedUncertainty = bigUncertainty.setScale(displayScale,
roundingMode);
+ final var roundedValue = bigValue.setScale(displayScale, roundingMode);
valueString = roundedValue.toString();
uncertaintyString = roundedUncertainty.toString();
}
- // return "value" or "value � uncertainty" depending on showUncertainty
- return valueString + (showUncertainty ? " � " + uncertaintyString : "");
+ // return "value" or "value ± uncertainty" depending on showUncertainty
+ return valueString + (showUncertainty ? " ± " + uncertaintyString : "");
}
/**
* @return absolute uncertainty
* @since 2020-09-07
+ * @since v0.3.0
*/
- public final double uncertainty() {
+ public double uncertainty() {
return this.uncertainty;
}
/**
* @return value without uncertainty
* @since 2020-09-07
+ * @since v0.3.0
*/
- public final double value() {
+ public double value() {
return this.value;
}
}
diff --git a/src/main/java/sevenUnits/utils/package-info.java b/src/main/java/sevenUnits/utils/package-info.java
index 350c62d..b600c17 100644
--- a/src/main/java/sevenUnits/utils/package-info.java
+++ b/src/main/java/sevenUnits/utils/package-info.java
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2018-2020 Adrien Hopkins
+ * Copyright (C) 2018-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,7 +17,7 @@
/**
* Supplementary classes that are not related to units, but are necessary for
* their function.
- *
+ *
* @author Adrien Hopkins
* @since 2019-03-14
* @since v0.2.0