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. 015 * 016 * @param <N> the type of the numerator unit 017 * @param <D> the type of the denominator unit 018 */ 019public class Per<N extends Unit<N>, D extends Unit<D>> extends Unit<Per<N, D>> { 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 LongToObjectHashMap<Per> cache = new LongToObjectHashMap<>(); 029 030 protected Per(Class<Per<N, D>> baseType, N numerator, D denominator) { 031 super( 032 baseType, 033 numerator.toBaseUnits(1) / denominator.toBaseUnits(1), 034 numerator.name() + " per " + denominator.name(), 035 numerator.symbol() + "/" + denominator.symbol()); 036 m_numerator = numerator; 037 m_denominator = denominator; 038 } 039 040 /** 041 * Creates a new Per unit derived from an arbitrary numerator and time denominator units. Using a 042 * denominator with a unit of time is discouraged; use {@link Velocity} instead. 043 * 044 * <pre> 045 * Per.combine(Volts, Meters) // possible PID constant 046 * </pre> 047 * 048 * <p>It's recommended to use the convenience function {@link Unit#per(Unit)} instead of calling 049 * this factory directly. 050 * 051 * @param <N> the type of the numerator unit 052 * @param <D> the type of the denominator unit 053 * @param numerator the numerator unit 054 * @param denominator the denominator for unit time 055 * @return the combined unit 056 */ 057 @SuppressWarnings({"unchecked", "rawtypes"}) 058 public static <N extends Unit<N>, D extends Unit<D>> Per<N, D> combine( 059 N numerator, D denominator) { 060 final long key = 061 ((long) numerator.hashCode()) << 32L | ((long) denominator.hashCode()) & 0xFFFFFFFFL; 062 063 var existing = cache.get(key); 064 if (existing != null) { 065 return existing; 066 } 067 068 var newUnit = new Per<N, D>((Class) Per.class, numerator, denominator); 069 cache.put(key, newUnit); 070 return newUnit; 071 } 072 073 public N numerator() { 074 return m_numerator; 075 } 076 077 public D denominator() { 078 return m_denominator; 079 } 080 081 @Override 082 public boolean equals(Object o) { 083 if (this == o) { 084 return true; 085 } 086 if (o == null || getClass() != o.getClass()) { 087 return false; 088 } 089 if (!super.equals(o)) { 090 return false; 091 } 092 Per<?, ?> per = (Per<?, ?>) o; 093 return Objects.equals(m_numerator, per.m_numerator) 094 && Objects.equals(m_denominator, per.m_denominator); 095 } 096 097 @Override 098 public int hashCode() { 099 return Objects.hash(super.hashCode(), m_numerator, m_denominator); 100 } 101}