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.collections.LongToObjectHashMap; 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 * <p>Note: {@link Velocity} is used to represent the velocity dimension, rather than {@code 015 * Per<Distance, Time>}. 016 * 017 * @param <N> the type of the numerator unit 018 * @param <D> the type of the denominator unit 019 */ 020public class Per<N extends Unit<N>, D extends Unit<D>> extends Unit<Per<N, D>> { 021 private final N m_numerator; 022 private final D m_denominator; 023 024 /** 025 * Keep a cache of created instances so expressions like Volts.per(Meter) don't do any allocations 026 * after the first. 027 */ 028 @SuppressWarnings("rawtypes") 029 private static final LongToObjectHashMap<Per> cache = new LongToObjectHashMap<>(); 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 protected Per(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 Per( 051 Per<N, D> baseUnit, 052 UnaryFunction toBaseConverter, 053 UnaryFunction fromBaseConverter, 054 String name, 055 String symbol) { 056 super(baseUnit, toBaseConverter, fromBaseConverter, name, symbol); 057 m_numerator = baseUnit.numerator(); 058 m_denominator = baseUnit.denominator(); 059 } 060 061 /** 062 * Creates a new Per unit derived from an arbitrary numerator and time denominator units. Using a 063 * denominator with a unit of time is discouraged; use {@link Velocity} instead. 064 * 065 * <pre> 066 * Per.combine(Volts, Meters) // possible PID constant 067 * </pre> 068 * 069 * <p>It's recommended to use the convenience function {@link Unit#per(Unit)} instead of calling 070 * this factory directly. 071 * 072 * @param <N> the type of the numerator unit 073 * @param <D> the type of the denominator unit 074 * @param numerator the numerator unit 075 * @param denominator the denominator for unit time 076 * @return the combined unit 077 */ 078 @SuppressWarnings("unchecked") 079 public static <N extends Unit<N>, D extends Unit<D>> Per<N, D> combine( 080 N numerator, D denominator) { 081 final long key = 082 ((long) numerator.hashCode()) << 32L | (((long) denominator.hashCode()) & 0xFFFFFFFFL); 083 084 var existing = cache.get(key); 085 if (existing != null) { 086 return existing; 087 } 088 089 var newUnit = new Per<>(numerator, denominator); 090 cache.put(key, newUnit); 091 return newUnit; 092 } 093 094 /** 095 * Gets the numerator unit. For a {@code Per<A, B>}, this will return the {@code A} unit. 096 * 097 * @return the numerator unit 098 */ 099 public N numerator() { 100 return m_numerator; 101 } 102 103 /** 104 * Gets the denominator unit. For a {@code Per<A, B>}, this will return the {@code B} unit. 105 * 106 * @return the denominator unit 107 */ 108 public D denominator() { 109 return m_denominator; 110 } 111 112 /** 113 * Returns the reciprocal of this Per. 114 * 115 * @return the reciprocal 116 */ 117 public Per<D, N> reciprocal() { 118 return m_denominator.per(m_numerator); 119 } 120 121 @Override 122 public boolean equals(Object o) { 123 if (this == o) { 124 return true; 125 } 126 if (o == null || getClass() != o.getClass()) { 127 return false; 128 } 129 if (!super.equals(o)) { 130 return false; 131 } 132 Per<?, ?> per = (Per<?, ?>) o; 133 return Objects.equals(m_numerator, per.m_numerator) 134 && Objects.equals(m_denominator, per.m_denominator); 135 } 136 137 @Override 138 public int hashCode() { 139 return Objects.hash(super.hashCode(), m_numerator, m_denominator); 140 } 141}