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 org.wpilib.units;
006
007import java.util.Objects;
008import org.wpilib.units.measure.Per;
009
010/**
011 * Generic combinatory unit type that represents the proportion of one unit to another, such as
012 * Meters per Second or Radians per Celsius.
013 *
014 * @param <N> the type of the numerator unit
015 * @param <D> the type of the denominator unit
016 */
017public class PerUnit<N extends Unit, D extends Unit> extends Unit {
018  private final N m_numerator;
019  private final D m_denominator;
020
021  /**
022   * Keep a cache of created instances so expressions like Volts.per(Meter) don't do any allocations
023   * after the first.
024   */
025  @SuppressWarnings("rawtypes")
026  private static final CombinatoryUnitCache<Unit, Unit, PerUnit> cache =
027      new CombinatoryUnitCache<>(PerUnit::new);
028
029  /**
030   * Creates a new proportional unit derived from the ratio of one unit to another. Consider using
031   * {@link #combine} instead of manually calling this constructor.
032   *
033   * @param numerator the numerator unit
034   * @param denominator the denominator unit
035   */
036  private PerUnit(N numerator, D denominator) {
037    super(
038        numerator.isBaseUnit() && denominator.isBaseUnit()
039            ? null
040            : combine(numerator.getBaseUnit(), denominator.getBaseUnit()),
041        numerator.toBaseUnits(1) / denominator.toBaseUnits(1),
042        numerator.name() + " per " + denominator.name(),
043        numerator.symbol() + "/" + denominator.symbol());
044    m_numerator = numerator;
045    m_denominator = denominator;
046  }
047
048  /**
049   * Creates a new proportional unit derived from the ratio of one unit to another. Subclasses of
050   * {@code PerUnit} should use this constructor.
051   *
052   * @param baseUnit the base unit. Set this to null if the unit being constructed is its own base
053   *     unit
054   * @param numerator the numerator unit
055   * @param denominator the denominator unit
056   */
057  protected PerUnit(PerUnit<N, D> baseUnit, N numerator, D denominator) {
058    super(
059        baseUnit,
060        numerator.toBaseUnits(1) / denominator.toBaseUnits(1),
061        numerator.name() + " per " + denominator.name(),
062        numerator.symbol() + "/" + denominator.symbol());
063    m_numerator = numerator;
064    m_denominator = denominator;
065  }
066
067  /** {@inheritDoc} */
068  PerUnit(
069      PerUnit<N, D> baseUnit,
070      UnaryFunction toBaseConverter,
071      UnaryFunction fromBaseConverter,
072      String name,
073      String symbol) {
074    super(baseUnit, toBaseConverter, fromBaseConverter, name, symbol);
075    m_numerator = getBaseUnit().numerator();
076    m_denominator = getBaseUnit().denominator();
077  }
078
079  /**
080   * Creates a new PerUnit unit derived from an arbitrary numerator and time denominator units.
081   *
082   * <pre>
083   *   PerUnit.combine(Volts, Meters) // possible PID constant
084   * </pre>
085   *
086   * @param <N> the type of the numerator unit
087   * @param <D> the type of the denominator unit
088   * @param numerator the numerator unit
089   * @param denominator the denominator for unit time
090   * @return the combined unit
091   */
092  @SuppressWarnings("unchecked")
093  public static <N extends Unit, D extends Unit> PerUnit<N, D> combine(N numerator, D denominator) {
094    return cache.combine(numerator, denominator);
095  }
096
097  @Override
098  @SuppressWarnings("unchecked")
099  public PerUnit<N, D> getBaseUnit() {
100    return (PerUnit<N, D>) super.getBaseUnit();
101  }
102
103  /**
104   * Gets the numerator unit. For a {@code PerUnit<A, B>}, this will return the {@code A} unit.
105   *
106   * @return the numerator unit
107   */
108  public N numerator() {
109    return m_numerator;
110  }
111
112  /**
113   * Gets the denominator unit. For a {@code PerUnit<A, B>}, this will return the {@code B} unit.
114   *
115   * @return the denominator unit
116   */
117  public D denominator() {
118    return m_denominator;
119  }
120
121  /**
122   * Returns the reciprocal of this PerUnit.
123   *
124   * @return the reciprocal
125   */
126  @SuppressWarnings("unchecked")
127  public PerUnit<D, N> reciprocal() {
128    if (m_numerator instanceof TimeUnit t) {
129      // Dividing by time, return a velocity
130      return (PerUnit<D, N>) VelocityUnit.combine(m_denominator, t);
131    } else {
132      // Generic case
133      return combine(m_denominator, m_numerator);
134    }
135  }
136
137  /**
138   * Multiplies this unit by a unit of its denominator.
139   *
140   * @param denom the denominator-typed unit to multiply by
141   * @return the result
142   */
143  public N mult(D denom) {
144    if (denom.equivalent(denominator())) {
145      return numerator();
146    }
147
148    return Units.derive(numerator())
149        .toBase(denom.getConverterToBase().div(denominator().getConverterToBase()))
150        .fromBase(denom.getConverterFromBase().div(denominator().getConverterFromBase()))
151        .named(name() + " " + denom.name())
152        .symbol(symbol() + "-" + denom.symbol())
153        .make();
154  }
155
156  /**
157   * {@inheritDoc}
158   *
159   * <p>Note: When called on an object of type {@code PerUnit} (and <i>not</i> a subclass!), this
160   * method will always return a {@link org.wpilib.units.measure.Per} instance. If you want to avoid
161   * casting, use {@link #ofNative(double)} that returns a {@code Per} instance directly.
162   *
163   * @param magnitude the magnitude of the measure
164   * @return the ratio measure
165   */
166  @Override
167  public Measure<? extends PerUnit<N, D>> of(double magnitude) {
168    return ofNative(magnitude);
169  }
170
171  /**
172   * {@inheritDoc}
173   *
174   * <p>Note: When called on an object of type {@code PerUnit} (and <i>not</i> a subclass!), this
175   * method will always return a {@link org.wpilib.units.measure.Per} instance. If you want to avoid
176   * casting, use {@link #ofNativeBaseUnits(double)} that returns a {@code Per} instance directly.
177   *
178   * @param baseUnitMagnitude the magnitude of the measure in terms of its base units.
179   * @return the ratio measure
180   */
181  @Override
182  public Measure<? extends PerUnit<N, D>> ofBaseUnits(double baseUnitMagnitude) {
183    return ofNativeBaseUnits(baseUnitMagnitude);
184  }
185
186  /**
187   * Creates a new immutable measurement of the given magnitude in terms of the ratio unit. This
188   * will always return a {@code Per} object and cannot be overridden by subclasses.
189   *
190   * @param magnitude the magnitude of the measurement.
191   * @return the measurement object
192   * @see #of(double)
193   */
194  public final Per<N, D> ofNative(double magnitude) {
195    return new Per<>(magnitude, toBaseUnits(magnitude), this);
196  }
197
198  /**
199   * Creates a new immutable measurement of the given magnitude in terms of the ratio unit's base
200   * unit. This will always return a {@code Per} object and cannot be overridden by subclasses.
201   *
202   * @param baseUnitMagnitude the magnitude of the measure in terms of its base units.
203   * @return the measurement object
204   * @see #ofBaseUnits(double)
205   */
206  public final Per<N, D> ofNativeBaseUnits(double baseUnitMagnitude) {
207    return new Per<>(fromBaseUnits(baseUnitMagnitude), baseUnitMagnitude, this);
208  }
209
210  @Override
211  @SuppressWarnings("unchecked")
212  public Measure<? extends PerUnit<N, D>> zero() {
213    return (Measure<? extends PerUnit<N, D>>) super.zero();
214  }
215
216  @Override
217  @SuppressWarnings("unchecked")
218  public Measure<? extends PerUnit<N, D>> one() {
219    return (Measure<? extends PerUnit<N, D>>) super.one();
220  }
221
222  @Override
223  public Unit per(TimeUnit time) {
224    return VelocityUnit.combine(this, time);
225  }
226
227  /**
228   * Converts a measurement value in terms of another unit to this unit.
229   *
230   * @param magnitude the magnitude of the measurement in terms of the other unit
231   * @param otherUnit the other unit
232   * @return the value of the measurement in terms of this unit
233   */
234  public double convertFrom(double magnitude, PerUnit<? extends N, ? extends D> otherUnit) {
235    return fromBaseUnits(otherUnit.toBaseUnits(magnitude));
236  }
237
238  @Override
239  public boolean equals(Object o) {
240    if (this == o) {
241      return true;
242    }
243    if (o == null || getClass() != o.getClass()) {
244      return false;
245    }
246    if (!super.equals(o)) {
247      return false;
248    }
249    PerUnit<?, ?> perUnit = (PerUnit<?, ?>) o;
250    return Objects.equals(m_numerator, perUnit.m_numerator)
251        && Objects.equals(m_denominator, perUnit.m_denominator);
252  }
253
254  @Override
255  public int hashCode() {
256    return Objects.hash(super.hashCode(), m_numerator, m_denominator);
257  }
258}