summaryrefslogtreecommitdiff
path: root/src/main/java/sevenUnits/utils
diff options
context:
space:
mode:
authorAdrien Hopkins <adrien.p.hopkins@gmail.com>2024-03-24 13:25:22 -0500
committerAdrien Hopkins <adrien.p.hopkins@gmail.com>2024-03-24 13:25:22 -0500
commited53492243ecad8d975401a97f5b634328ad2c71 (patch)
tree8a744f46320710355a02c9b2c371602ce69aefec /src/main/java/sevenUnits/utils
parentc878761f737c90fc3fa1caedd48e2ee01637108f (diff)
parent91d51c3c49c4c0877483220ac0f12db4efab8f60 (diff)
Release version 0.5.0 (merge into stable)
Diffstat (limited to 'src/main/java/sevenUnits/utils')
-rw-r--r--src/main/java/sevenUnits/utils/ConditionalExistenceCollections.java104
-rw-r--r--src/main/java/sevenUnits/utils/DecimalComparison.java20
-rw-r--r--src/main/java/sevenUnits/utils/ExpressionParser.java308
-rw-r--r--src/main/java/sevenUnits/utils/NameSymbol.java44
-rw-r--r--src/main/java/sevenUnits/utils/Nameable.java10
-rw-r--r--src/main/java/sevenUnits/utils/ObjectProduct.java56
-rw-r--r--src/main/java/sevenUnits/utils/SemanticVersionNumber.java94
-rw-r--r--src/main/java/sevenUnits/utils/UncertainDouble.java116
8 files changed, 453 insertions, 299 deletions
diff --git a/src/main/java/sevenUnits/utils/ConditionalExistenceCollections.java b/src/main/java/sevenUnits/utils/ConditionalExistenceCollections.java
index bee4dd1..b71a4e0 100644
--- a/src/main/java/sevenUnits/utils/ConditionalExistenceCollections.java
+++ b/src/main/java/sevenUnits/utils/ConditionalExistenceCollections.java
@@ -67,7 +67,7 @@ public final class ConditionalExistenceCollections {
extends AbstractCollection<E> {
final Collection<E> collection;
final Predicate<E> existenceCondition;
-
+
/**
* Creates the {@code ConditionalExistenceCollection}.
*
@@ -80,38 +80,38 @@ public final class ConditionalExistenceCollections {
this.collection = collection;
this.existenceCondition = existenceCondition;
}
-
+
@Override
public boolean add(final E e) {
return this.collection.add(e) && this.existenceCondition.test(e);
}
-
+
@Override
public void clear() {
this.collection.clear();
}
-
+
@Override
public boolean contains(final Object o) {
if (!this.collection.contains(o))
return false;
-
+
// this collection can only contain instances of E
// since the object is in the collection, we know that it must be an
// instance of E
// therefore this cast will always work
@SuppressWarnings("unchecked")
final E e = (E) o;
-
+
return this.existenceCondition.test(e);
}
-
+
@Override
public Iterator<E> iterator() {
return conditionalExistenceIterator(this.collection.iterator(),
this.existenceCondition);
}
-
+
@Override
public boolean remove(final Object o) {
// remove() must be first in the && statement, otherwise it may not
@@ -119,32 +119,32 @@ public final class ConditionalExistenceCollections {
final boolean containedObject = this.contains(o);
return this.collection.remove(o) && containedObject;
}
-
+
@Override
public int size() {
return (int) this.collection.stream().filter(this.existenceCondition)
.count();
}
-
+
@Override
public Object[] toArray() {
// ensure the toArray operation is supported
this.collection.toArray();
-
+
// if it works, do it for real
return super.toArray();
}
-
+
@Override
public <T> T[] toArray(T[] a) {
// ensure the toArray operation is supported
this.collection.toArray();
-
+
// if it works, do it for real
return super.toArray(a);
}
}
-
+
/**
* Elements in this wrapper iterator only exist if they pass a condition.
*
@@ -157,7 +157,7 @@ public final class ConditionalExistenceCollections {
final Predicate<E> existenceCondition;
E nextElement;
boolean hasNext;
-
+
/**
* Creates the {@code ConditionalExistenceIterator}.
*
@@ -171,7 +171,7 @@ public final class ConditionalExistenceCollections {
this.existenceCondition = condition;
this.getAndSetNextElement();
}
-
+
/**
* Gets the next element, and sets nextElement and hasNext accordingly.
*
@@ -188,12 +188,12 @@ public final class ConditionalExistenceCollections {
} while (!this.existenceCondition.test(this.nextElement));
this.hasNext = true;
}
-
+
@Override
public boolean hasNext() {
return this.hasNext;
}
-
+
@Override
public E next() {
if (this.hasNext()) {
@@ -203,13 +203,13 @@ public final class ConditionalExistenceCollections {
} else
throw new NoSuchElementException();
}
-
+
@Override
public void remove() {
this.iterator.remove();
}
}
-
+
/**
* Mappings in this map only exist if the entry passes some condition.
*
@@ -221,7 +221,7 @@ public final class ConditionalExistenceCollections {
static final class ConditionalExistenceMap<K, V> extends AbstractMap<K, V> {
Map<K, V> map;
Predicate<Entry<K, V>> entryExistenceCondition;
-
+
/**
* Creates the {@code ConditionalExistenceMap}.
*
@@ -234,81 +234,81 @@ public final class ConditionalExistenceCollections {
this.map = map;
this.entryExistenceCondition = entryExistenceCondition;
}
-
+
@Override
public boolean containsKey(final Object key) {
if (!this.map.containsKey(key))
return false;
-
+
// 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;
-
+
// get and test entry
final V value = this.map.get(key);
final Entry<K, V> entry = new SimpleEntry<>(keyAsK, value);
return this.entryExistenceCondition.test(entry);
}
-
+
@Override
public Set<Entry<K, V>> entrySet() {
return conditionalExistenceSet(this.map.entrySet(),
this.entryExistenceCondition);
}
-
+
@Override
public V get(final Object key) {
return this.containsKey(key) ? this.map.get(key) : null;
}
-
+
private final Entry<K, V> getEntry(K key) {
return new Entry<>() {
@Override
public K getKey() {
return key;
}
-
+
@Override
public V getValue() {
return ConditionalExistenceMap.this.map.get(key);
}
-
+
@Override
public V setValue(V value) {
return ConditionalExistenceMap.this.map.put(key, value);
}
};
}
-
+
@Override
public Set<K> keySet() {
return conditionalExistenceSet(this.map.keySet(),
k -> this.entryExistenceCondition.test(this.getEntry(k)));
}
-
+
@Override
public V put(final K key, final V value) {
final V oldValue = this.map.put(key, value);
-
+
// get and test entry
final Entry<K, V> entry = new SimpleEntry<>(key, oldValue);
return this.entryExistenceCondition.test(entry) ? oldValue : null;
}
-
+
@Override
public V remove(final Object key) {
final V oldValue = this.map.remove(key);
return this.containsKey(key) ? oldValue : null;
}
-
+
@Override
public Collection<V> values() {
// maybe change this to use ConditionalExistenceCollection
return super.values();
}
}
-
+
/**
* Elements in this set only exist if a certain condition is true.
*
@@ -319,7 +319,7 @@ public final class ConditionalExistenceCollections {
static final class ConditionalExistenceSet<E> extends AbstractSet<E> {
private final Set<E> set;
private final Predicate<E> existenceCondition;
-
+
/**
* Creates the {@code ConditionalNonexistenceSet}.
*
@@ -332,7 +332,7 @@ public final class ConditionalExistenceCollections {
this.set = set;
this.existenceCondition = existenceCondition;
}
-
+
/**
* {@inheritDoc}
* <p>
@@ -343,33 +343,33 @@ public final class ConditionalExistenceCollections {
public boolean add(final E e) {
return this.set.add(e) && this.existenceCondition.test(e);
}
-
+
@Override
public void clear() {
this.set.clear();
}
-
+
@Override
public boolean contains(final Object o) {
if (!this.set.contains(o))
return false;
-
+
// this set can only contain instances of E
// since the object is in the set, we know that it must be an instance
// of E
// therefore this cast will always work
@SuppressWarnings("unchecked")
final E e = (E) o;
-
+
return this.existenceCondition.test(e);
}
-
+
@Override
public Iterator<E> iterator() {
return conditionalExistenceIterator(this.set.iterator(),
this.existenceCondition);
}
-
+
@Override
public boolean remove(final Object o) {
// remove() must be first in the && statement, otherwise it may not
@@ -377,31 +377,31 @@ public final class ConditionalExistenceCollections {
final boolean containedObject = this.contains(o);
return this.set.remove(o) && containedObject;
}
-
+
@Override
public int size() {
return (int) this.set.stream().filter(this.existenceCondition).count();
}
-
+
@Override
public Object[] toArray() {
// ensure the toArray operation is supported
this.set.toArray();
-
+
// if it works, do it for real
return super.toArray();
}
-
+
@Override
public <T> T[] toArray(T[] a) {
// ensure the toArray operation is supported
this.set.toArray();
-
+
// if it works, do it for real
return super.toArray(a);
}
}
-
+
/**
* Elements in the returned wrapper collection are ignored if they don't pass
* a condition.
@@ -418,7 +418,7 @@ public final class ConditionalExistenceCollections {
return new ConditionalExistenceCollection<>(collection,
existenceCondition);
}
-
+
/**
* Elements in the returned wrapper iterator are ignored if they don't pass a
* condition.
@@ -433,7 +433,7 @@ public final class ConditionalExistenceCollections {
final Iterator<E> iterator, final Predicate<E> existenceCondition) {
return new ConditionalExistenceIterator<>(iterator, existenceCondition);
}
-
+
/**
* Mappings in the returned wrapper map are ignored if the corresponding
* entry doesn't pass a condition
@@ -450,7 +450,7 @@ public final class ConditionalExistenceCollections {
final Predicate<Entry<K, V>> entryExistenceCondition) {
return new ConditionalExistenceMap<>(map, entryExistenceCondition);
}
-
+
/**
* Elements in the returned wrapper set are ignored if they don't pass a
* condition.
diff --git a/src/main/java/sevenUnits/utils/DecimalComparison.java b/src/main/java/sevenUnits/utils/DecimalComparison.java
index a5cbbaa..0515b6b 100644
--- a/src/main/java/sevenUnits/utils/DecimalComparison.java
+++ b/src/main/java/sevenUnits/utils/DecimalComparison.java
@@ -34,7 +34,7 @@ public final class DecimalComparison {
* @since v0.2.0
*/
public static final double DOUBLE_EPSILON = 1.0e-15;
-
+
/**
* The value used for float comparison. If two float values are within this
* value multiplied by the larger value, they are considered equal.
@@ -43,7 +43,7 @@ public final class DecimalComparison {
* @since v0.2.0
*/
public static final float FLOAT_EPSILON = 1.0e-6f;
-
+
/**
* Tests for equality of double values using {@link #DOUBLE_EPSILON}.
* <p>
@@ -74,7 +74,7 @@ public final class DecimalComparison {
public static final boolean equals(final double a, final double b) {
return DecimalComparison.equals(a, b, DOUBLE_EPSILON);
}
-
+
/**
* Tests for double equality using a custom epsilon value.
*
@@ -106,7 +106,7 @@ public final class DecimalComparison {
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}.
*
@@ -136,7 +136,7 @@ public final class DecimalComparison {
public static final boolean equals(final float a, final float b) {
return DecimalComparison.equals(a, b, FLOAT_EPSILON);
}
-
+
/**
* Tests for float equality using a custom epsilon value.
*
@@ -168,7 +168,7 @@ public final class DecimalComparison {
final float epsilon) {
return Math.abs(a - b) <= epsilon * Math.max(Math.abs(a), Math.abs(b));
}
-
+
/**
* Tests for equality of {@code UncertainDouble} values using
* {@link #DOUBLE_EPSILON}.
@@ -201,7 +201,7 @@ public final class DecimalComparison {
return DecimalComparison.equals(a.value(), b.value())
&& DecimalComparison.equals(a.uncertainty(), b.uncertainty());
}
-
+
/**
* Tests for {@code UncertainDouble} equality using a custom epsilon value.
*
@@ -235,7 +235,7 @@ public final class DecimalComparison {
&& 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.
@@ -247,10 +247,10 @@ public final class DecimalComparison {
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 941c2a4..e248ff0 100644
--- a/src/main/java/sevenUnits/utils/ExpressionParser.java
+++ b/src/main/java/sevenUnits/utils/ExpressionParser.java
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2019 Adrien Hopkins
+ * Copyright (C) 2019, 2024 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,6 +24,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.function.BiFunction;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.UnaryOperator;
@@ -55,7 +56,7 @@ public final class ExpressionParser<T> {
* @since v0.2.0
*/
private final Function<String, ? extends T> objectObtainer;
-
+
/**
* The function of the space as an operator (like 3 x y)
*
@@ -63,7 +64,7 @@ public final class ExpressionParser<T> {
* @since v0.2.0
*/
private String spaceFunction = null;
-
+
/**
* A map mapping operator strings to operator functions, for unary
* operators.
@@ -72,7 +73,7 @@ public final class ExpressionParser<T> {
* @since v0.2.0
*/
private final Map<String, PriorityUnaryOperator<T>> unaryOperators;
-
+
/**
* A map mapping operator strings to operator functions, for binary
* operators.
@@ -81,7 +82,14 @@ public final class ExpressionParser<T> {
* @since v0.2.0
*/
private final Map<String, PriorityBinaryOperator<T>> binaryOperators;
-
+
+ /**
+ * A map mapping operator strings to numeric functions.
+ *
+ * @since 2024-03-23
+ */
+ private final Map<String, PriorityBiFunction<T, UncertainDouble, T>> numericOperators;
+
/**
* Creates the {@code Builder}.
*
@@ -96,8 +104,9 @@ public final class ExpressionParser<T> {
"objectObtainer must not be null.");
this.unaryOperators = new HashMap<>();
this.binaryOperators = new HashMap<>();
+ this.numericOperators = new HashMap<>();
}
-
+
/**
* Adds a binary operator to the builder.
*
@@ -115,7 +124,7 @@ public final class ExpressionParser<T> {
final BinaryOperator<T> operator, final int priority) {
Objects.requireNonNull(text, "text must not be null.");
Objects.requireNonNull(operator, "operator must not be null.");
-
+
// Unfortunately, I cannot use a lambda because the
// PriorityBinaryOperator requires arguments.
final PriorityBinaryOperator<T> priorityOperator = new PriorityBinaryOperator<>(
@@ -124,12 +133,42 @@ public final class ExpressionParser<T> {
public T apply(final T t, final T u) {
return operator.apply(t, u);
}
-
+
};
this.binaryOperators.put(text, priorityOperator);
return this;
}
-
+
+ /**
+ * 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
+ * are applied first
+ * @return this builder
+ */
+ public Builder<T> addNumericOperator(final String text,
+ final BiFunction<T, UncertainDouble, T> operator,
+ final int priority) {
+ Objects.requireNonNull(text, "text must not be null.");
+ Objects.requireNonNull(operator, "operator must not be null.");
+
+ // Unfortunately, I cannot use a lambda because the
+ // PriorityBinaryOperator requires arguments.
+ final PriorityBiFunction<T, UncertainDouble, T> priorityOperator = new PriorityBiFunction<>(
+ priority) {
+ @Override
+ public T apply(final T t, final UncertainDouble u) {
+ return operator.apply(t, u);
+ }
+
+ };
+ this.numericOperators.put(text, priorityOperator);
+ return this;
+ }
+
/**
* Adds a function for spaces. You must use the text of an existing binary
* operator.
@@ -141,15 +180,15 @@ public final class ExpressionParser<T> {
*/
public Builder<T> addSpaceFunction(final String operator) {
Objects.requireNonNull(operator, "operator must not be null.");
-
+
if (!this.binaryOperators.containsKey(operator))
throw new IllegalArgumentException(String
.format("Could not find binary operator '%s'", operator));
-
+
this.spaceFunction = operator;
return this;
}
-
+
/**
* Adds a unary operator to the builder.
*
@@ -167,7 +206,7 @@ public final class ExpressionParser<T> {
final UnaryOperator<T> operator, final int priority) {
Objects.requireNonNull(text, "text must not be null.");
Objects.requireNonNull(operator, "operator must not be null.");
-
+
// Unfortunately, I cannot use a lambda because the
// PriorityUnaryOperator requires arguments.
final PriorityUnaryOperator<T> priorityOperator = new PriorityUnaryOperator<>(
@@ -180,7 +219,7 @@ public final class ExpressionParser<T> {
this.unaryOperators.put(text, priorityOperator);
return this;
}
-
+
/**
* @return an {@code ExpressionParser<T>} instance with the properties
* given to this builder
@@ -189,10 +228,10 @@ public final class ExpressionParser<T> {
*/
public ExpressionParser<T> build() {
return new ExpressionParser<>(this.objectObtainer, this.unaryOperators,
- this.binaryOperators, this.spaceFunction);
+ this.binaryOperators, this.numericOperators, this.spaceFunction);
}
}
-
+
/**
* A binary operator with a priority field that determines which operators
* apply first.
@@ -212,7 +251,7 @@ public final class ExpressionParser<T> {
* @since v0.2.0
*/
private final int priority;
-
+
/**
* Creates the {@code PriorityBinaryOperator}.
*
@@ -223,7 +262,7 @@ public final class ExpressionParser<T> {
public PriorityBinaryOperator(final int priority) {
this.priority = priority;
}
-
+
/**
* Compares this object to another by priority.
*
@@ -243,7 +282,68 @@ public final class ExpressionParser<T> {
else
return 0;
}
-
+
+ /**
+ * @return priority
+ * @since 2019-03-22
+ * @since v0.2.0
+ */
+ public final int getPriority() {
+ return this.priority;
+ }
+ }
+
+ /**
+ * 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>> {
+ /**
+ * The operator's priority. Higher-priority operators are applied before
+ * lower-priority operators
+ *
+ * @since 2019-03-17
+ * @since v0.2.0
+ */
+ private final int priority;
+
+ /**
+ * Creates the {@code PriorityBinaryOperator}.
+ *
+ * @param priority operator's priority
+ * @since 2019-03-17
+ * @since v0.2.0
+ */
+ 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 PriorityBiFunction<T, U, R> o) {
+ if (this.priority < o.priority)
+ return -1;
+ else if (this.priority > o.priority)
+ return 1;
+ else
+ return 0;
+ }
+
/**
* @return priority
* @since 2019-03-22
@@ -253,7 +353,7 @@ public final class ExpressionParser<T> {
return this.priority;
}
}
-
+
/**
* A unary operator with a priority field that determines which operators
* apply first.
@@ -273,7 +373,7 @@ public final class ExpressionParser<T> {
* @since v0.2.0
*/
private final int priority;
-
+
/**
* Creates the {@code PriorityUnaryOperator}.
*
@@ -284,7 +384,7 @@ public final class ExpressionParser<T> {
public PriorityUnaryOperator(final int priority) {
this.priority = priority;
}
-
+
/**
* Compares this object to another by priority.
*
@@ -304,7 +404,7 @@ public final class ExpressionParser<T> {
else
return 0;
}
-
+
/**
* @return priority
* @since 2019-03-22
@@ -314,7 +414,7 @@ public final class ExpressionParser<T> {
return this.priority;
}
}
-
+
/**
* The types of tokens that are available.
*
@@ -323,9 +423,9 @@ public final class ExpressionParser<T> {
* @since v0.2.0
*/
private static enum TokenType {
- OBJECT, UNARY_OPERATOR, BINARY_OPERATOR;
+ OBJECT, UNARY_OPERATOR, BINARY_OPERATOR, NUMERIC_OPERATOR;
}
-
+
/**
* The opening bracket.
*
@@ -333,7 +433,7 @@ public final class ExpressionParser<T> {
* @since v0.2.0
*/
public static final char OPENING_BRACKET = '(';
-
+
/**
* The closing bracket.
*
@@ -341,7 +441,7 @@ public final class ExpressionParser<T> {
* @since v0.2.0
*/
public static final char CLOSING_BRACKET = ')';
-
+
/**
* Finds the other bracket in a pair of brackets, given the position of one.
*
@@ -355,9 +455,9 @@ public final class ExpressionParser<T> {
private static int findBracketPair(final String string,
final int bracketPosition) {
Objects.requireNonNull(string, "string must not be null.");
-
+
final char openingBracket = string.charAt(bracketPosition);
-
+
// figure out what closing bracket to look for
final char closingBracket;
switch (openingBracket) {
@@ -374,16 +474,16 @@ public final class ExpressionParser<T> {
throw new IllegalArgumentException(
String.format("Invalid bracket '%s'", openingBracket));
}
-
+
// level of brackets. every opening bracket increments this; every closing
// bracket decrements it
int bracketLevel = 0;
-
+
// iterate over the string to find the closing bracket
for (int currentPosition = bracketPosition; currentPosition < string
.length(); currentPosition++) {
final char currentCharacter = string.charAt(currentPosition);
-
+
if (currentCharacter == openingBracket) {
bracketLevel++;
} else if (currentCharacter == closingBracket) {
@@ -392,10 +492,10 @@ public final class ExpressionParser<T> {
return currentPosition;
}
}
-
+
throw new IllegalArgumentException("No matching bracket found.");
}
-
+
/**
* A function that obtains a parseable object from a string. For example, an
* integer {@code ExpressionParser} would use {@code Integer::parseInt}.
@@ -404,7 +504,7 @@ public final class ExpressionParser<T> {
* @since v0.2.0
*/
private final Function<String, ? extends T> objectObtainer;
-
+
/**
* A map mapping operator strings to operator functions, for unary operators.
*
@@ -412,7 +512,7 @@ public final class ExpressionParser<T> {
* @since v0.2.0
*/
private final Map<String, PriorityUnaryOperator<T>> unaryOperators;
-
+
/**
* A map mapping operator strings to operator functions, for binary
* operators.
@@ -421,7 +521,14 @@ public final class ExpressionParser<T> {
* @since v0.2.0
*/
private final Map<String, PriorityBinaryOperator<T>> binaryOperators;
-
+
+ /**
+ * A map mapping operator strings to numeric functions.
+ *
+ * @since 2024-03-23
+ */
+ private final Map<String, PriorityBiFunction<T, UncertainDouble, T>> numericOperators;
+
/**
* The operator for space, or null if spaces have no function.
*
@@ -429,27 +536,30 @@ public final class ExpressionParser<T> {
* @since v0.2.0
*/
private final String spaceOperator;
-
+
/**
* 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
- * @param spaceOperator operator used by spaces
+ * @param objectObtainer function to get objects from strings
+ * @param unaryOperators unary operators available to the parser
+ * @param binaryOperators binary operators available to the parser
+ * @param numericOperators numeric operators available to the parser
+ * @param spaceOperator operator used by spaces
* @since 2019-03-14
* @since v0.2.0
*/
private ExpressionParser(final Function<String, ? extends T> objectObtainer,
final Map<String, PriorityUnaryOperator<T>> unaryOperators,
final Map<String, PriorityBinaryOperator<T>> binaryOperators,
+ final Map<String, PriorityBiFunction<T, UncertainDouble, T>> numericOperators,
final String spaceOperator) {
this.objectObtainer = objectObtainer;
this.unaryOperators = unaryOperators;
this.binaryOperators = binaryOperators;
+ this.numericOperators = numericOperators;
this.spaceOperator = spaceOperator;
}
-
+
/**
* Converts a given mathematical expression to reverse Polish notation
* (operators after operands).
@@ -468,19 +578,19 @@ public final class ExpressionParser<T> {
*/
String convertExpressionToReversePolish(final String expression) {
Objects.requireNonNull(expression, "expression must not be null.");
-
+
final List<String> components = new ArrayList<>();
-
+
// the part of the expression remaining to parse
String partialExpression = expression;
-
+
// find and deal with brackets
while (partialExpression.indexOf(OPENING_BRACKET) != -1) {
final int openingBracketPosition = partialExpression
.indexOf(OPENING_BRACKET);
final int closingBracketPosition = findBracketPair(partialExpression,
openingBracketPosition);
-
+
// check for function
if (openingBracketPosition > 0
&& partialExpression.charAt(openingBracketPosition - 1) != ' ') {
@@ -510,15 +620,15 @@ public final class ExpressionParser<T> {
.substring(closingBracketPosition + 1);
}
}
-
+
// add everything else
components.addAll(Arrays.asList(partialExpression.split(" ")));
-
+
// remove empty entries
while (components.contains("")) {
components.remove("");
}
-
+
// deal with space multiplication (x y)
if (this.spaceOperator != null) {
for (int i = 0; i < components.size() - 1; i++) {
@@ -528,7 +638,7 @@ public final class ExpressionParser<T> {
}
}
}
-
+
// turn the expression into reverse Polish
while (true) {
final int highestPriorityOperatorPosition = this
@@ -536,7 +646,7 @@ public final class ExpressionParser<T> {
if (highestPriorityOperatorPosition == -1) {
break;
}
-
+
// swap components based on what kind of operator there is
// 1 + 2 becomes 2 1 +
// - 1 becomes 1 -
@@ -554,6 +664,7 @@ public final class ExpressionParser<T> {
operand + " " + unaryOperator);
break;
case BINARY_OPERATOR:
+ case NUMERIC_OPERATOR:
if (components.size() < 3)
throw new IllegalArgumentException(
"Invalid expression \"" + expression + "\"");
@@ -570,11 +681,11 @@ public final class ExpressionParser<T> {
throw new AssertionError("Expected operator, found non-operator.");
}
}
-
+
// 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);
}
@@ -583,7 +694,7 @@ public final class ExpressionParser<T> {
}
return expressionRPN;
}
-
+
/**
* Finds the position of the highest-priority operator in a list
*
@@ -601,18 +712,18 @@ public final class ExpressionParser<T> {
// find highest priority
int maxPriority = Integer.MIN_VALUE;
int 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++) {
-
+
switch (this.getTokenType(components.get(i))) {
case UNARY_OPERATOR:
final PriorityUnaryOperator<T> unaryOperator = this.unaryOperators
.get(components.get(i));
final int unaryPriority = unaryOperator.getPriority();
-
+
if (unaryPriority > maxPriority) {
maxPriority = unaryPriority;
maxPriorityPosition = i;
@@ -622,21 +733,31 @@ public final class ExpressionParser<T> {
final PriorityBinaryOperator<T> binaryOperator = this.binaryOperators
.get(components.get(i));
final int binaryPriority = binaryOperator.getPriority();
-
+
if (binaryPriority > maxPriority) {
maxPriority = binaryPriority;
maxPriorityPosition = i;
}
break;
+ case NUMERIC_OPERATOR:
+ final PriorityBiFunction<T, UncertainDouble, T> numericOperator = this.numericOperators
+ .get(components.get(i));
+ final int numericPriority = numericOperator.getPriority();
+
+ if (numericPriority > maxPriority) {
+ maxPriority = numericPriority;
+ maxPriorityPosition = i;
+ }
+ break;
default:
break;
}
}
-
+
// max priority position found
return maxPriorityPosition;
}
-
+
/**
* Determines whether an inputted string is an object or an operator
*
@@ -648,15 +769,17 @@ public final class ExpressionParser<T> {
*/
private TokenType getTokenType(final String token) {
Objects.requireNonNull(token, "token must not be null.");
-
+
if (this.unaryOperators.containsKey(token))
return TokenType.UNARY_OPERATOR;
else if (this.binaryOperators.containsKey(token))
return TokenType.BINARY_OPERATOR;
+ else if (this.numericOperators.containsKey(token))
+ return TokenType.NUMERIC_OPERATOR;
else
return TokenType.OBJECT;
}
-
+
/**
* Parses an expression.
*
@@ -670,7 +793,7 @@ public final class ExpressionParser<T> {
return this.parseReversePolishExpression(
this.convertExpressionToReversePolish(expression));
}
-
+
/**
* Parses an expression expressed in reverse Polish notation.
*
@@ -682,55 +805,86 @@ public final class ExpressionParser<T> {
*/
T parseReversePolishExpression(final String expression) {
Objects.requireNonNull(expression, "expression must not be null.");
-
+
final Deque<T> stack = new ArrayDeque<>();
-
+ final Deque<UncertainDouble> doubleStack = new ArrayDeque<>();
+
// iterate over every item in the expression, then
for (final String item : expression.split(" ")) {
// choose a path based on what kind of thing was just read
switch (this.getTokenType(item)) {
-
+
case BINARY_OPERATOR:
if (stack.size() < 2)
throw new IllegalStateException(String.format(
"Attempted to call binary operator %s with only %d arguments.",
item, stack.size()));
-
+
// get two arguments and operator, then apply!
final T o1 = stack.pop();
final T o2 = stack.pop();
final BinaryOperator<T> binaryOperator = this.binaryOperators
.get(item);
-
+
stack.push(binaryOperator.apply(o1, o2));
break;
-
+
+ case NUMERIC_OPERATOR:
+ if (stack.size() < 1 || doubleStack.size() < 1)
+ throw new IllegalStateException(String.format(
+ "Attempted to call binary operator %s with insufficient arguments.",
+ item));
+
+ final T ot = stack.pop();
+ final UncertainDouble on = doubleStack.pop();
+ final BiFunction<T, UncertainDouble, T> op = this.numericOperators
+ .get(item);
+ stack.push(op.apply(ot, on));
+ break;
+
case OBJECT:
// just add it to the stack
- stack.push(this.objectObtainer.apply(item));
+ // these try-catch statements are necessary
+ // to make the code as generalizable as possible
+ // also they're required for number formatting code because
+ // 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) {
+ try {
+ doubleStack.push(UncertainDouble.fromString(item));
+ } catch (IllegalArgumentException e2) {
+ try {
+ doubleStack.push(
+ UncertainDouble.of(Double.parseDouble(item), 0));
+ } catch (NumberFormatException e3) {
+ throw e;
+ }
+ }
+ }
break;
-
+
case UNARY_OPERATOR:
if (stack.size() < 1)
throw new IllegalStateException(String.format(
"Attempted to call unary operator %s with only %d arguments.",
item, stack.size()));
-
+
// get one argument and operator, then apply!
final T o = stack.pop();
final UnaryOperator<T> unaryOperator = this.unaryOperators
.get(item);
-
+
stack.push(unaryOperator.apply(o));
break;
default:
throw new AssertionError(
String.format("Internal error: Invalid token type %s.",
this.getTokenType(item)));
-
+
}
}
-
+
// return answer, or throw an exception if I can't
if (stack.size() > 1)
throw new IllegalStateException(
diff --git a/src/main/java/sevenUnits/utils/NameSymbol.java b/src/main/java/sevenUnits/utils/NameSymbol.java
index 9388f63..49c44fa 100644
--- a/src/main/java/sevenUnits/utils/NameSymbol.java
+++ b/src/main/java/sevenUnits/utils/NameSymbol.java
@@ -33,7 +33,7 @@ import java.util.Set;
public final class NameSymbol {
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
@@ -43,7 +43,7 @@ public final class NameSymbol {
private static final 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();
@@ -53,11 +53,11 @@ public final class NameSymbol {
} else {
primaryName = Optional.ofNullable(name);
}
-
+
return new NameSymbol(primaryName, Optional.ofNullable(symbol),
otherNames);
}
-
+
/**
* Gets a {@code NameSymbol} with a primary name, a symbol and no other
* names.
@@ -72,7 +72,7 @@ public final class NameSymbol {
return new NameSymbol(Optional.of(name), Optional.of(symbol),
new HashSet<>());
}
-
+
/**
* Gets a {@code NameSymbol} with a primary name, a symbol and additional
* names.
@@ -90,7 +90,7 @@ public final class NameSymbol {
new HashSet<>(Objects.requireNonNull(otherNames,
"otherNames must not be null.")));
}
-
+
/**
* h * Gets a {@code NameSymbol} with a primary name, a symbol and additional
* names.
@@ -108,7 +108,7 @@ public final class NameSymbol {
new HashSet<>(Arrays.asList(Objects.requireNonNull(otherNames,
"otherNames must not be null."))));
}
-
+
/**
* Gets a {@code NameSymbol} with a primary name, no symbol, and no other
* names.
@@ -122,7 +122,7 @@ public final class NameSymbol {
return new NameSymbol(Optional.of(name), Optional.empty(),
new HashSet<>());
}
-
+
/**
* Gets a {@code NameSymbol} with a primary name, a symbol and additional
* names.
@@ -145,7 +145,7 @@ public final class NameSymbol {
return NameSymbol.create(name, symbol,
otherNames == null ? new HashSet<>() : new HashSet<>(otherNames));
}
-
+
/**
* h * Gets a {@code NameSymbol} with a primary name, a symbol and additional
* names.
@@ -168,7 +168,7 @@ public final class NameSymbol {
return create(name, symbol, otherNames == null ? new HashSet<>()
: new HashSet<>(Arrays.asList(otherNames)));
}
-
+
/**
* Gets a {@code NameSymbol} with a symbol and no names.
*
@@ -181,12 +181,12 @@ public final class NameSymbol {
return new NameSymbol(Optional.empty(), Optional.of(symbol),
new HashSet<>());
}
-
+
private final Optional<String> primaryName;
private final Optional<String> symbol;
-
+
private final Set<String> otherNames;
-
+
/**
* Creates the {@code NameSymbol}.
*
@@ -202,12 +202,12 @@ public final class NameSymbol {
this.symbol = symbol;
otherNames.remove(null);
this.otherNames = Collections.unmodifiableSet(otherNames);
-
+
if (this.primaryName.isEmpty()) {
assert this.otherNames.isEmpty();
}
}
-
+
@Override
public boolean equals(Object obj) {
if (this == obj)
@@ -232,7 +232,7 @@ public final class NameSymbol {
return false;
return true;
}
-
+
/**
* @return otherNames
* @since 2019-10-21
@@ -240,7 +240,7 @@ public final class NameSymbol {
public final Set<String> getOtherNames() {
return this.otherNames;
}
-
+
/**
* @return primaryName
* @since 2019-10-21
@@ -248,7 +248,7 @@ public final class NameSymbol {
public final Optional<String> getPrimaryName() {
return this.primaryName;
}
-
+
/**
* @return symbol
* @since 2019-10-21
@@ -256,7 +256,7 @@ public final class NameSymbol {
public final Optional<String> getSymbol() {
return this.symbol;
}
-
+
@Override
public int hashCode() {
final int prime = 31;
@@ -269,7 +269,7 @@ public final class NameSymbol {
+ (this.symbol == null ? 0 : this.symbol.hashCode());
return result;
}
-
+
/**
* @return true iff this {@code NameSymbol} contains no names or symbols.
*/
@@ -277,7 +277,7 @@ public final class NameSymbol {
// if primaryName is empty, otherNames must also be empty
return this.primaryName.isEmpty() && this.symbol.isEmpty();
}
-
+
@Override
public String toString() {
if (this.isEmpty())
@@ -288,7 +288,7 @@ public final class NameSymbol {
else
return this.primaryName.orElseGet(this.symbol::orElseThrow);
}
-
+
/**
* Creates and returns a copy of this {@code NameSymbol} with the provided
* extra name. If this {@code NameSymbol} has a primary name, the provided
diff --git a/src/main/java/sevenUnits/utils/Nameable.java b/src/main/java/sevenUnits/utils/Nameable.java
index e469d04..3959a64 100644
--- a/src/main/java/sevenUnits/utils/Nameable.java
+++ b/src/main/java/sevenUnits/utils/Nameable.java
@@ -35,14 +35,14 @@ public interface Nameable {
final NameSymbol ns = this.getNameSymbol();
return ns.getPrimaryName().or(ns::getSymbol).orElse("Unnamed");
}
-
+
/**
* @return a {@code NameSymbol} that contains this object's primary name,
* symbol and other names
* @since 2020-09-07
*/
NameSymbol getNameSymbol();
-
+
/**
* @return set of alternate names
* @since 2020-09-07
@@ -50,7 +50,7 @@ public interface Nameable {
default Set<String> getOtherNames() {
return this.getNameSymbol().getOtherNames();
}
-
+
/**
* @return preferred name of object
* @since 2020-09-07
@@ -58,7 +58,7 @@ public interface Nameable {
default Optional<String> getPrimaryName() {
return this.getNameSymbol().getPrimaryName();
}
-
+
/**
* @return a short name for the object - if there's a symbol, it's that,
* otherwise the symbol, otherwise "Unnamed"
@@ -68,7 +68,7 @@ public interface Nameable {
final NameSymbol ns = this.getNameSymbol();
return ns.getSymbol().or(ns::getPrimaryName).orElse("Unnamed");
}
-
+
/**
* @return short symbol representing object
* @since 2020-09-07
diff --git a/src/main/java/sevenUnits/utils/ObjectProduct.java b/src/main/java/sevenUnits/utils/ObjectProduct.java
index 66bb773..5a29d79 100644
--- a/src/main/java/sevenUnits/utils/ObjectProduct.java
+++ b/src/main/java/sevenUnits/utils/ObjectProduct.java
@@ -44,7 +44,7 @@ public class ObjectProduct<T> implements Nameable {
public static final <T> ObjectProduct<T> empty() {
return new ObjectProduct<>(new HashMap<>());
}
-
+
/**
* Gets an {@code ObjectProduct} from an object-to-integer mapping
*
@@ -57,7 +57,7 @@ public class ObjectProduct<T> implements Nameable {
final Map<T, Integer> map) {
return new ObjectProduct<>(new HashMap<>(map));
}
-
+
/**
* Gets an ObjectProduct that has one of the inputted argument, and nothing
* else.
@@ -73,7 +73,7 @@ public class ObjectProduct<T> implements Nameable {
map.put(object, 1);
return new ObjectProduct<>(map);
}
-
+
/**
* The objects that make up the product, mapped to their exponents. This map
* treats zero as null, and is immutable.
@@ -81,12 +81,12 @@ public class ObjectProduct<T> implements Nameable {
* @since 2019-10-16
*/
final Map<T, Integer> exponents;
-
+
/**
* The object's name and symbol
*/
private final NameSymbol nameSymbol;
-
+
/**
* Creates a {@code ObjectProduct} without a name/symbol.
*
@@ -96,7 +96,7 @@ public class ObjectProduct<T> implements Nameable {
ObjectProduct(final Map<T, Integer> exponents) {
this(exponents, NameSymbol.EMPTY);
}
-
+
/**
* Creates the {@code ObjectProduct}.
*
@@ -110,7 +110,7 @@ public class ObjectProduct<T> implements Nameable {
e -> !Integer.valueOf(0).equals(e.getValue())));
this.nameSymbol = nameSymbol;
}
-
+
/**
* Calculates the quotient of two products
*
@@ -125,17 +125,17 @@ public class ObjectProduct<T> implements Nameable {
final Set<T> objects = new HashSet<>();
objects.addAll(this.getBaseSet());
objects.addAll(other.getBaseSet());
-
+
// get a list of all exponents
final Map<T, Integer> map = new HashMap<>(objects.size());
for (final T key : objects) {
map.put(key, this.getExponent(key) - other.getExponent(key));
}
-
+
// create the product
return new ObjectProduct<>(map);
}
-
+
// this method relies on the use of ZeroIsNullMap
@Override
public boolean equals(final Object obj) {
@@ -146,7 +146,7 @@ public class ObjectProduct<T> implements Nameable {
final ObjectProduct<?> other = (ObjectProduct<?>) obj;
return Objects.equals(this.exponents, other.exponents);
}
-
+
/**
* @return immutable map mapping objects to exponents
* @since 2019-10-16
@@ -154,7 +154,7 @@ public class ObjectProduct<T> implements Nameable {
public Map<T, Integer> exponentMap() {
return this.exponents;
}
-
+
/**
* @return a set of all of the base objects with non-zero exponents that make
* up this dimension.
@@ -163,7 +163,7 @@ public class ObjectProduct<T> implements Nameable {
*/
public final Set<T> getBaseSet() {
final Set<T> dimensions = new HashSet<>();
-
+
// add all dimensions with a nonzero exponent - zero exponents shouldn't
// be there in the first place
for (final T dimension : this.exponents.keySet()) {
@@ -171,10 +171,10 @@ public class ObjectProduct<T> implements Nameable {
dimensions.add(dimension);
}
}
-
+
return dimensions;
}
-
+
/**
* Gets the exponent for a specific dimension.
*
@@ -186,17 +186,17 @@ public class ObjectProduct<T> implements Nameable {
public int getExponent(final T dimension) {
return this.exponents.getOrDefault(dimension, 0);
}
-
+
@Override
public NameSymbol getNameSymbol() {
return this.nameSymbol;
}
-
+
@Override
public int hashCode() {
return Objects.hash(this.exponents);
}
-
+
/**
* @return true if this product is a single object, i.e. it has one exponent
* of one and no other nonzero exponents
@@ -214,7 +214,7 @@ public class ObjectProduct<T> implements Nameable {
}
return oneCount == 1 && !twoOrMore;
}
-
+
/**
* Multiplies this product by another
*
@@ -229,17 +229,17 @@ public class ObjectProduct<T> implements Nameable {
final Set<T> objects = new HashSet<>();
objects.addAll(this.getBaseSet());
objects.addAll(other.getBaseSet());
-
+
// get a list of all exponents
final Map<T, Integer> map = new HashMap<>(objects.size());
for (final T key : objects) {
map.put(key, this.getExponent(key) + other.getExponent(key));
}
-
+
// create the product
return new ObjectProduct<>(map);
}
-
+
/**
* Returns this product, but to an exponent
*
@@ -254,7 +254,7 @@ public class ObjectProduct<T> implements Nameable {
}
return new ObjectProduct<>(map);
}
-
+
/**
* Converts this product to a string using the objects'
* {@link Object#toString()} method (or {@link Nameable#getShortName} if
@@ -271,7 +271,7 @@ public class ObjectProduct<T> implements Nameable {
.toString(o -> o instanceof Nameable ? ((Nameable) o).getShortName()
: o.toString());
}
-
+
/**
* Converts this product to a string. The objects that make up this product
* are represented by {@code objectToString}
@@ -283,7 +283,7 @@ public class ObjectProduct<T> implements Nameable {
public String toString(final Function<T, String> objectToString) {
final List<String> positiveStringComponents = new ArrayList<>();
final List<String> negativeStringComponents = new ArrayList<>();
-
+
// for each base object that makes up this object, add it and its exponent
for (final T object : this.getBaseSet()) {
final int exponent = this.exponents.get(object);
@@ -297,15 +297,15 @@ public class ObjectProduct<T> implements Nameable {
objectToString.apply(object), -exponent));
}
}
-
+
final String positiveString = positiveStringComponents.isEmpty() ? "1"
: String.join(" * ", positiveStringComponents);
final String negativeString = negativeStringComponents.isEmpty() ? ""
: " / " + String.join(" * ", negativeStringComponents);
-
+
return positiveString + negativeString;
}
-
+
/**
* @return named version of this {@code ObjectProduct}, using data from
* {@code nameSymbol}
diff --git a/src/main/java/sevenUnits/utils/SemanticVersionNumber.java b/src/main/java/sevenUnits/utils/SemanticVersionNumber.java
index e80e16e..fc47baa 100644
--- a/src/main/java/sevenUnits/utils/SemanticVersionNumber.java
+++ b/src/main/java/sevenUnits/utils/SemanticVersionNumber.java
@@ -61,7 +61,7 @@ public final class SemanticVersionNumber
private final int patch;
private final List<String> preReleaseIdentifiers;
private final List<String> buildMetadata;
-
+
/**
* Creates a builder which can be used to create a
* {@code SemanticVersionNumber}
@@ -79,7 +79,7 @@ public final class SemanticVersionNumber
this.preReleaseIdentifiers = new ArrayList<>();
this.buildMetadata = new ArrayList<>();
}
-
+
/**
* @return version number created by this builder
* @since v0.4.0
@@ -89,7 +89,7 @@ public final class SemanticVersionNumber
return new SemanticVersionNumber(this.major, this.minor, this.patch,
this.preReleaseIdentifiers, this.buildMetadata);
}
-
+
/**
* Adds one or more build metadata identifiers
*
@@ -109,7 +109,7 @@ public final class SemanticVersionNumber
}
return this;
}
-
+
/**
* Adds one or more build metadata identifiers
*
@@ -129,7 +129,7 @@ public final class SemanticVersionNumber
}
return this;
}
-
+
@Override
public boolean equals(Object obj) {
if (this == obj)
@@ -142,13 +142,13 @@ public final class SemanticVersionNumber
&& this.patch == other.patch && Objects.equals(
this.preReleaseIdentifiers, other.preReleaseIdentifiers);
}
-
+
@Override
public int hashCode() {
return Objects.hash(this.buildMetadata, this.major, this.minor,
this.patch, this.preReleaseIdentifiers);
}
-
+
/**
* Adds one or more numeric identifiers to the version number
*
@@ -167,7 +167,7 @@ public final class SemanticVersionNumber
}
return this;
}
-
+
/**
* Adds one or more pre-release identifier(s) to the version number
*
@@ -187,7 +187,7 @@ public final class SemanticVersionNumber
}
return this;
}
-
+
/**
* Adds one or more pre-release identifier(s) to the version number
*
@@ -207,7 +207,7 @@ public final class SemanticVersionNumber
}
return this;
}
-
+
/**
* Adds a string identifier and an integer identifer to pre-release data
*
@@ -229,13 +229,13 @@ public final class SemanticVersionNumber
this.preReleaseIdentifiers.add(Integer.toString(identifier2));
return this;
}
-
+
@Override
public String toString() {
return "Semantic Version Builder: " + this.build().toString();
}
}
-
+
/**
* An alternative comparison method for version numbers. This uses the
* version's natural order, but the build metadata will be compared (using
@@ -257,21 +257,21 @@ public final class SemanticVersionNumber
return naturalComparison;
};
};
-
+
/** The alphanumeric pattern all identifiers must follow */
private static final Pattern VALID_IDENTIFIER = Pattern
.compile("[0-9A-Za-z-]+");
-
+
/** The numeric pattern which causes special behaviour */
private static final Pattern NUMERIC_IDENTIFER = Pattern.compile("[0-9]+");
-
+
/** The pattern for a version number */
private static final Pattern VERSION_NUMBER = Pattern
.compile("(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)" // main
// version
+ "(?:-([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?" // pre-release
+ "(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?"); // build data
-
+
/**
* Creates a builder that can be used to create a version number
*
@@ -296,7 +296,7 @@ public final class SemanticVersionNumber
"Patch version must be non-negative.");
return new SemanticVersionNumber.Builder(major, minor, patch);
}
-
+
/**
* Compares two lists of strings based on SemVer's precedence rules
*
@@ -311,24 +311,24 @@ public final class SemanticVersionNumber
// test pre-release size
final int aSize = a.size();
final int bSize = b.size();
-
+
// no identifiers is greater than any identifiers
if (aSize != 0 && bSize == 0)
return -1;
else 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);
-
+
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)
@@ -350,7 +350,7 @@ public final class SemanticVersionNumber
}
}
}
-
+
// we just tested the stuff that's in common, maybe someone has more
if (aSize < bSize)
return -1;
@@ -359,7 +359,7 @@ public final class SemanticVersionNumber
else
return 0;
}
-
+
/**
* Gets a version number from a string in the official format
*
@@ -377,12 +377,12 @@ public final class SemanticVersionNumber
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));
-
+
// pre release
final List<String> preRelease;
if (m.group(4) == null) {
@@ -390,7 +390,7 @@ public final class SemanticVersionNumber
} else {
preRelease = Arrays.asList(m.group(4).split("\\."));
}
-
+
// build metadata
final List<String> buildMetadata;
if (m.group(5) == null) {
@@ -398,12 +398,12 @@ public final class SemanticVersionNumber
} else {
buildMetadata = Arrays.asList(m.group(5).split("\\."));
}
-
+
// return number
return new SemanticVersionNumber(major, minor, patch, preRelease,
buildMetadata);
}
-
+
/**
* Tests whether a string is a valid Semantic Version string
*
@@ -415,7 +415,7 @@ public final class SemanticVersionNumber
public static final boolean isValidVersionString(String versionString) {
return VERSION_NUMBER.matcher(versionString).matches();
}
-
+
/**
* Creates a simple pre-release version number of the form
* MAJOR.MINOR.PATH-TYPE.NUMBER (e.g. 1.2.3-alpha.4).
@@ -454,7 +454,7 @@ public final class SemanticVersionNumber
List.of(preReleaseType, Integer.toString(preReleaseNumber)),
List.of());
}
-
+
/**
* Creates a {@code SemanticVersionNumber} instance without pre-release
* identifiers or build metadata.
@@ -484,14 +484,14 @@ public final class SemanticVersionNumber
return new SemanticVersionNumber(major, minor, patch, List.of(),
List.of());
}
-
+
// parts of the version number
private final int major;
private final int minor;
private final int patch;
private final List<String> preReleaseIdentifiers;
private final List<String> buildMetadata;
-
+
/**
* Creates a version number
*
@@ -511,7 +511,7 @@ public final class SemanticVersionNumber
this.preReleaseIdentifiers = preReleaseIdentifiers;
this.buildMetadata = buildMetadata;
}
-
+
/**
* @return build metadata (empty if there is none)
* @since v0.4.0
@@ -520,7 +520,7 @@ public final class SemanticVersionNumber
public List<String> buildMetadata() {
return Collections.unmodifiableList(this.buildMetadata);
}
-
+
/**
* Compares two version numbers according to the official Semantic Versioning
* order.
@@ -538,23 +538,23 @@ public final class SemanticVersionNumber
return -1;
else if (this.major > o.major)
return 1;
-
+
if (this.minor < o.minor)
return -1;
else if (this.minor > o.minor)
return 1;
-
+
if (this.patch < o.patch)
return -1;
else if (this.patch > o.patch)
return 1;
-
+
// now we just compare pre-release identifiers
// (remember: build metadata is ignored)
return SemanticVersionNumber.compareIdentifiers(
this.preReleaseIdentifiers, o.preReleaseIdentifiers);
}
-
+
/**
* Determines the compatibility of code written for this version to
* {@code other}. More specifically:
@@ -590,11 +590,11 @@ public final class SemanticVersionNumber
*/
public boolean compatibleWith(SemanticVersionNumber other) {
Objects.requireNonNull(other, "other may not be null");
-
+
return this.compareTo(other) == 0 || this.major != 0
&& this.major == other.major && this.compareTo(other) < 0;
}
-
+
@Override
public boolean equals(Object obj) {
if (this == obj)
@@ -621,7 +621,7 @@ public final class SemanticVersionNumber
return false;
return true;
}
-
+
@Override
public int hashCode() {
final int prime = 31;
@@ -635,7 +635,7 @@ public final class SemanticVersionNumber
: this.preReleaseIdentifiers.hashCode());
return result;
}
-
+
/**
* @return true iff this version is stable (major version > 0 and not a
* pre-release)
@@ -645,7 +645,7 @@ public final class SemanticVersionNumber
public boolean isStable() {
return this.major > 0 && this.preReleaseIdentifiers.isEmpty();
}
-
+
/**
* @return the MAJOR version number, incremented when you make backwards
* incompatible API changes
@@ -655,7 +655,7 @@ public final class SemanticVersionNumber
public int majorVersion() {
return this.major;
}
-
+
/**
* @return the MINOR version number, incremented when you add backwards
* compatible functionality
@@ -665,7 +665,7 @@ public final class SemanticVersionNumber
public int minorVersion() {
return this.minor;
}
-
+
/**
* @return the PATCH version number, incremented when you make backwards
* compatible bug fixes
@@ -675,7 +675,7 @@ public final class SemanticVersionNumber
public int patchVersion() {
return this.patch;
}
-
+
/**
* @return identifiers describing this pre-release (empty if not a
* pre-release)
@@ -685,7 +685,7 @@ public final class SemanticVersionNumber
public List<String> preReleaseIdentifiers() {
return Collections.unmodifiableList(this.preReleaseIdentifiers);
}
-
+
/**
* Converts a version number to a string using the official SemVer format.
* The core of a version is MAJOR.MINOR.PATCH, without zero-padding. If
diff --git a/src/main/java/sevenUnits/utils/UncertainDouble.java b/src/main/java/sevenUnits/utils/UncertainDouble.java
index ac523b3..66d8103 100644
--- a/src/main/java/sevenUnits/utils/UncertainDouble.java
+++ b/src/main/java/sevenUnits/utils/UncertainDouble.java
@@ -23,7 +23,7 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
- * A double with an associated uncertainty value. For example, 3.2 ± 0.2.
+ * A double with an associated uncertainty value. For example, 3.2 � 0.2.
* <p>
* All methods in this class throw a NullPointerException if any of their
* arguments is null.
@@ -35,20 +35,20 @@ public final class UncertainDouble implements Comparable<UncertainDouble> {
* 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
*/
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.
*
* @throws NumberFormatException if the argument is not a number
*
@@ -59,13 +59,13 @@ public final class UncertainDouble implements Comparable<UncertainDouble> {
final double 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.
* <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
@@ -75,11 +75,11 @@ public final class UncertainDouble implements Comparable<UncertainDouble> {
public static final UncertainDouble fromString(String s) {
Objects.requireNonNull(s, "s may not be null");
final Matcher matcher = TO_STRING.matcher(s);
-
+
if (!matcher.matches())
throw new IllegalArgumentException(
"Could not parse stirng \"" + s + "\".");
-
+
double value, uncertainty;
try {
value = Double.parseDouble(matcher.group(1));
@@ -87,7 +87,7 @@ public final class UncertainDouble implements Comparable<UncertainDouble> {
throw new IllegalArgumentException(
"String " + s + " not in correct format.");
}
-
+
final String uncertaintyString = matcher.group(2);
if (uncertaintyString == null) {
uncertainty = 0;
@@ -99,10 +99,10 @@ public final class UncertainDouble implements Comparable<UncertainDouble> {
"String " + s + " not in correct format.");
}
}
-
+
return UncertainDouble.of(value, uncertainty);
}
-
+
/**
* Gets an {@code UncertainDouble} from its value and <b>absolute</b>
* uncertainty.
@@ -112,7 +112,7 @@ public final class UncertainDouble implements Comparable<UncertainDouble> {
public static final UncertainDouble of(double value, double uncertainty) {
return new UncertainDouble(value, uncertainty);
}
-
+
/**
* Gets an {@code UncertainDouble} from its value and <b>relative</b>
* uncertainty.
@@ -123,11 +123,11 @@ public final class UncertainDouble implements Comparable<UncertainDouble> {
double relativeUncertainty) {
return new UncertainDouble(value, value * relativeUncertainty);
}
-
+
private final double value;
-
+
private final double uncertainty;
-
+
/**
* @param value
* @param uncertainty
@@ -138,13 +138,13 @@ public final class UncertainDouble implements Comparable<UncertainDouble> {
// uncertainty should only ever be positive
this.uncertainty = Math.abs(uncertainty);
}
-
+
/**
* Compares this {@code UncertainDouble} with another
* {@code UncertainDouble}.
* <p>
- * This method only compares the values, not the uncertainties. So 3.1 ± 0.5
- * is considered less than 3.2 ± 0.5, even though they are equivalent.
+ * This method only compares the values, not the uncertainties. So 3.1 � 0.5
+ * is considered less than 3.2 � 0.5, even though they are equivalent.
* <p>
* <b>Note:</b> The natural ordering of this class is inconsistent with
* equals. Specifically, if two {@code UncertainDouble} instances {@code a}
@@ -156,7 +156,7 @@ public final class UncertainDouble implements Comparable<UncertainDouble> {
public final int compareTo(UncertainDouble o) {
return Double.compare(this.value, o.value);
}
-
+
/**
* Returns the quotient of {@code this} and {@code other}.
*
@@ -167,7 +167,7 @@ public final class UncertainDouble implements Comparable<UncertainDouble> {
return UncertainDouble.ofRelative(this.value / other.value, Math
.hypot(this.relativeUncertainty(), other.relativeUncertainty()));
}
-
+
/**
* Returns the quotient of {@code this} and the exact value {@code other}.
*
@@ -176,7 +176,7 @@ public final class UncertainDouble implements Comparable<UncertainDouble> {
public final UncertainDouble dividedByExact(double other) {
return UncertainDouble.of(this.value / other, this.uncertainty / other);
}
-
+
@Override
public final boolean equals(Object obj) {
if (this == obj)
@@ -190,7 +190,7 @@ public final class UncertainDouble implements Comparable<UncertainDouble> {
return false;
return true;
}
-
+
/**
* @param other another {@code UncertainDouble}
* @return true iff this and {@code other} are within each other's
@@ -202,7 +202,7 @@ public final class UncertainDouble implements Comparable<UncertainDouble> {
return Math.abs(this.value - other.value) <= Math.min(this.uncertainty,
other.uncertainty);
}
-
+
/**
* Gets the preferred scale for rounding a value for toString.
*
@@ -217,19 +217,19 @@ public final class UncertainDouble implements Comparable<UncertainDouble> {
// the value is rounded to the same number of decimal places as the
// uncertainty.
final BigDecimal bigUncertainty = BigDecimal.valueOf(this.uncertainty);
-
+
// the scale that will give the uncertainty two decimal places
final int twoDecimalPlacesScale = bigUncertainty.scale()
- bigUncertainty.precision() + 2;
final BigDecimal roundedUncertainty = bigUncertainty
.setScale(twoDecimalPlacesScale, RoundingMode.HALF_EVEN);
-
+
if (roundedUncertainty.unscaledValue().intValue() >= 20)
return twoDecimalPlacesScale - 1; // one decimal place
else
return twoDecimalPlacesScale;
}
-
+
@Override
public final int hashCode() {
final int prime = 31;
@@ -238,7 +238,7 @@ public final class UncertainDouble implements Comparable<UncertainDouble> {
result = prime * result + Double.hashCode(this.uncertainty);
return result;
}
-
+
/**
* @return true iff the value has no uncertainty
*
@@ -247,7 +247,7 @@ public final class UncertainDouble implements Comparable<UncertainDouble> {
public final boolean isExact() {
return this.uncertainty == 0;
}
-
+
/**
* Returns the difference of {@code this} and {@code other}.
*
@@ -258,7 +258,7 @@ public final class UncertainDouble implements Comparable<UncertainDouble> {
return UncertainDouble.of(this.value - other.value,
Math.hypot(this.uncertainty, other.uncertainty));
}
-
+
/**
* Returns the difference of {@code this} and the exact value {@code other}.
*
@@ -267,7 +267,7 @@ public final class UncertainDouble implements Comparable<UncertainDouble> {
public final UncertainDouble minusExact(double other) {
return UncertainDouble.of(this.value - other, this.uncertainty);
}
-
+
/**
* Returns the sum of {@code this} and {@code other}.
*
@@ -278,7 +278,7 @@ public final class UncertainDouble implements Comparable<UncertainDouble> {
return UncertainDouble.of(this.value + other.value,
Math.hypot(this.uncertainty, other.uncertainty));
}
-
+
/**
* Returns the sum of {@code this} and the exact value {@code other}.
*
@@ -287,7 +287,7 @@ public final class UncertainDouble implements Comparable<UncertainDouble> {
public final UncertainDouble plusExact(double other) {
return UncertainDouble.of(this.value + other, this.uncertainty);
}
-
+
/**
* @return relative uncertainty
* @since 2020-09-07
@@ -295,7 +295,7 @@ public final class UncertainDouble implements Comparable<UncertainDouble> {
public final double relativeUncertainty() {
return this.uncertainty / this.value;
}
-
+
/**
* Returns the product of {@code this} and {@code other}.
*
@@ -306,7 +306,7 @@ public final class UncertainDouble implements Comparable<UncertainDouble> {
return UncertainDouble.ofRelative(this.value * other.value, Math
.hypot(this.relativeUncertainty(), other.relativeUncertainty()));
}
-
+
/**
* Returns the product of {@code this} and the exact value {@code other}.
*
@@ -315,7 +315,7 @@ public final class UncertainDouble implements Comparable<UncertainDouble> {
public final 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}.
*
@@ -323,15 +323,15 @@ public final class UncertainDouble implements Comparable<UncertainDouble> {
*/
public final 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(
other.value * this.relativeUncertainty(),
Math.log(this.value) * other.uncertainty);
-
+
return UncertainDouble.ofRelative(result, relativeUncertainty);
}
-
+
/**
* Returns the result of {@code this} raised the exact exponent
* {@code other}.
@@ -342,7 +342,7 @@ public final class UncertainDouble implements Comparable<UncertainDouble> {
return UncertainDouble.ofRelative(Math.pow(this.value, other),
this.relativeUncertainty() * other);
}
-
+
/**
* Returns a string representation of this {@code UncertainDouble}.
* <p>
@@ -354,8 +354,8 @@ public final class UncertainDouble implements Comparable<UncertainDouble> {
* 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(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>
*
@@ -365,12 +365,12 @@ public final class UncertainDouble implements Comparable<UncertainDouble> {
public final String toString() {
return this.toString(!this.isExact(), RoundingMode.HALF_EVEN);
}
-
+
/**
* 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
@@ -385,11 +385,11 @@ public final class UncertainDouble implements Comparable<UncertainDouble> {
*
* <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>
*
* @since 2020-09-07
@@ -397,31 +397,31 @@ public final class UncertainDouble implements Comparable<UncertainDouble> {
public final String toString(boolean showUncertainty,
RoundingMode roundingMode) {
String valueString, uncertaintyString;
-
+
// generate the string representation of value and uncertainty
if (this.isExact()) {
uncertaintyString = "0.0";
valueString = Double.toString(this.value);
-
+
} else {
// round the value and uncertainty according to getDisplayScale()
final BigDecimal bigValue = BigDecimal.valueOf(this.value);
final BigDecimal bigUncertainty = BigDecimal.valueOf(this.uncertainty);
-
+
final int displayScale = this.getDisplayScale();
final BigDecimal roundedUncertainty = bigUncertainty
.setScale(displayScale, roundingMode);
final BigDecimal 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
@@ -429,7 +429,7 @@ public final class UncertainDouble implements Comparable<UncertainDouble> {
public final double uncertainty() {
return this.uncertainty;
}
-
+
/**
* @return value without uncertainty
* @since 2020-09-07