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.Per;
008import java.util.Objects;
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 edu.wpi.first.units.measure.Per} instance. If you want to
161   * avoid 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 edu.wpi.first.units.measure.Per} instance. If you want to
176   * avoid casting, use {@link #ofNativeBaseUnits(double)} that returns a {@code Per} instance
177   * directly.
178   *
179   * @param baseUnitMagnitude the magnitude of the measure in terms of its base units.
180   * @return the ratio measure
181   */
182  @Override
183  public Measure<? extends PerUnit<N, D>> ofBaseUnits(double baseUnitMagnitude) {
184    return ofNativeBaseUnits(baseUnitMagnitude);
185  }
186
187  /**
188   * Creates a new immutable measurement of the given magnitude in terms of the ratio unit. This
189   * will always return a {@code Per} object and cannot be overridden by subclasses.
190   *
191   * @param magnitude the magnitude of the measurement.
192   * @return the measurement object
193   * @see #of(double)
194   */
195  public final Per<N, D> ofNative(double magnitude) {
196    return new Per<>(magnitude, toBaseUnits(magnitude), this);
197  }
198
199  /**
200   * Creates a new immutable measurement of the given magnitude in terms of the ratio unit's base
201   * unit. This will always return a {@code Per} object and cannot be overridden by subclasses.
202   *
203   * @param baseUnitMagnitude the magnitude of the measure in terms of its base units.
204   * @return the measurement object
205   * @see #ofBaseUnits(double)
206   */
207  public final Per<N, D> ofNativeBaseUnits(double baseUnitMagnitude) {
208    return new Per<>(fromBaseUnits(baseUnitMagnitude), baseUnitMagnitude, this);
209  }
210
211  @Override
212  @SuppressWarnings("unchecked")
213  public Measure<? extends PerUnit<N, D>> zero() {
214    return (Measure<? extends PerUnit<N, D>>) super.zero();
215  }
216
217  @Override
218  @SuppressWarnings("unchecked")
219  public Measure<? extends PerUnit<N, D>> one() {
220    return (Measure<? extends PerUnit<N, D>>) super.one();
221  }
222
223  @Override
224  public Unit per(TimeUnit time) {
225    return VelocityUnit.combine(this, time);
226  }
227
228  /**
229   * Converts a measurement value in terms of another unit to this unit.
230   *
231   * @param magnitude the magnitude of the measurement in terms of the other unit
232   * @param otherUnit the other unit
233   * @return the value of the measurement in terms of this unit
234   */
235  public double convertFrom(double magnitude, PerUnit<? extends N, ? extends D> otherUnit) {
236    return fromBaseUnits(otherUnit.toBaseUnits(magnitude));
237  }
238
239  @Override
240  public boolean equals(Object o) {
241    if (this == o) {
242      return true;
243    }
244    if (o == null || getClass() != o.getClass()) {
245      return false;
246    }
247    if (!super.equals(o)) {
248      return false;
249    }
250    PerUnit<?, ?> perUnit = (PerUnit<?, ?>) o;
251    return Objects.equals(m_numerator, perUnit.m_numerator)
252        && Objects.equals(m_denominator, perUnit.m_denominator);
253  }
254
255  @Override
256  public int hashCode() {
257    return Objects.hash(super.hashCode(), m_numerator, m_denominator);
258  }
259}