/**
* Copyright (C) 2018 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package unitConverter.dimension;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
/**
* An object that represents what a unit measures, like length, mass, area, energy, etc.
*
* @author Adrien Hopkins
* @since 2018-12-11
* @since v0.1.0
*/
public final class UnitDimension {
/**
* The unit dimension where every exponent is zero
*
* @since 2018-12-12
* @since v0.1.0
*/
public static final UnitDimension EMPTY = new UnitDimension(new HashMap<>());
/**
* Gets an UnitDimension that has 1 of a certain dimension and nothing else
*
* @param dimension
* dimension to get
* @return unit dimension
* @since 2018-12-11
* @since v0.1.0
*/
public static final UnitDimension getBase(final BaseDimension dimension) {
final Map map = new HashMap<>();
map.put(dimension, 1);
return new UnitDimension(map);
}
/**
* The base dimensions that make up this dimension.
*
* @since 2018-12-11
* @since v0.1.0
*/
final Map exponents;
/**
* Creates the {@code UnitDimension}.
*
* @param exponents
* base dimensions that make up this dimension
* @since 2018-12-11
* @since v0.1.0
*/
private UnitDimension(final Map exponents) {
this.exponents = new HashMap<>(exponents);
}
/**
* Divides this dimension by another
*
* @param other
* other dimension
* @return quotient of two dimensions
* @since 2018-12-11
* @since v0.1.0
*/
public UnitDimension dividedBy(final UnitDimension other) {
final Map map = new HashMap<>(this.exponents);
for (final BaseDimension key : other.exponents.keySet()) {
if (map.containsKey(key)) {
// add the dimensions
map.put(key, map.get(key) - other.exponents.get(key));
} else {
map.put(key, -other.exponents.get(key));
}
}
return new UnitDimension(map);
}
@Override
public boolean equals(final Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof UnitDimension))
return false;
final UnitDimension other = (UnitDimension) obj;
// anything with a value of 0 is equal to a nonexistent value
for (final BaseDimension b : this.getBaseSet()) {
if (this.exponents.get(b) != other.exponents.get(b))
if (!(this.exponents.get(b) == 0 && !other.exponents.containsKey(b)))
return false;
}
for (final BaseDimension b : other.getBaseSet()) {
if (this.exponents.get(b) != other.exponents.get(b))
if (!(other.exponents.get(b) == 0 && !this.exponents.containsKey(b)))
return false;
}
return true;
}
/**
* @return a set of all of the base dimensions with non-zero exponents that make up this dimension.
* @since 2018-12-12
* @since v0.1.0
*/
public final Set getBaseSet() {
final Set dimensions = new HashSet<>();
// add all dimensions with a nonzero exponent - they shouldn't be there in the first place
for (final BaseDimension dimension : this.exponents.keySet()) {
if (!this.exponents.get(dimension).equals(0)) {
dimensions.add(dimension);
}
}
return dimensions;
}
/**
* Gets the exponent for a specific dimension.
*
* @param dimension
* dimension to check
* @return exponent for that dimension
* @since 2018-12-12
* @since v0.1.0
*/
public int getExponent(final BaseDimension dimension) {
return this.exponents.getOrDefault(dimension, 0);
}
@Override
public int hashCode() {
return Objects.hash(this.exponents);
}
/**
* @return true if this dimension is a base, i.e. it has one exponent of one and no other nonzero exponents
* @since 2019-01-15
* @since v0.1.0
*/
public boolean isBase() {
int oneCount = 0;
boolean twoOrMore = false; // has exponents of 2 or more
for (final BaseDimension b : this.getBaseSet()) {
if (this.exponents.get(b) == 1) {
oneCount++;
} else if (this.exponents.get(b) != 0) {
twoOrMore = true;
}
}
return (oneCount == 0 || oneCount == 1) && !twoOrMore;
}
/**
* Multiplies this dimension by another
*
* @param other
* other dimension
* @return product of two dimensions
* @since 2018-12-11
* @since v0.1.0
*/
public UnitDimension times(final UnitDimension other) {
final Map map = new HashMap<>(this.exponents);
for (final BaseDimension key : other.exponents.keySet()) {
if (map.containsKey(key)) {
// add the dimensions
map.put(key, map.get(key) + other.exponents.get(key));
} else {
map.put(key, other.exponents.get(key));
}
}
return new UnitDimension(map);
}
/**
* Returns this dimension, but to an exponent
*
* @param exp
* exponent
* @return result of exponientation
* @since 2019-01-15
* @since v0.1.0
*/
public UnitDimension toExponent(final int exp) {
final Map map = new HashMap<>(this.exponents);
for (final BaseDimension key : this.exponents.keySet()) {
map.put(key, this.getExponent(key) * exp);
}
return new UnitDimension(map);
}
@Override
public String toString() {
final List positiveStringComponents = new ArrayList<>();
final List negativeStringComponents = new ArrayList<>();
// for each base dimension that makes up this dimension, add it and its exponent
for (final BaseDimension dimension : this.getBaseSet()) {
final int exponent = this.exponents.get(dimension);
if (exponent > 0) {
positiveStringComponents.add(String.format("%s^%d", dimension.getSymbol(), exponent));
} else if (exponent < 0) {
negativeStringComponents.add(String.format("%s^%d", dimension.getSymbol(), -exponent));
}
}
final String positiveString = positiveStringComponents.isEmpty() ? "1"
: String.join(" ", positiveStringComponents);
final String negativeString = negativeStringComponents.isEmpty() ? ""
: " / " + String.join(" ", negativeStringComponents);
return positiveString + negativeString;
}
}