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 a the first unit of the product
030   * @param b the second unit of the product
031   */
032  protected Mult(A a, B b) {
033    super(
034        a.isBaseUnit() && b.isBaseUnit() ? null : combine(a.getBaseUnit(), b.getBaseUnit()),
035        a.toBaseUnits(1) * b.toBaseUnits(1),
036        a.name() + "-" + b.name(),
037        a.symbol() + "*" + b.symbol());
038    m_unitA = a;
039    m_unitB = b;
040  }
041
042  Mult(
043      Mult<A, B> baseUnit,
044      UnaryFunction toBaseConverter,
045      UnaryFunction fromBaseConverter,
046      String name,
047      String symbol) {
048    super(baseUnit, toBaseConverter, fromBaseConverter, name, symbol);
049    m_unitA = baseUnit.unitA();
050    m_unitB = baseUnit.unitB();
051  }
052
053  /**
054   * Creates a new Mult unit derived from two arbitrary units multiplied together.
055   *
056   * <pre>
057   *   Mult.combine(Volts, Meters) // Volt-Meters
058   * </pre>
059   *
060   * <p>It's recommended to use the convenience function {@link Unit#mult(Unit)} instead of calling
061   * this factory directly.
062   *
063   * @param <A> the type of the first unit
064   * @param <B> the type of the second unit
065   * @param a the first unit
066   * @param b the second unit
067   * @return the combined unit
068   */
069  @SuppressWarnings({"unchecked", "rawtypes"})
070  public static <A extends Unit<A>, B extends Unit<B>> Mult<A, B> combine(A a, B b) {
071    final long key = ((long) a.hashCode()) << 32L | (((long) b.hashCode()) & 0xFFFFFFFFL);
072    if (cache.containsKey(key)) {
073      return cache.get(key);
074    }
075
076    var mult = new Mult<A, B>(a, b);
077    cache.put(key, mult);
078    return mult;
079  }
080
081  /**
082   * Gets the first unit of the product.
083   *
084   * @return the first unit
085   */
086  public A unitA() {
087    return m_unitA;
088  }
089
090  /**
091   * Gets the second unit of the product.
092   *
093   * @return the second unit
094   */
095  public B unitB() {
096    return m_unitB;
097  }
098
099  @Override
100  public String toString() {
101    return "(" + m_unitA.toString() + " * " + m_unitB.toString() + ")";
102  }
103
104  @Override
105  public boolean equals(Object o) {
106    if (this == o) {
107      return true;
108    }
109    if (o == null || getClass() != o.getClass()) {
110      return false;
111    }
112    if (!super.equals(o)) {
113      return false;
114    }
115    Mult<?, ?> mult = (Mult<?, ?>) o;
116    return Objects.equals(m_unitA, mult.m_unitA) && Objects.equals(m_unitB, mult.m_unitB);
117  }
118
119  @Override
120  public int hashCode() {
121    return Objects.hash(super.hashCode(), m_unitA, m_unitB);
122  }
123}