/**
* Copyright (C) 2020 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 sevenUnits.unit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import sevenUnits.utils.NameSymbol;
import sevenUnits.utils.ObjectProduct;
/**
* A combination of units, like "5 foot + 7 inch". All but the last units should
* have a whole number value associated with them.
*
* @since 2020-10-02
*/
public final class MultiUnit extends Unitlike> {
/**
* Creates a {@code MultiUnit} from its units. It will not have a name or
* symbol.
*
* @since 2020-10-03
*/
public static final MultiUnit of(LinearUnit... units) {
return of(Arrays.asList(units));
}
/**
* Creates a {@code MultiUnit} from its units. It will not have a name or
* symbol.
*
* @since 2020-10-03
*/
public static final MultiUnit of(List units) {
if (units.size() < 1)
throw new IllegalArgumentException("Must have at least one unit");
final ObjectProduct unitBase = units.get(0).getBase();
for (final LinearUnit unit : units) {
if (!unitBase.equals(unit.getBase()))
throw new IllegalArgumentException(
"All units must have the same base.");
}
return new MultiUnit(new ArrayList<>(units), unitBase, NameSymbol.EMPTY);
}
/**
* The units that make up this value.
*/
private final List units;
/**
* Creates a {@code MultiUnit}.
*
* @since 2020-10-03
*/
private MultiUnit(List units, ObjectProduct unitBase,
NameSymbol ns) {
super(unitBase, ns);
this.units = units;
}
@Override
protected List convertFromBase(double value) {
final List values = new ArrayList<>(this.units.size());
double temp = value;
for (final LinearUnit unit : this.units.subList(0,
this.units.size() - 1)) {
values.add(Math.floor(temp / unit.getConversionFactor()));
temp %= unit.getConversionFactor();
}
values.add(this.units.size() - 1,
this.units.get(this.units.size() - 1).convertFromBase(temp));
return values;
}
/**
* Converts a value expressed in this unitlike form to a value expressed in
* {@code other}.
*
* @implSpec If conversion is possible, this implementation returns
* {@code other.convertFromBase(this.convertToBase(value))}.
* Therefore, overriding either of those methods will change the
* output of this method.
*
* @param other unit to convert to
* @param values values to convert
* @return converted value
* @since 2020-10-03
* @throws IllegalArgumentException if {@code other} is incompatible for
* conversion with this unitlike form (as
* tested by {@link Unit#canConvertTo}).
* @throws NullPointerException if other is null
*/
public final , V> V convertTo(U other,
double... values) {
final List valueList = new ArrayList<>(values.length);
for (final double d : values) {
valueList.add(d);
}
return this.convertTo(other, valueList);
}
/**
* Converts a value expressed in this unitlike form to a value expressed in
* {@code other}.
*
* @implSpec If conversion is possible, this implementation returns
* {@code other.convertFromBase(this.convertToBase(value))}.
* Therefore, overriding either of those methods will change the
* output of this method.
*
* @param other unit to convert to
* @param values values to convert
* @return converted value
* @since 2020-10-03
* @throws IllegalArgumentException if {@code other} is incompatible for
* conversion with this unitlike form (as
* tested by {@link Unit#canConvertTo}).
* @throws NullPointerException if other is null
*/
public final double convertTo(Unit other, double... values) {
final List valueList = new ArrayList<>(values.length);
for (final double d : values) {
valueList.add(d);
}
return this.convertTo(other, valueList);
}
@Override
protected double convertToBase(List value) {
if (value.size() != this.units.size())
throw new IllegalArgumentException("Wrong number of values for "
+ this.units.size() + "-unit MultiUnit.");
double baseValue = 0;
for (int i = 0; i < this.units.size(); i++) {
baseValue += value.get(i) * this.units.get(i).getConversionFactor();
}
return baseValue;
}
}