001// Copyright (c) FIRST and other WPILib contributors.
002// Open Source Software; you can modify and/or share it under the terms of
003// the WPILib BSD license file in the root directory of this project.
004
005package edu.wpi.first.units;
006
007import edu.wpi.first.units.measure.ImmutableMult;
008import edu.wpi.first.units.measure.Mult;
009import edu.wpi.first.units.measure.MutMult;
010import java.util.Objects;
011
012/**
013 * A combinatory unit type that is equivalent to the product of two other others. Note that
014 * algebraic reduction is not possible in Java's generic type system, so {@code MultUnit<A, B>} is
015 * not type-compatible with {@code MultUnit<B, A>}!
016 *
017 * @param <A> the type of the first unit in the result
018 * @param <B> the type of the second unit in the result
019 */
020public class MultUnit<A extends Unit, B extends Unit> extends Unit {
021  private final A m_unitA;
022  private final B m_unitB;
023
024  @SuppressWarnings("rawtypes")
025  private static final CombinatoryUnitCache<Unit, Unit, MultUnit> cache =
026      new CombinatoryUnitCache<>(MultUnit::new);
027
028  /**
029   * Creates a new product unit. Consider using {@link #combine} instead of manually calling this
030   * constructor.
031   *
032   * @param a the first unit of the product
033   * @param b the second unit of the product
034   */
035  private MultUnit(A a, B b) {
036    super(
037        a.isBaseUnit() && b.isBaseUnit() ? null : combine(a.getBaseUnit(), b.getBaseUnit()),
038        a.toBaseUnits(1) * b.toBaseUnits(1),
039        a.name() + "-" + b.name(),
040        a.symbol() + "*" + b.symbol());
041    m_unitA = a;
042    m_unitB = b;
043  }
044
045  /**
046   * Creates a new product unit. Subclasses of {@code MultUnit} should use this constructor.
047   *
048   * @param baseUnit the base unit. Set this to null if the unit being constructed is its own base
049   *     unit
050   * @param a the first unit of the product
051   * @param b the second unit of the product
052   */
053  protected MultUnit(MultUnit<A, B> baseUnit, A a, B b) {
054    super(
055        baseUnit,
056        a.toBaseUnits(1) * b.toBaseUnits(1),
057        a.name() + "-" + b.name(),
058        a.symbol() + "*" + b.symbol());
059    m_unitA = a;
060    m_unitB = b;
061  }
062
063  MultUnit(
064      MultUnit<A, B> baseUnit,
065      UnaryFunction toBaseConverter,
066      UnaryFunction fromBaseConverter,
067      String name,
068      String symbol) {
069    super(baseUnit, toBaseConverter, fromBaseConverter, name, symbol);
070    m_unitA = getBaseUnit().unitA();
071    m_unitB = getBaseUnit().unitB();
072  }
073
074  /**
075   * Creates a new MultUnit unit derived from two arbitrary units multiplied together.
076   *
077   * <pre>
078   *   MultUnit.combine(Volts, Meters) // Volt-Meters
079   * </pre>
080   *
081   * @param <A> the type of the first unit
082   * @param <B> the type of the second unit
083   * @param a the first unit
084   * @param b the second unit
085   * @return the combined unit
086   */
087  @SuppressWarnings("unchecked")
088  public static <A extends Unit, B extends Unit> MultUnit<A, B> combine(A a, B b) {
089    return cache.combine(a, b);
090  }
091
092  /**
093   * {@inheritDoc}
094   *
095   * <p>Note: When called on an object of type {@code MultUnit} (and <i>not</i> a subclass!), this
096   * method will always return a {@link edu.wpi.first.units.measure.Mult} instance. If you want to
097   * avoid casting, use {@link #ofNativeBaseUnits(double)} that returns a {@code Per} instance
098   * directly.
099   *
100   * @param magnitude the magnitude of the measure in terms of its base units.
101   * @return the measurement object
102   */
103  @Override
104  public Measure<? extends MultUnit<A, B>> of(double magnitude) {
105    return ofNative(magnitude);
106  }
107
108  /**
109   * {@inheritDoc}
110   *
111   * <p>Note: When called on an object of type {@code MultUnit} (and <i>not</i> a subclass!), this
112   * method will always return a {@link edu.wpi.first.units.measure.Mult} instance. If you want to
113   * avoid casting, use {@link #ofNativeBaseUnits(double)} that returns a {@code Per} instance
114   * directly.
115   *
116   * @param baseUnitMagnitude the magnitude of the measure in terms of its base units.
117   * @return the measurement object
118   */
119  @Override
120  public Measure<? extends MultUnit<A, B>> ofBaseUnits(double baseUnitMagnitude) {
121    return ofNativeBaseUnits(baseUnitMagnitude);
122  }
123
124  /**
125   * Creates a new immutable measurement of the given magnitude in terms of this unit. This will
126   * always return a {@code Mult} object and cannot be overridden by subclasses.
127   *
128   * @param magnitude the magnitude of the measurement.
129   * @return the measurement object
130   * @see #of(double)
131   */
132  public final Mult<A, B> ofNative(double magnitude) {
133    return new ImmutableMult<>(magnitude, toBaseUnits(magnitude), this);
134  }
135
136  /**
137   * Creates a new immutable measurement of the given magnitude in terms of the unit's base unit.
138   * This will always return a {@code Mult} object and cannot be overridden by subclasses.
139   *
140   * @param baseUnitMagnitude the magnitude of the measure in terms of its base units.
141   * @return the measurement object
142   * @see #ofBaseUnits(double)
143   */
144  public final Mult<A, B> ofNativeBaseUnits(double baseUnitMagnitude) {
145    return new ImmutableMult<>(fromBaseUnits(baseUnitMagnitude), baseUnitMagnitude, this);
146  }
147
148  @Override
149  @SuppressWarnings("unchecked")
150  public Measure<? extends MultUnit<A, B>> zero() {
151    return (Measure<? extends MultUnit<A, B>>) super.zero();
152  }
153
154  @Override
155  @SuppressWarnings("unchecked")
156  public Measure<? extends MultUnit<A, B>> one() {
157    return (Measure<? extends MultUnit<A, B>>) super.one();
158  }
159
160  @Override
161  public MutableMeasure<? extends MultUnit<A, B>, ?, ?> mutable(double initialMagnitude) {
162    return new MutMult<>(initialMagnitude, toBaseUnits(initialMagnitude), this);
163  }
164
165  @Override
166  public Unit per(TimeUnit time) {
167    return VelocityUnit.combine(this, time);
168  }
169
170  /**
171   * Converts a measurement value in terms of another unit to this unit.
172   *
173   * @param magnitude the magnitude of the measurement in terms of the other unit
174   * @param otherUnit the other unit
175   * @return the value of the measurement in terms of this unit
176   */
177  public double convertFrom(double magnitude, MultUnit<A, B> otherUnit) {
178    return fromBaseUnits(otherUnit.toBaseUnits(magnitude));
179  }
180
181  @Override
182  @SuppressWarnings("unchecked")
183  public MultUnit<A, B> getBaseUnit() {
184    return (MultUnit<A, B>) super.getBaseUnit();
185  }
186
187  /**
188   * Gets the first unit of the product.
189   *
190   * @return the first unit
191   */
192  public A unitA() {
193    return m_unitA;
194  }
195
196  /**
197   * Gets the second unit of the product.
198   *
199   * @return the second unit
200   */
201  public B unitB() {
202    return m_unitB;
203  }
204
205  @Override
206  public String toString() {
207    return "(" + m_unitA.toString() + " * " + m_unitB.toString() + ")";
208  }
209
210  @Override
211  public boolean equals(Object o) {
212    if (this == o) {
213      return true;
214    }
215    if (o == null || getClass() != o.getClass()) {
216      return false;
217    }
218    if (!super.equals(o)) {
219      return false;
220    }
221    MultUnit<?, ?> multUnit = (MultUnit<?, ?>) o;
222    return Objects.equals(m_unitA, multUnit.m_unitA) && Objects.equals(m_unitB, multUnit.m_unitB);
223  }
224
225  @Override
226  public int hashCode() {
227    return Objects.hash(super.hashCode(), m_unitA, m_unitB);
228  }
229}