/** * Copyright (C) 2019 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 org.unitConverter.newUnits; import java.util.Objects; import org.unitConverter.math.DecimalComparison; import org.unitConverter.math.ObjectProduct; /** * A unit that can be expressed as a product of its base and a number. For example, kilometres, inches and pounds. * * @author Adrien Hopkins * @since 2019-10-16 */ public final class LinearUnit extends AbstractUnit { /** * Gets a {@code LinearUnit} from a unit and a value. For example, converts '59 °F' to a linear unit with the value * of '288.15 K' * * @param unit * unit to convert * @param value * value to convert * @return value expressed as a {@code LinearUnit} * @since 2019-10-16 */ public static LinearUnit fromValue(final Unit unit, final double value) { return new LinearUnit(unit.getBase(), unit.convertToBase(value)); } /** * Gets a {@code LinearUnit} from a unit base and a conversion factor. In other words, gets the product of * {@code unitBase} and {@code conversionFactor}, expressed as a {@code LinearUnit}. * * @param unitBase * unit base to multiply by * @param conversionFactor * number to multiply base by * @return product of base and conversion factor * @since 2019-10-16 */ public static LinearUnit valueOf(final ObjectProduct unitBase, final double conversionFactor) { return new LinearUnit(unitBase, conversionFactor); } /** * The value of this unit as represented in its base form. Mathematically, * *
	 * this = conversionFactor * getBase()
	 * 
* * @since 2019-10-16 */ private final double conversionFactor; /** * Creates the {@code LinearUnit}. * * @param unitBase * base of linear unit * @param conversionFactor * conversion factor between base and unit * @since 2019-10-16 */ private LinearUnit(final ObjectProduct unitBase, final double conversionFactor) { super(unitBase); this.conversionFactor = conversionFactor; } @Override public double convertFromBase(final double value) { return value / this.getConversionFactor(); } @Override public double convertToBase(final double value) { return value * this.getConversionFactor(); } /** * Divides this unit by a scalar. * * @param divisor * scalar to divide by * @return quotient * @since 2018-12-23 * @since v0.1.0 */ public LinearUnit dividedBy(final double divisor) { return valueOf(this.getBase(), this.getConversionFactor() / divisor); } /** * Returns the quotient of this unit and another. * * @param divisor * unit to divide by * @return quotient of two units * @throws NullPointerException * if {@code divisor} is null * @since 2018-12-22 * @since v0.1.0 */ public LinearUnit dividedBy(final LinearUnit divisor) { Objects.requireNonNull(divisor, "other must not be null"); // divide the units final ObjectProduct base = this.getBase().dividedBy(divisor.getBase()); return valueOf(base, this.getConversionFactor() / divisor.getConversionFactor()); } @Override public boolean equals(final Object obj) { if (!(obj instanceof LinearUnit)) return false; final LinearUnit other = (LinearUnit) obj; return Objects.equals(this.getBase(), other.getBase()) && DecimalComparison.equals(this.getConversionFactor(), other.getConversionFactor()); } /** * @return conversion factor * @since 2019-10-16 */ public double getConversionFactor() { return this.conversionFactor; } @Override public int hashCode() { return 31 * this.getBase().hashCode() + DecimalComparison.hash(this.getConversionFactor()); } /** * @return whether this unit is equivalent to a {@code BaseUnit} (i.e. there is a {@code BaseUnit b} where * {@code b.asLinearUnit().equals(this)} returns {@code true}.) * @since 2019-10-16 */ public boolean isBase() { return this.isCoherent() && this.getBase().isSingleObject(); } /** * @return whether this unit is coherent (i.e. has conversion factor 1) * @since 2019-10-16 */ public boolean isCoherent() { return this.getConversionFactor() == 1; } /** * Returns the difference of this unit and another. *

* Two units can be subtracted if they have the same base. If {@code subtrahend} does not meet this condition, an * {@code IllegalArgumentException} will be thrown. *

* * @param subtrahend * unit to subtract * @return difference of units * @throws IllegalArgumentException * if {@code subtrahend} is not compatible for subtraction as described above * @throws NullPointerException * if {@code subtrahend} is null * @since 2019-03-17 * @since v0.2.0 */ public LinearUnit minus(final LinearUnit subtrahendend) { Objects.requireNonNull(subtrahendend, "addend must not be null."); // reject subtrahends that cannot be added to this unit if (!this.getBase().equals(subtrahendend.getBase())) throw new IllegalArgumentException( String.format("Incompatible units for subtraction \"%s\" and \"%s\".", this, subtrahendend)); // add the units return valueOf(this.getBase(), this.getConversionFactor() - subtrahendend.getConversionFactor()); } /** * Returns the sum of this unit and another. *

* Two units can be added if they have the same base. If {@code addend} does not meet this condition, an * {@code IllegalArgumentException} will be thrown. *

* * @param addend * unit to add * @return sum of units * @throws IllegalArgumentException * if {@code addend} is not compatible for addition as described above * @throws NullPointerException * if {@code addend} is null * @since 2019-03-17 * @since v0.2.0 */ public LinearUnit plus(final LinearUnit addend) { Objects.requireNonNull(addend, "addend must not be null."); // reject addends that cannot be added to this unit if (!this.getBase().equals(addend.getBase())) throw new IllegalArgumentException( String.format("Incompatible units for addition \"%s\" and \"%s\".", this, addend)); // add the units return valueOf(this.getBase(), this.getConversionFactor() + addend.getConversionFactor()); } /** * Multiplies this unit by a scalar. * * @param multiplier * scalar to multiply by * @return product * @since 2018-12-23 * @since v0.1.0 */ public LinearUnit times(final double multiplier) { return valueOf(this.getBase(), this.getConversionFactor() * multiplier); } /** * Returns the product of this unit and another. * * @param multiplier * unit to multiply by * @return product of two units * @throws NullPointerException * if {@code multiplier} is null * @since 2018-12-22 * @since v0.1.0 */ public LinearUnit times(final LinearUnit multiplier) { Objects.requireNonNull(multiplier, "other must not be null"); // multiply the units final ObjectProduct base = this.getBase().times(multiplier.getBase()); return valueOf(base, this.getConversionFactor() * multiplier.getConversionFactor()); } /** * Returns this unit but to an exponent. * * @param exponent * exponent to exponentiate unit to * @return exponentiated unit * @since 2019-01-15 * @since v0.1.0 */ public LinearUnit toExponent(final int exponent) { return valueOf(this.getBase().toExponent(exponent), Math.pow(this.conversionFactor, exponent)); } // returns a definition of the unit @Override public String toString() { return Double.toString(this.conversionFactor) + " * " + this.getBase().toString(BaseUnit::getSymbol); } /** * Returns the result of applying {@code prefix} to this unit. * * @param prefix * prefix to apply * @return unit with prefix * @since 2019-03-18 * @since v0.2.0 */ public LinearUnit withPrefix(final UnitPrefix prefix) { return this.times(prefix.getMultiplier()); } }