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