diff options
Diffstat (limited to 'src/main/java/sevenUnits/utils')
-rw-r--r-- | src/main/java/sevenUnits/utils/ConditionalExistenceCollections.java | 104 | ||||
-rw-r--r-- | src/main/java/sevenUnits/utils/DecimalComparison.java | 20 | ||||
-rw-r--r-- | src/main/java/sevenUnits/utils/ExpressionParser.java | 308 | ||||
-rw-r--r-- | src/main/java/sevenUnits/utils/NameSymbol.java | 44 | ||||
-rw-r--r-- | src/main/java/sevenUnits/utils/Nameable.java | 10 | ||||
-rw-r--r-- | src/main/java/sevenUnits/utils/ObjectProduct.java | 56 | ||||
-rw-r--r-- | src/main/java/sevenUnits/utils/SemanticVersionNumber.java | 94 | ||||
-rw-r--r-- | src/main/java/sevenUnits/utils/UncertainDouble.java | 116 |
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 |