From b16d864d3d4b4629508bc47e00d37d0bd68477b3 Mon Sep 17 00:00:00 2001 From: Prateek Machiraju Date: Thu, 27 Jun 2019 16:47:47 -0400 Subject: [PATCH] Use units in TimedTrajectory --- build.gradle | 2 + libs/build.gradle | 15 +- libs/units/units.h | 4854 +++++++++++++++++ src/include/fl/Utilities.h | 6 + .../mathematics/control/PurePursuitTracker.h | 16 +- .../fl/mathematics/control/RamseteTracker.h | 2 +- .../mathematics/control/TrajectoryTracker.h | 20 +- .../mathematics/trajectory/TimedTrajectory.h | 83 +- src/include/fl/types/Interpolatable.h | 3 +- src/test/cpp/pure-pursuit-tests.cpp | 8 +- src/test/cpp/ramsete-tests.cpp | 6 +- src/test/cpp/trajectory-tests.cpp | 14 +- 12 files changed, 4965 insertions(+), 64 deletions(-) create mode 100644 libs/units/units.h diff --git a/build.gradle b/build.gradle index 254f88a..15590e7 100644 --- a/build.gradle +++ b/build.gradle @@ -16,12 +16,14 @@ model { srcDir "src/include" include "**/*.h" } + lib project: ':libs', library: 'units', linkage: 'static' } } } binaries { withType(GoogleTestTestSuiteBinarySpec) { lib project: ":libs", library: "googleTest", linkage: "static" + lib project: ':libs', library: 'units', linkage: 'static' } } testSuites { diff --git a/libs/build.gradle b/libs/build.gradle index 92fa5d0..17a09f3 100644 --- a/libs/build.gradle +++ b/libs/build.gradle @@ -3,6 +3,7 @@ apply plugin: "cpp" ext.libroot = new File(rootProject.rootDir, "libs") ext.gtest_root = new File(libroot, "googletest/googletest") ext.gbench_root = new File(libroot, "benchmark") +ext.units_root = new File(libroot, "units") model { components { @@ -43,5 +44,17 @@ model { lib project: ':libs', library: "googleTest", linkage: "static" } } - } + units(NativeLibrarySpec) { + sources.cpp { + source { + srcDir units_root + include "**/*.cpp" + } + exportedHeaders { + srcDirs units_root + include "**/*.h" + } + } + } + } } \ No newline at end of file diff --git a/libs/units/units.h b/libs/units/units.h new file mode 100644 index 0000000..9be4cec --- /dev/null +++ b/libs/units/units.h @@ -0,0 +1,4854 @@ +//-------------------------------------------------------------------------------------------------- +// +// Units: A compile-time c++14 unit conversion library with no dependencies +// +//-------------------------------------------------------------------------------------------------- +// +// The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software +// and associated documentation files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +// BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//-------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2016 Nic Holthaus +// +//-------------------------------------------------------------------------------------------------- +// +// ATTRIBUTION: +// Parts of this work have been adapted from: +// http://stackoverflow.com/questions/35069778/create-comparison-trait-for-template-classes-whose-parameters-are-in-a-different +// http://stackoverflow.com/questions/28253399/check-traits-for-all-variadic-template-arguments/28253503 +// http://stackoverflow.com/questions/36321295/rational-approximation-of-square-root-of-stdratio-at-compile-time?noredirect=1#comment60266601_36321295 +// +//-------------------------------------------------------------------------------------------------- +// +/// @file units.h +/// @brief Complete implementation of `units` - a compile-time, header-only, unit conversion +/// library built on c++14 with no dependencies. +// +//-------------------------------------------------------------------------------------------------- + +#pragma once + +#ifndef units_h__ +#define units_h__ + +#ifdef _MSC_VER +# pragma push_macro("pascal") +# undef pascal +# if _MSC_VER <= 1800 +# define _ALLOW_KEYWORD_MACROS +# pragma warning(push) +# pragma warning(disable : 4520) +# pragma push_macro("constexpr") +# define constexpr /*constexpr*/ +# pragma push_macro("noexcept") +# define noexcept throw() +# endif // _MSC_VER < 1800 +#endif // _MSC_VER + +#if !defined(_MSC_VER) || _MSC_VER > 1800 +# define UNIT_HAS_LITERAL_SUPPORT +# define UNIT_HAS_VARIADIC_TEMPLATE_SUPPORT +#endif + +#ifndef UNIT_LIB_DEFAULT_TYPE +# define UNIT_LIB_DEFAULT_TYPE double +#endif + +//-------------------- +// INCLUDES +//-------------------- + +#include +#include +#include +#include +#include +#include + +#if !defined(UNIT_LIB_DISABLE_IOSTREAM) + #include + #include + #include + + //------------------------------ + // STRING FORMATTER + //------------------------------ + + namespace units + { + namespace detail + { + template std::string to_string(const T& t) + { + std::string str{ std::to_string(t) }; + int offset{ 1 }; + + // remove trailing decimal points for integer value units. Locale aware! + struct lconv * lc; + lc = localeconv(); + char decimalPoint = *lc->decimal_point; + if (str.find_last_not_of('0') == str.find(decimalPoint)) { offset = 0; } + str.erase(str.find_last_not_of('0') + offset, std::string::npos); + return str; + } + } + } +#endif + +namespace units +{ + template inline constexpr const char* name(const T&); + template inline constexpr const char* abbreviation(const T&); +} + +//------------------------------ +// MACROS +//------------------------------ + +/** + * @def UNIT_ADD_UNIT_TAGS(namespaceName,nameSingular, namePlural, abbreviation, definition) + * @brief Helper macro for generating the boiler-plate code generating the tags of a new unit. + * @details The macro generates singular, plural, and abbreviated forms + * of the unit definition (e.g. `meter`, `meters`, and `m`), as aliases for the + * unit tag. + * @param namespaceName namespace in which the new units will be encapsulated. + * @param nameSingular singular version of the unit name, e.g. 'meter' + * @param namePlural - plural version of the unit name, e.g. 'meters' + * @param abbreviation - abbreviated unit name, e.g. 'm' + * @param definition - the variadic parameter is used for the definition of the unit + * (e.g. `unit, units::category::length_unit>`) + * @note a variadic template is used for the definition to allow templates with + * commas to be easily expanded. All the variadic 'arguments' should together + * comprise the unit definition. + */ +#define UNIT_ADD_UNIT_TAGS(namespaceName,nameSingular, namePlural, abbreviation, /*definition*/...)\ + namespace namespaceName\ + {\ + /** @name Units (full names plural) */ /** @{ */ typedef __VA_ARGS__ namePlural; /** @} */\ + /** @name Units (full names singular) */ /** @{ */ typedef namePlural nameSingular; /** @} */\ + /** @name Units (abbreviated) */ /** @{ */ typedef namePlural abbreviation; /** @} */\ + } + +/** + * @def UNIT_ADD_UNIT_DEFINITION(namespaceName,nameSingular) + * @brief Macro for generating the boiler-plate code for the unit_t type definition. + * @details The macro generates the definition of the unit container types, e.g. `meter_t` + * @param namespaceName namespace in which the new units will be encapsulated. + * @param nameSingular singular version of the unit name, e.g. 'meter' + */ +#define UNIT_ADD_UNIT_DEFINITION(namespaceName,nameSingular)\ + namespace namespaceName\ + {\ + /** @name Unit Containers */ /** @{ */ typedef unit_t nameSingular ## _t; /** @} */\ + } + +/** + * @def UNIT_ADD_CUSTOM_TYPE_UNIT_DEFINITION(namespaceName,nameSingular,underlyingType) + * @brief Macro for generating the boiler-plate code for a unit_t type definition with a non-default underlying type. + * @details The macro generates the definition of the unit container types, e.g. `meter_t` + * @param namespaceName namespace in which the new units will be encapsulated. + * @param nameSingular singular version of the unit name, e.g. 'meter' + * @param underlyingType the underlying type + */ +#define UNIT_ADD_CUSTOM_TYPE_UNIT_DEFINITION(namespaceName,nameSingular, underlyingType)\ + namespace namespaceName\ + {\ + /** @name Unit Containers */ /** @{ */ typedef unit_t nameSingular ## _t; /** @} */\ + } +/** + * @def UNIT_ADD_IO(namespaceName,nameSingular, abbreviation) + * @brief Macro for generating the boiler-plate code needed for I/O for a new unit. + * @details The macro generates the code to insert units into an ostream. It + * prints both the value and abbreviation of the unit when invoked. + * @param namespaceName namespace in which the new units will be encapsulated. + * @param nameSingular singular version of the unit name, e.g. 'meter' + * @param abbrev - abbreviated unit name, e.g. 'm' + * @note When UNIT_LIB_DISABLE_IOSTREAM is defined, the macro does not generate any code + */ +#if defined(UNIT_LIB_DISABLE_IOSTREAM) + #define UNIT_ADD_IO(namespaceName, nameSingular, abbrev) +#else + #define UNIT_ADD_IO(namespaceName, nameSingular, abbrev)\ + namespace namespaceName\ + {\ + inline std::ostream& operator<<(std::ostream& os, const nameSingular ## _t& obj) \ + {\ + os << obj() << " "#abbrev; return os; \ + }\ + inline std::string to_string(const nameSingular ## _t& obj)\ + {\ + return units::detail::to_string(obj()) + std::string(" "#abbrev);\ + }\ + } +#endif + + /** + * @def UNIT_ADD_NAME(namespaceName,nameSingular,abbreviation) + * @brief Macro for generating constexpr names/abbreviations for units. + * @details The macro generates names for units. E.g. name() of 1_m would be "meter", and + * abbreviation would be "m". + * @param namespaceName namespace in which the new units will be encapsulated. All literal values + * are placed in the `units::literals` namespace. + * @param nameSingular singular version of the unit name, e.g. 'meter' + * @param abbreviation - abbreviated unit name, e.g. 'm' + */ +#define UNIT_ADD_NAME(namespaceName, nameSingular, abbrev)\ +template<> inline constexpr const char* name(const namespaceName::nameSingular ## _t&)\ +{\ + return #nameSingular;\ +}\ +template<> inline constexpr const char* abbreviation(const namespaceName::nameSingular ## _t&)\ +{\ + return #abbrev;\ +} + +/** + * @def UNIT_ADD_LITERALS(namespaceName,nameSingular,abbreviation) + * @brief Macro for generating user-defined literals for units. + * @details The macro generates user-defined literals for units. A literal suffix is created + * using the abbreviation (e.g. `10.0_m`). + * @param namespaceName namespace in which the new units will be encapsulated. All literal values + * are placed in the `units::literals` namespace. + * @param nameSingular singular version of the unit name, e.g. 'meter' + * @param abbreviation - abbreviated unit name, e.g. 'm' + * @note When UNIT_HAS_LITERAL_SUPPORT is not defined, the macro does not generate any code + */ +#if defined(UNIT_HAS_LITERAL_SUPPORT) + #define UNIT_ADD_LITERALS(namespaceName, nameSingular, abbreviation)\ + namespace literals\ + {\ + inline constexpr namespaceName::nameSingular ## _t operator""_ ## abbreviation(long double d)\ + {\ + return namespaceName::nameSingular ## _t(static_cast(d));\ + }\ + inline constexpr namespaceName::nameSingular ## _t operator""_ ## abbreviation (unsigned long long d)\ + {\ + return namespaceName::nameSingular ## _t(static_cast(d));\ + }\ + } +#else + #define UNIT_ADD_LITERALS(namespaceName, nameSingular, abbreviation) +#endif + +/** + * @def UNIT_ADD(namespaceName,nameSingular, namePlural, abbreviation, definition) + * @brief Macro for generating the boiler-plate code needed for a new unit. + * @details The macro generates singular, plural, and abbreviated forms + * of the unit definition (e.g. `meter`, `meters`, and `m`), as well as the + * appropriately named unit container (e.g. `meter_t`). A literal suffix is created + * using the abbreviation (e.g. `10.0_m`). It also defines a class-specific + * cout function which prints both the value and abbreviation of the unit when invoked. + * @param namespaceName namespace in which the new units will be encapsulated. All literal values + * are placed in the `units::literals` namespace. + * @param nameSingular singular version of the unit name, e.g. 'meter' + * @param namePlural - plural version of the unit name, e.g. 'meters' + * @param abbreviation - abbreviated unit name, e.g. 'm' + * @param definition - the variadic parameter is used for the definition of the unit + * (e.g. `unit, units::category::length_unit>`) + * @note a variadic template is used for the definition to allow templates with + * commas to be easily expanded. All the variadic 'arguments' should together + * comprise the unit definition. + */ +#define UNIT_ADD(namespaceName, nameSingular, namePlural, abbreviation, /*definition*/...)\ + UNIT_ADD_UNIT_TAGS(namespaceName,nameSingular, namePlural, abbreviation, __VA_ARGS__)\ + UNIT_ADD_UNIT_DEFINITION(namespaceName,nameSingular)\ + UNIT_ADD_NAME(namespaceName,nameSingular, abbreviation)\ + UNIT_ADD_IO(namespaceName,nameSingular, abbreviation)\ + UNIT_ADD_LITERALS(namespaceName,nameSingular, abbreviation) + +/** + * @def UNIT_ADD_WITH_CUSTOM_TYPE(namespaceName,nameSingular, namePlural, abbreviation, underlyingType, definition) + * @brief Macro for generating the boiler-plate code needed for a new unit with a non-default underlying type. + * @details The macro generates singular, plural, and abbreviated forms + * of the unit definition (e.g. `meter`, `meters`, and `m`), as well as the + * appropriately named unit container (e.g. `meter_t`). A literal suffix is created + * using the abbreviation (e.g. `10.0_m`). It also defines a class-specific + * cout function which prints both the value and abbreviation of the unit when invoked. + * @param namespaceName namespace in which the new units will be encapsulated. All literal values + * are placed in the `units::literals` namespace. + * @param nameSingular singular version of the unit name, e.g. 'meter' + * @param namePlural - plural version of the unit name, e.g. 'meters' + * @param abbreviation - abbreviated unit name, e.g. 'm' + * @param underlyingType - the underlying type, e.g. 'int' or 'float' + * @param definition - the variadic parameter is used for the definition of the unit + * (e.g. `unit, units::category::length_unit>`) + * @note a variadic template is used for the definition to allow templates with + * commas to be easily expanded. All the variadic 'arguments' should together + * comprise the unit definition. + */ +#define UNIT_ADD_WITH_CUSTOM_TYPE(namespaceName, nameSingular, namePlural, abbreviation, underlyingType, /*definition*/...)\ + UNIT_ADD_UNIT_TAGS(namespaceName,nameSingular, namePlural, abbreviation, __VA_ARGS__)\ + UNIT_ADD_CUSTOM_TYPE_UNIT_DEFINITION(namespaceName,nameSingular,underlyingType)\ + UNIT_ADD_IO(namespaceName,nameSingular, abbreviation)\ + UNIT_ADD_LITERALS(namespaceName,nameSingular, abbreviation) + +/** + * @def UNIT_ADD_DECIBEL(namespaceName, nameSingular, abbreviation) + * @brief Macro to create decibel container and literals for an existing unit type. + * @details This macro generates the decibel unit container, cout overload, and literal definitions. + * @param namespaceName namespace in which the new units will be encapsulated. All literal values + * are placed in the `units::literals` namespace. + * @param nameSingular singular version of the base unit name, e.g. 'watt' + * @param abbreviation - abbreviated decibel unit name, e.g. 'dBW' + */ +#define UNIT_ADD_DECIBEL(namespaceName, nameSingular, abbreviation)\ + namespace namespaceName\ + {\ + /** @name Unit Containers */ /** @{ */ typedef unit_t abbreviation ## _t; /** @} */\ + }\ + UNIT_ADD_IO(namespaceName, abbreviation, abbreviation)\ + UNIT_ADD_LITERALS(namespaceName, abbreviation, abbreviation) + +/** + * @def UNIT_ADD_CATEGORY_TRAIT(unitCategory, baseUnit) + * @brief Macro to create the `is_category_unit` type trait. + * @details This trait allows users to test whether a given type matches + * an intended category. This macro comprises all the boiler-plate + * code necessary to do so. + * @param unitCategory The name of the category of unit, e.g. length or mass. + */ + +#define UNIT_ADD_CATEGORY_TRAIT_DETAIL(unitCategory)\ + namespace traits\ + {\ + /** @cond */\ + namespace detail\ + {\ + template struct is_ ## unitCategory ## _unit_impl : std::false_type {};\ + template\ + struct is_ ## unitCategory ## _unit_impl> : std::is_same>::base_unit_type>, units::category::unitCategory ## _unit>::type {};\ + template class N>\ + struct is_ ## unitCategory ## _unit_impl> : std::is_same>::unit_type>, units::category::unitCategory ## _unit>::type {};\ + }\ + /** @endcond */\ + } + +#if defined(UNIT_HAS_VARIADIC_TEMPLATE_SUPPORT) +#define UNIT_ADD_IS_UNIT_CATEGORY_TRAIT(unitCategory)\ + namespace traits\ + {\ + template struct is_ ## unitCategory ## _unit : std::integral_constant>::value...>::value> {};\ + } +#else +#define UNIT_ADD_IS_UNIT_CATEGORY_TRAIT(unitCategory)\ + namespace traits\ + {\ + template\ + struct is_ ## unitCategory ## _unit : std::integral_constant::type>::value &&\ + units::traits::detail::is_ ## unitCategory ## _unit_impl::type>::value &&\ + units::traits::detail::is_ ## unitCategory ## _unit_impl::type>::value>{};\ + } +#endif + +#define UNIT_ADD_CATEGORY_TRAIT(unitCategory)\ + UNIT_ADD_CATEGORY_TRAIT_DETAIL(unitCategory)\ + /** @ingroup TypeTraits*/\ + /** @brief Trait which tests whether a type represents a unit of unitCategory*/\ + /** @details Inherits from `std::true_type` or `std::false_type`. Use `is_ ## unitCategory ## _unit::value` to test the unit represents a unitCategory quantity.*/\ + /** @tparam T one or more types to test*/\ + UNIT_ADD_IS_UNIT_CATEGORY_TRAIT(unitCategory) + +/** + * @def UNIT_ADD_WITH_METRIC_PREFIXES(nameSingular, namePlural, abbreviation, definition) + * @brief Macro for generating the boiler-plate code needed for a new unit, including its metric + * prefixes from femto to peta. + * @details See UNIT_ADD. In addition to generating the unit definition and containers '(e.g. `meters` and 'meter_t', + * it also creates corresponding units with metric suffixes such as `millimeters`, and `millimeter_t`), as well as the + * literal suffixes (e.g. `10.0_mm`). + * @param namespaceName namespace in which the new units will be encapsulated. All literal values + * are placed in the `units::literals` namespace. + * @param nameSingular singular version of the unit name, e.g. 'meter' + * @param namePlural - plural version of the unit name, e.g. 'meters' + * @param abbreviation - abbreviated unit name, e.g. 'm' + * @param definition - the variadic parameter is used for the definition of the unit + * (e.g. `unit, units::category::length_unit>`) + * @note a variadic template is used for the definition to allow templates with + * commas to be easily expanded. All the variadic 'arguments' should together + * comprise the unit definition. + */ +#define UNIT_ADD_WITH_METRIC_PREFIXES(namespaceName, nameSingular, namePlural, abbreviation, /*definition*/...)\ + UNIT_ADD(namespaceName, nameSingular, namePlural, abbreviation, __VA_ARGS__)\ + UNIT_ADD(namespaceName, femto ## nameSingular, femto ## namePlural, f ## abbreviation, femto)\ + UNIT_ADD(namespaceName, pico ## nameSingular, pico ## namePlural, p ## abbreviation, pico)\ + UNIT_ADD(namespaceName, nano ## nameSingular, nano ## namePlural, n ## abbreviation, nano)\ + UNIT_ADD(namespaceName, micro ## nameSingular, micro ## namePlural, u ## abbreviation, micro)\ + UNIT_ADD(namespaceName, milli ## nameSingular, milli ## namePlural, m ## abbreviation, milli)\ + UNIT_ADD(namespaceName, centi ## nameSingular, centi ## namePlural, c ## abbreviation, centi)\ + UNIT_ADD(namespaceName, deci ## nameSingular, deci ## namePlural, d ## abbreviation, deci)\ + UNIT_ADD(namespaceName, deca ## nameSingular, deca ## namePlural, da ## abbreviation, deca)\ + UNIT_ADD(namespaceName, hecto ## nameSingular, hecto ## namePlural, h ## abbreviation, hecto)\ + UNIT_ADD(namespaceName, kilo ## nameSingular, kilo ## namePlural, k ## abbreviation, kilo)\ + UNIT_ADD(namespaceName, mega ## nameSingular, mega ## namePlural, M ## abbreviation, mega)\ + UNIT_ADD(namespaceName, giga ## nameSingular, giga ## namePlural, G ## abbreviation, giga)\ + UNIT_ADD(namespaceName, tera ## nameSingular, tera ## namePlural, T ## abbreviation, tera)\ + UNIT_ADD(namespaceName, peta ## nameSingular, peta ## namePlural, P ## abbreviation, peta)\ + + /** + * @def UNIT_ADD_WITH_METRIC_AND_BINARY_PREFIXES(nameSingular, namePlural, abbreviation, definition) + * @brief Macro for generating the boiler-plate code needed for a new unit, including its metric + * prefixes from femto to peta, and binary prefixes from kibi to exbi. + * @details See UNIT_ADD. In addition to generating the unit definition and containers '(e.g. `bytes` and 'byte_t', + * it also creates corresponding units with metric suffixes such as `millimeters`, and `millimeter_t`), as well as the + * literal suffixes (e.g. `10.0_B`). + * @param namespaceName namespace in which the new units will be encapsulated. All literal values + * are placed in the `units::literals` namespace. + * @param nameSingular singular version of the unit name, e.g. 'byte' + * @param namePlural - plural version of the unit name, e.g. 'bytes' + * @param abbreviation - abbreviated unit name, e.g. 'B' + * @param definition - the variadic parameter is used for the definition of the unit + * (e.g. `unit, units::category::data_unit>`) + * @note a variadic template is used for the definition to allow templates with + * commas to be easily expanded. All the variadic 'arguments' should together + * comprise the unit definition. + */ +#define UNIT_ADD_WITH_METRIC_AND_BINARY_PREFIXES(namespaceName, nameSingular, namePlural, abbreviation, /*definition*/...)\ + UNIT_ADD_WITH_METRIC_PREFIXES(namespaceName, nameSingular, namePlural, abbreviation, __VA_ARGS__)\ + UNIT_ADD(namespaceName, kibi ## nameSingular, kibi ## namePlural, Ki ## abbreviation, kibi)\ + UNIT_ADD(namespaceName, mebi ## nameSingular, mebi ## namePlural, Mi ## abbreviation, mebi)\ + UNIT_ADD(namespaceName, gibi ## nameSingular, gibi ## namePlural, Gi ## abbreviation, gibi)\ + UNIT_ADD(namespaceName, tebi ## nameSingular, tebi ## namePlural, Ti ## abbreviation, tebi)\ + UNIT_ADD(namespaceName, pebi ## nameSingular, pebi ## namePlural, Pi ## abbreviation, pebi)\ + UNIT_ADD(namespaceName, exbi ## nameSingular, exbi ## namePlural, Ei ## abbreviation, exbi) + +//-------------------- +// UNITS NAMESPACE +//-------------------- + +/** + * @namespace units + * @brief Unit Conversion Library namespace + */ +namespace units +{ + //---------------------------------- + // DOXYGEN + //---------------------------------- + + /** + * @defgroup UnitContainers Unit Containers + * @brief Defines a series of classes which contain dimensioned values. Unit containers + * store a value, and support various arithmetic operations. + */ + + /** + * @defgroup UnitTypes Unit Types + * @brief Defines a series of classes which represent units. These types are tags used by + * the conversion function, to create compound units, or to create `unit_t` types. + * By themselves, they are not containers and have no stored value. + */ + + /** + * @defgroup UnitManipulators Unit Manipulators + * @brief Defines a series of classes used to manipulate unit types, such as `inverse<>`, `squared<>`, and metric prefixes. + * Unit manipulators can be chained together, e.g. `inverse>>` to + * represent picoseconds^-2. + */ + + /** + * @defgroup CompileTimeUnitManipulators Compile-time Unit Manipulators + * @brief Defines a series of classes used to manipulate `unit_value_t` types at compile-time, such as `unit_value_add<>`, `unit_value_sqrt<>`, etc. + * Compile-time manipulators can be chained together, e.g. `unit_value_sqrt, unit_value_power>>` to + * represent `c = sqrt(a^2 + b^2). + */ + + /** + * @defgroup UnitMath Unit Math + * @brief Defines a collection of unit-enabled, strongly-typed versions of `` functions. + * @details Includes most c++11 extensions. + */ + + /** + * @defgroup Conversion Explicit Conversion + * @brief Functions used to convert values of one logical type to another. + */ + + /** + * @defgroup TypeTraits Type Traits + * @brief Defines a series of classes to obtain unit type information at compile-time. + */ + + //------------------------------ + // FORWARD DECLARATIONS + //------------------------------ + + /** @cond */ // DOXYGEN IGNORE + namespace constants + { + namespace detail + { + static constexpr const UNIT_LIB_DEFAULT_TYPE PI_VAL = 3.14159265358979323846264338327950288419716939937510; + } + } + /** @endcond */ // END DOXYGEN IGNORE + + //------------------------------ + // RATIO TRAITS + //------------------------------ + + /** + * @ingroup TypeTraits + * @{ + */ + + /** @cond */ // DOXYGEN IGNORE + namespace detail + { + /// has_num implementation. + template + struct has_num_impl + { + template + static constexpr auto test(U*)->std::is_integral {return std::is_integral{}; } + template + static constexpr std::false_type test(...) { return std::false_type{}; } + + using type = decltype(test(0)); + }; + } + + /** + * @brief Trait which checks for the existence of a static numerator. + * @details Inherits from `std::true_type` or `std::false_type`. Use `has_num::value` to test + * whether `class T` has a numerator static member. + */ + template + struct has_num : units::detail::has_num_impl::type {}; + + namespace detail + { + /// has_den implementation. + template + struct has_den_impl + { + template + static constexpr auto test(U*)->std::is_integral { return std::is_integral{}; } + template + static constexpr std::false_type test(...) { return std::false_type{}; } + + using type = decltype(test(0)); + }; + } + + /** + * @brief Trait which checks for the existence of a static denominator. + * @details Inherits from `std::true_type` or `std::false_type`. Use `has_den::value` to test + * whether `class T` has a denominator static member. + */ + template + struct has_den : units::detail::has_den_impl::type {}; + + /** @endcond */ // END DOXYGEN IGNORE + + namespace traits + { + /** + * @brief Trait that tests whether a type represents a std::ratio. + * @details Inherits from `std::true_type` or `std::false_type`. Use `is_ratio::value` to test + * whether `class T` implements a std::ratio. + */ + template + struct is_ratio : std::integral_constant::value && + has_den::value> + {}; + } + + //------------------------------ + // UNIT TRAITS + //------------------------------ + + /** @cond */ // DOXYGEN IGNORE + /** + * @brief void type. + * @details Helper class for creating type traits. + */ + template + struct void_t { typedef void type; }; + + /** + * @brief parameter pack for boolean arguments. + */ + template struct bool_pack {}; + + /** + * @brief Trait which tests that a set of other traits are all true. + */ + template + struct all_true : std::is_same, units::bool_pack> {}; + /** @endcond */ // DOXYGEN IGNORE + + /** + * @brief namespace representing type traits which can access the properties of types provided by the units library. + */ + namespace traits + { +#ifdef FOR_DOXYGEN_PURPOSES_ONLY + /** + * @ingroup TypeTraits + * @brief Traits class defining the properties of units. + * @details The units library determines certain properties of the units passed to + * them and what they represent by using the members of the corresponding + * unit_traits instantiation. + */ + template + struct unit_traits + { + typedef typename T::base_unit_type base_unit_type; ///< Unit type that the unit was derived from. May be a `base_unit` or another `unit`. Use the `base_unit_of` trait to find the SI base unit type. This will be `void` if type `T` is not a unit. + typedef typename T::conversion_ratio conversion_ratio; ///< `std::ratio` representing the conversion factor to the `base_unit_type`. This will be `void` if type `T` is not a unit. + typedef typename T::pi_exponent_ratio pi_exponent_ratio; ///< `std::ratio` representing the exponent of pi to be used in the conversion. This will be `void` if type `T` is not a unit. + typedef typename T::translation_ratio translation_ratio; ///< `std::ratio` representing a datum translation to the base unit (i.e. degrees C to degrees F conversion). This will be `void` if type `T` is not a unit. + }; +#endif + /** @cond */ // DOXYGEN IGNORE + /** + * @brief unit traits implementation for classes which are not units. + */ + template + struct unit_traits + { + typedef void base_unit_type; + typedef void conversion_ratio; + typedef void pi_exponent_ratio; + typedef void translation_ratio; + }; + + template + struct unit_traits + ::type> + { + typedef typename T::base_unit_type base_unit_type; ///< Unit type that the unit was derived from. May be a `base_unit` or another `unit`. Use the `base_unit_of` trait to find the SI base unit type. This will be `void` if type `T` is not a unit. + typedef typename T::conversion_ratio conversion_ratio; ///< `std::ratio` representing the conversion factor to the `base_unit_type`. This will be `void` if type `T` is not a unit. + typedef typename T::pi_exponent_ratio pi_exponent_ratio; ///< `std::ratio` representing the exponent of pi to be used in the conversion. This will be `void` if type `T` is not a unit. + typedef typename T::translation_ratio translation_ratio; ///< `std::ratio` representing a datum translation to the base unit (i.e. degrees C to degrees F conversion). This will be `void` if type `T` is not a unit. + }; + /** @endcond */ // END DOXYGEN IGNORE + } + + /** @cond */ // DOXYGEN IGNORE + namespace detail + { + /** + * @brief helper type to identify base units. + * @details A non-templated base class for `base_unit` which enables RTTI testing. + */ + struct _base_unit_t {}; + } + /** @endcond */ // END DOXYGEN IGNORE + + namespace traits + { + /** + * @ingroup TypeTraits + * @brief Trait which tests if a class is a `base_unit` type. + * @details Inherits from `std::true_type` or `std::false_type`. Use `is_base_unit::value` to test + * whether `class T` implements a `base_unit`. + */ + template + struct is_base_unit : std::is_base_of {}; + } + + /** @cond */ // DOXYGEN IGNORE + namespace detail + { + /** + * @brief helper type to identify units. + * @details A non-templated base class for `unit` which enables RTTI testing. + */ + struct _unit {}; + + template + using meter_ratio = std::ratio; + } + /** @endcond */ // END DOXYGEN IGNORE + + namespace traits + { + /** + * @ingroup TypeTraits + * @brief Traits which tests if a class is a `unit` + * @details Inherits from `std::true_type` or `std::false_type`. Use `is_unit::value` to test + * whether `class T` implements a `unit`. + */ + template + struct is_unit : std::is_base_of::type {}; + } + + /** @} */ // end of TypeTraits + + //------------------------------ + // BASE UNIT CLASS + //------------------------------ + + /** + * @ingroup UnitTypes + * @brief Class representing SI base unit types. + * @details Base units are represented by a combination of `std::ratio` template parameters, each + * describing the exponent of the type of unit they represent. Example: meters per second + * would be described by a +1 exponent for meters, and a -1 exponent for seconds, thus: + * `base_unit, std::ratio<0>, std::ratio<-1>>` + * @tparam Meter `std::ratio` representing the exponent value for meters. + * @tparam Kilogram `std::ratio` representing the exponent value for kilograms. + * @tparam Second `std::ratio` representing the exponent value for seconds. + * @tparam Radian `std::ratio` representing the exponent value for radians. Although radians are not SI base units, they are included because radians are described by the SI as m * m^-1, which would make them indistinguishable from scalars. + * @tparam Ampere `std::ratio` representing the exponent value for amperes. + * @tparam Kelvin `std::ratio` representing the exponent value for Kelvin. + * @tparam Mole `std::ratio` representing the exponent value for moles. + * @tparam Candela `std::ratio` representing the exponent value for candelas. + * @tparam Byte `std::ratio` representing the exponent value for bytes. + * @sa category for type aliases for SI base_unit types. + */ + template, + class Kilogram = std::ratio<0>, + class Second = std::ratio<0>, + class Radian = std::ratio<0>, + class Ampere = std::ratio<0>, + class Kelvin = std::ratio<0>, + class Mole = std::ratio<0>, + class Candela = std::ratio<0>, + class Byte = std::ratio<0>> + struct base_unit : units::detail::_base_unit_t + { + static_assert(traits::is_ratio::value, "Template parameter `Meter` must be a `std::ratio` representing the exponent of meters the unit has"); + static_assert(traits::is_ratio::value, "Template parameter `Kilogram` must be a `std::ratio` representing the exponent of kilograms the unit has"); + static_assert(traits::is_ratio::value, "Template parameter `Second` must be a `std::ratio` representing the exponent of seconds the unit has"); + static_assert(traits::is_ratio::value, "Template parameter `Ampere` must be a `std::ratio` representing the exponent of amperes the unit has"); + static_assert(traits::is_ratio::value, "Template parameter `Kelvin` must be a `std::ratio` representing the exponent of kelvin the unit has"); + static_assert(traits::is_ratio::value, "Template parameter `Candela` must be a `std::ratio` representing the exponent of candelas the unit has"); + static_assert(traits::is_ratio::value, "Template parameter `Mole` must be a `std::ratio` representing the exponent of moles the unit has"); + static_assert(traits::is_ratio::value, "Template parameter `Radian` must be a `std::ratio` representing the exponent of radians the unit has"); + static_assert(traits::is_ratio::value, "Template parameter `Byte` must be a `std::ratio` representing the exponent of bytes the unit has"); + + typedef Meter meter_ratio; + typedef Kilogram kilogram_ratio; + typedef Second second_ratio; + typedef Radian radian_ratio; + typedef Ampere ampere_ratio; + typedef Kelvin kelvin_ratio; + typedef Mole mole_ratio; + typedef Candela candela_ratio; + typedef Byte byte_ratio; + }; + + //------------------------------ + // UNIT CATEGORIES + //------------------------------ + + /** + * @brief namespace representing the implemented base and derived unit types. These will not generally be needed by library users. + * @sa base_unit for the definition of the category parameters. + */ + namespace category + { + // SCALAR (DIMENSIONLESS) TYPES + typedef base_unit<> scalar_unit; ///< Represents a quantity with no dimension. + typedef base_unit<> dimensionless_unit; ///< Represents a quantity with no dimension. + + // SI BASE UNIT TYPES + // METERS KILOGRAMS SECONDS RADIANS AMPERES KELVIN MOLE CANDELA BYTE --- CATEGORY + typedef base_unit> length_unit; ///< Represents an SI base unit of length + typedef base_unit, std::ratio<1>> mass_unit; ///< Represents an SI base unit of mass + typedef base_unit, std::ratio<0>, std::ratio<1>> time_unit; ///< Represents an SI base unit of time + typedef base_unit, std::ratio<0>, std::ratio<0>, std::ratio<1>> angle_unit; ///< Represents an SI base unit of angle + typedef base_unit, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<1>> current_unit; ///< Represents an SI base unit of current + typedef base_unit, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<1>> temperature_unit; ///< Represents an SI base unit of temperature + typedef base_unit, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<1>> substance_unit; ///< Represents an SI base unit of amount of substance + typedef base_unit, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<1>> luminous_intensity_unit; ///< Represents an SI base unit of luminous intensity + + // SI DERIVED UNIT TYPES + // METERS KILOGRAMS SECONDS RADIANS AMPERES KELVIN MOLE CANDELA BYTE --- CATEGORY + typedef base_unit, std::ratio<0>, std::ratio<0>, std::ratio<2>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>> solid_angle_unit; ///< Represents an SI derived unit of solid angle + typedef base_unit, std::ratio<0>, std::ratio<-1>> frequency_unit; ///< Represents an SI derived unit of frequency + typedef base_unit, std::ratio<0>, std::ratio<-1>> velocity_unit; ///< Represents an SI derived unit of velocity + typedef base_unit, std::ratio<0>, std::ratio<-1>, std::ratio<1>> angular_velocity_unit; ///< Represents an SI derived unit of angular velocity + typedef base_unit, std::ratio<0>, std::ratio<-2>> acceleration_unit; ///< Represents an SI derived unit of acceleration + typedef base_unit, std::ratio<1>, std::ratio<-2>> force_unit; ///< Represents an SI derived unit of force + typedef base_unit, std::ratio<1>, std::ratio<-2>> pressure_unit; ///< Represents an SI derived unit of pressure + typedef base_unit, std::ratio<0>, std::ratio<1>, std::ratio<0>, std::ratio<1>> charge_unit; ///< Represents an SI derived unit of charge + typedef base_unit, std::ratio<1>, std::ratio<-2>> energy_unit; ///< Represents an SI derived unit of energy + typedef base_unit, std::ratio<1>, std::ratio<-3>> power_unit; ///< Represents an SI derived unit of power + typedef base_unit, std::ratio<1>, std::ratio<-3>, std::ratio<0>, std::ratio<-1>> voltage_unit; ///< Represents an SI derived unit of voltage + typedef base_unit, std::ratio<-1>, std::ratio<4>, std::ratio<0>, std::ratio<2>> capacitance_unit; ///< Represents an SI derived unit of capacitance + typedef base_unit, std::ratio<1>, std::ratio<-3>, std::ratio<0>, std::ratio<-2>> impedance_unit; ///< Represents an SI derived unit of impedance + typedef base_unit, std::ratio<-1>, std::ratio<3>, std::ratio<0>, std::ratio<2>> conductance_unit; ///< Represents an SI derived unit of conductance + typedef base_unit, std::ratio<1>, std::ratio<-2>, std::ratio<0>, std::ratio<-1>> magnetic_flux_unit; ///< Represents an SI derived unit of magnetic flux + typedef base_unit, std::ratio<1>, std::ratio<-2>, std::ratio<0>, std::ratio<-1>> magnetic_field_strength_unit; ///< Represents an SI derived unit of magnetic field strength + typedef base_unit, std::ratio<1>, std::ratio<-2>, std::ratio<0>, std::ratio<-2>> inductance_unit; ///< Represents an SI derived unit of inductance + typedef base_unit, std::ratio<0>, std::ratio<0>, std::ratio<2>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<1>> luminous_flux_unit; ///< Represents an SI derived unit of luminous flux + typedef base_unit, std::ratio<0>, std::ratio<0>, std::ratio<2>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<1>> illuminance_unit; ///< Represents an SI derived unit of illuminance + typedef base_unit, std::ratio<0>, std::ratio<-1>> radioactivity_unit; ///< Represents an SI derived unit of radioactivity + + // OTHER UNIT TYPES + // METERS KILOGRAMS SECONDS RADIANS AMPERES KELVIN MOLE CANDELA BYTE --- CATEGORY + typedef base_unit, std::ratio<1>, std::ratio<-2>> torque_unit; ///< Represents an SI derived unit of torque + typedef base_unit> area_unit; ///< Represents an SI derived unit of area + typedef base_unit> volume_unit; ///< Represents an SI derived unit of volume + typedef base_unit, std::ratio<1>> density_unit; ///< Represents an SI derived unit of density + typedef base_unit<> concentration_unit; ///< Represents a unit of concentration + typedef base_unit, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<1>> data_unit; ///< Represents a unit of data size + typedef base_unit, std::ratio<0>, std::ratio<-1>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<1>> data_transfer_rate_unit; ///< Represents a unit of data transfer rate + } + + //------------------------------ + // UNIT CLASSES + //------------------------------ + + /** @cond */ // DOXYGEN IGNORE + /** + * @brief unit type template specialization for units derived from base units. + */ + template struct unit; + template + struct unit, PiExponent, Translation> : units::detail::_unit + { + static_assert(traits::is_ratio::value, "Template parameter `Conversion` must be a `std::ratio` representing the conversion factor to `BaseUnit`."); + static_assert(traits::is_ratio::value, "Template parameter `PiExponent` must be a `std::ratio` representing the exponents of Pi the unit has."); + static_assert(traits::is_ratio::value, "Template parameter `Translation` must be a `std::ratio` representing an additive translation required by the unit conversion."); + + typedef typename units::base_unit base_unit_type; + typedef Conversion conversion_ratio; + typedef Translation translation_ratio; + typedef PiExponent pi_exponent_ratio; + }; + /** @endcond */ // END DOXYGEN IGNORE + + /** + * @brief Type representing an arbitrary unit. + * @ingroup UnitTypes + * @details `unit` types are used as tags for the `conversion` function. They are *not* containers + * (see `unit_t` for a container class). Each unit is defined by: + * + * - A `std::ratio` defining the conversion factor to the base unit type. (e.g. `std::ratio<1,12>` for inches to feet) + * - A base unit that the unit is derived from (or a unit category. Must be of type `unit` or `base_unit`) + * - An exponent representing factors of PI required by the conversion. (e.g. `std::ratio<-1>` for a radians to degrees conversion) + * - a ratio representing a datum translation required for the conversion (e.g. `std::ratio<32>` for a farenheit to celsius conversion) + * + * Typically, a specific unit, like `meters`, would be implemented as a type alias + * of `unit`, i.e. `using meters = unit, units::category::length_unit`, or + * `using inches = unit, feet>`. + * @tparam Conversion std::ratio representing scalar multiplication factor. + * @tparam BaseUnit Unit type which this unit is derived from. May be a `base_unit`, or another `unit`. + * @tparam PiExponent std::ratio representing the exponent of pi required by the conversion. + * @tparam Translation std::ratio representing any datum translation required by the conversion. + */ + template, class Translation = std::ratio<0>> + struct unit : units::detail::_unit + { + static_assert(traits::is_unit::value, "Template parameter `BaseUnit` must be a `unit` type."); + static_assert(traits::is_ratio::value, "Template parameter `Conversion` must be a `std::ratio` representing the conversion factor to `BaseUnit`."); + static_assert(traits::is_ratio::value, "Template parameter `PiExponent` must be a `std::ratio` representing the exponents of Pi the unit has."); + + typedef typename units::traits::unit_traits::base_unit_type base_unit_type; + typedef typename std::ratio_multiply conversion_ratio; + typedef typename std::ratio_add pi_exponent_ratio; + typedef typename std::ratio_add, typename BaseUnit::translation_ratio> translation_ratio; + }; + + //------------------------------ + // BASE UNIT MANIPULATORS + //------------------------------ + + /** @cond */ // DOXYGEN IGNORE + namespace detail + { + /** + * @brief base_unit_of trait implementation + * @details recursively seeks base_unit type that a unit is derived from. Since units can be + * derived from other units, the `base_unit_type` typedef may not represent this value. + */ + template struct base_unit_of_impl; + template + struct base_unit_of_impl> : base_unit_of_impl {}; + template + struct base_unit_of_impl> + { + typedef base_unit type; + }; + template<> + struct base_unit_of_impl + { + typedef void type; + }; + } + /** @endcond */ // END DOXYGEN IGNORE + + namespace traits + { + /** + * @brief Trait which returns the `base_unit` type that a unit is originally derived from. + * @details Since units can be derived from other `unit` types in addition to `base_unit` types, + * the `base_unit_type` typedef will not always be a `base_unit` (or unit category). + * Since compatible + */ + template + using base_unit_of = typename units::detail::base_unit_of_impl::type; + } + + /** @cond */ // DOXYGEN IGNORE + namespace detail + { + /** + * @brief implementation of base_unit_multiply + * @details 'multiples' (adds exponent ratios of) two base unit types. Base units can be found + * using `base_unit_of`. + */ + template struct base_unit_multiply_impl; + template + struct base_unit_multiply_impl, base_unit> { + using type = base_unit...>; + }; + + /** + * @brief represents type of two base units multiplied together + */ + template + using base_unit_multiply = typename base_unit_multiply_impl::type; + + /** + * @brief implementation of base_unit_divide + * @details 'dived' (subtracts exponent ratios of) two base unit types. Base units can be found + * using `base_unit_of`. + */ + template struct base_unit_divide_impl; + template + struct base_unit_divide_impl, base_unit> { + using type = base_unit...>; + }; + + /** + * @brief represents the resulting type of `base_unit` U1 divided by U2. + */ + template + using base_unit_divide = typename base_unit_divide_impl::type; + + /** + * @brief implementation of inverse_base + * @details multiplies all `base_unit` exponent ratios by -1. The resulting type represents + * the inverse base unit of the given `base_unit` type. + */ + template struct inverse_base_impl; + + template + struct inverse_base_impl> { + using type = base_unit>...>; + }; + + /** + * @brief represent the inverse type of `class U` + * @details E.g. if `U` is `length_unit`, then `inverse` will represent `length_unit^-1`. + */ + template using inverse_base = typename inverse_base_impl::type; + + /** + * @brief implementation of `squared_base` + * @details multiplies all the exponent ratios of the given class by 2. The resulting type is + * equivalent to the given type squared. + */ + template struct squared_base_impl; + template + struct squared_base_impl> { + using type = base_unit>...>; + }; + + /** + * @brief represents the type of a `base_unit` squared. + * @details E.g. `squared` will represent `length_unit^2`. + */ + template using squared_base = typename squared_base_impl::type; + + /** + * @brief implementation of `cubed_base` + * @details multiplies all the exponent ratios of the given class by 3. The resulting type is + * equivalent to the given type cubed. + */ + template struct cubed_base_impl; + template + struct cubed_base_impl> { + using type = base_unit>...>; + }; + + /** + * @brief represents the type of a `base_unit` cubed. + * @details E.g. `cubed` will represent `length_unit^3`. + */ + template using cubed_base = typename cubed_base_impl::type; + + /** + * @brief implementation of `sqrt_base` + * @details divides all the exponent ratios of the given class by 2. The resulting type is + * equivalent to the square root of the given type. + */ + template struct sqrt_base_impl; + template + struct sqrt_base_impl> { + using type = base_unit>...>; + }; + + /** + * @brief represents the square-root type of a `base_unit`. + * @details E.g. `sqrt` will represent `length_unit^(1/2)`. + */ + template using sqrt_base = typename sqrt_base_impl::type; + + /** + * @brief implementation of `cbrt_base` + * @details divides all the exponent ratios of the given class by 3. The resulting type is + * equivalent to the given type's cube-root. + */ + template struct cbrt_base_impl; + template + struct cbrt_base_impl> { + using type = base_unit>...>; + }; + + /** + * @brief represents the cube-root type of a `base_unit` . + * @details E.g. `cbrt` will represent `length_unit^(1/3)`. + */ + template using cbrt_base = typename cbrt_base_impl::type; + } + /** @endcond */ // END DOXYGEN IGNORE + + //------------------------------ + // UNIT MANIPULATORS + //------------------------------ + + /** @cond */ // DOXYGEN IGNORE + namespace detail + { + /** + * @brief implementation of `unit_multiply`. + * @details multiplies two units. The base unit becomes the base units of each with their exponents + * added together. The conversion factors of each are multiplied by each other. Pi exponent ratios + * are added, and datum translations are removed. + */ + template + struct unit_multiply_impl + { + using type = unit < std::ratio_multiply, + base_unit_multiply , traits::base_unit_of>, + std::ratio_add, + std::ratio < 0 >> ; + }; + + /** + * @brief represents the type of two units multiplied together. + * @details recalculates conversion and exponent ratios at compile-time. + */ + template + using unit_multiply = typename unit_multiply_impl::type; + + /** + * @brief implementation of `unit_divide`. + * @details divides two units. The base unit becomes the base units of each with their exponents + * subtracted from each other. The conversion factors of each are divided by each other. Pi exponent ratios + * are subtracted, and datum translations are removed. + */ + template + struct unit_divide_impl + { + using type = unit < std::ratio_divide, + base_unit_divide, traits::base_unit_of>, + std::ratio_subtract, + std::ratio < 0 >> ; + }; + + /** + * @brief represents the type of two units divided by each other. + * @details recalculates conversion and exponent ratios at compile-time. + */ + template + using unit_divide = typename unit_divide_impl::type; + + /** + * @brief implementation of `inverse` + * @details inverts a unit (equivalent to 1/unit). The `base_unit` and pi exponents are all multiplied by + * -1. The conversion ratio numerator and denominator are swapped. Datum translation + * ratios are removed. + */ + template + struct inverse_impl + { + using type = unit < std::ratio, + inverse_base::base_unit_type>>, + std::ratio_multiply::pi_exponent_ratio, std::ratio<-1>>, + std::ratio < 0 >> ; // inverses are rates or change, the translation factor goes away. + }; + } + /** @endcond */ // END DOXYGEN IGNORE + + /** + * @brief represents the inverse unit type of `class U`. + * @ingroup UnitManipulators + * @tparam U `unit` type to invert. + * @details E.g. `inverse` will represent meters^-1 (i.e. 1/meters). + */ + template using inverse = typename units::detail::inverse_impl::type; + + /** @cond */ // DOXYGEN IGNORE + namespace detail + { + /** + * @brief implementation of `squared` + * @details Squares the conversion ratio, `base_unit` exponents, pi exponents, and removes + * datum translation ratios. + */ + template + struct squared_impl + { + static_assert(traits::is_unit::value, "Template parameter `Unit` must be a `unit` type."); + using Conversion = typename Unit::conversion_ratio; + using type = unit < std::ratio_multiply, + squared_base>, + std::ratio_multiply>, + typename Unit::translation_ratio + > ; + }; + } + /** @endcond */ // END DOXYGEN IGNORE + + /** + * @brief represents the unit type of `class U` squared + * @ingroup UnitManipulators + * @tparam U `unit` type to square. + * @details E.g. `square` will represent meters^2. + */ + template + using squared = typename units::detail::squared_impl::type; + + /** @cond */ // DOXYGEN IGNORE + namespace detail + { + /** + * @brief implementation of `cubed` + * @details Cubes the conversion ratio, `base_unit` exponents, pi exponents, and removes + * datum translation ratios. + */ + template + struct cubed_impl + { + static_assert(traits::is_unit::value, "Template parameter `Unit` must be a `unit` type."); + using Conversion = typename Unit::conversion_ratio; + using type = unit < std::ratio_multiply>, + cubed_base>, + std::ratio_multiply>, + typename Unit::translation_ratio> ; + }; + } + /** @endcond */ // END DOXYGEN IGNORE + + /** + * @brief represents the type of `class U` cubed. + * @ingroup UnitManipulators + * @tparam U `unit` type to cube. + * @details E.g. `cubed` will represent meters^3. + */ + template + using cubed = typename units::detail::cubed_impl::type; + + /** @cond */ // DOXYGEN IGNORE + namespace detail + { + //---------------------------------- + // RATIO_SQRT IMPLEMENTATION + //---------------------------------- + + using Zero = std::ratio<0>; + using One = std::ratio<1>; + template using Square = std::ratio_multiply; + + // Find the largest std::integer N such that Predicate::value is true. + template