// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.units;
import edu.wpi.first.units.collections.LongToObjectHashMap;
import java.util.Objects;
/**
* Generic combinatory unit type that represents the proportion of one unit to another, such as
* Meters per Second or Radians per Celsius.
*
*
Note that due to restrictions with the Java type system, velocities (change per unit time) are
* represented by the {@link Velocity} class. Accelerations are represented by {@code
* Velocity>}, and so on.
*
* @param the type of the numerator unit
* @param the type of the denominator unit
*/
public class Per, D extends Unit> extends Unit> {
private final N m_numerator;
private final D m_denominator;
/**
* Keep a cache of created instances so expressions like Volts.per(Meter) don't do any allocations
* after the first.
*/
@SuppressWarnings("rawtypes")
private static final LongToObjectHashMap cache = new LongToObjectHashMap<>();
protected Per(Class> baseType, N numerator, D denominator) {
super(
baseType,
numerator.toBaseUnits(1) / denominator.toBaseUnits(1),
numerator.name() + " per " + denominator.name(),
numerator.symbol() + "/" + denominator.symbol());
m_numerator = numerator;
m_denominator = denominator;
}
/**
* Creates a new Per unit derived from an arbitrary numerator and time denominator units. Using a
* denominator with a unit of time is discouraged; use {@link Velocity} instead.
*
*
* Per.combine(Volts, Meters) // possible PID constant
*
*
* It's recommended to use the convenience function {@link Unit#per(Unit)} instead of calling
* this factory directly.
*
* @param the type of the numerator unit
* @param the type of the denominator unit
* @param numerator the numerator unit
* @param denominator the denominator for unit time
* @return the combined unit
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public static , D extends Unit> Per combine(
N numerator, D denominator) {
final long key =
((long) numerator.hashCode()) << 32L | ((long) denominator.hashCode()) & 0xFFFFFFFFL;
var existing = cache.get(key);
if (existing != null) {
return existing;
}
var newUnit = new Per((Class) Per.class, numerator, denominator);
cache.put(key, newUnit);
return newUnit;
}
public N numerator() {
return m_numerator;
}
public D denominator() {
return m_denominator;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
if (!super.equals(o)) {
return false;
}
Per, ?> per = (Per, ?>) o;
return Objects.equals(m_numerator, per.m_numerator)
&& Objects.equals(m_denominator, per.m_denominator);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), m_numerator, m_denominator);
}
}