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