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 * A combinatory unit type that is equivalent to the product of two other others. Note that
012 * algebraic reduction is not possible in Java's generic type system, so {@code Mult<A, B>} is not
013 * type-compatible with {@code Mult<B, A>}!
014 *
015 * @param <A> the type of the first unit in the result
016 * @param <B> the type of the second unit in the result
017 */
018public class Mult<A extends Unit<A>, B extends Unit<B>> extends Unit<Mult<A, B>> {
019  private final A m_unitA;
020  private final B m_unitB;
021
022  @SuppressWarnings("rawtypes")
023  private static final LongToObjectHashMap<Mult> cache = new LongToObjectHashMap<>();
024
025  /**
026   * Creates a new product unit. Consider using {@link #combine} instead of manually calling this
027   * constructor.
028   *
029   * @param baseType the base type representing the unit product
030   * @param a the first unit of the product
031   * @param b the second unit of the product
032   */
033  protected Mult(Class<? extends Mult<A, B>> baseType, A a, B b) {
034    super(
035        baseType,
036        a.toBaseUnits(1) * b.toBaseUnits(1),
037        a.name() + "-" + b.name(),
038        a.symbol() + "*" + b.symbol());
039    m_unitA = a;
040    m_unitB = b;
041  }
042
043  /**
044   * Creates a new Mult unit derived from two arbitrary units multiplied together.
045   *
046   * <pre>
047   *   Mult.combine(Volts, Meters) // Volt-Meters
048   * </pre>
049   *
050   * <p>It's recommended to use the convenience function {@link Unit#mult(Unit)} instead of calling
051   * this factory directly.
052   *
053   * @param <A> the type of the first unit
054   * @param <B> the type of the second unit
055   * @param a the first unit
056   * @param b the second unit
057   * @return the combined unit
058   */
059  @SuppressWarnings({"unchecked", "rawtypes"})
060  public static <A extends Unit<A>, B extends Unit<B>> Mult<A, B> combine(A a, B b) {
061    final long key = ((long) a.hashCode()) << 32L | (((long) b.hashCode()) & 0xFFFFFFFFL);
062    if (cache.containsKey(key)) {
063      return cache.get(key);
064    }
065
066    var mult = new Mult<A, B>((Class) Mult.class, a, b);
067    cache.put(key, mult);
068    return mult;
069  }
070
071  /**
072   * Gets the first unit of the product.
073   *
074   * @return the first unit
075   */
076  public A unitA() {
077    return m_unitA;
078  }
079
080  /**
081   * Gets the second unit of the product.
082   *
083   * @return the second unit
084   */
085  public B unitB() {
086    return m_unitB;
087  }
088
089  @Override
090  public String toString() {
091    return "(" + m_unitA.toString() + " * " + m_unitB.toString() + ")";
092  }
093
094  @Override
095  public boolean equals(Object o) {
096    if (this == o) {
097      return true;
098    }
099    if (o == null || getClass() != o.getClass()) {
100      return false;
101    }
102    if (!super.equals(o)) {
103      return false;
104    }
105    Mult<?, ?> mult = (Mult<?, ?>) o;
106    return Objects.equals(m_unitA, mult.m_unitA) && Objects.equals(m_unitB, mult.m_unitB);
107  }
108
109  @Override
110  public int hashCode() {
111    return Objects.hash(super.hashCode(), m_unitA, m_unitB);
112  }
113}