Skip to content

Commit

Permalink
Pull out and combine scale factors for units
Browse files Browse the repository at this point in the history
Scale factors are not part of the "name" of a unit.  Therefore, we
should gather and combine them every time we form products and powers of
units.  This will also make us consistent with the way we handle
anonymous scaled units in the _common unit_ use case.

Fixes aurora-opensource#382.
  • Loading branch information
chiphogg committed Jan 22, 2025
1 parent 40b3226 commit 3d7aea3
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 11 deletions.
44 changes: 33 additions & 11 deletions au/code/au/unit_of_measure.hh
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,31 @@ struct ScaledUnit : Unit {
using Mag = MagProductT<detail::MagT<Unit>, ScaleFactor>;
};

template <typename U>
struct UnscaledUnitImpl : stdx::type_identity<U> {};
template <typename U>
using UnscaledUnit = typename UnscaledUnitImpl<U>::type;
template <typename U, typename M>
struct UnscaledUnitImpl<ScaledUnit<U, M>> : stdx::type_identity<U> {};
template <typename U, std::intmax_t N>
struct UnscaledUnitImpl<Pow<U, N>> : stdx::type_identity<Pow<UnscaledUnit<U>, N>> {};
template <typename U, std::intmax_t N, std::intmax_t D>
struct UnscaledUnitImpl<RatioPow<U, N, D>> : stdx::type_identity<RatioPow<UnscaledUnit<U>, N, D>> {
};

template <typename U>
struct ExtractScaleFactorImpl : stdx::type_identity<Magnitude<>> {};
template <typename U>
using ExtractScaleFactor = typename ExtractScaleFactorImpl<U>::type;
template <typename U, typename M>
struct ExtractScaleFactorImpl<ScaledUnit<U, M>> : stdx::type_identity<M> {};
template <typename U, std::intmax_t N>
struct ExtractScaleFactorImpl<Pow<U, N>>
: stdx::type_identity<MagPowerT<ExtractScaleFactor<U>, N>> {};
template <typename U, std::intmax_t N, std::intmax_t D>
struct ExtractScaleFactorImpl<RatioPow<U, N, D>>
: stdx::type_identity<MagPowerT<ExtractScaleFactor<U>, N, D>> {};

// Type template to hold the product of powers of Units.
template <typename... UnitPows>
struct UnitProduct {
Expand All @@ -332,13 +357,17 @@ struct UnitProduct {
// simplify it using `UnpackIfSoloT`. (The motivation is that we don't want to return, say,
// `UnitProduct<Meters>`; we'd rather just return `Meters`.)
template <typename... UnitPows>
using UnitProductT =
UnpackIfSoloT<UnitProduct, PackProductT<UnitProduct, AsPackT<UnitProduct, UnitPows>...>>;
using UnitProductT = ComputeScaledUnit<
UnpackIfSoloT<UnitProduct,
PackProductT<UnitProduct, AsPackT<UnitProduct, UnscaledUnit<UnitPows>>...>>,
MagProductT<ExtractScaleFactor<UnitPows>...>>;

// Raise a Unit to a (possibly rational) Power.
template <typename U, std::intmax_t ExpNum, std::intmax_t ExpDen = 1>
using UnitPowerT =
UnpackIfSoloT<UnitProduct, PackPowerT<UnitProduct, AsPackT<UnitProduct, U>, ExpNum, ExpDen>>;
using UnitPowerT = ComputeScaledUnit<
UnpackIfSoloT<UnitProduct,
PackPowerT<UnitProduct, AsPackT<UnitProduct, UnscaledUnit<U>>, ExpNum, ExpDen>>,
MagPowerT<ExtractScaleFactor<U>, ExpNum, ExpDen>>;

// Compute the inverse of a unit.
template <typename U>
Expand Down Expand Up @@ -653,13 +682,6 @@ constexpr typename CommonUnitLabelImpl<Us...>::LabelT CommonUnitLabelImpl<Us...>
template <typename U>
struct CommonUnitLabelImpl<U> : UnitLabel<U> {};

template <typename U>
struct UnscaledUnitImpl : stdx::type_identity<U> {};
template <typename U, typename M>
struct UnscaledUnitImpl<ScaledUnit<U, M>> : stdx::type_identity<U> {};
template <typename U>
using UnscaledUnit = typename UnscaledUnitImpl<U>::type;

template <typename U>
struct DistinctUnscaledUnitsImpl : stdx::type_identity<UnitList<UnscaledUnit<U>>> {};
template <typename U>
Expand Down
10 changes: 10 additions & 0 deletions au/code/au/unit_of_measure_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,11 @@ TEST(UnitProductT, CreatesPowForIntegerPowers) {
StaticAssertTypeEq<UnitProductT<FeetSquared, UnitInverseT<Feet>>, Feet>();
}

TEST(UnitProductT, PullsOutAndCombinesAllScaleFactors) {
StaticAssertTypeEq<UnitProductT<decltype(Feet{} * mag<3>()), decltype(Feet{} * mag<5>())>,
decltype(squared(Feet{}) * mag<15>())>();
}

TEST(UnitPowerT, ProducesSimplifiedPowersOfAllExponents) {
using Input = UnitProductT<Feet, Pow<Minutes, 3>, Pow<Inches, -6>, RatioPow<Yards, 3, 2>>;

Expand All @@ -216,6 +221,11 @@ TEST(UnitPowerT, ProducesSimplifiedPowersOfAllExponents) {
StaticAssertTypeEq<UnitPowerT<Input, 1, 3>, ExpectedCbrtInput>();
}

TEST(UnitPowerT, PullsOutAndCombinesAllScaleFactors) {
StaticAssertTypeEq<UnitPowerT<decltype(Feet{} * mag<10>()), 3>,
decltype(cubed(Feet{}) * mag<1'000>())>();
}

TEST(UnitQuotientT, InteractsAndCancelsAsExpected) {
StaticAssertTypeEq<UnitProductT<UnitQuotientT<Feet, Minutes>, Minutes>, Feet>();
}
Expand Down

0 comments on commit 3d7aea3

Please sign in to comment.