HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
au.hh
1// Copyright 2024 Aurora Operations, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#pragma once
16
17#include <algorithm>
18#include <chrono>
19#include <cmath>
20#include <cstdint>
21#include <limits>
22#include <ostream>
23#include <ratio>
24#include <type_traits>
25#include <utility>
26
27// Version identifier: ffea274
28// <iostream> support: INCLUDED
29// List of included units:
30// feet
31// inches
32// meters
33// miles
34
35
36namespace au {
37
38// A type representing a quantity of "zero" in any units.
39//
40// Zero is special: it's the only number that we can meaningfully compare or assign to a Quantity of
41// _any_ dimension. Giving it a special type (and a predefined constant of that type, `ZERO`,
42// defined below) lets our code be both concise and readable.
43//
44// For example, we can zero-initialize any arbitrary Quantity, even if it doesn't have a
45// user-defined literal, and even if it's in a header file so we couldn't use the literals anyway:
46//
47// struct PathPoint {
48// QuantityD<RadiansPerMeter> curvature = ZERO;
49// };
50struct Zero {
51 // Implicit conversion to arithmetic types.
52 template <typename T, typename Enable = std::enable_if_t<std::is_arithmetic<T>::value>>
53 constexpr operator T() const {
54 return 0;
55 }
56
57 // Implicit conversion to chrono durations.
58 template <typename Rep, typename Period>
59 constexpr operator std::chrono::duration<Rep, Period>() const {
61 }
62};
63
64// A value of Zero.
65//
66// This exists purely for convenience, so people don't have to call the initializer. i.e., it lets
67// us write `ZERO` instead of `Zero{}`.
68static constexpr auto ZERO = Zero{};
69
70// Addition, subtraction, and comparison of Zero are well defined.
71inline constexpr Zero operator+(Zero, Zero) { return ZERO; }
72inline constexpr Zero operator-(Zero, Zero) { return ZERO; }
73inline constexpr bool operator==(Zero, Zero) { return true; }
74inline constexpr bool operator>=(Zero, Zero) { return true; }
75inline constexpr bool operator<=(Zero, Zero) { return true; }
76inline constexpr bool operator!=(Zero, Zero) { return false; }
77inline constexpr bool operator>(Zero, Zero) { return false; }
78inline constexpr bool operator<(Zero, Zero) { return false; }
79
80} // namespace au
81
82
83namespace au {
84namespace stdx {
85
86// Source: adapted from (https://en.cppreference.com/w/cpp/types/type_identity).
87template <class T>
89 using type = T;
90};
91
92// Source: adapted from (https://en.cppreference.com/w/cpp/types/integral_constant).
93template <bool B>
95
96// Source: adapted from (https://en.cppreference.com/w/cpp/types/conjunction).
97template <class...>
99template <class B1>
100struct conjunction<B1> : B1 {};
101template <class B1, class... Bn>
102struct conjunction<B1, Bn...> : std::conditional_t<bool(B1::value), conjunction<Bn...>, B1> {};
103
104// Source: adapted from (https://en.cppreference.com/w/cpp/types/disjunction).
105template <class...>
107template <class B1>
108struct disjunction<B1> : B1 {};
109template <class B1, class... Bn>
110struct disjunction<B1, Bn...> : std::conditional_t<bool(B1::value), B1, disjunction<Bn...>> {};
111
112// Source: adapted from (https://en.cppreference.com/w/cpp/types/negation).
113template <class B>
114struct negation : stdx::bool_constant<!static_cast<bool>(B::value)> {};
115
116// Source: adapted from (https://en.cppreference.com/w/cpp/types/remove_cvref).
117template <class T>
119 typedef std::remove_cv_t<std::remove_reference_t<T>> type;
120};
121template <class T>
122using remove_cvref_t = typename remove_cvref<T>::type;
123
124// Source: adapted from (https://en.cppreference.com/w/cpp/types/void_t).
125template <class...>
126using void_t = void;
127
128} // namespace stdx
129} // namespace au
130
131
132namespace au {
133namespace detail {
134
135//
136// A constexpr-compatible string constant class of a given size.
137//
138// The point is to make it easy to build up compile-time strings by using "join" and "concatenate"
139// operations.
140//
141// Beware that this is not one type, but a family of types, one for each length! If you're in a
142// context where you can't use `auto` (say, because you're making a member variable), you'll need to
143// know the length in order to name the type.
144//
145template <std::size_t Strlen>
146class StringConstant;
147
148//
149// `as_string_constant()`: Create StringConstant<N>, of correct length, corresponding to the input.
150//
151// Possible inputs include char-arrays, or `StringConstant` (for which this is the identity).
152//
153template <std::size_t N>
154constexpr StringConstant<N - 1> as_string_constant(const char (&c_string)[N]) {
155 return {c_string};
156}
157template <std::size_t N>
158constexpr StringConstant<N> as_string_constant(const StringConstant<N> &x) {
159 return x;
160}
161
162//
163// Create StringConstant which concatenates all arguments.
164//
165// Each argument will be treated as a StringConstant. The final length will automatically be
166// computed from the lengths of the inputs.
167//
168template <typename... Ts>
169constexpr auto concatenate(const Ts &...ts);
170
171//
172// Join arbitrarily many arguments into a new StringConstant, using the first argument as separator.
173//
174// Each argument will be treated as a StringConstant. The final length will automatically be
175// computed from the lengths of the inputs.
176//
177// As usual for the join algorithm, the separator will not appear in the output unless there are at
178// least two arguments (apart from the separator) being joined.
179//
180template <typename SepT, typename... StringTs>
181constexpr auto join_by(const SepT &sep, const StringTs &...ts);
182
183//
184// Wrap a StringConstant in parentheses () if the supplied template param is true.
185//
186template <bool Enable, typename StringT>
187constexpr auto parens_if(const StringT &s);
188
189//
190// A constexpr-compatible utility to generate compile-time string representations of integers.
191//
192// `IToA<N>::value` is a `StringConstant<Len>` of whatever appropriate length `Len` is needed to
193// represent `N`. This includes handling the negative sign (if any).
194//
195template <int64_t N>
196struct IToA;
197
199// Implementation details below.
201
202// The string-length needed to hold a representation of this integer.
203constexpr std::size_t string_size(int64_t x) {
204 if (x < 0) {
205 return string_size(-x) + 1;
206 }
207
208 std::size_t digits = 1;
209 while (x > 9) {
210 x /= 10;
211 ++digits;
212 }
213 return digits;
214}
215
216// The sum of the template parameters.
217template <std::size_t... Ns>
218constexpr std::size_t sum() {
219 std::size_t result{0};
220 std::size_t values[] = {0u, Ns...}; // Dummy `0u` avoids empty array.
221 for (std::size_t i = 0; i < sizeof...(Ns); ++i) {
222 result += values[i + 1u]; // "+ 1u" to skip the dummy value.
223 }
224 return result;
225}
226
227template <std::size_t Strlen>
229 public:
230 constexpr StringConstant(const char (&c_string)[Strlen + 1])
231 : StringConstant{c_string, std::make_index_sequence<Strlen>{}} {}
232
233 static constexpr std::size_t length = Strlen;
234
235 // Get a C-string representation of this constant.
236 //
237 // (Note that the constructors have guaranteed a correct placement of the '\0'.)
238 constexpr const char *c_str() const { return data_array_; }
239 constexpr operator const char *() const { return c_str(); }
240
241 // Get a (sizeof()-compatible) char array reference to the data.
242 constexpr auto char_array() const -> const char (&)[Strlen + 1] { return data_array_; }
243
244 // The string-length of this string (i.e., NOT including the null terminator).
245 constexpr std::size_t size() const { return Strlen; }
246
247 // Whether this string is empty (i.e., has size zero).
248 constexpr bool empty() const { return size() == 0u; }
249
250 // Join multiple `StringConstant<Ns>` inputs, using `*this` as the separator.
251 template <std::size_t... Ns>
252 constexpr auto join(const StringConstant<Ns> &...items) const {
253 constexpr std::size_t N =
254 sum<Ns...>() + Strlen * (sizeof...(items) > 0 ? (sizeof...(items) - 1) : 0);
255 char result[N + 1]{'\0'};
256 join_impl(result, items...);
257 return StringConstant<N>{result};
258 }
259
260 private:
261 // This would be unsafe if called with arbitrary pointers and/or integer sequences. However,
262 // this is a private constructor of this class, called only by its public constructor(s), and we
263 // know they satisfy the conditions needed to call this function safely.
264 template <std::size_t... Is>
265 constexpr StringConstant(const char *data, std::index_sequence<Is...>)
266 : data_array_{data[Is]..., '\0'} {
267 (void)data; // Suppress unused-var error when `Is` is empty in platform-independent way.
268 }
269
270 // Base case for the join algorithm.
271 constexpr void join_impl(char *) const {}
272
273 // Recursive case for the join algorithm.
274 template <std::size_t N, std::size_t... Ns>
275 constexpr void join_impl(char *out_iter,
276 const StringConstant<N> &head,
277 const StringConstant<Ns> &...tail) const {
278 // Definitely copy data from the head element.
279 //
280 // The `static_cast<int>` incantation mollifies certain "helpful" compilers, which notice
281 // that the comparison is always false when `Strlen` is `0`, and disregard best practices
282 // for generic programming by failing the build for this.
283 for (std::size_t i = 0; static_cast<int>(i) < static_cast<int>(N); ++i) {
284 *out_iter++ = head.c_str()[i];
285 }
286
287 // If there are tail elements, copy out the separator, and recurse.
288 if (sizeof...(tail) > 0) {
289 // The `static_cast<int>` incantation mollifies certain "helpful" compilers, which
290 // notice that the comparison is always false when `Strlen` is `0`, and disregard best
291 // practices for generic programming by failing the build for this.
292 for (std::size_t i = 0; static_cast<int>(i) < static_cast<int>(Strlen); ++i) {
293 *out_iter++ = data_array_[i];
294 }
295 join_impl(out_iter, tail...);
296 }
297 }
298
299 // Data storage for the string constant.
300 const char data_array_[Strlen + 1];
301};
302
303template <std::size_t Strlen>
305
306template <typename... Ts>
307constexpr auto concatenate(const Ts &...ts) {
308 return join_by("", ts...);
309}
310
311template <typename SepT, typename... StringTs>
312constexpr auto join_by(const SepT &sep, const StringTs &...ts) {
313 return as_string_constant(sep).join(as_string_constant(ts)...);
314}
315
316template <int64_t N>
317struct IToA {
318 private:
319 static constexpr auto print_to_array() {
320 char data[length + 1] = {'\0'};
321
322 int num = N;
323 if (num < 0) {
324 data[0] = '-';
325 num = -num;
326 }
327
328 std::size_t i = length - 1;
329 do {
330 data[i--] = '0' + static_cast<char>(num % 10);
331 num /= 10;
332 } while (num > 0);
333
334 return StringConstant<length>{data};
335 }
336
337 public:
338 static constexpr std::size_t length = string_size(N);
339
340 static constexpr StringConstant<length> value = print_to_array();
341};
342
343// Definitions for IToA<N>::value. (Needed to prevent linker errors.)
344template <int64_t N>
346template <int64_t N>
348
349template <bool Enable>
350struct ParensIf;
351
352template <>
353struct ParensIf<true> {
354 static constexpr StringConstant<1> open() { return as_string_constant("("); }
355 static constexpr StringConstant<1> close() { return as_string_constant(")"); }
356};
357
358template <>
359struct ParensIf<false> {
360 static constexpr StringConstant<0> open() { return as_string_constant(""); }
361 static constexpr StringConstant<0> close() { return as_string_constant(""); }
362};
363
364template <bool Enable, typename StringT>
365constexpr auto parens_if(const StringT &s) {
366 return concatenate(ParensIf<Enable>::open(), s, ParensIf<Enable>::close());
367}
368
369} // namespace detail
370} // namespace au
371
372
373namespace au {
374
376// Generic mathematical convenience functions.
377//
378// The reason these exist is to be able to make unit expressions easier to read in common cases.
379// They also work for dimensions and magnitudes.
380
381//
382// This section works around an error:
383//
384// warning: use of function template name with no prior declaration in function call with
385// explicit template arguments is a C++20 extension [-Wc++20-extensions]
386//
387// We work around it by providing declarations, even though those declarations are never used.
388//
389namespace no_prior_declaration_workaround {
390struct Dummy;
391} // namespace no_prior_declaration_workaround
392template <std::intmax_t N>
393auto root(no_prior_declaration_workaround::Dummy);
394template <std::intmax_t N>
395auto pow(no_prior_declaration_workaround::Dummy);
396
397// Make "inverse" an alias for "pow<-1>" when the latter exists (for anything).
398template <typename T>
399constexpr auto inverse(T x) -> decltype(pow<-1>(x)) {
400 return pow<-1>(x);
401}
402
403// Make "squared" an alias for "pow<2>" when the latter exists (for anything).
404template <typename T>
405constexpr auto squared(T x) -> decltype(pow<2>(x)) {
406 return pow<2>(x);
407}
408
409// Make "cubed" an alias for "pow<3>" when the latter exists (for anything).
410template <typename T>
411constexpr auto cubed(T x) -> decltype(pow<3>(x)) {
412 return pow<3>(x);
413}
414
415// Make "sqrt" an alias for "root<2>" when the latter exists (for anything).
416template <typename T>
417constexpr auto sqrt(T x) -> decltype(root<2>(x)) {
418 return root<2>(x);
419}
420
421// Make "cbrt" an alias for "root<3>" when the latter exists (for anything).
422template <typename T>
423constexpr auto cbrt(T x) -> decltype(root<3>(x)) {
424 return root<3>(x);
425}
426
427} // namespace au
428
429
430namespace au {
431namespace detail {
432
433// Find the smallest factor which divides n.
434//
435// Undefined unless (n > 1).
436constexpr std::uintmax_t find_first_factor(std::uintmax_t n) {
437 if (n % 2u == 0u) {
438 return 2u;
439 }
440
441 std::uintmax_t factor = 3u;
442 while (factor * factor <= n) {
443 if (n % factor == 0u) {
444 return factor;
445 }
446 factor += 2u;
447 }
448
449 return n;
450}
451
452// Check whether a number is prime.
453constexpr bool is_prime(std::uintmax_t n) { return (n > 1) && (find_first_factor(n) == n); }
454
455// Find the largest power of `factor` which divides `n`.
456//
457// Undefined unless n > 0, and factor > 1.
458constexpr std::uintmax_t multiplicity(std::uintmax_t factor, std::uintmax_t n) {
459 std::uintmax_t m = 0u;
460 while (n % factor == 0u) {
461 ++m;
462 n /= factor;
463 }
464 return m;
465}
466
467template <typename T>
468constexpr T square(T n) {
469 return n * n;
470}
471
472// Raise a base to an integer power.
473//
474// Undefined behavior if base^exp overflows T.
475template <typename T>
476constexpr T int_pow(T base, std::uintmax_t exp) {
477 if (exp == 0u) {
478 return T{1};
479 }
480
481 if (exp % 2u == 1u) {
482 return base * int_pow(base, exp - 1u);
483 }
484
485 return square(int_pow(base, exp / 2u));
486}
487
488} // namespace detail
489} // namespace au
490
491
492namespace au {
493namespace stdx {
494
495// Source: adapted from (https://en.cppreference.com/w/cpp/utility/intcmp).
496//
497// For C++14 compatibility, we needed to change `if constexpr` to SFINAE.
498template <typename T, typename U, typename Enable = void>
500template <class T, class U>
501constexpr bool cmp_equal(T t, U u) noexcept {
502 return CmpEqualImpl<T, U>{}(t, u);
503}
504
505// Source: adapted from (https://en.cppreference.com/w/cpp/utility/intcmp).
506template <class T, class U>
507constexpr bool cmp_not_equal(T t, U u) noexcept {
508 return !cmp_equal(t, u);
509}
510
511// Source: adapted from (https://en.cppreference.com/w/cpp/utility/intcmp).
512//
513// For C++14 compatibility, we needed to change `if constexpr` to SFINAE.
514template <typename T, typename U, typename Enable = void>
516template <class T, class U>
517constexpr bool cmp_less(T t, U u) noexcept {
518 return CmpLessImpl<T, U>{}(t, u);
519}
520
521// Source: adapted from (https://en.cppreference.com/w/cpp/utility/intcmp).
522template <class T, class U>
523constexpr bool cmp_greater(T t, U u) noexcept {
524 return cmp_less(u, t);
525}
526
527// Source: adapted from (https://en.cppreference.com/w/cpp/utility/intcmp).
528template <class T, class U>
529constexpr bool cmp_less_equal(T t, U u) noexcept {
530 return !cmp_greater(t, u);
531}
532
533// Source: adapted from (https://en.cppreference.com/w/cpp/utility/intcmp).
534template <class T, class U>
535constexpr bool cmp_greater_equal(T t, U u) noexcept {
536 return !cmp_less(t, u);
537}
538
539// Source: adapted from (https://en.cppreference.com/w/cpp/utility/in_range).
540template <class R, class T>
541constexpr bool in_range(T t) noexcept {
542 return cmp_greater_equal(t, std::numeric_limits<R>::min()) &&
543 cmp_less_equal(t, std::numeric_limits<R>::max());
544}
545
547// Implementation details below.
549
550template <typename T, typename U>
551struct CmpEqualImpl<T, U, std::enable_if_t<std::is_signed<T>::value == std::is_signed<U>::value>> {
552 constexpr bool operator()(T t, U u) { return t == u; }
553};
554
555template <typename T, typename U>
556struct CmpEqualImpl<T, U, std::enable_if_t<std::is_signed<T>::value && !std::is_signed<U>::value>> {
557 constexpr bool operator()(T t, U u) { return t < 0 ? false : std::make_unsigned_t<T>(t) == u; }
558};
559
560template <typename T, typename U>
561struct CmpEqualImpl<T, U, std::enable_if_t<!std::is_signed<T>::value && std::is_signed<U>::value>> {
562 constexpr bool operator()(T t, U u) { return u < 0 ? false : t == std::make_unsigned_t<U>(u); }
563};
564
565template <typename T, typename U>
566struct CmpLessImpl<T, U, std::enable_if_t<std::is_signed<T>::value == std::is_signed<U>::value>> {
567 constexpr bool operator()(T t, U u) { return t < u; }
568};
569
570template <typename T, typename U>
571struct CmpLessImpl<T, U, std::enable_if_t<std::is_signed<T>::value && !std::is_signed<U>::value>> {
572 constexpr bool operator()(T t, U u) { return t < 0 ? true : std::make_unsigned_t<T>(t) < u; }
573};
574
575template <typename T, typename U>
576struct CmpLessImpl<T, U, std::enable_if_t<!std::is_signed<T>::value && std::is_signed<U>::value>> {
577 constexpr bool operator()(T t, U u) { return u < 0 ? false : t < std::make_unsigned_t<U>(u); }
578};
579
580} // namespace stdx
581} // namespace au
582
583
584
585namespace au {
586namespace stdx {
587namespace experimental {
588
590// `nonesuch`: adapted from (https://en.cppreference.com/w/cpp/experimental/nonesuch).
591
592struct nonesuch {
593 ~nonesuch() = delete;
594 nonesuch(nonesuch const &) = delete;
595 void operator=(nonesuch const &) = delete;
596};
597
599// `is_detected` and friends: adapted from
600// (https://en.cppreference.com/w/cpp/experimental/is_detected).
601
602namespace detail {
603template <class Default, class AlwaysVoid, template <class...> class Op, class... Args>
604struct detector {
605 using value_t = std::false_type;
606 using type = Default;
607};
608
609template <class Default, template <class...> class Op, class... Args>
610struct detector<Default, stdx::void_t<Op<Args...>>, Op, Args...> {
611 using value_t = std::true_type;
612 using type = Op<Args...>;
613};
614
615} // namespace detail
616
617template <template <class...> class Op, class... Args>
618using is_detected = typename detail::detector<nonesuch, void, Op, Args...>::value_t;
619
620template <template <class...> class Op, class... Args>
621using detected_t = typename detail::detector<nonesuch, void, Op, Args...>::type;
622
623template <class Default, template <class...> class Op, class... Args>
624using detected_or = detail::detector<Default, void, Op, Args...>;
625
626template <class Default, template <class...> class Op, class... Args>
627using detected_or_t = typename detected_or<Default, Op, Args...>::type;
628
629} // namespace experimental
630} // namespace stdx
631} // namespace au
632
633
634namespace au {
635namespace stdx {
636
637// Source: adapted from (https://en.cppreference.com/w/cpp/utility/functional/identity)
638struct identity {
639 template <class T>
640 constexpr T &&operator()(T &&t) const noexcept {
641 return std::forward<T>(t);
642 }
643};
644
645} // namespace stdx
646} // namespace au
647
648// This file provides drop-in replacements for certain standard library function objects for
649// comparison and arithmetic: `std::less<void>`, `std::plus<void>`, etc.
650//
651// These are _not_ intended as _fully general_ replacements. They are _only_ intended for certain
652// specific use cases in this library, where we can ensure certain preconditions are met before they
653// are called. For example, these utilities don't handle comparing signed and unsigned integral
654// types, because we only ever use them in places where we've already explicitly cast our quantities
655// to the same Rep.
656//
657// There are two main reasons we rolled our own versions instead of just using the ones from the
658// standard library (as we had initially done). First, the `<functional>` header is moderately
659// expensive to include---using these alternatives could save 100 ms or more on every file. Second,
660// certain compilers (such as the Green Hills compiler) struggle with the trailing return types in,
661// say, `std::less<void>::operator()`, but work correctly with our alternatives.
662
663namespace au {
664namespace detail {
665
666//
667// Comparison operators.
668//
669
670struct Equal {
671 template <typename T>
672 constexpr bool operator()(const T &a, const T &b) const {
673 return a == b;
674 }
675};
676constexpr auto equal = Equal{};
677
678struct NotEqual {
679 template <typename T>
680 constexpr bool operator()(const T &a, const T &b) const {
681 return a != b;
682 }
683};
684constexpr auto not_equal = NotEqual{};
685
686struct Greater {
687 template <typename T>
688 constexpr bool operator()(const T &a, const T &b) const {
689 return a > b;
690 }
691};
692constexpr auto greater = Greater{};
693
694struct Less {
695 template <typename T>
696 constexpr bool operator()(const T &a, const T &b) const {
697 return a < b;
698 }
699};
700constexpr auto less = Less{};
701
703 template <typename T>
704 constexpr bool operator()(const T &a, const T &b) const {
705 return a >= b;
706 }
707};
708constexpr auto greater_equal = GreaterEqual{};
709
710struct LessEqual {
711 template <typename T>
712 constexpr bool operator()(const T &a, const T &b) const {
713 return a <= b;
714 }
715};
716constexpr auto less_equal = LessEqual{};
717
718//
719// Arithmetic operators.
720//
721
722struct Plus {
723 template <typename T, typename U>
724 constexpr auto operator()(const T &a, const U &b) const {
725 return a + b;
726 }
727};
728constexpr auto plus = Plus{};
729
730struct Minus {
731 template <typename T, typename U>
732 constexpr auto operator()(const T &a, const U &b) const {
733 return a - b;
734 }
735};
736constexpr auto minus = Minus{};
737
738} // namespace detail
739} // namespace au
740
741
742
743namespace au {
744namespace detail {
745
746template <typename PackT, typename T>
747struct Prepend;
748template <typename PackT, typename T>
749using PrependT = typename Prepend<PackT, T>::type;
750
751template <typename T, typename U>
752struct SameTypeIgnoringCvref : std::is_same<stdx::remove_cvref_t<T>, stdx::remove_cvref_t<U>> {};
753
754template <typename T, typename U>
755constexpr bool same_type_ignoring_cvref(T, U) {
757}
758
760// Implementation details below.
761
762template <template <typename...> class Pack, typename T, typename... Us>
763struct Prepend<Pack<Us...>, T> {
764 using type = Pack<T, Us...>;
765};
766
767} // namespace detail
768} // namespace au
769
770
771
772// Products of base powers are the foundation of au. We use them for:
773//
774// - The Dimension of a Unit.
775// - The Magnitude of a Unit.
776// - Making *compound* Units (products of powers, e.g., m^1 * s^(-2)).
777
778namespace au {
779
780// A base type B raised to an integer exponent N.
781template <typename B, std::intmax_t N>
782struct Pow;
783
784// A base type B raised to a rational exponent (N/D).
785template <typename B, std::intmax_t N, std::intmax_t D>
786struct RatioPow;
787
788// Type trait for the "base" of a type, interpreted as a base power.
789//
790// Any type can act as a base, with an implicit power of 1. `Pow<B, N>` can represent integer
791// powers of a base type `B`, and `RatioPow<B, N, D>` can represent rational powers of `B` (where
792// the power is `(N/D)`).
793template <typename T>
795template <typename T>
796using BaseT = typename Base<T>::type;
797
798// Type trait for the rational exponent of a type, interpreted as a base power.
799template <typename T>
800struct Exp : stdx::type_identity<std::ratio<1>> {};
801template <typename T>
802using ExpT = typename Exp<T>::type;
803
804// Type trait for treating an arbitrary type as a given type of pack.
805//
806// This should be the identity for anything that is already a pack of this type, and otherwise
807// should wrap it in this type of pack.
808template <template <class... Ts> class Pack, typename T>
809struct AsPack : stdx::type_identity<Pack<T>> {};
810template <template <class... Ts> class Pack, typename T>
811using AsPackT = typename AsPack<Pack, T>::type;
812
813// Type trait to remove a Pack enclosing a single item.
814//
815// Defined only if T is Pack<Ts...> for some typelist. Always the identity, unless sizeof...(Ts) is
816// exactly 1, in which case, it returns the (sole) element.
817template <template <class... Ts> class Pack, typename T>
819template <template <class... Ts> class Pack, typename T>
820using UnpackIfSoloT = typename UnpackIfSolo<Pack, T>::type;
821
822// Trait to define whether two types are in order, based on the total ordering for some pack.
823//
824// Each pack should individually define its desired total ordering. For these implementations,
825// prefer to inherit from LexicographicTotalOrdering (below), because it guards against the most
826// common way to fail to achieve a strict total ordering (namely, by having two types which are not
827// identical nevertheless compare equal).
828template <template <class...> class Pack, typename A, typename B>
830
831// A strict total ordering which combines strict partial orderings serially, using the first which
832// distinguishes A and B.
833template <typename A, typename B, template <class, class> class... Orderings>
835
836// A (somewhat arbitrary) total ordering on _packs themselves_.
837//
838// Built on top of the total ordering for the _bases_ of the packs.
839template <typename T, typename U>
841
842// Make a List of deduplicated, sorted types.
843//
844// The result will always be List<...>, and the elements will be sorted according to the total
845// ordering for List, with duplicates removed. It will be "flattened" in that any elements which
846// are already `List<Ts...>` will be effectively replaced by `Ts...`.
847//
848// A precondition for `FlatDedupedTypeListT` is that any inputs which are already of type
849// `List<...>`, respect the _ordering_ for `List`, with no duplicates. Otherwise, behaviour is
850// undefined. (This precondition will automatically be satisfied if *every* instance of `List<...>`
851// arises as the result of a call to `FlatDedupedTypeListT<...>`.)
852template <template <class...> class List, typename... Ts>
854template <template <class...> class List, typename... Ts>
855using FlatDedupedTypeListT = typename FlatDedupedTypeList<List, AsPackT<List, Ts>...>::type;
856
857namespace detail {
858// Express a base power in its simplest form (base alone if power is 1, or Pow if exp is integral).
859template <typename T>
861template <typename T>
862using SimplifyBasePowersT = typename SimplifyBasePowers<T>::type;
863} // namespace detail
864
865// Compute the product between two power packs.
866template <template <class...> class Pack, typename... Ts>
868template <template <class...> class Pack, typename... Ts>
869using PackProductT = detail::SimplifyBasePowersT<typename PackProduct<Pack, Ts...>::type>;
870
871// Compute a rational power of a pack.
872template <template <class...> class Pack, typename T, typename E>
874template <template <class...> class Pack,
875 typename T,
876 std::intmax_t ExpNum,
877 std::intmax_t ExpDen = 1>
878using PackPowerT =
879 detail::SimplifyBasePowersT<typename PackPower<Pack, T, std::ratio<ExpNum, ExpDen>>::type>;
880
881// Compute the inverse of a power pack.
882template <template <class...> class Pack, typename T>
883using PackInverseT = PackPowerT<Pack, T, -1>;
884
885// Compute the quotient of two power packs.
886template <template <class...> class Pack, typename T, typename U>
887using PackQuotientT = PackProductT<Pack, T, PackInverseT<Pack, U>>;
888
889namespace detail {
890// Pull out all of the elements in a Pack whose exponents are positive.
891template <typename T>
893template <typename T>
894using NumeratorPartT = typename NumeratorPart<T>::type;
895
896// Pull out all of the elements in a Pack whose exponents are negative.
897template <typename T>
899template <typename T>
900using DenominatorPartT = typename DenominatorPart<T>::type;
901} // namespace detail
902
903// A validator for a pack of Base Powers.
904//
905// `IsValidPack<Pack, T>::value` is `true` iff `T` is an instance of the variadic `Pack<...>`, and
906// its parameters fulfill all of the appropriate type traits, namely:
907//
908// - `AreBasesInOrder<Pack, T>`
909template <template <class...> class Pack, typename T>
910struct IsValidPack;
911
912// Assuming that `T` is an instance of `Pack<BPs...>`, validates that every consecutive pair from
913// `BaseT<BPs>...` satisfies the strict total ordering `InOrderFor<Pack, ...>` for `Pack`.
914template <template <class...> class Pack, typename T>
916
917// Assuming that `T` is an instance of `Pack<BPs...>`, validates that every consecutive pair from
918// `BPs...` satisfies the strict total ordering `InOrderFor<Pack, ...>` for `Pack`.
919//
920// This is very similar to AreBasesInOrder, but is intended for packs that _don't_ represent
921// products-of-powers.
922template <template <class...> class Pack, typename T>
924
925// Assuming `T` is an instance of `Pack<BPs...>`, validates that `Exp<BPs>...` is always nonzero.
926template <template <class...> class Pack, typename T>
928
930// Implementation details below.
932
933// These forward declarations and traits go here to enable us to treat Pow and RatioPow (below) as
934// full-fledged "Units". A "Unit" is any type U where `DimT<U>` gives a valid Dimension, and
935// `MagT<U>` gives a valid Magnitude. Even though we can't define Dimension and Magnitude precisely
936// in this file, we'll take advantage of the fact that we know they're going to be parameter packs.
937
938template <typename... BPs>
939struct Dimension;
940
941template <typename... BPs>
942struct Magnitude;
943
944namespace detail {
945
946// The default dimension, `DimT<U>`, of a type `U`, is the `::Dim` typedef (or `void` if none).
947//
948// Users can customize by specializing `DimImpl<U>` and setting the `type` member variable.
949template <typename U>
950using DimMemberT = typename U::Dim;
951template <typename U>
952struct DimImpl : stdx::experimental::detected_or<void, DimMemberT, U> {};
953template <typename U>
954using DimT = typename DimImpl<U>::type;
955
956// The default magnitude, `MagT<U>`, of a type `U`, is the `::Mag` typedef (or `void` if none).
957//
958// Users can customize by specializing `MagImpl<U>` and setting the `type` member variable.
959template <typename U>
960using MagMemberT = typename U::Mag;
961template <typename U>
962struct MagImpl : stdx::experimental::detected_or<void, MagMemberT, U> {};
963template <typename U>
964using MagT = typename MagImpl<U>::type;
965
966} // namespace detail
967
969// `Pow` implementation.
970
971template <typename B, std::intmax_t N>
972struct Pow {
973 // TODO(#40): Clean up relationship between Dim/Mag and Pow, if compile times are OK.
974 using Dim = PackPowerT<Dimension, AsPackT<Dimension, detail::DimT<B>>, N>;
975 using Mag = PackPowerT<Magnitude, AsPackT<Magnitude, detail::MagT<B>>, N>;
976};
977
979// `RatioPow` implementation.
980
981// A base type B raised to a rational exponent (N/D).
982template <typename B, std::intmax_t N, std::intmax_t D>
983struct RatioPow {
984 // TODO(#40): Clean up relationship between Dim/Mag and RatioPow, if compile times are OK.
985 using Dim = PackPowerT<Dimension, AsPackT<Dimension, detail::DimT<B>>, N, D>;
986 using Mag = PackPowerT<Magnitude, AsPackT<Magnitude, detail::MagT<B>>, N, D>;
987};
988
990// `BaseT` implementation.
991
992template <typename T, std::intmax_t N>
993struct Base<Pow<T, N>> : stdx::type_identity<T> {};
994
995template <typename T, std::intmax_t N, std::intmax_t D>
996struct Base<RatioPow<T, N, D>> : stdx::type_identity<T> {};
997
999// `ExpT` implementation.
1000
1001template <typename T, std::intmax_t N>
1002struct Exp<Pow<T, N>> : stdx::type_identity<std::ratio<N>> {};
1003
1004template <typename T, std::intmax_t N, std::intmax_t D>
1005struct Exp<RatioPow<T, N, D>> : stdx::type_identity<std::ratio<N, D>> {};
1006
1008// `AsPackT` implementation.
1009
1010template <template <class... Ts> class Pack, typename... Ts>
1011struct AsPack<Pack, Pack<Ts...>> : stdx::type_identity<Pack<Ts...>> {};
1012
1014// `UnpackIfSoloT` implementation.
1015
1016// Null pack case: do not unpack.
1017template <template <class... Ts> class Pack>
1018struct UnpackIfSolo<Pack, Pack<>> : stdx::type_identity<Pack<>> {};
1019
1020// Non-null pack case: unpack only if there is nothing after the head element.
1021template <template <class... Ts> class Pack, typename T, typename... Ts>
1022struct UnpackIfSolo<Pack, Pack<T, Ts...>>
1023 : std::conditional<(sizeof...(Ts) == 0u), T, Pack<T, Ts...>> {};
1024
1026// `LexicographicTotalOrdering` implementation.
1027
1028// Base case: if there is no ordering, then the inputs are not in order.
1029template <typename A, typename B>
1031 // LexicographicTotalOrdering is for strict total orderings only. If two types compare equal,
1032 // then they must be the same type; otherwise, we have not created a strict total ordering among
1033 // all types being used in some pack.
1034 static_assert(std::is_same<A, B>::value,
1035 "Broken strict total ordering: distinct input types compare equal");
1036};
1037
1038// Recursive case.
1039template <typename A,
1040 typename B,
1041 template <class, class>
1042 class PrimaryOrdering,
1043 template <class, class>
1044 class... Tiebreakers>
1045struct LexicographicTotalOrdering<A, B, PrimaryOrdering, Tiebreakers...> :
1046
1047 // Short circuit for when the inputs are the same.
1048 //
1049 // This can prevent us from instantiating a tiebreaker which doesn't exist for a given type.
1050 std::conditional_t<
1051 (std::is_same<A, B>::value),
1052 std::false_type,
1053
1054 // If A and B are properly ordered by the primary criterion, they are definitely ordered.
1055 std::conditional_t<(PrimaryOrdering<A, B>::value),
1056 std::true_type,
1057
1058 // If B and A are properly ordered by the primary criterion, then A and B
1059 // are definitely _not_ properly ordered.
1060 std::conditional_t<(PrimaryOrdering<B, A>::value),
1061 std::false_type,
1062
1063 // Fall back to the remaining orderings as
1064 // tiebreakers.
1065 LexicographicTotalOrdering<A, B, Tiebreakers...>>>> {
1066};
1067
1069// `InStandardPackOrder` implementation.
1070
1071namespace detail {
1072// Helper: check that the lead bases are in order.
1073template <typename T, typename U>
1075template <template <class...> class P, typename H1, typename... T1, typename H2, typename... T2>
1076struct LeadBasesInOrder<P<H1, T1...>, P<H2, T2...>> : InOrderFor<P, BaseT<H1>, BaseT<H2>> {};
1077
1078// Helper: check that the lead exponents are in order.
1079template <typename T, typename U>
1081template <template <class...> class P, typename H1, typename... T1, typename H2, typename... T2>
1082struct LeadExpsInOrder<P<H1, T1...>, P<H2, T2...>>
1083 : stdx::bool_constant<(std::ratio_subtract<ExpT<H1>, ExpT<H2>>::num < 0)> {};
1084
1085// Helper: apply InStandardPackOrder to tails.
1086template <typename T, typename U>
1087struct TailsInStandardPackOrder;
1088template <template <class...> class P, typename H1, typename... T1, typename H2, typename... T2>
1089struct TailsInStandardPackOrder<P<H1, T1...>, P<H2, T2...>>
1090 : InStandardPackOrder<P<T1...>, P<T2...>> {};
1091} // namespace detail
1092
1093// Base case: left pack is null.
1094template <template <class...> class P, typename... Ts>
1095struct InStandardPackOrder<P<>, P<Ts...>> : stdx::bool_constant<(sizeof...(Ts) > 0)> {};
1096
1097// Base case: right pack (only) is null.
1098template <template <class...> class P, typename H, typename... T>
1099struct InStandardPackOrder<P<H, T...>, P<>> : std::false_type {};
1100
1101// Recursive case: try ordering the heads, and fall back to the tails.
1102template <template <class...> class P, typename H1, typename... T1, typename H2, typename... T2>
1103struct InStandardPackOrder<P<H1, T1...>, P<H2, T2...>>
1104 : LexicographicTotalOrdering<P<H1, T1...>,
1105 P<H2, T2...>,
1106 detail::LeadBasesInOrder,
1107 detail::LeadExpsInOrder,
1108 detail::TailsInStandardPackOrder> {};
1109
1111// `FlatDedupedTypeListT` implementation.
1112
1113// 1-ary Base case: a list with a single element is already done.
1114//
1115// (We explicitly assumed that any `List<...>` inputs would already be in sorted order.)
1116template <template <class...> class List, typename... Ts>
1117struct FlatDedupedTypeList<List, List<Ts...>> : stdx::type_identity<List<Ts...>> {};
1118
1119// 2-ary base case: if we exhaust elements in the second list, the first list is the answer.
1120//
1121// (Again: this relies on the explicit assumption that any `List<...>` inputs are already in order.)
1122template <template <class...> class List, typename... Ts>
1123struct FlatDedupedTypeList<List, List<Ts...>, List<>> : stdx::type_identity<List<Ts...>> {};
1124
1125// 2-ary recursive case, single-element head.
1126//
1127// This use case also serves as the core "insertion logic", inserting `T` into the proper place
1128// within `List<H, Ts...>`.
1129template <template <class...> class List, typename T, typename H, typename... Ts>
1130struct FlatDedupedTypeList<List, List<T>, List<H, Ts...>> :
1131
1132 // If the candidate element exactly equals the head, disregard it (de-dupe!).
1134 (std::is_same<T, H>::value),
1135 List<H, Ts...>,
1136
1137 // If the candidate element is strictly before the head, prepend it.
1138 std::conditional_t<(InOrderFor<List, T, H>::value),
1139 List<T, H, Ts...>,
1140
1141 // If we're here, we know the candidate comes after the head. So, try
1142 // inserting it (recursively) in the tail, and then prepend the old Head
1143 // (because we know it comes first).
1144 detail::PrependT<FlatDedupedTypeListT<List, List<T>, List<Ts...>>, H>>> {
1145};
1146
1147// 2-ary recursive case, multi-element head: insert head of second element, and recurse.
1148template <template <class...> class List,
1149 typename H1,
1150 typename N1,
1151 typename... T1,
1152 typename H2,
1153 typename... T2>
1154struct FlatDedupedTypeList<List, List<H1, N1, T1...>, List<H2, T2...>>
1155 : FlatDedupedTypeList<List,
1156 // Put H2 first so we can use single-element-head case from above.
1157 FlatDedupedTypeListT<List, List<H2>, List<H1, N1, T1...>>,
1158 List<T2...>> {};
1159
1160// N-ary case, multi-element head: peel off tail-of-head, and recurse.
1161//
1162// Note that this also handles the 2-ary case where the head list has more than one element.
1163template <template <class...> class List, typename L1, typename L2, typename L3, typename... Ls>
1164struct FlatDedupedTypeList<List, L1, L2, L3, Ls...>
1165 : FlatDedupedTypeList<List, FlatDedupedTypeListT<List, L1, L2>, L3, Ls...> {};
1166
1168// `PackProductT` implementation.
1169
1170// 0-ary case:
1171template <template <class...> class Pack>
1172struct PackProduct<Pack> : stdx::type_identity<Pack<>> {};
1173
1174// 1-ary case:
1175template <template <class...> class Pack, typename... Ts>
1176struct PackProduct<Pack, Pack<Ts...>> : stdx::type_identity<Pack<Ts...>> {};
1177
1178// 2-ary Base case: two null packs.
1179template <template <class...> class Pack>
1180struct PackProduct<Pack, Pack<>, Pack<>> : stdx::type_identity<Pack<>> {};
1181
1182// 2-ary Base case: only left pack is null.
1183template <template <class...> class Pack, typename T, typename... Ts>
1184struct PackProduct<Pack, Pack<>, Pack<T, Ts...>> : stdx::type_identity<Pack<T, Ts...>> {};
1185
1186// 2-ary Base case: only right pack is null.
1187template <template <class...> class Pack, typename T, typename... Ts>
1188struct PackProduct<Pack, Pack<T, Ts...>, Pack<>> : stdx::type_identity<Pack<T, Ts...>> {};
1189
1190namespace detail {
1191template <typename B, typename E1, typename E2>
1196template <typename B, typename E1, typename E2>
1197using ComputeRationalPowerT = typename ComputeRationalPower<B, E1, E2>::type;
1198} // namespace detail
1199
1200// 2-ary Recursive case: two non-null packs.
1201template <template <class...> class P, typename H1, typename... T1, typename H2, typename... T2>
1202struct PackProduct<P, P<H1, T1...>, P<H2, T2...>> :
1203
1204 // If the bases for H1 and H2 are in-order, prepend H1 to the product of the remainder.
1206 (InOrderFor<P, BaseT<H1>, BaseT<H2>>::value),
1207 detail::PrependT<PackProductT<P, P<T1...>, P<H2, T2...>>, H1>,
1208
1209 // If the bases for H2 and H1 are in-order, prepend H2 to the product of the remainder.
1210 std::conditional_t<
1211 (InOrderFor<P, BaseT<H2>, BaseT<H1>>::value),
1212 detail::PrependT<PackProductT<P, P<T2...>, P<H1, T1...>>, H2>,
1213
1214 // If the bases have the same position, assume they really _are_ the same (because
1215 // InOrderFor will verify this if it uses LexicographicTotalOrdering), and add the
1216 // exponents. (If the exponents add to zero, omit the term.)
1217 std::conditional_t<
1218 (std::ratio_add<ExpT<H1>, ExpT<H2>>::num == 0),
1219 PackProductT<P, P<T1...>, P<T2...>>,
1220 detail::PrependT<PackProductT<P, P<T2...>, P<T1...>>,
1221 detail::ComputeRationalPowerT<BaseT<H1>, ExpT<H1>, ExpT<H2>>>>>> {
1222};
1223
1224// N-ary case, N > 2: recurse.
1225template <template <class...> class P,
1226 typename... T1s,
1227 typename... T2s,
1228 typename... T3s,
1229 typename... Ps>
1230struct PackProduct<P, P<T1s...>, P<T2s...>, P<T3s...>, Ps...>
1231 : PackProduct<P, P<T1s...>, PackProductT<P, P<T2s...>, P<T3s...>, Ps...>> {};
1232
1234// `PackPowerT` implementation.
1235
1236namespace detail {
1237template <typename T, typename E>
1238using MultiplyExpFor = std::ratio_multiply<ExpT<T>, E>;
1239}
1240
1241template <template <class...> class P, typename... Ts, typename E>
1242struct PackPower<P, P<Ts...>, E>
1243 : std::conditional<(E::num == 0),
1244 P<>,
1245 P<RatioPow<BaseT<Ts>,
1246 detail::MultiplyExpFor<Ts, E>::num,
1247 detail::MultiplyExpFor<Ts, E>::den>...>> {};
1248
1250// `IsValidPack` implementation.
1251
1252namespace detail {
1253template <template <class...> class Pack, typename T>
1255
1256template <template <class...> class Pack, typename... Ts>
1257struct IsPackOf<Pack, Pack<Ts...>> : std::true_type {};
1258} // namespace detail
1259
1260template <template <class...> class Pack, typename T>
1261struct IsValidPack : stdx::conjunction<detail::IsPackOf<Pack, T>,
1262 AreBasesInOrder<Pack, T>,
1263 AreAllPowersNonzero<Pack, T>> {};
1264
1266// `AreElementsInOrder` implementation.
1267
1268template <template <class...> class Pack>
1269struct AreElementsInOrder<Pack, Pack<>> : std::true_type {};
1270
1271template <template <class...> class Pack, typename T>
1272struct AreElementsInOrder<Pack, Pack<T>> : std::true_type {};
1273
1274template <template <class...> class Pack, typename T1, typename T2, typename... Ts>
1275struct AreElementsInOrder<Pack, Pack<T1, T2, Ts...>>
1276 : stdx::conjunction<InOrderFor<Pack, T1, T2>, AreElementsInOrder<Pack, Pack<T2, Ts...>>> {};
1277
1278namespace detail {
1279
1280constexpr bool all_true() { return true; }
1281
1282template <typename... Predicates>
1283constexpr bool all_true(Predicates &&...values) {
1284 // The reason we bother to make an array is so that we can iterate over it.
1285 const bool value_array[] = {values...};
1286
1287 for (auto i = 0u; i < sizeof...(Predicates); ++i) {
1288 if (!value_array[i]) {
1289 return false;
1290 }
1291 }
1292
1293 return true;
1294}
1295} // namespace detail
1296
1298// `AreBasesInOrder` implementation.
1299
1300template <template <class...> class Pack, typename... Ts>
1301struct AreBasesInOrder<Pack, Pack<Ts...>> : AreElementsInOrder<Pack, Pack<BaseT<Ts>...>> {};
1302
1304// `AreAllPowersNonzero` implementation.
1305
1306template <template <class...> class Pack, typename... Ts>
1307struct AreAllPowersNonzero<Pack, Pack<Ts...>>
1308 : stdx::bool_constant<detail::all_true((ExpT<Ts>::num != 0)...)> {};
1309
1311// `SimplifyBasePowersT` implementation.
1312
1313namespace detail {
1314// To simplify an individual base power, by default, do nothing.
1315template <typename T>
1317template <typename T>
1318using SimplifyBasePowerT = typename SimplifyBasePower<T>::type;
1319
1320// To simplify an integer power of a base, give the base alone if the exponent is 1; otherwise, do
1321// nothing.
1322template <typename B, std::intmax_t N>
1323struct SimplifyBasePower<Pow<B, N>> : std::conditional<(N == 1), B, Pow<B, N>> {};
1324
1325// To simplify a rational power of a base, simplify the integer power if the exponent is an integer
1326// (i.e., if its denominator is 1); else, do nothing.
1327template <typename B, std::intmax_t N, std::intmax_t D>
1329 : std::conditional<(D == 1), SimplifyBasePowerT<Pow<B, N>>, RatioPow<B, N, D>> {};
1330
1331// To simplify the base powers in a pack, give the pack with each base power simplified.
1332template <template <class...> class Pack, typename... BPs>
1333struct SimplifyBasePowers<Pack<BPs...>> : stdx::type_identity<Pack<SimplifyBasePowerT<BPs>...>> {};
1334} // namespace detail
1335
1337// `NumeratorPartT` and `DenominatorPartT` implementation.
1338
1339namespace detail {
1340template <template <class...> class Pack>
1341struct NumeratorPart<Pack<>> : stdx::type_identity<Pack<>> {};
1342
1343template <template <class...> class Pack, typename Head, typename... Tail>
1344struct NumeratorPart<Pack<Head, Tail...>>
1345 : std::conditional<(ExpT<Head>::num > 0),
1346 PackProductT<Pack, Pack<Head>, NumeratorPartT<Pack<Tail...>>>,
1347 NumeratorPartT<Pack<Tail...>>> {};
1348
1349template <template <class...> class Pack, typename... Ts>
1350struct DenominatorPart<Pack<Ts...>> : NumeratorPart<PackInverseT<Pack, Pack<Ts...>>> {};
1351} // namespace detail
1352
1353} // namespace au
1354
1355
1356namespace au {
1357
1358template <typename... BPs>
1360 // Having separate `static_assert` instances for the individual conditions produces more
1361 // readable errors if we fail.
1362 static_assert(AreAllPowersNonzero<Dimension, Dimension<BPs...>>::value,
1363 "All powers must be nonzero");
1364 static_assert(AreBasesInOrder<Dimension, Dimension<BPs...>>::value,
1365 "Bases must be listed in ascending order");
1366
1367 // We also want to use the "full" validity check. This should be equivalent to the above
1368 // conditions, but if we add more conditions later, we want them to get picked up here
1369 // automatically.
1370 static_assert(IsValidPack<Dimension, Dimension<BPs...>>::value, "Ill-formed Dimension");
1371};
1372
1373// Define readable operations for product, quotient, power, inverse on Dimensions.
1374template <typename... BPs>
1375using DimProductT = PackProductT<Dimension, BPs...>;
1376template <typename T, std::intmax_t ExpNum, std::intmax_t ExpDen = 1>
1377using DimPowerT = PackPowerT<Dimension, T, ExpNum, ExpDen>;
1378template <typename T, typename U>
1379using DimQuotientT = PackQuotientT<Dimension, T, U>;
1380template <typename T>
1381using DimInverseT = PackInverseT<Dimension, T>;
1382
1383template <typename... BP1s, typename... BP2s>
1384constexpr auto operator*(Dimension<BP1s...>, Dimension<BP2s...>) {
1385 return DimProductT<Dimension<BP1s...>, Dimension<BP2s...>>{};
1386}
1387
1388template <typename... BP1s, typename... BP2s>
1389constexpr auto operator/(Dimension<BP1s...>, Dimension<BP2s...>) {
1390 return DimQuotientT<Dimension<BP1s...>, Dimension<BP2s...>>{};
1391}
1392
1393// Roots and powers for Dimension instances.
1394template <std::intmax_t N, typename... BPs>
1395constexpr DimPowerT<Dimension<BPs...>, N> pow(Dimension<BPs...>) {
1396 return {};
1397}
1398template <std::intmax_t N, typename... BPs>
1399constexpr DimPowerT<Dimension<BPs...>, 1, N> root(Dimension<BPs...>) {
1400 return {};
1401}
1402
1403template <typename... Dims>
1405template <typename... Dims>
1406using CommonDimensionT = typename CommonDimension<Dims...>::type;
1407
1408template <typename... BaseDims>
1409struct CommonDimension<Dimension<BaseDims...>> : stdx::type_identity<Dimension<BaseDims...>> {};
1410template <typename Head, typename... Tail>
1411struct CommonDimension<Head, Tail...> : CommonDimension<Tail...> {
1412 static_assert(std::is_same<Head, CommonDimensionT<Tail...>>::value,
1413 "Common dimension only defined when all dimensions are identical");
1414};
1415
1416namespace base_dim {
1417
1418template <int64_t I>
1420 static constexpr int64_t base_dim_index = I;
1421};
1422template <int64_t I>
1423constexpr int64_t BaseDimension<I>::base_dim_index;
1424
1425template <typename T, typename U>
1426struct OrderByBaseDimIndex : stdx::bool_constant<(T::base_dim_index < U::base_dim_index)> {};
1427
1428struct Length : BaseDimension<-99> {};
1429struct Mass : BaseDimension<-98> {};
1430struct Time : BaseDimension<-97> {};
1431struct Current : BaseDimension<-96> {};
1432struct Temperature : BaseDimension<-95> {};
1433struct Angle : BaseDimension<-94> {};
1434struct Information : BaseDimension<-93> {};
1435struct AmountOfSubstance : BaseDimension<-92> {};
1436struct LuminousIntensity : BaseDimension<-91> {};
1437
1438} // namespace base_dim
1439
1440template <typename A, typename B>
1441struct InOrderFor<Dimension, A, B>
1442 : LexicographicTotalOrdering<A, B, base_dim::OrderByBaseDimIndex> {};
1443
1444// The types we want to expose to the rest of the library internals are the full-fledged Dimensions,
1445// not the Base Dimensions, because Dimensions are easier to work with (we can take products,
1446// quotients, powers, etc.).
1447using Length = Dimension<base_dim::Length>;
1448using Mass = Dimension<base_dim::Mass>;
1449using Time = Dimension<base_dim::Time>;
1450using Current = Dimension<base_dim::Current>;
1451using Temperature = Dimension<base_dim::Temperature>;
1452using Angle = Dimension<base_dim::Angle>;
1453using Information = Dimension<base_dim::Information>;
1454using AmountOfSubstance = Dimension<base_dim::AmountOfSubstance>;
1455using LuminousIntensity = Dimension<base_dim::LuminousIntensity>;
1456
1457} // namespace au
1458
1459
1460
1461// "Magnitude" is a collection of templated types, representing positive real numbers.
1462//
1463// The key design goal is to support products and rational powers _exactly_, including for many
1464// irrational numbers, such as Pi, or sqrt(2).
1465//
1466// Even though there is only one possible value for each type, we encourage users to use these
1467// values wherever possible, because they interact correctly via standard `*`, `/`, `==`, and `!=`
1468// operations, and this leads to more readable code.
1469
1470namespace au {
1471
1472template <typename... BPs>
1473struct Magnitude {
1474 // Having separate `static_assert` instances for the individual conditions produces more
1475 // readable errors if we fail.
1476 static_assert(AreAllPowersNonzero<Magnitude, Magnitude<BPs...>>::value,
1477 "All powers must be nonzero");
1478 static_assert(AreBasesInOrder<Magnitude, Magnitude<BPs...>>::value,
1479 "Bases must be listed in ascending order");
1480
1481 // We also want to use the "full" validity check. This should be equivalent to the above
1482 // conditions, but if we add more conditions later, we want them to get picked up here
1483 // automatically.
1484 static_assert(IsValidPack<Magnitude, Magnitude<BPs...>>::value, "Ill-formed Magnitude");
1485};
1486
1487// Define readable operations for product, quotient, power, inverse on Magnitudes.
1488template <typename... BPs>
1489using MagProductT = PackProductT<Magnitude, BPs...>;
1490template <typename T, std::intmax_t ExpNum, std::intmax_t ExpDen = 1>
1491using MagPowerT = PackPowerT<Magnitude, T, ExpNum, ExpDen>;
1492template <typename T, typename U>
1493using MagQuotientT = PackQuotientT<Magnitude, T, U>;
1494template <typename T>
1495using MagInverseT = PackInverseT<Magnitude, T>;
1496
1497// A helper function to create a Magnitude from an integer constant.
1498template <std::size_t N>
1499constexpr auto mag();
1500
1501// A base type for prime numbers.
1502template <std::uintmax_t N>
1503struct Prime {
1504 static_assert(detail::is_prime(N), "Prime<N> requires that N is prime");
1505
1506 static constexpr std::uintmax_t value() { return N; }
1507};
1508
1509// A base type for pi.
1510struct Pi {
1511 // The reason we define this manually, rather than using something like `M_PIl`, is because the
1512 // latter is not available on certain architectures. We do test against `M_PIl`. Those tests
1513 // are not run on architectures that don't support `M_PIl`, but as long as they are run on any
1514 // architectures at all, that's enough to give confidence in this value.
1515 //
1516 // Source for value: http://www.pi-world-ranking-list.com/lists/details/hogg.html
1517 static constexpr long double value() { return 3.14159265358979323846264338327950288419716939L; }
1518};
1519
1521// Define the lexicographic ordering of bases for Magnitude.
1522
1523namespace detail {
1524template <typename T, typename U>
1525struct OrderByValue : stdx::bool_constant<(T::value() < U::value())> {};
1526} // namespace detail
1527
1528template <typename A, typename B>
1529struct InOrderFor<Magnitude, A, B> : LexicographicTotalOrdering<A, B, detail::OrderByValue> {};
1530
1532// Type trait based interface for Magnitude.
1533
1534template <typename MagT>
1535struct IntegerPartImpl;
1536template <typename MagT>
1537using IntegerPartT = typename IntegerPartImpl<MagT>::type;
1538
1539template <typename MagT>
1540struct NumeratorImpl;
1541template <typename MagT>
1542using NumeratorT = typename NumeratorImpl<MagT>::type;
1543
1544template <typename MagT>
1545using DenominatorT = NumeratorT<MagInverseT<MagT>>;
1546
1547template <typename MagT>
1548struct IsRational
1549 : std::is_same<MagT,
1550 MagQuotientT<IntegerPartT<NumeratorT<MagT>>, IntegerPartT<DenominatorT<MagT>>>> {
1551};
1552
1553template <typename MagT>
1554struct IsInteger : std::is_same<MagT, IntegerPartT<MagT>> {};
1555
1556// The "common magnitude" of two Magnitudes is the largest Magnitude that evenly divides both.
1557//
1558// This is possible only if the quotient of the inputs is rational. If it's not, then the "common
1559// magnitude" is one that is related to both inputs, and symmetrical under a change in order (to
1560// fulfill the requirements of a `std::common_type` specialization).
1561template <typename... Ms>
1562struct CommonMagnitude;
1563template <typename... Ms>
1564using CommonMagnitudeT = typename CommonMagnitude<Ms...>::type;
1565
1567// Value based interface for Magnitude.
1568
1569static constexpr auto ONE = Magnitude<>{};
1570static constexpr auto PI = Magnitude<Pi>{};
1571
1572template <typename... BP1s, typename... BP2s>
1573constexpr auto operator*(Magnitude<BP1s...>, Magnitude<BP2s...>) {
1574 return MagProductT<Magnitude<BP1s...>, Magnitude<BP2s...>>{};
1575}
1576
1577template <typename... BP1s, typename... BP2s>
1578constexpr auto operator/(Magnitude<BP1s...>, Magnitude<BP2s...>) {
1579 return MagQuotientT<Magnitude<BP1s...>, Magnitude<BP2s...>>{};
1580}
1581
1582template <int E, typename... BPs>
1583constexpr auto pow(Magnitude<BPs...>) {
1584 return MagPowerT<Magnitude<BPs...>, E>{};
1585}
1586
1587template <int N, typename... BPs>
1588constexpr auto root(Magnitude<BPs...>) {
1589 return MagPowerT<Magnitude<BPs...>, 1, N>{};
1590}
1591
1592template <typename... BP1s, typename... BP2s>
1593constexpr auto operator==(Magnitude<BP1s...>, Magnitude<BP2s...>) {
1594 return std::is_same<Magnitude<BP1s...>, Magnitude<BP2s...>>::value;
1595}
1596
1597template <typename... BP1s, typename... BP2s>
1598constexpr auto operator!=(Magnitude<BP1s...> m1, Magnitude<BP2s...> m2) {
1599 return !(m1 == m2);
1600}
1601
1602template <typename... BPs>
1603constexpr auto integer_part(Magnitude<BPs...>) {
1604 return IntegerPartT<Magnitude<BPs...>>{};
1605}
1606
1607template <typename... BPs>
1608constexpr auto numerator(Magnitude<BPs...>) {
1609 return NumeratorT<Magnitude<BPs...>>{};
1610}
1611
1612template <typename... BPs>
1613constexpr auto denominator(Magnitude<BPs...>) {
1614 return DenominatorT<Magnitude<BPs...>>{};
1615}
1616
1617template <typename... BPs>
1618constexpr bool is_rational(Magnitude<BPs...>) {
1619 return IsRational<Magnitude<BPs...>>::value;
1620}
1621
1622template <typename... BPs>
1623constexpr bool is_integer(Magnitude<BPs...>) {
1624 return IsInteger<Magnitude<BPs...>>::value;
1625}
1626
1627// Get the value of this Magnitude in a "traditional" numeric type T.
1628//
1629// If T is an integral type, then the Magnitude must be integral as well.
1630template <typename T, typename... BPs>
1631constexpr T get_value(Magnitude<BPs...>);
1632
1633// Value-based interface around CommonMagnitude.
1634template <typename... Ms>
1635constexpr auto common_magnitude(Ms...) {
1636 return CommonMagnitudeT<Ms...>{};
1637}
1638
1640// Implementation details below.
1642
1644// `mag<N>()` implementation.
1645
1646namespace detail {
1647
1648// Helper to perform prime factorization.
1649template <std::uintmax_t N>
1650struct PrimeFactorization;
1651template <std::uintmax_t N>
1652using PrimeFactorizationT = typename PrimeFactorization<N>::type;
1653
1654// Base case: factorization of 1.
1655template <>
1656struct PrimeFactorization<1u> : stdx::type_identity<Magnitude<>> {};
1657
1658template <std::uintmax_t N>
1659struct PrimeFactorization {
1660 static_assert(N > 0, "Can only factor positive integers");
1661
1662 static constexpr std::uintmax_t first_base = find_first_factor(N);
1663 static constexpr std::uintmax_t first_power = multiplicity(first_base, N);
1664 static constexpr std::uintmax_t remainder = N / int_pow(first_base, first_power);
1665
1666 using type =
1667 MagProductT<Magnitude<Pow<Prime<first_base>, first_power>>, PrimeFactorizationT<remainder>>;
1668};
1669
1670} // namespace detail
1671
1672template <std::size_t N>
1673constexpr auto mag() {
1674 return detail::PrimeFactorizationT<N>{};
1675}
1676
1678// `integer_part()` implementation.
1679
1680template <typename B, typename P>
1681struct IntegerPartOfBasePower : stdx::type_identity<Magnitude<>> {};
1682
1683// Raise B to the largest natural number power which won't exceed (N/D), or 0 if there isn't one.
1684template <std::uintmax_t B, std::intmax_t N, std::intmax_t D>
1685struct IntegerPartOfBasePower<Prime<B>, std::ratio<N, D>>
1686 : stdx::type_identity<MagPowerT<Magnitude<Prime<B>>, ((N >= D) ? (N / D) : 0)>> {};
1687
1688template <typename... BPs>
1691 MagProductT<typename IntegerPartOfBasePower<BaseT<BPs>, ExpT<BPs>>::type...>> {};
1692
1694// `numerator()` implementation.
1695
1696template <typename... BPs>
1699 MagProductT<std::conditional_t<(ExpT<BPs>::num > 0), Magnitude<BPs>, Magnitude<>>...>> {};
1700
1702// `get_value<T>(Magnitude)` implementation.
1703
1704namespace detail {
1705
1706enum class MagRepresentationOutcome {
1707 OK,
1708 ERR_NON_INTEGER_IN_INTEGER_TYPE,
1709 ERR_INVALID_ROOT,
1710 ERR_CANNOT_FIT,
1711};
1712
1713template <typename T>
1715 MagRepresentationOutcome outcome;
1716
1717 // Only valid/meaningful if `outcome` is `OK`.
1718 T value = {0};
1719};
1720
1721// The widest arithmetic type in the same category.
1722//
1723// Used for intermediate computations.
1724template <typename T>
1725using Widen = std::conditional_t<
1727 std::conditional_t<std::is_floating_point<T>::value,
1728 long double,
1729 std::conditional_t<std::is_signed<T>::value, std::intmax_t, std::uintmax_t>>,
1730 T>;
1731
1732template <typename T>
1733constexpr MagRepresentationOrError<T> checked_int_pow(T base, std::uintmax_t exp) {
1734 MagRepresentationOrError<T> result = {MagRepresentationOutcome::OK, T{1}};
1735 while (exp > 0u) {
1736 if (exp % 2u == 1u) {
1737 if (base > std::numeric_limits<T>::max() / result.value) {
1738 return MagRepresentationOrError<T>{MagRepresentationOutcome::ERR_CANNOT_FIT};
1739 }
1740 result.value *= base;
1741 }
1742
1743 exp /= 2u;
1744
1745 if (base > std::numeric_limits<T>::max() / base) {
1746 return (exp == 0u)
1747 ? result
1748 : MagRepresentationOrError<T>{MagRepresentationOutcome::ERR_CANNOT_FIT};
1749 }
1750 base *= base;
1751 }
1752 return result;
1753}
1754
1755template <typename T>
1756constexpr MagRepresentationOrError<T> root(T x, std::uintmax_t n) {
1757 // The "zeroth root" would be mathematically undefined.
1758 if (n == 0) {
1759 return {MagRepresentationOutcome::ERR_INVALID_ROOT};
1760 }
1761
1762 // The "first root" is trivial.
1763 if (n == 1) {
1764 return {MagRepresentationOutcome::OK, x};
1765 }
1766
1767 // We only support nontrivial roots of floating point types.
1769 return {MagRepresentationOutcome::ERR_NON_INTEGER_IN_INTEGER_TYPE};
1770 }
1771
1772 // Handle negative numbers: only odd roots are allowed.
1773 if (x < 0) {
1774 if (n % 2 == 0) {
1775 return {MagRepresentationOutcome::ERR_INVALID_ROOT};
1776 } else {
1777 const auto negative_result = root(-x, n);
1778 if (negative_result.outcome != MagRepresentationOutcome::OK) {
1779 return {negative_result.outcome};
1780 }
1781 return {MagRepresentationOutcome::OK, static_cast<T>(-negative_result.value)};
1782 }
1783 }
1784
1785 // Handle special cases of zero and one.
1786 if (x == 0 || x == 1) {
1787 return {MagRepresentationOutcome::OK, x};
1788 }
1789
1790 // Handle numbers bewtween 0 and 1.
1791 if (x < 1) {
1792 const auto inverse_result = root(T{1} / x, n);
1793 if (inverse_result.outcome != MagRepresentationOutcome::OK) {
1794 return {inverse_result.outcome};
1795 }
1796 return {MagRepresentationOutcome::OK, static_cast<T>(T{1} / inverse_result.value)};
1797 }
1798
1799 //
1800 // At this point, error conditions are finished, and we can proceed with the "core" algorithm.
1801 //
1802
1803 // Always use `long double` for intermediate computations. We don't ever expect people to be
1804 // calling this at runtime, so we want maximum accuracy.
1805 long double lo = 1.0;
1806 long double hi = x;
1807
1808 // Do a binary search to find the closest value such that `checked_int_pow` recovers the input.
1809 //
1810 // Because we know `n > 1`, and `x > 1`, and x^n is monotonically increasing, we know that
1811 // `checked_int_pow(lo, n) < x < checked_int_pow(hi, n)`. We will preserve this as an
1812 // invariant.
1813 while (lo < hi) {
1814 long double mid = lo + (hi - lo) / 2;
1815
1816 auto result = checked_int_pow(mid, n);
1817
1818 if (result.outcome != MagRepresentationOutcome::OK) {
1819 return {result.outcome};
1820 }
1821
1822 // Early return if we get lucky with an exact answer.
1823 if (result.value == x) {
1824 return {MagRepresentationOutcome::OK, static_cast<T>(mid)};
1825 }
1826
1827 // Check for stagnation.
1828 if (mid == lo || mid == hi) {
1829 break;
1830 }
1831
1832 // Preserve the invariant that `checked_int_pow(lo, n) < x < checked_int_pow(hi, n)`.
1833 if (result.value < x) {
1834 lo = mid;
1835 } else {
1836 hi = mid;
1837 }
1838 }
1839
1840 // Pick whichever one gets closer to the target.
1841 const auto lo_diff = x - checked_int_pow(lo, n).value;
1842 const auto hi_diff = checked_int_pow(hi, n).value - x;
1843 return {MagRepresentationOutcome::OK, static_cast<T>(lo_diff < hi_diff ? lo : hi)};
1844}
1845
1846template <typename T, std::intmax_t N, std::uintmax_t D, typename B>
1847constexpr MagRepresentationOrError<Widen<T>> base_power_value(B base) {
1848 if (N < 0) {
1849 const auto inverse_result = base_power_value<T, -N, D>(base);
1850 if (inverse_result.outcome != MagRepresentationOutcome::OK) {
1851 return inverse_result;
1852 }
1853 return {
1854 MagRepresentationOutcome::OK,
1855 Widen<T>{1} / inverse_result.value,
1856 };
1857 }
1858
1859 const auto power_result =
1860 checked_int_pow(static_cast<Widen<T>>(base), static_cast<std::uintmax_t>(N));
1861 if (power_result.outcome != MagRepresentationOutcome::OK) {
1862 return {power_result.outcome};
1863 }
1864 return (D > 1) ? root(power_result.value, D) : power_result;
1865}
1866
1867template <typename T, std::size_t N>
1868constexpr MagRepresentationOrError<T> product(const MagRepresentationOrError<T> (&values)[N]) {
1869 for (const auto &x : values) {
1870 if (x.outcome != MagRepresentationOutcome::OK) {
1871 return x;
1872 }
1873 }
1874
1875 T result{1};
1876 for (const auto &x : values) {
1877 if ((x.value > 1) && (result > std::numeric_limits<T>::max() / x.value)) {
1878 return {MagRepresentationOutcome::ERR_CANNOT_FIT};
1879 }
1880 result *= x.value;
1881 }
1882 return {MagRepresentationOutcome::OK, result};
1883}
1884
1885template <std::size_t N>
1886constexpr bool all(const bool (&values)[N]) {
1887 for (const auto &x : values) {
1888 if (!x) {
1889 return false;
1890 }
1891 }
1892 return true;
1893}
1894
1895template <typename Target, typename Enable = void>
1897 template <typename T>
1898 constexpr bool operator()(T x) {
1899 return stdx::cmp_less_equal(std::numeric_limits<Target>::lowest(), x) &&
1900 stdx::cmp_greater_equal(std::numeric_limits<Target>::max(), x);
1901 }
1902};
1903
1904template <typename Target>
1905struct SafeCastingChecker<Target, std::enable_if_t<std::is_integral<Target>::value>> {
1906 template <typename T>
1907 constexpr bool operator()(T x) {
1909 stdx::cmp_less_equal(std::numeric_limits<Target>::lowest(), x) &&
1910 stdx::cmp_greater_equal(std::numeric_limits<Target>::max(), x);
1911 }
1912};
1913
1914template <typename T, typename InputT>
1915constexpr bool safe_to_cast_to(InputT x) {
1916 return SafeCastingChecker<T>{}(x);
1917}
1918
1919template <typename T, typename... BPs>
1920constexpr MagRepresentationOrError<T> get_value_result(Magnitude<BPs...>) {
1921 // Representing non-integer values in integral types is something we never plan to support.
1922 constexpr bool REPRESENTING_NON_INTEGER_IN_INTEGRAL_TYPE =
1923 stdx::conjunction<std::is_integral<T>, stdx::negation<IsInteger<Magnitude<BPs...>>>>::value;
1924 if (REPRESENTING_NON_INTEGER_IN_INTEGRAL_TYPE) {
1925 return {MagRepresentationOutcome::ERR_NON_INTEGER_IN_INTEGER_TYPE};
1926 }
1927
1928 // Force the expression to be evaluated in a constexpr context.
1929 constexpr auto widened_result =
1930 product({base_power_value<T, ExpT<BPs>::num, static_cast<std::uintmax_t>(ExpT<BPs>::den)>(
1931 BaseT<BPs>::value())...});
1932
1933 if ((widened_result.outcome != MagRepresentationOutcome::OK) ||
1934 !safe_to_cast_to<T>(widened_result.value)) {
1935 return {MagRepresentationOutcome::ERR_CANNOT_FIT};
1936 }
1937
1938 return {MagRepresentationOutcome::OK, static_cast<T>(widened_result.value)};
1939}
1940
1941// This simple overload avoids edge cases with creating and passing zero-sized arrays.
1942template <typename T>
1943constexpr MagRepresentationOrError<T> get_value_result(Magnitude<>) {
1944 return {MagRepresentationOutcome::OK, static_cast<T>(1)};
1945}
1946} // namespace detail
1947
1948template <typename T, typename... BPs>
1949constexpr bool representable_in(Magnitude<BPs...> m) {
1950 using namespace detail;
1951
1952 return get_value_result<T>(m).outcome == MagRepresentationOutcome::OK;
1953}
1954
1955template <typename T, typename... BPs>
1956constexpr T get_value(Magnitude<BPs...> m) {
1957 using namespace detail;
1958
1959 constexpr auto result = get_value_result<T>(m);
1960
1961 static_assert(result.outcome != MagRepresentationOutcome::ERR_NON_INTEGER_IN_INTEGER_TYPE,
1962 "Cannot represent non-integer in integral destination type");
1963 static_assert(result.outcome != MagRepresentationOutcome::ERR_INVALID_ROOT,
1964 "Could not compute root for rational power of base");
1965 static_assert(result.outcome != MagRepresentationOutcome::ERR_CANNOT_FIT,
1966 "Value outside range of destination type");
1967
1968 static_assert(result.outcome == MagRepresentationOutcome::OK, "Unknown error occurred");
1969 return result.value;
1970}
1971
1973// `CommonMagnitude` implementation.
1974
1975namespace detail {
1976// Helper: prepend a base power, but only if the Exp is negative.
1977template <typename BP, typename MagT>
1979template <typename BP, typename MagT>
1980using PrependIfExpNegativeT = typename PrependIfExpNegative<BP, MagT>::type;
1981template <typename BP, typename... Ts>
1983 : std::conditional<(ExpT<BP>::num < 0), Magnitude<BP, Ts...>, Magnitude<Ts...>> {};
1984
1985// If M is (N/D), DenominatorPartT<M> is D; we want 1/D.
1986template <typename M>
1987using NegativePowers = MagInverseT<DenominatorPartT<M>>;
1988} // namespace detail
1989
1990// 1-ary case: identity.
1991template <typename M>
1992struct CommonMagnitude<M> : stdx::type_identity<M> {};
1993
1994// 2-ary base case: both Magnitudes null.
1995template <>
1996struct CommonMagnitude<Magnitude<>, Magnitude<>> : stdx::type_identity<Magnitude<>> {};
1997
1998// 2-ary base case: only left Magnitude is null.
1999template <typename Head, typename... Tail>
2000struct CommonMagnitude<Magnitude<>, Magnitude<Head, Tail...>>
2001 : stdx::type_identity<detail::NegativePowers<Magnitude<Head, Tail...>>> {};
2002
2003// 2-ary base case: only right Magnitude is null.
2004template <typename Head, typename... Tail>
2005struct CommonMagnitude<Magnitude<Head, Tail...>, Magnitude<>>
2006 : stdx::type_identity<detail::NegativePowers<Magnitude<Head, Tail...>>> {};
2007
2008// 2-ary recursive case: two non-null Magnitudes.
2009template <typename H1, typename... T1, typename H2, typename... T2>
2010struct CommonMagnitude<Magnitude<H1, T1...>, Magnitude<H2, T2...>> :
2011
2012 // If the bases for H1 and H2 are in-order, prepend H1-if-negative to the remainder.
2013 std::conditional<
2014 (InOrderFor<Magnitude, BaseT<H1>, BaseT<H2>>::value),
2015 detail::PrependIfExpNegativeT<H1, CommonMagnitudeT<Magnitude<T1...>, Magnitude<H2, T2...>>>,
2016
2017 // If the bases for H2 and H1 are in-order, prepend H2-if-negative to the remainder.
2018 std::conditional_t<
2019 (InOrderFor<Magnitude, BaseT<H2>, BaseT<H1>>::value),
2020 detail::PrependIfExpNegativeT<H2,
2021 CommonMagnitudeT<Magnitude<T2...>, Magnitude<H1, T1...>>>,
2022
2023 // If we got here, the bases must be the same. (We can assume that `InOrderFor` does
2024 // proper checking to guard against equivalent-but-not-identical bases, which would
2025 // violate total ordering.)
2026 std::conditional_t<
2027 (std::ratio_subtract<ExpT<H1>, ExpT<H2>>::num < 0),
2028 detail::PrependT<CommonMagnitudeT<Magnitude<T1...>, Magnitude<T2...>>, H1>,
2029 detail::PrependT<CommonMagnitudeT<Magnitude<T1...>, Magnitude<T2...>>, H2>>>> {};
2030
2031// N-ary case: recurse.
2032template <typename M1, typename M2, typename... Tail>
2033struct CommonMagnitude<M1, M2, Tail...> : CommonMagnitude<M1, CommonMagnitudeT<M2, Tail...>> {};
2034
2035// Zero is always ignored.
2036template <typename M>
2037struct CommonMagnitude<M, Zero> : stdx::type_identity<M> {};
2038template <typename M>
2039struct CommonMagnitude<Zero, M> : stdx::type_identity<M> {};
2040template <>
2041struct CommonMagnitude<Zero, Zero> : stdx::type_identity<Zero> {};
2042
2043} // namespace au
2044
2045
2046
2047// This file exists to analyze one single calculation: `x * N / D`, where `x` is
2048// some integral type, and `N` and `D` are the numerator and denominator of a
2049// rational magnitude (and hence, are automatically in lowest terms),
2050// represented in that same type. We want to answer one single question: will
2051// this calculation overflow at any stage?
2052//
2053// Importantly, we need to produce correct answers even when `N` and/or `D`
2054// _cannot be represented_ in that type (because they would overflow). We also
2055// need to handle subtleties around integer promotion, where the type of `x * x`
2056// can be different from the type of `x` when those types are small.
2057//
2058// The goal for the final solution we produce is to be as fast and efficient as
2059// the best such function that an expert C++ engineer could produce by hand, for
2060// every combination of integral type and numerator and denominator magnitudes.
2061
2062namespace au {
2063namespace detail {
2064
2066//
2067// `PromotedType<T>` is the result type for arithmetic operations involving `T`. Of course, this is
2068// normally just `T`, but integer promotion for small integral types can change this.
2069//
2070template <typename T>
2071struct PromotedTypeImpl {
2072 using type = decltype(std::declval<T>() * std::declval<T>());
2073
2074 static_assert(std::is_same<type, typename PromotedTypeImpl<type>::type>::value,
2075 "We explicitly assume that promoted types are not again promotable");
2076};
2077template <typename T>
2078using PromotedType = typename PromotedTypeImpl<T>::type;
2079
2081//
2082// `clamp_to_range_of<T>(x)` returns `x` if it is in the range of `T`, and otherwise returns the
2083// maximum value representable in `T` if `x` is too large, or the minimum value representable in `T`
2084// if `x` is too small.
2085//
2086
2087template <typename T, typename U>
2088constexpr T clamp_to_range_of(U x) {
2089 return stdx::cmp_greater(x, std::numeric_limits<T>::max())
2090 ? std::numeric_limits<T>::max()
2091 : (stdx::cmp_less(x, std::numeric_limits<T>::lowest())
2092 ? std::numeric_limits<T>::lowest()
2093 : static_cast<T>(x));
2094}
2095
2097//
2098// `is_known_to_be_less_than_one(MagT)` is true if the magnitude `MagT` is purely rational; its
2099// numerator is representable in `std::uintmax_t`; and, it is less than 1.
2100//
2101
2102template <typename... BPs>
2103constexpr bool is_known_to_be_less_than_one(Magnitude<BPs...> m) {
2104 using MagT = Magnitude<BPs...>;
2105 static_assert(is_rational(m), "Magnitude must be rational");
2106
2107 constexpr auto num_result = get_value_result<std::uintmax_t>(numerator(MagT{}));
2108 static_assert(num_result.outcome == MagRepresentationOutcome::OK,
2109 "Magnitude must be representable in std::uintmax_t");
2110
2111 constexpr auto den_result = get_value_result<std::uintmax_t>(denominator(MagT{}));
2112 static_assert(
2113 den_result.outcome == MagRepresentationOutcome::OK ||
2114 den_result.outcome == MagRepresentationOutcome::ERR_CANNOT_FIT,
2115 "Magnitude must either be representable in std::uintmax_t, or fail due to overflow");
2116
2117 return den_result.outcome == MagRepresentationOutcome::OK ? num_result.value < den_result.value
2118 : true;
2119}
2120
2122//
2123// `MaxNonOverflowingValue<T, MagT>` is the maximum value of type `T` that can have `MagT` applied
2124// as numerator-and-denominator without overflowing. We require that `T` is some integral
2125// arithmetic type, and that `MagT` is a rational magnitude that is neither purely integral nor
2126// purely inverse-integral.
2127//
2128// If you are trying to understand these helpers, we suggest starting at the bottom with
2129// `MaxNonOverflowingValue`, and reading upwards.
2130//
2131
2132//
2133// Branch based on whether `MagT` is less than 1.
2134//
2135template <typename T, typename MagT, bool IsMagLessThanOne>
2136struct MaxNonOverflowingValueImplWhenNumFits;
2137
2138// If `MagT` is less than 1, then we only need to check for the limiting value where the _numerator
2139// multiplication step alone_ would overflow.
2140template <typename T, typename MagT>
2141struct MaxNonOverflowingValueImplWhenNumFits<T, MagT, true> {
2142 using P = PromotedType<T>;
2143
2144 static constexpr T value() {
2145 return clamp_to_range_of<T>(std::numeric_limits<P>::max() /
2146 get_value<P>(numerator(MagT{})));
2147 }
2148};
2149
2150// If `MagT` is greater than 1, then we have two opportunities for overflow: the numerator
2151// multiplication step can overflow the promoted type; or, the denominator division step can fail to
2152// restore it to the original type's range.
2153template <typename T, typename MagT>
2154struct MaxNonOverflowingValueImplWhenNumFits<T, MagT, false> {
2155 using P = PromotedType<T>;
2156
2157 static constexpr T value() {
2158 constexpr auto num = get_value<P>(numerator(MagT{}));
2159 constexpr auto den = get_value<P>(denominator(MagT{}));
2160 constexpr auto t_max = std::numeric_limits<T>::max();
2161 constexpr auto p_max = std::numeric_limits<P>::max();
2162 constexpr auto limit_to_avoid = (den > p_max / t_max) ? p_max : t_max * den;
2163 return clamp_to_range_of<T>(limit_to_avoid / num);
2164 }
2165};
2166
2167//
2168// Branch based on whether the numerator of `MagT` can fit in the promoted type of `T`.
2169//
2170template <typename T, typename MagT, MagRepresentationOutcome NumOutcome>
2171struct MaxNonOverflowingValueImpl;
2172
2173// If the numerator fits in the promoted type of `T`, delegate further based on whether the
2174// denominator is bigger.
2175template <typename T, typename MagT>
2176struct MaxNonOverflowingValueImpl<T, MagT, MagRepresentationOutcome::OK>
2177 : MaxNonOverflowingValueImplWhenNumFits<T, MagT, is_known_to_be_less_than_one(MagT{})> {};
2178
2179// If `MagT` can't be represented in the promoted type of `T`, then the result is 0.
2180template <typename T, typename MagT>
2181struct MaxNonOverflowingValueImpl<T, MagT, MagRepresentationOutcome::ERR_CANNOT_FIT> {
2182 static constexpr T value() { return T{0}; }
2183};
2184
2185template <typename T, typename MagT>
2186struct ValidateTypeAndMagnitude {
2187 static_assert(std::is_integral<T>::value, "Only designed for integral types");
2188 static_assert(is_rational(MagT{}), "Magnitude must be rational");
2189 static_assert(!is_integer(MagT{}), "Magnitude must not be purely integral");
2190 static_assert(!is_integer(inverse(MagT{})), "Magnitude must not be purely inverse-integral");
2191};
2192
2193template <typename T, typename MagT>
2194struct MaxNonOverflowingValue
2195 : ValidateTypeAndMagnitude<T, MagT>,
2196 MaxNonOverflowingValueImpl<T,
2197 MagT,
2198 get_value_result<PromotedType<T>>(numerator(MagT{})).outcome> {};
2199
2201//
2202// `MinNonOverflowingValue<T, MagT>` is the minimum (i.e., most-negative) value of type `T` that can
2203// have `MagT` applied as numerator-and-denominator without overflowing (i.e., becoming too-negative
2204// to represent). We require that `T` is some integral arithmetic type, and that `MagT` is a
2205// rational magnitude that is neither purely integral nor purely inverse-integral.
2206//
2207// If you are trying to understand these helpers, we suggest starting at the bottom with
2208// `MinNonOverflowingValue`, and reading upwards.
2209//
2210
2211//
2212// Branch based on whether `MagT` is less than 1.
2213//
2214template <typename T, typename MagT, bool IsMagLessThanOne>
2215struct MinNonOverflowingValueImplWhenNumFits;
2216
2217// If `MagT` is less than 1, then we only need to check for the limiting value where the _numerator
2218// multiplication step alone_ would overflow.
2219template <typename T, typename MagT>
2220struct MinNonOverflowingValueImplWhenNumFits<T, MagT, true> {
2221 using P = PromotedType<T>;
2222
2223 static constexpr T value() {
2224 return clamp_to_range_of<T>(std::numeric_limits<P>::lowest() /
2225 get_value<P>(numerator(MagT{})));
2226 }
2227};
2228
2229// If `MagT` is greater than 1, then we have two opportunities for overflow: the numerator
2230// multiplication step can overflow the promoted type; or, the denominator division step can fail to
2231// restore it to the original type's range.
2232template <typename T, typename MagT>
2233struct MinNonOverflowingValueImplWhenNumFits<T, MagT, false> {
2234 using P = PromotedType<T>;
2235
2236 static constexpr T value() {
2237 constexpr auto num = get_value<P>(numerator(MagT{}));
2238 constexpr auto den = get_value<P>(denominator(MagT{}));
2239 constexpr auto t_min = std::numeric_limits<T>::lowest();
2240 constexpr auto p_min = std::numeric_limits<P>::lowest();
2241 constexpr auto limit_to_avoid = (den > p_min / t_min) ? p_min : t_min * den;
2242 return clamp_to_range_of<T>(limit_to_avoid / num);
2243 }
2244};
2245
2246//
2247// Branch based on whether the denominator of `MagT` can fit in the promoted type of `T`.
2248//
2249template <typename T, typename MagT, MagRepresentationOutcome NumOutcome>
2250struct MinNonOverflowingValueImpl;
2251
2252// If the numerator fits in the promoted type of `T`, delegate further based on whether the
2253// denominator is bigger.
2254template <typename T, typename MagT>
2255struct MinNonOverflowingValueImpl<T, MagT, MagRepresentationOutcome::OK>
2256 : MinNonOverflowingValueImplWhenNumFits<T, MagT, is_known_to_be_less_than_one(MagT{})> {};
2257
2258// If the numerator can't be represented in the promoted type of `T`, then the result is 0.
2259template <typename T, typename MagT>
2260struct MinNonOverflowingValueImpl<T, MagT, MagRepresentationOutcome::ERR_CANNOT_FIT> {
2261 static constexpr T value() { return T{0}; }
2262};
2263
2264template <typename T, typename MagT>
2265struct MinNonOverflowingValue
2266 : ValidateTypeAndMagnitude<T, MagT>,
2267 MinNonOverflowingValueImpl<T,
2268 MagT,
2269 get_value_result<PromotedType<T>>(numerator(MagT{})).outcome> {
2270 static_assert(std::is_signed<T>::value, "Only designed for signed types");
2271 static_assert(std::is_signed<PromotedType<T>>::value,
2272 "We assume the promoted type is also signed");
2273};
2274
2275} // namespace detail
2276} // namespace au
2277
2278
2279namespace au {
2280
2281// A "unit" is any type which has:
2282// - a member typedef `Dim`, which is a valid Dimension; and,
2283// - a member typedef `Mag`, which is a valid Magnitude.
2284//
2285// These can be accessed by traits `detail::DimT` and `detail::MagT`, respectively. The detail
2286// namespace is meant to discourage _end users_ from accessing these concepts directly. For
2287// example, we don't want end users to ask _which dimension_ a Unit has. We'd rather they ask
2288// whether it is the _same_ as some other unit. (It's also meaningful to ask whether it is
2289// dimensionless.) And we certainly don't want end users to try to reason about "the magnitude" of
2290// a Unit, since this is totally meaningless; rather, we want them to ask about the _relative_
2291// magnitude with another unit of _the same dimension_.
2292
2293// A UnitImpl is one easy way (although not the only way) to make a "Unit".
2294template <typename D, typename M = Magnitude<>>
2295struct UnitImpl {
2296 using Dim = D;
2297 using Mag = M;
2298};
2299
2301// Printable labels for units.
2302
2303// A printable label to indicate the unit for human readers.
2304//
2305// To name a unit explicitly, specialize this class template for the unit's type. For any unit not
2306// manually labeled, we provide a default label so that this template is always defined.
2307//
2308// Valid ways to define the label include a C-style const char array, or a StringConstant<N>.
2309template <typename Unit>
2310struct UnitLabel;
2311
2312// A sizeof()-compatible API to get the label for a unit.
2313template <typename Unit>
2314constexpr const auto &unit_label(Unit = Unit{});
2315
2316// Default label for a unit which hasn't been manually labeled yet.
2317//
2318// The dummy template parameter exists to enable `au` to be a header-only library.
2319template <typename T = void>
2320struct DefaultUnitLabel {
2321 static constexpr const char value[] = "[UNLABELED UNIT]";
2322};
2323template <typename T>
2324constexpr const char DefaultUnitLabel<T>::value[];
2325
2326namespace detail {
2327// To preserve support for C++14, we need to _name the type_ of the member variable. However, the
2328// `StringConstant` template produces a different type for every length, and that length depends on
2329// _both_ the prefix _and_ the unit label.
2330//
2331// To minimize friction as much as possible, we create this alias, which computes the type we need
2332// for a given unit and prefix-length.
2333//
2334// While clunky, this approach is at least robust against errors. If the user supplies the wrong
2335// prefix length, it will fail to compile, because there is no assignment operator between
2336// `StringConstant` instances of different lengths.
2337template <std::size_t ExtensionStrlen, typename... Us>
2338using ExtendedLabel = StringConstant<concatenate(unit_label<Us>()...).size() + ExtensionStrlen>;
2339} // namespace detail
2340
2342// Type traits.
2343
2344// Type trait to detect whether a type fulfills our definition of a "Unit".
2345template <typename T>
2346struct IsUnit : stdx::conjunction<IsValidPack<Dimension, detail::DimT<T>>,
2347 IsValidPack<Magnitude, detail::MagT<T>>> {};
2348
2349// Type trait to detect whether two Units have the same Dimension.
2350template <typename... Us>
2351struct HasSameDimension;
2352
2353// Type trait to detect whether two Units are quantity-equivalent.
2354//
2355// In this library, Units are "quantity-equivalent" exactly when they have the same Dimension and
2356// Magnitude. Quantity instances whose Units are quantity-equivalent can be freely interconverted
2357// with each other.
2358template <typename U1, typename U2>
2359struct AreUnitsQuantityEquivalent;
2360
2361// Type trait to detect whether two Units are point-equivalent.
2362//
2363// In this library, Units are "point-equivalent" exactly when they are quantity-equivalent (see
2364// above), _and_ they have the same origin. QuantityPoint instances whose Units are
2365// point-equivalent can be freely interconverted with each other.
2366template <typename U1, typename U2>
2367struct AreUnitsPointEquivalent;
2368
2369// Type trait to detect whether U is a Unit which is dimensionless.
2370template <typename U>
2371struct IsDimensionless : std::is_same<detail::DimT<U>, Dimension<>> {};
2372
2373// Type trait to detect whether a Unit is "quantity-equivalent" to "the unitless unit".
2374//
2375// The "unitless unit" is a dimensionless unit of Magnitude 1 (as opposed to, say, other
2376// dimensionless units such as Percent).
2377template <typename U>
2378struct IsUnitlessUnit
2379 : stdx::conjunction<IsDimensionless<U>, std::is_same<detail::MagT<U>, Magnitude<>>> {};
2380
2381// A Magnitude representing the ratio of two same-dimensioned units.
2382//
2383// Useful in doing unit conversions.
2384template <typename U1, typename U2>
2385struct UnitRatio : stdx::type_identity<MagQuotientT<detail::MagT<U1>, detail::MagT<U2>>> {
2386 static_assert(HasSameDimension<U1, U2>::value,
2387 "Can only compute ratio of same-dimension units");
2388};
2389template <typename U1, typename U2>
2390using UnitRatioT = typename UnitRatio<U1, U2>::type;
2391
2392// Some units have an "origin". This is not meaningful by itself, but its difference w.r.t. the
2393// "origin" of another unit of the same Dimension _is_ meaningful. This type trait provides access
2394// to that difference.
2395template <typename U1, typename U2>
2396struct OriginDisplacement;
2397
2398template <typename U>
2399struct AssociatedUnit : stdx::type_identity<U> {};
2400template <typename U>
2401using AssociatedUnitT = typename AssociatedUnit<U>::type;
2402
2403// `CommonUnitT`: the largest unit that evenly divides all input units.
2404//
2405// A specialization will only exist if all input types are units.
2406//
2407// If the inputs are units, but their Dimensions aren't all identical, then the request is
2408// ill-formed and we will produce a hard error.
2409//
2410// It may happen that the input units have the same Dimension, but there is no unit which evenly
2411// divides them (because some pair of input units has an irrational quotient). In this case, there
2412// is no uniquely defined answer, but the program should still produce _some_ answer. We guarantee
2413// that the result is associative, and symmetric under any reordering of the input units. The
2414// specific implementation choice will be driven by convenience and simplicity.
2415template <typename... Us>
2416struct ComputeCommonUnit;
2417template <typename... Us>
2418using CommonUnitT = typename ComputeCommonUnit<Us...>::type;
2419
2420// `CommonPointUnitT`: the largest-magnitude, highest-origin unit which is "common" to the units of
2421// a collection of `QuantityPoint` instances.
2422//
2423// The key goal to keep in mind is that for a `QuantityPoint` of any unit `U` in `Us...`, converting
2424// its value to the common point-unit should involve only:
2425//
2426// - multiplication by a _positive integer_
2427// - addition of a _non-negative integer_
2428//
2429// This helps us support the widest range of Rep types (in particular, unsigned integers).
2430//
2431// As with `CommonUnitT`, this isn't always possible: in particular, we can't do this for units with
2432// irrational relative magnitudes or origin displacements. However, we still provide _some_ answer,
2433// which is consistent with the above policy whenever it's achievable, and produces reasonable
2434// results in all other cases.
2435//
2436// A specialization will only exist if the inputs are all units, and will exist but produce a hard
2437// error if any two input units have different Dimensions. We also strive to keep the result
2438// associative, and symmetric under interchange of any inputs.
2439template <typename... Us>
2440struct ComputeCommonPointUnit;
2441template <typename... Us>
2442using CommonPointUnitT = typename ComputeCommonPointUnit<Us...>::type;
2443
2445// Type traits (instance-based interface).
2446
2447// `is_unit(T)`: check whether this value is an instance of some Unit type.
2448template <typename T>
2449constexpr bool is_unit(T) {
2450 return IsUnit<T>::value;
2451}
2452
2453// `fits_in_unit_slot(T)`: check whether this value is valid for a unit slot.
2454template <typename T>
2455constexpr bool fits_in_unit_slot(T) {
2456 return IsUnit<AssociatedUnitT<T>>::value;
2457}
2458
2459// Check whether the units associated with these objects have the same Dimension.
2460template <typename... Us>
2461constexpr bool has_same_dimension(Us...) {
2462 return HasSameDimension<AssociatedUnitT<Us>...>::value;
2463}
2464
2465// Check whether two Unit types are exactly quantity-equivalent.
2466template <typename U1, typename U2>
2467constexpr bool are_units_quantity_equivalent(U1, U2) {
2468 return AreUnitsQuantityEquivalent<AssociatedUnitT<U1>, AssociatedUnitT<U2>>::value;
2469}
2470
2471// Check whether two Unit types are exactly point-equivalent.
2472template <typename U1, typename U2>
2473constexpr bool are_units_point_equivalent(U1, U2) {
2474 return AreUnitsPointEquivalent<AssociatedUnitT<U1>, AssociatedUnitT<U2>>::value;
2475}
2476
2477// Check whether this value is an instance of a dimensionless Unit.
2478template <typename U>
2479constexpr bool is_dimensionless(U) {
2480 return IsDimensionless<AssociatedUnitT<U>>::value;
2481}
2482
2483// Type trait to detect whether a Unit is "the unitless unit".
2484template <typename U>
2485constexpr bool is_unitless_unit(U) {
2486 return IsUnitlessUnit<AssociatedUnitT<U>>::value;
2487}
2488
2489// A Magnitude representing the ratio of two same-dimensioned units.
2490//
2491// Useful in doing unit conversions.
2492template <typename U1, typename U2>
2493constexpr UnitRatioT<AssociatedUnitT<U1>, AssociatedUnitT<U2>> unit_ratio(U1, U2) {
2494 return {};
2495}
2496
2497template <typename U1, typename U2>
2498constexpr auto origin_displacement(U1, U2) {
2499 return OriginDisplacement<AssociatedUnitT<U1>, AssociatedUnitT<U2>>::value();
2500}
2501
2502template <typename U>
2503constexpr auto associated_unit(U) {
2504 return AssociatedUnitT<U>{};
2505}
2506
2508// Unit arithmetic traits: products, powers, and derived operations.
2509
2510// A Unit, scaled by some factor.
2511//
2512// Retains all of the member variables and typedefs of the existing Unit, except that the
2513// `detail::MagT` trait is appropriately scaled, and the unit label is erased.
2514//
2515// NOTE: This strategy will lead to long chains of inherited types when we scale a unit multiple
2516// times (say, going from Meters -> Centi<Meters> -> Inches -> Feet -> Miles). What's more, each
2517// element in this chain yields _two_ types: one for the named opaque typedef (e.g., `Feet`), and
2518// one for the anonymous scaled unit (e.g., `Inches * mag<12>()`). We explicitly assume that this
2519// will not cause any performance problems, because these should all be empty classes anyway. If we
2520// find out we're mistaken, we'll need to revisit this idea.
2521template <typename Unit, typename ScaleFactor>
2522struct ScaledUnit : Unit {
2523 static_assert(IsValidPack<Magnitude, ScaleFactor>::value,
2524 "Can only scale by a Magnitude<...> type");
2525 using Dim = detail::DimT<Unit>;
2526 using Mag = MagProductT<detail::MagT<Unit>, ScaleFactor>;
2527
2528 // We must ensure we don't give this unit the same label as the unscaled version!
2529 //
2530 // Later on, we could try generating a new label by "pretty printing" the scale factor.
2531 static constexpr auto &label = DefaultUnitLabel<void>::value;
2532};
2533
2534// Type template to hold the product of powers of Units.
2535template <typename... UnitPows>
2536struct UnitProduct {
2537 using Dim = DimProductT<detail::DimT<UnitPows>...>;
2538 using Mag = MagProductT<detail::MagT<UnitPows>...>;
2539};
2540
2541// Helper to make a canonicalized product of units.
2542//
2543// On the input side, we treat every input unit as a UnitProduct. Once we get our final result, we
2544// simplify it using `UnpackIfSoloT`. (The motivation is that we don't want to return, say,
2545// `UnitProduct<Meters>`; we'd rather just return `Meters`.)
2546template <typename... UnitPows>
2547using UnitProductT =
2548 UnpackIfSoloT<UnitProduct, PackProductT<UnitProduct, AsPackT<UnitProduct, UnitPows>...>>;
2549
2550// Raise a Unit to a (possibly rational) Power.
2551template <typename U, std::intmax_t ExpNum, std::intmax_t ExpDen = 1>
2552using UnitPowerT =
2553 UnpackIfSoloT<UnitProduct, PackPowerT<UnitProduct, AsPackT<UnitProduct, U>, ExpNum, ExpDen>>;
2554
2555// Compute the inverse of a unit.
2556template <typename U>
2557using UnitInverseT = UnitPowerT<U, -1>;
2558
2559// Compute the quotient of two units.
2560template <typename U1, typename U2>
2561using UnitQuotientT = UnitProductT<U1, UnitInverseT<U2>>;
2562
2564// Unit arithmetic on _instances_ of Units and/or Magnitudes.
2565
2566// Scale this Unit by multiplying by a Magnitude.
2567template <typename U, typename = std::enable_if_t<IsUnit<U>::value>, typename... BPs>
2568constexpr ScaledUnit<U, Magnitude<BPs...>> operator*(U, Magnitude<BPs...>) {
2569 return {};
2570}
2571
2572// Scale this Unit by dividing by a Magnitude.
2573template <typename U, typename = std::enable_if_t<IsUnit<U>::value>, typename... BPs>
2574constexpr ScaledUnit<U, MagInverseT<Magnitude<BPs...>>> operator/(U, Magnitude<BPs...>) {
2575 return {};
2576}
2577
2578// Compute the product of two unit instances.
2579template <typename U1,
2580 typename U2,
2581 typename = std::enable_if_t<stdx::conjunction<IsUnit<U1>, IsUnit<U2>>::value>>
2582constexpr UnitProductT<U1, U2> operator*(U1, U2) {
2583 return {};
2584}
2585
2586// Compute the quotient of two unit instances.
2587template <typename U1,
2588 typename U2,
2589 typename = std::enable_if_t<stdx::conjunction<IsUnit<U1>, IsUnit<U2>>::value>>
2590constexpr UnitQuotientT<U1, U2> operator/(U1, U2) {
2591 return {};
2592}
2593
2594// Raise a Unit to an integral power.
2595template <std::intmax_t Exp, typename U, typename = std::enable_if_t<IsUnit<U>::value>>
2596constexpr UnitPowerT<U, Exp> pow(U) {
2597 return {};
2598}
2599
2600// Take the Root (of some integral degree) of a Unit.
2601template <std::uintmax_t Deg, typename U, typename = std::enable_if_t<IsUnit<U>::value>>
2602constexpr UnitPowerT<U, 1, Deg> root(U) {
2603 return {};
2604}
2605
2607// Miscellaneous interfaces.
2608
2609// An instance which lets us refer to a unit by its singular name.
2610//
2611// To use this, whenever you define a new unit (e.g., `struct Meters`), follow it up with a line
2612// like the following:
2613//
2614// constexpr auto meter = SingularNameFor<Meters>{};
2615//
2616// This is just to help us write grammatically natural code. Examples:
2617//
2618// - `torque.in(newton * meters)`
2619// ^^^^^^
2620// - `speed.as(miles / hour)`
2621// ^^^^
2622template <typename Unit>
2623struct SingularNameFor {
2624
2625 // Multiplying `SingularNameFor` instances enables compound units such as:
2626 // `radians / (meter * second)`.
2627 template <typename OtherUnit>
2628 constexpr auto operator*(SingularNameFor<OtherUnit>) const {
2629 return SingularNameFor<UnitProductT<Unit, OtherUnit>>{};
2630 }
2631};
2632
2633template <int Exp, typename Unit>
2634constexpr auto pow(SingularNameFor<Unit>) {
2635 return SingularNameFor<UnitPowerT<Unit, Exp>>{};
2636}
2637
2639// Implementation details below
2641
2643// `OriginDisplacement` implementation.
2644
2645namespace detail {
2646// Callable type trait for the default origin of a unit: choose ZERO.
2647struct ZeroValue {
2648 static constexpr Zero value() { return Zero{}; }
2649};
2650
2651template <typename U>
2652using OriginMemberType = decltype(U::origin());
2653
2654// If any unit U has an explicit origin member, then treat that as its origin.
2655template <typename U>
2656struct OriginMember {
2657 static constexpr const OriginMemberType<U> value() { return U::origin(); }
2658};
2659
2660template <typename U>
2661struct OriginOf : std::conditional_t<stdx::experimental::is_detected<OriginMemberType, U>::value,
2662 OriginMember<U>,
2663 ZeroValue> {};
2664
2665template <typename T, typename U>
2666struct ValueDifference {
2667 static constexpr auto value() { return T::value() - U::value(); }
2668};
2669} // namespace detail
2670
2671// Why this conditional, instead of just using `ValueDifference` unconditionally? The use case is
2672// somewhat subtle. Without it, we would still deduce a displacement _numerically_ equal to 0, but
2673// it would be stored in specific _units_. For example, for Celsius, the displacement would be "0
2674// millikelvins" rather than a generic ZERO. This has implications for type deduction. It means
2675// that, e.g., the following would fail!
2676//
2677// celsius_pt(20).in(celsius_pt);
2678//
2679// The reason it would fail is because under the hood, we'd be subtracting a `QuantityI32<Celsius>`
2680// from a `QuantityI32<Milli<Kelvins>>`, yielding a result expressed in millikelvins for what should
2681// be an integer number of degrees Celsius. True, that result happens to have a _value_ of 0... but
2682// values don't affect overload resolution!
2683//
2684// Using ZeroValue when the origins are equal fixes this problem, by expressing the "zero-ness" in
2685// the _type_.
2686template <typename U1, typename U2>
2687struct OriginDisplacement
2688 : std::conditional_t<detail::OriginOf<U1>::value() == detail::OriginOf<U2>::value(),
2689 detail::ZeroValue,
2690 detail::ValueDifference<detail::OriginOf<U2>, detail::OriginOf<U1>>> {};
2691
2693// `HasSameDimension` implementation.
2694
2695template <typename U>
2696struct HasSameDimension<U> : std::true_type {};
2697
2698template <typename U1, typename U2, typename... Us>
2699struct HasSameDimension<U1, U2, Us...>
2700 : stdx::conjunction<std::is_same<detail::DimT<U1>, detail::DimT<U2>>,
2701 HasSameDimension<U2, Us...>> {};
2702
2704// `AreUnitsQuantityEquivalent` implementation.
2705
2706namespace detail {
2707// We don't want to advertise this utility, because "same magnitude" is meaningless unless the units
2708// also have the same dimension.
2709template <typename U1, typename U2>
2710struct HasSameMagnitude : std::is_same<detail::MagT<U1>, detail::MagT<U2>> {};
2711} // namespace detail
2712
2713template <typename U1, typename U2>
2714struct AreUnitsQuantityEquivalent
2715 : stdx::conjunction<HasSameDimension<U1, U2>, detail::HasSameMagnitude<U1, U2>> {};
2716
2718// `AreUnitsPointEquivalent` implementation.
2719
2720namespace detail {
2721template <typename U1, typename U2>
2722struct HasSameOrigin : stdx::bool_constant<(OriginDisplacement<U1, U2>::value() == ZERO)> {};
2723} // namespace detail
2724
2725template <typename U1, typename U2>
2726struct AreUnitsPointEquivalent
2727 : stdx::conjunction<AreUnitsQuantityEquivalent<U1, U2>, detail::HasSameOrigin<U1, U2>> {};
2728
2730// `CommonUnit` helper implementation.
2731
2732// This exists to be the "named type" for the common unit of a bunch of input units.
2733//
2734// To be well-formed, the units must be listed in the same order every time. End users cannot be
2735// responsible for this; thus, they should never name this type directly. Rather, they should name
2736// the `CommonUnitT` alias, which will handle the canonicalization.
2737template <typename... Us>
2738struct CommonUnit {
2739 static_assert(AreElementsInOrder<CommonUnit, CommonUnit<Us...>>::value,
2740 "Elements must be listed in ascending order");
2741 static_assert(HasSameDimension<Us...>::value,
2742 "Common unit only meaningful if units have same dimension");
2743
2744 using Dim = CommonDimensionT<detail::DimT<Us>...>;
2745 using Mag = CommonMagnitudeT<detail::MagT<Us>...>;
2746};
2747
2748template <typename A, typename B>
2749struct InOrderFor<CommonUnit, A, B> : InOrderFor<UnitProduct, A, B> {};
2750
2751namespace detail {
2752// This machinery searches a unit list for one that "matches" a target unit.
2753//
2754// If none do, it will produce the target unit.
2755
2756// Generic template.
2757template <template <class, class> class Matcher,
2758 typename TargetUnit,
2759 typename UnitList = TargetUnit>
2760struct FirstMatchingUnit;
2761
2762// Base case for an empty list: the target unit is the best match.
2763template <template <class, class> class Matcher,
2764 typename TargetUnit,
2765 template <class...>
2766 class List>
2767struct FirstMatchingUnit<Matcher, TargetUnit, List<>> : stdx::type_identity<TargetUnit> {};
2768
2769// Recursive case for a non-empty list: return head if it matches, or else recurse.
2770template <template <class, class> class Matcher,
2771 typename TargetUnit,
2772 template <class...>
2773 class List,
2774 typename H,
2775 typename... Ts>
2776struct FirstMatchingUnit<Matcher, TargetUnit, List<H, Ts...>>
2777 : std::conditional_t<Matcher<TargetUnit, H>::value,
2778 stdx::type_identity<H>,
2779 FirstMatchingUnit<Matcher, TargetUnit, List<Ts...>>> {};
2780
2781} // namespace detail
2782
2783template <typename... Us>
2784using ComputeCommonUnitImpl = FlatDedupedTypeListT<CommonUnit, Us...>;
2785
2786template <typename... Us>
2787struct ComputeCommonUnit
2788 : detail::FirstMatchingUnit<AreUnitsQuantityEquivalent, ComputeCommonUnitImpl<Us...>> {};
2789
2791// `CommonPointUnitT` helper implementation.
2792
2793namespace detail {
2794
2795// For equal origins expressed in different units, we can compare the values in their native units
2796// as a way to decide which unit has the biggest Magnitude. Bigger Magnitude, smaller value. (We
2797// could have tried to assess the Magnitude directly, but this method works better with Zero, and we
2798// will often encounter Zero when dealing with origins.)
2799//
2800// This will be used as a tiebreaker for different origin types. (For example, the origin of
2801// Celsius may be represented as Centikelvins or Millikelvins, and we want Centikelvins to "win"
2802// because it will result in smaller multiplications.)
2803template <typename T>
2804constexpr auto get_value_in_native_unit(const T &t) {
2805 return t.in(T::unit);
2806}
2807
2808// If the input is "0", then its value _in any unit_ is 0.
2809constexpr auto get_value_in_native_unit(const Zero &) { return 0; }
2810
2811// The common origin of a collection of units is the smallest origin.
2812//
2813// We try to keep the result symmetric under reordering of the inputs.
2814template <typename... Us>
2815struct CommonOrigin;
2816
2817template <typename U>
2818struct CommonOrigin<U> : OriginOf<U> {};
2819
2820template <typename Head, typename... Tail>
2821struct CommonOrigin<Head, Tail...> :
2822 // If the new value is strictly less than the common-so-far, then it wins, so choose it.
2823 std::conditional_t<
2824 (OriginOf<Head>::value() < CommonOrigin<Tail...>::value()),
2825 OriginOf<Head>,
2826
2827 // If the new value is strictly greater than the common-so-far, it's worse, so skip it.
2828 std::conditional_t<
2829 (OriginOf<Head>::value() > CommonOrigin<Tail...>::value()),
2830 CommonOrigin<Tail...>,
2831
2832 // If we're here, the origins represent the same _quantity_, but may be expressed in
2833 // different _units_. We'd like the biggest unit, since it leads to the smallest
2834 // multiplications. For equal quantities, "biggest unit" is equivalent to "smallest
2835 // value", so we compare the values.
2836 std::conditional_t<(get_value_in_native_unit(OriginOf<Head>::value()) <
2837 get_value_in_native_unit(CommonOrigin<Tail...>::value())),
2838 OriginOf<Head>,
2839 CommonOrigin<Tail...>>>> {};
2840
2841// MagTypeT<T> gives some measure of the size of the unit for this "quantity-alike" type.
2842//
2843// Zero acts like a quantity in this context, and we treat it as if its unit's Magnitude is Zero.
2844// This is specifically done for the `CommonPointUnit` implementation; there is no guarantee that
2845template <typename QuantityOrZero>
2846struct MagType : stdx::type_identity<MagT<typename QuantityOrZero::Unit>> {};
2847template <typename QuantityOrZero>
2848using MagTypeT = typename MagType<stdx::remove_cvref_t<QuantityOrZero>>::type;
2849template <>
2850struct MagType<Zero> : stdx::type_identity<Zero> {};
2851
2852} // namespace detail
2853
2854// This exists to be the "named type" for the common unit of a bunch of input units.
2855//
2856// To be well-formed, the units must be listed in the same order every time. End users cannot be
2857// responsible for this; thus, they should never name this type directly. Rather, they should name
2858// the `CommonPointUnitT` alias, which will handle the canonicalization.
2859template <typename... Us>
2860struct CommonPointUnit {
2861 static_assert(AreElementsInOrder<CommonPointUnit, CommonPointUnit<Us...>>::value,
2862 "Elements must be listed in ascending order");
2863 static_assert(HasSameDimension<Us...>::value,
2864 "Common unit only meaningful if units have same dimension");
2865
2866 // We need to store the origin member inside of a type, so that it will act "just enough" like a
2867 // unit to let us use `OriginDisplacement`. (We'll link to this nested type's origin member for
2868 // our own origin member.)
2869 struct TypeHoldingCommonOrigin {
2870 using OriginT = decltype(detail::CommonOrigin<Us...>::value());
2871 static constexpr OriginT origin() { return detail::CommonOrigin<Us...>::value(); }
2872 };
2873 static constexpr auto origin() { return TypeHoldingCommonOrigin::origin(); }
2874
2875 // This handles checking that all the dimensions are the same. It's what lets us reason in
2876 // terms of pure Magnitudes below, whereas usually this kind of reasoning is meaningless.
2877 using Dim = CommonDimensionT<detail::DimT<Us>...>;
2878
2879 // Now, for Magnitude reasoning. `OriginDisplacementMagnitude` tells us how finely grained we
2880 // are forced to split our Magnitude to handle the additive displacements from the common
2881 // origin. It might be `Zero` if there is no such constraint (which would mean all the units
2882 // have the _same_ origin).
2883 using OriginDisplacementMagnitude = CommonMagnitudeT<
2884 detail::MagTypeT<decltype(OriginDisplacement<TypeHoldingCommonOrigin, Us>::value())>...>;
2885
2886 // The final Magnitude is just what it would have been before, except that we also take the
2887 // results of `OriginDisplacementMagnitude` into account.
2888 using Mag = CommonMagnitudeT<detail::MagT<Us>..., OriginDisplacementMagnitude>;
2889};
2890
2891template <typename A, typename B>
2892struct InOrderFor<CommonPointUnit, A, B> : InOrderFor<UnitProduct, A, B> {};
2893
2894template <typename... Us>
2895using ComputeCommonPointUnitImpl = FlatDedupedTypeListT<CommonPointUnit, Us...>;
2896
2897template <typename... Us>
2898struct ComputeCommonPointUnit
2899 : detail::FirstMatchingUnit<AreUnitsPointEquivalent, ComputeCommonPointUnitImpl<Us...>> {};
2900
2902// `UnitLabel` implementation.
2903
2904namespace detail {
2905template <std::size_t N>
2906constexpr auto as_char_array(const char (&x)[N]) -> const char (&)[N] {
2907 return x;
2908}
2909
2910template <std::size_t N>
2911constexpr auto as_char_array(const StringConstant<N> &x) -> const char (&)[N + 1] {
2912 return x.char_array();
2913}
2914
2915template <typename Unit>
2916using HasLabel = decltype(Unit::label);
2917
2918// Implementation for units that do have a label.
2919template <typename T>
2920struct LabelRef {
2921 static constexpr auto &value = T::label;
2922};
2923
2924// Utility for labeling a unit raised to some power.
2925template <typename ExpLabel, typename Unit>
2927 using LabelT = ExtendedLabel<ExpLabel::value().size() + 1, Unit>;
2928 static constexpr LabelT value = join_by("^", unit_label<Unit>(), ExpLabel::value());
2929};
2930template <typename ExpLabeler, typename Unit>
2932
2933// Utility to generate the exponent label for a Pow.
2934template <std::intmax_t N>
2936 static constexpr auto value() { return parens_if<(N < 0)>(IToA<N>::value); }
2937};
2938
2939// Utility to generate the exponent label for a RatioPow.
2940template <std::intmax_t N, std::intmax_t D>
2942 static constexpr auto value() {
2943 return concatenate("(", IToA<N>::value, "/", IToA<D>::value, ")");
2944 }
2945};
2946
2947enum class ParensPolicy {
2948 OMIT,
2949 ADD_IF_MULITPLE,
2950};
2951
2952template <typename T, ParensPolicy Policy = ParensPolicy::ADD_IF_MULITPLE>
2954template <typename... Us, ParensPolicy Policy>
2955struct CompoundLabel<UnitProduct<Us...>, Policy> {
2956 static constexpr auto value() {
2957 constexpr bool add_parens =
2958 (Policy == ParensPolicy::ADD_IF_MULITPLE) && (sizeof...(Us) > 1);
2959 return parens_if<add_parens>(join_by(" * ", unit_label<Us>()...));
2960 }
2961};
2962
2963// Labeler for a quotient of products-of-Units: general case.
2964//
2965// The dummy template parameter exists to enable `au` to be a header-only library.
2966template <typename N, typename D, typename T = void>
2968 using LabelT =
2970 static constexpr LabelT value =
2972};
2973template <typename N, typename D, typename T>
2975
2976// Special case for denominator of 1.
2977template <typename N, typename T>
2982template <typename N, typename T>
2983constexpr typename QuotientLabeler<N, UnitProduct<>, T>::LabelT
2985
2986// Special case for numerator of 1.
2987template <typename D, typename T>
2989 using LabelT = StringConstant<CompoundLabel<D>::value().size() + 4>;
2990 static constexpr LabelT value = concatenate("1 / ", CompoundLabel<D>::value());
2991};
2992template <typename D, typename T>
2993constexpr typename QuotientLabeler<UnitProduct<>, D, T>::LabelT
2994 QuotientLabeler<UnitProduct<>, D, T>::value;
2995
2996// Special case for numerator _and_ denominator of 1 (null product).
2997template <typename T>
2999 static constexpr const char value[] = "";
3000};
3001template <typename T>
3002constexpr const char QuotientLabeler<UnitProduct<>, UnitProduct<>, T>::value[];
3003} // namespace detail
3004
3005// Unified implementation.
3006template <typename Unit>
3008 : std::conditional_t<stdx::experimental::is_detected<detail::HasLabel, Unit>::value,
3009 detail::LabelRef<Unit>,
3010 DefaultUnitLabel<void>> {};
3011
3012// Implementation for Pow.
3013template <typename Unit, std::intmax_t N>
3014struct UnitLabel<Pow<Unit, N>> : detail::PowerLabeler<detail::ExpLabelForPow<N>, Unit> {};
3015
3016// Implementation for RatioPow.
3017template <typename Unit, std::intmax_t N, std::intmax_t D>
3018struct UnitLabel<RatioPow<Unit, N, D>>
3019 : detail::PowerLabeler<detail::ExpLabelForRatioPow<N, D>, Unit> {};
3020
3021// Implementation for UnitProduct: split into positive and negative powers.
3022template <typename... Us>
3024 : detail::QuotientLabeler<detail::NumeratorPartT<UnitProduct<Us...>>,
3025 detail::DenominatorPartT<UnitProduct<Us...>>,
3026 void> {};
3027
3028// Implementation for CommonUnit: unite constituent labels.
3029template <typename... Us>
3030struct UnitLabel<CommonUnit<Us...>> {
3031 using LabelT = detail::ExtendedLabel<5 + 2 * (sizeof...(Us) - 1), Us...>;
3032 static constexpr LabelT value =
3033 detail::concatenate("COM[", detail::join_by(", ", unit_label(Us{})...), "]");
3034};
3035template <typename... Us>
3036constexpr typename UnitLabel<CommonUnit<Us...>>::LabelT UnitLabel<CommonUnit<Us...>>::value;
3037
3038// Implementation for CommonPointUnit: unite constituent labels.
3039template <typename... Us>
3041 using LabelT = detail::ExtendedLabel<8 + 2 * (sizeof...(Us) - 1), Us...>;
3042 static constexpr LabelT value =
3043 detail::concatenate("COM_PT[", detail::join_by(", ", unit_label(Us{})...), "]");
3044};
3045template <typename... Us>
3046constexpr
3047 typename UnitLabel<CommonPointUnit<Us...>>::LabelT UnitLabel<CommonPointUnit<Us...>>::value;
3048
3049template <typename Unit>
3050constexpr const auto &unit_label(Unit) {
3051 return detail::as_char_array(UnitLabel<AssociatedUnitT<Unit>>::value);
3052}
3053
3055// `UnitProduct` implementation.
3056//
3057// It's just a standard pack product, so all we need to do is carefully define the total ordering.
3058
3059namespace detail {
3060template <typename A, typename B>
3061struct OrderByDim : InStandardPackOrder<DimT<A>, DimT<B>> {};
3062
3063template <typename A, typename B>
3064struct OrderByMag : InStandardPackOrder<MagT<A>, MagT<B>> {};
3065
3066// OrderAsUnitProduct<A, B> can only be true if both A and B are unit products, _and_ they are in
3067// the standard pack order for unit products. This default case handles the usual case where either
3068// A or B (or both) is not a UnitProduct<...> in the first place.
3069template <typename A, typename B>
3071
3072// This specialization handles the non-trivial case, where we do have two UnitProduct instances.
3073template <typename... U1s, typename... U2s>
3075 : InStandardPackOrder<UnitProduct<U1s...>, UnitProduct<U2s...>> {};
3076
3077template <typename A, typename B>
3078struct OrderByOrigin : stdx::bool_constant<(OriginDisplacement<A, B>::value() < ZERO)> {};
3079
3080// "Unit avoidance" is a tiebreaker for quantity-equivalent units. Anonymous units, such as
3081// `UnitImpl<...>`, `ScaledUnit<...>`, and `UnitProduct<...>`, are more "avoidable" than units which
3082// are none of these, because the latter are likely explicitly named and thus more user-facing. The
3083// relative ordering among these built-in template types is probably less important than the fact
3084// that there _is_ a relative ordering among them (because we need to have a strict total ordering).
3085template <typename T>
3086struct UnitAvoidance : std::integral_constant<int, 0> {};
3087
3088template <typename A, typename B>
3089struct OrderByUnitAvoidance
3090 : stdx::bool_constant<(UnitAvoidance<A>::value < UnitAvoidance<B>::value)> {};
3091
3092template <typename... Ts>
3093struct UnitAvoidance<UnitProduct<Ts...>> : std::integral_constant<int, 1> {};
3094
3095template <typename... Ts>
3096struct UnitAvoidance<UnitImpl<Ts...>> : std::integral_constant<int, 2> {};
3097
3098template <typename... Ts>
3099struct UnitAvoidance<ScaledUnit<Ts...>> : std::integral_constant<int, 3> {};
3100
3101template <typename B, std::intmax_t N>
3102struct UnitAvoidance<Pow<B, N>> : std::integral_constant<int, 4> {};
3103
3104template <typename B, std::intmax_t N, std::intmax_t D>
3105struct UnitAvoidance<RatioPow<B, N, D>> : std::integral_constant<int, 5> {};
3106
3107template <typename... Us>
3108struct UnitAvoidance<CommonUnit<Us...>> : std::integral_constant<int, 6> {};
3109
3110template <typename... Us>
3111struct UnitAvoidance<CommonPointUnit<Us...>> : std::integral_constant<int, 7> {};
3112} // namespace detail
3113
3114template <typename A, typename B>
3115struct InOrderFor<UnitProduct, A, B> : LexicographicTotalOrdering<A,
3116 B,
3117 detail::OrderByUnitAvoidance,
3118 detail::OrderByDim,
3119 detail::OrderByMag,
3120 detail::OrderByOrigin,
3121 detail::OrderAsUnitProduct> {};
3122
3123} // namespace au
3124
3125
3126
3127namespace au {
3128
3129// Check that this particular Magnitude won't cause this specific value to overflow its type.
3130template <typename Rep, typename... BPs>
3131constexpr bool can_scale_without_overflow(Magnitude<BPs...> m, Rep value) {
3132 // Scales that shrink don't cause overflow.
3133 if (get_value<double>(m) <= 1.0) {
3134 (void)value;
3135 return true;
3136 } else {
3137 return std::numeric_limits<Rep>::max() / get_value<Rep>(m) >= value;
3138 }
3139}
3140
3141namespace detail {
3142// Chosen so as to allow populating a `QuantityI32<Hertz>` with an input in MHz.
3143constexpr auto OVERFLOW_THRESHOLD = 2'147;
3144
3145// This wrapper for `can_scale_without_overflow<...>(..., OVERFLOW_THRESHOLD)` can prevent an
3146// instantiation via short-circuiting, speeding up compile times.
3147template <typename Rep, typename ScaleFactor>
3148struct CanScaleThresholdWithoutOverflow
3149 : stdx::conjunction<
3150 stdx::bool_constant<stdx::in_range<Rep>(OVERFLOW_THRESHOLD)>,
3151 stdx::bool_constant<can_scale_without_overflow<Rep>(ScaleFactor{}, OVERFLOW_THRESHOLD)>> {
3152};
3153
3154template <typename U1, typename U2>
3155struct SameDimension : stdx::bool_constant<U1::dim_ == U2::dim_> {};
3156
3157template <typename Rep, typename ScaleFactor, typename SourceRep>
3158struct CoreImplicitConversionPolicyImpl
3159 : stdx::disjunction<
3160 std::is_floating_point<Rep>,
3161 stdx::conjunction<std::is_integral<SourceRep>,
3162 IsInteger<ScaleFactor>,
3163 detail::CanScaleThresholdWithoutOverflow<Rep, ScaleFactor>>> {};
3164
3165// Always permit the identity scaling.
3166template <typename Rep>
3167struct CoreImplicitConversionPolicyImpl<Rep, Magnitude<>, Rep> : std::true_type {};
3168
3169template <typename Rep, typename ScaleFactor, typename SourceRep>
3170using CoreImplicitConversionPolicy = CoreImplicitConversionPolicyImpl<Rep, ScaleFactor, SourceRep>;
3171
3172template <typename Rep, typename ScaleFactor, typename SourceRep>
3173struct PermitAsCarveOutForIntegerPromotion
3174 : stdx::conjunction<std::is_same<ScaleFactor, Magnitude<>>,
3175 std::is_integral<Rep>,
3176 std::is_integral<SourceRep>,
3177 std::is_assignable<Rep &, SourceRep>> {};
3178} // namespace detail
3179
3180template <typename Rep, typename ScaleFactor>
3181struct ImplicitRepPermitted : detail::CoreImplicitConversionPolicy<Rep, ScaleFactor, Rep> {};
3182
3183template <typename Rep, typename SourceUnitSlot, typename TargetUnitSlot>
3184constexpr bool implicit_rep_permitted_from_source_to_target(SourceUnitSlot, TargetUnitSlot) {
3185 using SourceUnit = AssociatedUnitT<SourceUnitSlot>;
3186 using TargetUnit = AssociatedUnitT<TargetUnitSlot>;
3187 static_assert(HasSameDimension<SourceUnit, TargetUnit>::value,
3188 "Can only convert same-dimension units");
3189
3190 return ImplicitRepPermitted<Rep, UnitRatioT<SourceUnit, TargetUnit>>::value;
3191}
3192
3193template <typename Unit, typename Rep>
3194struct ConstructionPolicy {
3195 // Note: it's tempting to use the UnitRatioT trait here, but we can't, because it produces a
3196 // hard error for units with different dimensions. This is for good reason: magnitude ratios
3197 // are meaningless unless the dimension is the same. UnitRatioT is the user-facing tool, so we
3198 // build in this hard error for safety. Here, we need a soft error, so we do the dimension
3199 // check manually below.
3200 template <typename SourceUnit>
3201 using ScaleFactor = MagQuotientT<detail::MagT<SourceUnit>, detail::MagT<Unit>>;
3202
3203 template <typename SourceUnit, typename SourceRep>
3204 using PermitImplicitFrom = stdx::conjunction<
3205 HasSameDimension<Unit, SourceUnit>,
3206 stdx::disjunction<
3207 detail::CoreImplicitConversionPolicy<Rep, ScaleFactor<SourceUnit>, SourceRep>,
3208 detail::PermitAsCarveOutForIntegerPromotion<Rep, ScaleFactor<SourceUnit>, SourceRep>>>;
3209};
3210
3211} // namespace au
3212
3213
3214namespace au {
3215namespace detail {
3216
3217// The various categories by which a magnitude can be applied to a numeric quantity.
3218enum class ApplyAs {
3219 INTEGER_MULTIPLY,
3220 INTEGER_DIVIDE,
3221 RATIONAL_MULTIPLY,
3222 IRRATIONAL_MULTIPLY,
3223};
3224
3225template <typename... BPs>
3226constexpr ApplyAs categorize_magnitude(Magnitude<BPs...>) {
3227 if (IsInteger<Magnitude<BPs...>>::value) {
3228 return ApplyAs::INTEGER_MULTIPLY;
3229 }
3230
3231 if (IsInteger<MagInverseT<Magnitude<BPs...>>>::value) {
3232 return ApplyAs::INTEGER_DIVIDE;
3233 }
3234
3235 return IsRational<Magnitude<BPs...>>::value ? ApplyAs::RATIONAL_MULTIPLY
3236 : ApplyAs::IRRATIONAL_MULTIPLY;
3237}
3238
3239template <typename Mag, ApplyAs Category, typename T, bool is_T_integral>
3240struct ApplyMagnitudeImpl;
3241
3242template <typename T, bool IsMagnitudeValid>
3243struct OverflowChecker {
3244 // Default case: `IsMagnitudeValid` is true.
3245 static constexpr bool would_product_overflow(T x, T mag_value) {
3246 return (x > (std::numeric_limits<T>::max() / mag_value)) ||
3247 (x < (std::numeric_limits<T>::lowest() / mag_value));
3248 }
3249};
3250
3251template <typename T>
3252struct OverflowChecker<T, false> {
3253 // Specialization for when `IsMagnitudeValid` is false.
3254 //
3255 // This means that the magnitude itself could not fit inside of the type; therefore, the only
3256 // possible value that would not overflow is zero.
3257 static constexpr bool would_product_overflow(T x, T) { return (x != T{0}); }
3258};
3259
3260template <typename T, bool IsTIntegral>
3261struct TruncationCheckerIfMagnitudeValid {
3262 // Default case: T is integral.
3263 static_assert(std::is_integral<T>::value && IsTIntegral,
3264 "Mismatched instantiation (should never be done manually)");
3265
3266 static constexpr bool would_truncate(T x, T mag_value) { return (x % mag_value != T{0}); }
3267};
3268
3269template <typename T>
3270struct TruncationCheckerIfMagnitudeValid<T, false> {
3271 // Specialization for when T is not integral: by convention, assume no truncation for floats.
3272 static_assert(!std::is_integral<T>::value,
3273 "Mismatched instantiation (should never be done manually)");
3274 static constexpr bool would_truncate(T, T) { return false; }
3275};
3276
3277template <typename T, bool IsMagnitudeValid>
3278// Default case: `IsMagnitudeValid` is true.
3279struct TruncationChecker : TruncationCheckerIfMagnitudeValid<T, std::is_integral<T>::value> {
3280 static_assert(IsMagnitudeValid, "Mismatched instantiation (should never be done manually)");
3281};
3282
3283template <typename T>
3284struct TruncationChecker<T, false> {
3285 // Specialization for when `IsMagnitudeValid` is false.
3286 //
3287 // This means that the magnitude itself could not fit inside of the type; therefore, the only
3288 // possible value that would not truncate is zero.
3289 static constexpr bool would_truncate(T x, T) { return (x != T{0}); }
3290};
3291
3292// Multiplying by an integer, for any type T.
3293template <typename Mag, typename T, bool is_T_integral>
3294struct ApplyMagnitudeImpl<Mag, ApplyAs::INTEGER_MULTIPLY, T, is_T_integral> {
3295 static_assert(categorize_magnitude(Mag{}) == ApplyAs::INTEGER_MULTIPLY,
3296 "Mismatched instantiation (should never be done manually)");
3297 static_assert(is_T_integral == std::is_integral<T>::value,
3298 "Mismatched instantiation (should never be done manually)");
3299
3300 constexpr T operator()(const T &x) { return x * get_value<T>(Mag{}); }
3301
3302 static constexpr bool would_overflow(const T &x) {
3303 constexpr auto mag_value_result = get_value_result<T>(Mag{});
3304 return OverflowChecker<T, mag_value_result.outcome == MagRepresentationOutcome::OK>::
3305 would_product_overflow(x, mag_value_result.value);
3306 }
3307
3308 static constexpr bool would_truncate(const T &) { return false; }
3309};
3310
3311// Dividing by an integer, for any type T.
3312template <typename Mag, typename T, bool is_T_integral>
3313struct ApplyMagnitudeImpl<Mag, ApplyAs::INTEGER_DIVIDE, T, is_T_integral> {
3314 static_assert(categorize_magnitude(Mag{}) == ApplyAs::INTEGER_DIVIDE,
3315 "Mismatched instantiation (should never be done manually)");
3316 static_assert(is_T_integral == std::is_integral<T>::value,
3317 "Mismatched instantiation (should never be done manually)");
3318
3319 constexpr T operator()(const T &x) { return x / get_value<T>(MagInverseT<Mag>{}); }
3320
3321 static constexpr bool would_overflow(const T &) { return false; }
3322
3323 static constexpr bool would_truncate(const T &x) {
3324 constexpr auto mag_value_result = get_value_result<T>(MagInverseT<Mag>{});
3325 return TruncationChecker<T, mag_value_result.outcome == MagRepresentationOutcome::OK>::
3326 would_truncate(x, mag_value_result.value);
3327 }
3328};
3329
3330template <typename T, typename Mag, bool is_T_signed>
3331struct RationalOverflowChecker;
3332template <typename T, typename Mag>
3333struct RationalOverflowChecker<T, Mag, true> {
3334 static constexpr bool would_overflow(const T &x) {
3335 static_assert(std::is_signed<T>::value,
3336 "Mismatched instantiation (should never be done manually)");
3337 const bool safe = (x <= MaxNonOverflowingValue<T, Mag>::value()) &&
3338 (x >= MinNonOverflowingValue<T, Mag>::value());
3339 return !safe;
3340 }
3341};
3342template <typename T, typename Mag>
3343struct RationalOverflowChecker<T, Mag, false> {
3344 static constexpr bool would_overflow(const T &x) {
3345 static_assert(!std::is_signed<T>::value,
3346 "Mismatched instantiation (should never be done manually)");
3347 const bool safe = (x <= MaxNonOverflowingValue<T, Mag>::value());
3348 return !safe;
3349 }
3350};
3351
3352// Applying a (non-integer, non-inverse-integer) rational, for any integral type T.
3353template <typename Mag, typename T>
3354struct ApplyMagnitudeImpl<Mag, ApplyAs::RATIONAL_MULTIPLY, T, true> {
3355 static_assert(categorize_magnitude(Mag{}) == ApplyAs::RATIONAL_MULTIPLY,
3356 "Mismatched instantiation (should never be done manually)");
3357 static_assert(std::is_integral<T>::value,
3358 "Mismatched instantiation (should never be done manually)");
3359
3360 constexpr T operator()(const T &x) {
3361 using P = PromotedType<T>;
3362 return static_cast<T>(x * get_value<P>(numerator(Mag{})) /
3363 get_value<P>(denominator(Mag{})));
3364 }
3365
3366 static constexpr bool would_overflow(const T &x) {
3367 return RationalOverflowChecker<T, Mag, std::is_signed<T>::value>::would_overflow(x);
3368 }
3369
3370 static constexpr bool would_truncate(const T &x) {
3371 constexpr auto mag_value_result = get_value_result<T>(denominator(Mag{}));
3372 return TruncationChecker<T, mag_value_result.outcome == MagRepresentationOutcome::OK>::
3373 would_truncate(x, mag_value_result.value);
3374 }
3375};
3376
3377// Applying a (non-integer, non-inverse-integer) rational, for any non-integral type T.
3378template <typename Mag, typename T>
3379struct ApplyMagnitudeImpl<Mag, ApplyAs::RATIONAL_MULTIPLY, T, false> {
3380 static_assert(categorize_magnitude(Mag{}) == ApplyAs::RATIONAL_MULTIPLY,
3381 "Mismatched instantiation (should never be done manually)");
3382 static_assert(!std::is_integral<T>::value,
3383 "Mismatched instantiation (should never be done manually)");
3384
3385 constexpr T operator()(const T &x) { return x * get_value<T>(Mag{}); }
3386
3387 static constexpr bool would_overflow(const T &x) {
3388 constexpr auto mag_value_result = get_value_result<T>(Mag{});
3389 return OverflowChecker<T, mag_value_result.outcome == MagRepresentationOutcome::OK>::
3390 would_product_overflow(x, mag_value_result.value);
3391 }
3392
3393 static constexpr bool would_truncate(const T &) { return false; }
3394};
3395
3396// Applying an irrational for any type T (although only non-integral T makes sense).
3397template <typename Mag, typename T, bool is_T_integral>
3398struct ApplyMagnitudeImpl<Mag, ApplyAs::IRRATIONAL_MULTIPLY, T, is_T_integral> {
3399 static_assert(!std::is_integral<T>::value, "Cannot apply irrational magnitude to integer type");
3400
3401 static_assert(categorize_magnitude(Mag{}) == ApplyAs::IRRATIONAL_MULTIPLY,
3402 "Mismatched instantiation (should never be done manually)");
3403 static_assert(is_T_integral == std::is_integral<T>::value,
3404 "Mismatched instantiation (should never be done manually)");
3405
3406 constexpr T operator()(const T &x) { return x * get_value<T>(Mag{}); }
3407
3408 static constexpr bool would_overflow(const T &x) {
3409 constexpr auto mag_value_result = get_value_result<T>(Mag{});
3410 return OverflowChecker<T, mag_value_result.outcome == MagRepresentationOutcome::OK>::
3411 would_product_overflow(x, mag_value_result.value);
3412 }
3413
3414 static constexpr bool would_truncate(const T &) { return false; }
3415};
3416
3417template <typename T, typename MagT>
3418struct ApplyMagnitudeType;
3419template <typename T, typename MagT>
3420using ApplyMagnitudeT = typename ApplyMagnitudeType<T, MagT>::type;
3421template <typename T, typename... BPs>
3422struct ApplyMagnitudeType<T, Magnitude<BPs...>>
3423 : stdx::type_identity<ApplyMagnitudeImpl<Magnitude<BPs...>,
3424 categorize_magnitude(Magnitude<BPs...>{}),
3425 T,
3426 std::is_integral<T>::value>> {};
3427
3428template <typename T, typename... BPs>
3429constexpr T apply_magnitude(const T &x, Magnitude<BPs...>) {
3430 return ApplyMagnitudeT<T, Magnitude<BPs...>>{}(x);
3431}
3432
3433} // namespace detail
3434} // namespace au
3435
3436
3437
3438namespace au {
3439
3440template <typename UnitT>
3441struct QuantityMaker;
3442
3443template <typename UnitT, typename RepT>
3444class Quantity;
3445
3446//
3447// Make a Quantity of the given Unit, which has this value as measured in the Unit.
3448//
3449template <typename UnitT, typename T>
3450constexpr auto make_quantity(T value) {
3451 return QuantityMaker<UnitT>{}(value);
3452}
3453
3454template <typename Unit, typename T>
3455constexpr auto make_quantity_unless_unitless(T value) {
3456 return std::conditional_t<IsUnitlessUnit<Unit>::value, stdx::identity, QuantityMaker<Unit>>{}(
3457 value);
3458}
3459
3460// Trait to check whether two Quantity types are exactly equivalent.
3461//
3462// For purposes of our library, "equivalent" means that they have the same Dimension and Magnitude.
3463template <typename Q1, typename Q2>
3464struct AreQuantityTypesEquivalent;
3465
3466// Trait for a type T which corresponds exactly to some Quantity type.
3467//
3468// "Correspondence" with a `Quantity<U, R>` means that T stores a value in a numeric datatype R, and
3469// this value represents a quantity whose unit of measure is quantity-equivalent to U.
3470//
3471// The canonical examples are the `duration` types from the `std::chrono::library`. For example,
3472// `std::chrono::duration<double, std::nano>` exactly corresponds to `QuantityD<Nano<Seconds>>`, and
3473// it is always OK to convert back and forth between these types implicitly.
3474//
3475// To add support for a type T which is equivalent to Quantity<U, R>, define a specialization of
3476// `CorrespondingQuantity<T>` with a member alias `Unit` for `U`, and `Rep` for `R`. You should
3477// then add static member functions as follows to add support for each direction of conversion.
3478// - For T -> Quantity, define `R extract_value(T)`.
3479// - For Quantity -> T, define `T construct_from_value(R)`.
3480template <typename T>
3481struct CorrespondingQuantity {};
3482template <typename T>
3483using CorrespondingQuantityT =
3484 Quantity<typename CorrespondingQuantity<T>::Unit, typename CorrespondingQuantity<T>::Rep>;
3485
3486// Redirect various cvref-qualified specializations to the "main" specialization.
3487//
3488// We use this slightly counterintuitive approach, rather than a more conventional
3489// `remove_cvref_t`-based approach, because the latter causes an _internal compiler error_ on the
3490// ACI QNX build.
3491template <typename T>
3492struct CorrespondingQuantity<const T> : CorrespondingQuantity<T> {};
3493template <typename T>
3494struct CorrespondingQuantity<T &> : CorrespondingQuantity<T> {};
3495template <typename T>
3496struct CorrespondingQuantity<const T &> : CorrespondingQuantity<T> {};
3497
3498// Request conversion of any type to its corresponding Quantity, if there is one.
3499//
3500// This is a way to explicitly and readably "enter the au Quantity domain" when we have some
3501// non-au-Quantity type which is nevertheless exactly and unambiguously equivalent to some Quantity.
3502//
3503// `as_quantity()` is SFINAE-friendly: we can use it to constrain templates to types `T` which are
3504// exactly equivalent to some Quantity type.
3505template <typename T>
3506constexpr auto as_quantity(T &&x) -> CorrespondingQuantityT<T> {
3507 using Q = CorrespondingQuantity<T>;
3508 static_assert(IsUnit<typename Q::Unit>{}, "No Quantity corresponding to type");
3509
3510 auto value = Q::extract_value(std::forward<T>(x));
3511 static_assert(std::is_same<decltype(value), typename Q::Rep>{},
3512 "Inconsistent CorrespondingQuantity implementation");
3513
3514 return make_quantity<typename Q::Unit>(value);
3515}
3516
3517template <typename UnitT, typename RepT>
3518class Quantity {
3519 template <bool ImplicitOk, typename OtherUnit, typename OtherRep>
3520 using EnableIfImplicitOkIs = std::enable_if_t<
3521 ImplicitOk ==
3522 ConstructionPolicy<UnitT, RepT>::template PermitImplicitFrom<OtherUnit, OtherRep>::value>;
3523
3524 public:
3525 using Rep = RepT;
3526 using Unit = UnitT;
3527 static constexpr auto unit = Unit{};
3528
3529 // IMPLICIT constructor for another Quantity of the same Dimension.
3530 template <typename OtherUnit,
3531 typename OtherRep,
3532 typename Enable = EnableIfImplicitOkIs<true, OtherUnit, OtherRep>>
3533 constexpr Quantity(Quantity<OtherUnit, OtherRep> other) // NOLINT(runtime/explicit)
3534 : Quantity{other.template as<Rep>(UnitT{})} {}
3535
3536 // EXPLICIT constructor for another Quantity of the same Dimension.
3537 template <typename OtherUnit,
3538 typename OtherRep,
3539 typename Enable = EnableIfImplicitOkIs<false, OtherUnit, OtherRep>,
3540 typename ThisUnusedTemplateParameterDistinguishesUsFromTheAboveConstructor = void>
3541 // Deleted: use `.as<NewRep>(new_unit)` to force a cast.
3542 explicit constexpr Quantity(Quantity<OtherUnit, OtherRep> other) = delete;
3543
3544 // Construct this Quantity with a value of exactly Zero.
3545 constexpr Quantity(Zero) : value_{0} {}
3546
3547 constexpr Quantity() noexcept = default;
3548
3549 // Implicit construction from any exactly-equivalent type.
3550 template <
3551 typename T,
3552 std::enable_if_t<std::is_convertible<CorrespondingQuantityT<T>, Quantity>::value, int> = 0>
3553 constexpr Quantity(T &&x) : Quantity{as_quantity(std::forward<T>(x))} {}
3554
3555 template <typename NewRep,
3556 typename NewUnit,
3557 typename = std::enable_if_t<IsUnit<AssociatedUnitT<NewUnit>>::value>>
3558 constexpr auto as(NewUnit) const {
3559 using Common = std::common_type_t<Rep, NewRep>;
3560 using Factor = UnitRatioT<AssociatedUnitT<Unit>, AssociatedUnitT<NewUnit>>;
3561
3562 return make_quantity<AssociatedUnitT<NewUnit>>(
3563 static_cast<NewRep>(detail::apply_magnitude(static_cast<Common>(value_), Factor{})));
3564 }
3565
3566 template <typename NewUnit,
3567 typename = std::enable_if_t<IsUnit<AssociatedUnitT<NewUnit>>::value>>
3568 constexpr auto as(NewUnit u) const {
3569 constexpr bool IMPLICIT_OK =
3570 implicit_rep_permitted_from_source_to_target<Rep>(unit, NewUnit{});
3571 constexpr bool INTEGRAL_REP = std::is_integral<Rep>::value;
3572 static_assert(
3573 IMPLICIT_OK || INTEGRAL_REP,
3574 "Should never occur. In the following static_assert, we assume that IMPLICIT_OK "
3575 "can never fail unless INTEGRAL_REP is true.");
3576 static_assert(
3577 IMPLICIT_OK,
3578 "Dangerous conversion for integer Rep! See: "
3579 "https://aurora-opensource.github.io/au/main/troubleshooting/#dangerous-conversion");
3580 return as<Rep>(u);
3581 }
3582
3583 template <typename NewRep,
3584 typename NewUnit,
3585 typename = std::enable_if_t<IsUnit<AssociatedUnitT<NewUnit>>::value>>
3586 constexpr NewRep in(NewUnit u) const {
3587 if (are_units_quantity_equivalent(unit, u) && std::is_same<Rep, NewRep>::value) {
3588 return static_cast<NewRep>(value_);
3589 } else {
3590 return as<NewRep>(u).in(u);
3591 }
3592 }
3593
3594 template <typename NewUnit,
3595 typename = std::enable_if_t<IsUnit<AssociatedUnitT<NewUnit>>::value>>
3596 constexpr Rep in(NewUnit u) const {
3597 if (are_units_quantity_equivalent(unit, u)) {
3598 return value_;
3599 } else {
3600 // Since Rep was requested _implicitly_, delegate to `.as()` for its safety checks.
3601 return as(u).in(u);
3602 }
3603 }
3604
3605 // "Old-style" overloads with <U, R> template parameters, and no function parameters.
3606 //
3607 // Matches the syntax from the CppCon 2021 talk, and legacy Aurora usage.
3608 template <typename U>
3609 [[deprecated(
3610 "Do not write `.as<YourUnits>()`; write `.as(your_units)` instead.")]] constexpr auto
3611 as() const -> decltype(as(U{})) {
3612 return as(U{});
3613 }
3614 template <typename U, typename R, typename = std::enable_if_t<IsUnit<U>::value>>
3615 [[deprecated(
3616 "Do not write `.as<YourUnits, T>()`; write `.as<T>(your_units)` instead.")]] constexpr auto
3617 as() const {
3618 return as<R>(U{});
3619 }
3620 template <typename U>
3621 [[deprecated(
3622 "Do not write `.in<YourUnits>()`; write `.in(your_units)` instead.")]] constexpr auto
3623 in() const -> decltype(in(U{})) {
3624 return in(U{});
3625 }
3626 template <typename U, typename R, typename = std::enable_if_t<IsUnit<U>::value>>
3627 [[deprecated(
3628 "Do not write `.in<YourUnits, T>()`; write `.in<T>(your_units)` instead.")]] constexpr auto
3629 in() const {
3630 return in<R>(U{});
3631 }
3632
3633 // "Forcing" conversions, which explicitly ignore safety checks for overflow and truncation.
3634 template <typename NewUnit>
3635 constexpr auto coerce_as(NewUnit) const {
3636 // Usage example: `q.coerce_as(new_units)`.
3637 return as<Rep>(NewUnit{});
3638 }
3639 template <typename NewRep, typename NewUnit>
3640 constexpr auto coerce_as(NewUnit) const {
3641 // Usage example: `q.coerce_as<T>(new_units)`.
3642 return as<NewRep>(NewUnit{});
3643 }
3644 template <typename NewUnit>
3645 constexpr auto coerce_in(NewUnit) const {
3646 // Usage example: `q.coerce_in(new_units)`.
3647 return in<Rep>(NewUnit{});
3648 }
3649 template <typename NewRep, typename NewUnit>
3650 constexpr auto coerce_in(NewUnit) const {
3651 // Usage example: `q.coerce_in<T>(new_units)`.
3652 return in<NewRep>(NewUnit{});
3653 }
3654
3655 // Direct access to the underlying value member, with any Quantity-equivalent Unit.
3656 //
3657 // Mutable access, QuantityMaker input.
3658 template <typename U>
3659 Rep &data_in(const QuantityMaker<U> &) {
3660 static_assert(AreUnitsQuantityEquivalent<U, Unit>::value,
3661 "Can only access value via Quantity-equivalent unit");
3662 return value_;
3663 }
3664 // Mutable access, Unit input.
3665 template <typename U>
3666 Rep &data_in(const U &) {
3667 return data_in(QuantityMaker<U>{});
3668 }
3669 // Const access, QuantityMaker input.
3670 template <typename U>
3671 const Rep &data_in(const QuantityMaker<U> &) const {
3672 static_assert(AreUnitsQuantityEquivalent<U, Unit>::value,
3673 "Can only access value via Quantity-equivalent unit");
3674 return value_;
3675 }
3676 // Const access, Unit input.
3677 template <typename U>
3678 const Rep &data_in(const U &) const {
3679 return data_in(QuantityMaker<U>{});
3680 }
3681
3682 // Permit this factory functor to access our private constructor.
3683 //
3684 // We allow this because it explicitly names the unit at the callsite, even if people refer to
3685 // this present Quantity type by an alias that omits the unit. This preserves Unit Safety and
3686 // promotes callsite readability.
3687 friend struct QuantityMaker<UnitT>;
3688
3689 // Comparison operators.
3690 friend constexpr bool operator==(Quantity a, Quantity b) { return a.value_ == b.value_; }
3691 friend constexpr bool operator!=(Quantity a, Quantity b) { return a.value_ != b.value_; }
3692 friend constexpr bool operator<(Quantity a, Quantity b) { return a.value_ < b.value_; }
3693 friend constexpr bool operator<=(Quantity a, Quantity b) { return a.value_ <= b.value_; }
3694 friend constexpr bool operator>(Quantity a, Quantity b) { return a.value_ > b.value_; }
3695 friend constexpr bool operator>=(Quantity a, Quantity b) { return a.value_ >= b.value_; }
3696
3697 // Addition and subtraction for like quantities.
3698 friend constexpr Quantity<UnitT, decltype(std::declval<RepT>() + std::declval<RepT>())>
3699 operator+(Quantity a, Quantity b) {
3700 return make_quantity<UnitT>(a.value_ + b.value_);
3701 }
3702 friend constexpr Quantity<UnitT, decltype(std::declval<RepT>() - std::declval<RepT>())>
3703 operator-(Quantity a, Quantity b) {
3704 return make_quantity<UnitT>(a.value_ - b.value_);
3705 }
3706
3707 // Scalar multiplication.
3708 template <typename T, typename = std::enable_if_t<std::is_arithmetic<T>::value>>
3709 friend constexpr auto operator*(Quantity a, T s) {
3710 return make_quantity<UnitT>(a.value_ * s);
3711 }
3712 template <typename T, typename = std::enable_if_t<std::is_arithmetic<T>::value>>
3713 friend constexpr auto operator*(T s, Quantity a) {
3714 return make_quantity<UnitT>(s * a.value_);
3715 }
3716
3717 // Scalar division.
3718 template <typename T, typename = std::enable_if_t<std::is_arithmetic<T>::value>>
3719 friend constexpr auto operator/(Quantity a, T s) {
3720 return make_quantity<UnitT>(a.value_ / s);
3721 }
3722 template <typename T, typename = std::enable_if_t<std::is_arithmetic<T>::value>>
3723 friend constexpr auto operator/(T s, Quantity a) {
3724 warn_if_integer_division<T>();
3725 return make_quantity<decltype(pow<-1>(unit))>(s / a.value_);
3726 }
3727
3728 // Multiplication for dimensioned quantities.
3729 template <typename OtherUnit, typename OtherRep>
3730 constexpr auto operator*(Quantity<OtherUnit, OtherRep> q) const {
3731 return make_quantity_unless_unitless<UnitProductT<Unit, OtherUnit>>(value_ *
3732 q.in(OtherUnit{}));
3733 }
3734
3735 // Division for dimensioned quantities.
3736 template <typename OtherUnit, typename OtherRep>
3737 constexpr auto operator/(Quantity<OtherUnit, OtherRep> q) const {
3738 warn_if_integer_division<OtherRep>();
3739 return make_quantity_unless_unitless<UnitQuotientT<Unit, OtherUnit>>(value_ /
3740 q.in(OtherUnit{}));
3741 }
3742
3743 // Short-hand addition and subtraction assignment.
3744 constexpr Quantity &operator+=(Quantity other) {
3745 value_ += other.value_;
3746 return *this;
3747 }
3748 constexpr Quantity &operator-=(Quantity other) {
3749 value_ -= other.value_;
3750 return *this;
3751 }
3752
3753 // Short-hand multiplication assignment.
3754 template <typename T>
3755 constexpr Quantity &operator*=(T s) {
3756 static_assert(
3757 std::is_arithmetic<T>::value,
3758 "This overload is only for scalar multiplication-assignment with arithmetic types");
3759
3760 static_assert(
3761 std::is_floating_point<Rep>::value || std::is_integral<T>::value,
3762 "We don't support compound multiplication of integral types by floating point");
3763
3764 value_ *= s;
3765 return *this;
3766 }
3767
3768 // Short-hand division assignment.
3769 template <typename T>
3770 constexpr Quantity &operator/=(T s) {
3771 static_assert(std::is_arithmetic<T>::value,
3772 "This overload is only for scalar division-assignment with arithmetic types");
3773
3774 static_assert(std::is_floating_point<Rep>::value || std::is_integral<T>::value,
3775 "We don't support compound division of integral types by floating point");
3776
3777 value_ /= s;
3778 return *this;
3779 }
3780
3781 // Unary plus and minus.
3782 constexpr Quantity operator+() const { return {+value_}; }
3783 constexpr Quantity operator-() const { return {-value_}; }
3784
3785 // Automatic conversion to Rep for Unitless type.
3786 template <typename U = UnitT, typename = std::enable_if_t<IsUnitlessUnit<U>::value>>
3787 constexpr operator Rep() const {
3788 return value_;
3789 }
3790
3791 // Automatic conversion to any equivalent type that supports it.
3792 template <
3793 typename T,
3794 std::enable_if_t<std::is_convertible<Quantity, CorrespondingQuantityT<T>>::value, int> = 0>
3795 constexpr operator T() const {
3796 return CorrespondingQuantity<T>::construct_from_value(
3797 CorrespondingQuantityT<T>{*this}.in(typename CorrespondingQuantity<T>::Unit{}));
3798 }
3799
3800 private:
3801 template <typename OtherRep>
3802 static constexpr void warn_if_integer_division() {
3803 constexpr bool uses_integer_division =
3804 (std::is_integral<Rep>::value && std::is_integral<OtherRep>::value);
3805 static_assert(!uses_integer_division,
3806 "Integer division forbidden: use integer_quotient() if you really want it");
3807 }
3808
3809 constexpr Quantity(Rep value) : value_{value} {}
3810
3811 Rep value_{0};
3812};
3813
3814// Force integer division beteween two integer Quantities, in a callsite-obvious way.
3815template <typename U1, typename R1, typename U2, typename R2>
3816constexpr auto integer_quotient(Quantity<U1, R1> q1, Quantity<U2, R2> q2) {
3817 static_assert(std::is_integral<R1>::value && std::is_integral<R2>::value,
3818 "integer_quotient() can only be called with integral Rep");
3819 return make_quantity<UnitQuotientT<U1, U2>>(q1.in(U1{}) / q2.in(U2{}));
3820}
3821
3822// Force integer division beteween an integer Quantity and a raw number.
3823template <typename U, typename R, typename T>
3824constexpr auto integer_quotient(Quantity<U, R> q, T x) {
3825 static_assert(std::is_integral<R>::value && std::is_integral<T>::value,
3826 "integer_quotient() can only be called with integral Rep");
3827 return make_quantity<U>(q.in(U{}) / x);
3828}
3829
3830// Force integer division beteween a raw number and an integer Quantity.
3831template <typename T, typename U, typename R>
3832constexpr auto integer_quotient(T x, Quantity<U, R> q) {
3833 static_assert(std::is_integral<T>::value && std::is_integral<R>::value,
3834 "integer_quotient() can only be called with integral Rep");
3835 return make_quantity<UnitInverseT<U>>(x / q.in(U{}));
3836}
3837
3838// The modulo operator (i.e., the remainder of an integer division).
3839//
3840// Only defined whenever (R1{} % R2{}) is defined (i.e., for integral Reps), _and_
3841// `CommonUnitT<U1, U2>` is also defined. We convert to that common unit to perform the operation.
3842template <typename U1, typename R1, typename U2, typename R2>
3843constexpr auto operator%(Quantity<U1, R1> q1, Quantity<U2, R2> q2) {
3844 using U = CommonUnitT<U1, U2>;
3845 return make_quantity<U>(q1.in(U{}) % q2.in(U{}));
3846}
3847
3848// Type trait to detect whether two Quantity types are equivalent.
3849//
3850// In this library, Quantity types are "equivalent" exactly when they use the same Rep, and are
3851// based on equivalent units.
3852template <typename U1, typename U2, typename R1, typename R2>
3853struct AreQuantityTypesEquivalent<Quantity<U1, R1>, Quantity<U2, R2>>
3854 : stdx::conjunction<std::is_same<R1, R2>, AreUnitsQuantityEquivalent<U1, U2>> {};
3855
3856// Cast Quantity to a different underlying type.
3857template <typename NewRep, typename Unit, typename Rep>
3858constexpr auto rep_cast(Quantity<Unit, Rep> q) {
3859 return q.template as<NewRep>(Unit{});
3860}
3861
3862// Help Zero act more faithfully like a Quantity.
3863//
3864// Casting Zero to any "Rep" is trivial, because it has no Rep, and is already consistent with all.
3865template <typename NewRep>
3866constexpr auto rep_cast(Zero z) {
3867 return z;
3868}
3869
3870//
3871// Quantity aliases to set a particular Rep.
3872//
3873// This presents a less cumbersome interface for end users.
3874//
3875template <typename UnitT>
3876using QuantityD = Quantity<UnitT, double>;
3877template <typename UnitT>
3878using QuantityF = Quantity<UnitT, float>;
3879template <typename UnitT>
3880using QuantityI = Quantity<UnitT, int>;
3881template <typename UnitT>
3882using QuantityU = Quantity<UnitT, unsigned int>;
3883template <typename UnitT>
3884using QuantityI32 = Quantity<UnitT, int32_t>;
3885template <typename UnitT>
3886using QuantityU32 = Quantity<UnitT, uint32_t>;
3887template <typename UnitT>
3888using QuantityI64 = Quantity<UnitT, int64_t>;
3889template <typename UnitT>
3890using QuantityU64 = Quantity<UnitT, uint64_t>;
3891
3892template <typename UnitT>
3893struct QuantityMaker {
3894 using Unit = UnitT;
3895 static constexpr auto unit = Unit{};
3896
3897 template <typename T>
3898 constexpr Quantity<Unit, T> operator()(T value) const {
3899 return {value};
3900 }
3901
3902 template <typename... BPs>
3903 constexpr auto operator*(Magnitude<BPs...> m) const {
3904 return QuantityMaker<decltype(unit * m)>{};
3905 }
3906
3907 template <typename... BPs>
3908 constexpr auto operator/(Magnitude<BPs...> m) const {
3909 return QuantityMaker<decltype(unit / m)>{};
3910 }
3911
3912 template <typename DivisorUnit>
3913 constexpr auto operator/(SingularNameFor<DivisorUnit>) const {
3914 return QuantityMaker<UnitQuotientT<Unit, DivisorUnit>>{};
3915 }
3916
3917 template <typename MultiplierUnit>
3918 friend constexpr auto operator*(SingularNameFor<MultiplierUnit>, QuantityMaker) {
3919 return QuantityMaker<UnitProductT<MultiplierUnit, Unit>>{};
3920 }
3921
3922 template <typename OtherUnit>
3923 constexpr auto operator*(QuantityMaker<OtherUnit>) const {
3924 return QuantityMaker<UnitProductT<Unit, OtherUnit>>{};
3925 }
3926
3927 template <typename OtherUnit>
3928 constexpr auto operator/(QuantityMaker<OtherUnit>) const {
3929 return QuantityMaker<UnitQuotientT<Unit, OtherUnit>>{};
3930 }
3931};
3932
3933template <typename U>
3934struct AssociatedUnit<QuantityMaker<U>> : stdx::type_identity<U> {};
3935
3936template <int Exp, typename Unit>
3937constexpr auto pow(QuantityMaker<Unit>) {
3938 return QuantityMaker<UnitPowerT<Unit, Exp>>{};
3939}
3940
3941template <int N, typename Unit>
3942constexpr auto root(QuantityMaker<Unit>) {
3943 return QuantityMaker<UnitPowerT<Unit, 1, N>>{};
3944}
3945
3947// Runtime conversion checkers
3948
3949// Check conversion for overflow (no change of rep).
3950template <typename U, typename R, typename TargetUnitSlot>
3951constexpr bool will_conversion_overflow(Quantity<U, R> q, TargetUnitSlot target_unit) {
3952 return detail::ApplyMagnitudeT<R, decltype(unit_ratio(U{}, target_unit))>::would_overflow(
3953 q.in(U{}));
3954}
3955
3956// Check conversion for truncation (no change of rep).
3957template <typename U, typename R, typename TargetUnitSlot>
3958constexpr bool will_conversion_truncate(Quantity<U, R> q, TargetUnitSlot target_unit) {
3959 return detail::ApplyMagnitudeT<R, decltype(unit_ratio(U{}, target_unit))>::would_truncate(
3960 q.in(U{}));
3961}
3962
3963// Check for any lossiness in conversion (no change of rep).
3964template <typename U, typename R, typename TargetUnitSlot>
3965constexpr bool is_conversion_lossy(Quantity<U, R> q, TargetUnitSlot target_unit) {
3966 return will_conversion_truncate(q, target_unit) || will_conversion_overflow(q, target_unit);
3967}
3968
3970// Comparing and/or combining Quantities of different types.
3971
3972namespace detail {
3973// Helper to cast this Quantity to its common type with some other Quantity (explicitly supplied).
3974//
3975// Note that `TargetUnit` is supposed to be the common type of the input Quantity and some other
3976// Quantity. This function should never be called directly; it should only be called by
3977// `using_common_type()`. The program behaviour is undefined if anyone calls this function
3978// directly. (In particular, we explicitly assume that the conversion to the Rep of TargetUnit is
3979// not narrowing for the input Quantity.)
3980//
3981// We would have liked this to just be a simple lambda, but some old compilers sometimes struggle
3982// with understanding that the lambda implementation of this can be constexpr.
3983template <typename TargetUnit, typename U, typename R>
3984constexpr auto cast_to_common_type(Quantity<U, R> q) {
3985 // When we perform a unit conversion to U, we need to make sure the library permits this
3986 // conversion *implicitly* for a rep R. The form `rep_cast<R>(q).as(U{})` achieves
3987 // this. First, we cast the Rep to R (which will typically be the wider of the input Reps).
3988 // Then, we use the *unit-only* form of the conversion operator: `as(U{})`, not
3989 // `as<R>(U{})`, because only the former actually checks the conversion policy.
3990 return rep_cast<typename TargetUnit::Rep>(q).as(TargetUnit::unit);
3991}
3992
3993template <typename T, typename U, typename Func>
3994constexpr auto using_common_type(T t, U u, Func f) {
3995 using C = std::common_type_t<T, U>;
3996 static_assert(
3997 std::is_same<typename C::Rep, std::common_type_t<typename T::Rep, typename U::Rep>>::value,
3998 "Rep of common type is not common type of Reps (this should never occur)");
3999
4000 return f(cast_to_common_type<C>(t), cast_to_common_type<C>(u));
4001}
4002} // namespace detail
4003
4004// Comparison functions for compatible Quantity types.
4005template <typename U1, typename U2, typename R1, typename R2>
4006constexpr bool operator==(Quantity<U1, R1> q1, Quantity<U2, R2> q2) {
4007 return detail::using_common_type(q1, q2, detail::equal);
4008}
4009template <typename U1, typename U2, typename R1, typename R2>
4010constexpr bool operator!=(Quantity<U1, R1> q1, Quantity<U2, R2> q2) {
4011 return detail::using_common_type(q1, q2, detail::not_equal);
4012}
4013template <typename U1, typename U2, typename R1, typename R2>
4014constexpr bool operator<(Quantity<U1, R1> q1, Quantity<U2, R2> q2) {
4015 return detail::using_common_type(q1, q2, detail::less);
4016}
4017template <typename U1, typename U2, typename R1, typename R2>
4018constexpr bool operator<=(Quantity<U1, R1> q1, Quantity<U2, R2> q2) {
4019 return detail::using_common_type(q1, q2, detail::less_equal);
4020}
4021template <typename U1, typename U2, typename R1, typename R2>
4022constexpr bool operator>(Quantity<U1, R1> q1, Quantity<U2, R2> q2) {
4023 return detail::using_common_type(q1, q2, detail::greater);
4024}
4025template <typename U1, typename U2, typename R1, typename R2>
4026constexpr bool operator>=(Quantity<U1, R1> q1, Quantity<U2, R2> q2) {
4027 return detail::using_common_type(q1, q2, detail::greater_equal);
4028}
4029
4030// Addition and subtraction functions for compatible Quantity types.
4031template <typename U1, typename U2, typename R1, typename R2>
4032constexpr auto operator+(Quantity<U1, R1> q1, Quantity<U2, R2> q2) {
4033 return detail::using_common_type(q1, q2, detail::plus);
4034}
4035template <typename U1, typename U2, typename R1, typename R2>
4036constexpr auto operator-(Quantity<U1, R1> q1, Quantity<U2, R2> q2) {
4037 return detail::using_common_type(q1, q2, detail::minus);
4038}
4039
4040// Mixed-type operations with a left-Quantity, and right-Quantity-equivalent.
4041template <typename U, typename R, typename QLike>
4042constexpr auto operator+(Quantity<U, R> q1, QLike q2) -> decltype(q1 + as_quantity(q2)) {
4043 return q1 + as_quantity(q2);
4044}
4045template <typename U, typename R, typename QLike>
4046constexpr auto operator-(Quantity<U, R> q1, QLike q2) -> decltype(q1 - as_quantity(q2)) {
4047 return q1 - as_quantity(q2);
4048}
4049template <typename U, typename R, typename QLike>
4050constexpr auto operator==(Quantity<U, R> q1, QLike q2) -> decltype(q1 == as_quantity(q2)) {
4051 return q1 == as_quantity(q2);
4052}
4053template <typename U, typename R, typename QLike>
4054constexpr auto operator!=(Quantity<U, R> q1, QLike q2) -> decltype(q1 != as_quantity(q2)) {
4055 return q1 != as_quantity(q2);
4056}
4057template <typename U, typename R, typename QLike>
4058constexpr auto operator<(Quantity<U, R> q1, QLike q2) -> decltype(q1 < as_quantity(q2)) {
4059 return q1 < as_quantity(q2);
4060}
4061template <typename U, typename R, typename QLike>
4062constexpr auto operator<=(Quantity<U, R> q1, QLike q2) -> decltype(q1 <= as_quantity(q2)) {
4063 return q1 <= as_quantity(q2);
4064}
4065template <typename U, typename R, typename QLike>
4066constexpr auto operator>(Quantity<U, R> q1, QLike q2) -> decltype(q1 > as_quantity(q2)) {
4067 return q1 > as_quantity(q2);
4068}
4069template <typename U, typename R, typename QLike>
4070constexpr auto operator>=(Quantity<U, R> q1, QLike q2) -> decltype(q1 >= as_quantity(q2)) {
4071 return q1 >= as_quantity(q2);
4072}
4073
4074// Mixed-type operations with a left-Quantity-equivalent, and right-Quantity.
4075template <typename U, typename R, typename QLike>
4076constexpr auto operator+(QLike q1, Quantity<U, R> q2) -> decltype(as_quantity(q1) + q2) {
4077 return as_quantity(q1) + q2;
4078}
4079template <typename U, typename R, typename QLike>
4080constexpr auto operator-(QLike q1, Quantity<U, R> q2) -> decltype(as_quantity(q1) - q2) {
4081 return as_quantity(q1) - q2;
4082}
4083template <typename U, typename R, typename QLike>
4084constexpr auto operator==(QLike q1, Quantity<U, R> q2) -> decltype(as_quantity(q1) == q2) {
4085 return as_quantity(q1) == q2;
4086}
4087template <typename U, typename R, typename QLike>
4088constexpr auto operator!=(QLike q1, Quantity<U, R> q2) -> decltype(as_quantity(q1) != q2) {
4089 return as_quantity(q1) != q2;
4090}
4091template <typename U, typename R, typename QLike>
4092constexpr auto operator<(QLike q1, Quantity<U, R> q2) -> decltype(as_quantity(q1) < q2) {
4093 return as_quantity(q1) < q2;
4094}
4095template <typename U, typename R, typename QLike>
4096constexpr auto operator<=(QLike q1, Quantity<U, R> q2) -> decltype(as_quantity(q1) <= q2) {
4097 return as_quantity(q1) <= q2;
4098}
4099template <typename U, typename R, typename QLike>
4100constexpr auto operator>(QLike q1, Quantity<U, R> q2) -> decltype(as_quantity(q1) > q2) {
4101 return as_quantity(q1) > q2;
4102}
4103template <typename U, typename R, typename QLike>
4104constexpr auto operator>=(QLike q1, Quantity<U, R> q2) -> decltype(as_quantity(q1) >= q2) {
4105 return as_quantity(q1) >= q2;
4106}
4107
4108// Helper to compute the `std::common_type_t` of two `Quantity` types.
4109//
4110// `std::common_type` requires its specializations to be SFINAE-friendly, meaning that the `type`
4111// member should not exist for specializations with no common type. Unfortunately, we can't
4112// directly use SFINAE on `std::common_type`. What we can do is inherit our specialization's
4113// implementation from a different structure which we fully control, and which either has or doesn't
4114// have a `type` member as appropriate.
4115template <typename Q1, typename Q2, typename Enable = void>
4116struct CommonQuantity {};
4117template <typename U1, typename U2, typename R1, typename R2>
4118struct CommonQuantity<Quantity<U1, R1>,
4119 Quantity<U2, R2>,
4120 std::enable_if_t<HasSameDimension<U1, U2>::value>>
4121 : stdx::type_identity<Quantity<CommonUnitT<U1, U2>, std::common_type_t<R1, R2>>> {};
4122} // namespace au
4123
4124namespace std {
4125// Note: we would prefer not to reopen `namespace std` [1]. However, some older compilers (which we
4126// still want to support) incorrectly treat the preferred syntax recommended in [1] as an error.
4127// This usage does not encounter any of the pitfalls described in that link, so we use it.
4128//
4129// [1] https://quuxplusone.github.io/blog/2021/10/27/dont-reopen-namespace-std/
4130template <typename U1, typename U2, typename R1, typename R2>
4131struct common_type<au::Quantity<U1, R1>, au::Quantity<U2, R2>>
4132 : au::CommonQuantity<au::Quantity<U1, R1>, au::Quantity<U2, R2>> {};
4133} // namespace std
4134
4135
4136// "Mixin" classes to add operations for a "unit wrapper" --- that is, a template with a _single
4137// template parameter_ that is a unit.
4138//
4139// The operations are multiplication and division. The mixins will specify what types the wrapper
4140// can combine with in this way, and what the resulting type will be. They also take care of
4141// getting the resulting unit correct. Finally, they handle integer division carefully.
4142//
4143// Every mixin has at least two template parameters.
4144//
4145// 1. The unit wrapper (a template template parameter).
4146// 2. The specific unit that it's wrapping (for convenience in the implementation).
4147//
4148// For mixins that compose with something that is _not_ a unit wrapper --- e.g., a raw number, or a
4149// magnitude --- this is all they need. Other mixins compose with _other unit wrappers_, and these
4150// take two more template parameters: the wrapper we're composing with, and the resulting wrapper.
4151
4152namespace au {
4153namespace detail {
4154
4155// A SFINAE helper that is the identity, but only if we think a type is a valid rep.
4156//
4157// For now, we are restricting this to arithmetic types. This doesn't mean they're the only reps we
4158// support; it just means they're the only reps we can _construct via this method_. Later on, we
4159// would like to have a well-defined concept that defines what is and is not an acceptable rep for
4160// our `Quantity`. Once we have that, we can simply constrain on that concept. For more on this
4161// idea, see: https://github.com/aurora-opensource/au/issues/52
4162struct NoTypeMember {};
4163template <typename T>
4164struct TypeIdentityIfLooksLikeValidRep
4165 : std::conditional_t<std::is_arithmetic<T>::value, stdx::type_identity<T>, NoTypeMember> {};
4166template <typename T>
4167using TypeIdentityIfLooksLikeValidRepT = typename TypeIdentityIfLooksLikeValidRep<T>::type;
4168
4169//
4170// A mixin that enables turning a raw number into a Quantity by multiplying or dividing.
4171//
4172template <template <typename U> class UnitWrapper, typename Unit>
4173struct MakesQuantityFromNumber {
4174 // (N * W), for number N and wrapper W.
4175 template <typename T>
4176 friend constexpr auto operator*(T x, UnitWrapper<Unit>)
4177 -> Quantity<Unit, TypeIdentityIfLooksLikeValidRepT<T>> {
4178 return make_quantity<Unit>(x);
4179 }
4180
4181 // (W * N), for number N and wrapper W.
4182 template <typename T>
4183 friend constexpr auto operator*(UnitWrapper<Unit>, T x)
4184 -> Quantity<Unit, TypeIdentityIfLooksLikeValidRepT<T>> {
4185 return make_quantity<Unit>(x);
4186 }
4187
4188 // (N / W), for number N and wrapper W.
4189 template <typename T>
4190 friend constexpr auto operator/(T x, UnitWrapper<Unit>)
4191 -> Quantity<UnitInverseT<Unit>, TypeIdentityIfLooksLikeValidRepT<T>> {
4192 return make_quantity<UnitInverseT<Unit>>(x);
4193 }
4194
4195 // (W / N), for number N and wrapper W.
4196 template <typename T>
4197 friend constexpr auto operator/(UnitWrapper<Unit>, T x)
4199 static_assert(!std::is_integral<T>::value,
4200 "Dividing by an integer value disallowed: would almost always produce 0");
4201 return make_quantity<Unit>(T{1} / x);
4202 }
4203};
4204
4205//
4206// A mixin that enables scaling the units of a Quantity by multiplying or dividing.
4207//
4208template <template <typename U> class UnitWrapper, typename Unit>
4210 // (W * Q), for wrapper W and quantity Q.
4211 template <typename U, typename R>
4212 friend constexpr auto operator*(UnitWrapper<Unit>, Quantity<U, R> q) {
4213 return make_quantity<UnitProductT<Unit, U>>(q.in(U{}));
4214 }
4215
4216 // (Q * W), for wrapper W and quantity Q.
4217 template <typename U, typename R>
4218 friend constexpr auto operator*(Quantity<U, R> q, UnitWrapper<Unit>) {
4219 return make_quantity<UnitProductT<U, Unit>>(q.in(U{}));
4220 }
4221
4222 // (Q / W), for wrapper W and quantity Q.
4223 template <typename U, typename R>
4224 friend constexpr auto operator/(Quantity<U, R> q, UnitWrapper<Unit>) {
4225 return make_quantity<UnitQuotientT<U, Unit>>(q.in(U{}));
4226 }
4227
4228 // (W / Q), for wrapper W and quantity Q.
4229 template <typename U, typename R>
4230 friend constexpr auto operator/(UnitWrapper<Unit>, Quantity<U, R> q) {
4231 static_assert(!std::is_integral<R>::value,
4232 "Dividing by an integer value disallowed: would almost always produce 0");
4233 return make_quantity<UnitQuotientT<Unit, U>>(R{1} / q.in(U{}));
4234 }
4235};
4236
4237// A mixin to compose `op(U, O)` into a new unit wrapper, for "main" wrapper `U` and "other" wrapper
4238// `O`. (Implementation detail helper for `ComposesWith`.)
4239template <template <typename U> class UnitWrapper,
4240 typename Unit,
4241 template <typename U>
4242 class OtherWrapper,
4243 template <typename U>
4244 class ResultWrapper>
4246 // (U * O), for "main" wrapper U and "other" wrapper O.
4247 template <typename U>
4248 friend constexpr ResultWrapper<UnitProductT<Unit, U>> operator*(UnitWrapper<Unit>,
4249 OtherWrapper<U>) {
4250 return {};
4251 }
4252
4253 // (U / O), for "main" wrapper U and "other" wrapper O.
4254 template <typename U>
4255 friend constexpr ResultWrapper<UnitQuotientT<Unit, U>> operator/(UnitWrapper<Unit>,
4256 OtherWrapper<U>) {
4257 return {};
4258 }
4259};
4260
4261// A mixin to compose `op(O, U)` into a new unit wrapper, for "main" wrapper `U` and "other" wrapper
4262// `O`. (Implementation detail helper for `ComposesWith`.)
4263template <template <typename U> class UnitWrapper,
4264 typename Unit,
4265 template <typename U>
4266 class OtherWrapper,
4267 template <typename U>
4268 class ResultWrapper>
4270 // (O * U), for "main" wrapper U and "other" wrapper O.
4271 template <typename U>
4272 friend constexpr ResultWrapper<UnitProductT<U, Unit>> operator*(OtherWrapper<U>,
4273 UnitWrapper<Unit>) {
4274 return {};
4275 }
4276
4277 // (O / U), for "main" wrapper U and "other" wrapper O.
4278 template <typename U>
4279 friend constexpr ResultWrapper<UnitQuotientT<U, Unit>> operator/(OtherWrapper<U>,
4280 UnitWrapper<Unit>) {
4281 return {};
4282 }
4283};
4284
4285// An empty version of `PostcomposesWith` for when `UnitWrapper` is the same as `OtherWrapper`.
4286// In this case, if we left it non-empty, the definitions would be ambiguous/redundant with the ones
4287// in `PrecoposesWith`.
4288template <template <typename U> class UnitWrapper,
4289 typename Unit,
4290 template <typename U>
4291 class ResultWrapper>
4292struct PostcomposesWith<UnitWrapper, Unit, UnitWrapper, ResultWrapper> {};
4293
4294//
4295// A mixin to compose two unit wrappers into a new unit wrapper.
4296//
4297template <template <typename U> class UnitWrapper,
4298 typename Unit,
4299 template <typename U>
4300 class OtherWrapper,
4301 template <typename U>
4302 class ResultWrapper>
4303struct ComposesWith : PrecomposesWith<UnitWrapper, Unit, OtherWrapper, ResultWrapper>,
4304 PostcomposesWith<UnitWrapper, Unit, OtherWrapper, ResultWrapper> {};
4305
4306//
4307// A mixin to enable scaling a unit wrapper by a magnitude.
4308//
4309template <template <typename U> class UnitWrapper, typename Unit>
4311 // (M * W), for magnitude M and wrapper W.
4312 template <typename... BPs>
4313 friend constexpr auto operator*(Magnitude<BPs...> m, UnitWrapper<Unit>) {
4314 return UnitWrapper<decltype(Unit{} * m)>{};
4315 }
4316
4317 // (W * M), for magnitude M and wrapper W.
4318 template <typename... BPs>
4319 friend constexpr auto operator*(UnitWrapper<Unit>, Magnitude<BPs...> m) {
4320 return UnitWrapper<decltype(Unit{} * m)>{};
4321 }
4322
4323 // (M / W), for magnitude M and wrapper W.
4324 template <typename... BPs>
4325 friend constexpr auto operator/(Magnitude<BPs...> m, UnitWrapper<Unit>) {
4326 return UnitWrapper<decltype(UnitInverseT<Unit>{} * m)>{};
4327 }
4328
4329 // (W / M), for magnitude M and wrapper W.
4330 template <typename... BPs>
4331 friend constexpr auto operator/(UnitWrapper<Unit>, Magnitude<BPs...> m) {
4332 return UnitWrapper<decltype(Unit{} / m)>{};
4333 }
4334};
4335
4336} // namespace detail
4337} // namespace au
4338
4339
4340namespace au {
4341
4342// `QuantityPoint`: an _affine space type_ modeling points on a line.
4343//
4344// For a quick primer on affine space types, see: http://videocortex.io/2018/Affine-Space-Types/
4345//
4346// By "modeling points", we mean that `QuantityPoint` instances cannot be added to each other, and
4347// cannot be multiplied. However, they can be subtracted: the difference between two
4348// `QuantityPoint` instances (of the same Unit) is a `Quantity` of that unit. We can also add a
4349// `Quantity` to a `QuantityPoint`, and vice versa; the result is a new `QuantityPoint`.
4350//
4351// Key motivating examples include _mile markers_ (effectively `QuantityPoint<Miles, T>`), and
4352// _absolute temperature measurements_ (e.g., `QuantityPoint<Celsius, T>`). This type is also
4353// analogous to `std::chrono::time_point`, in the same way that `Quantity` is analogous to
4354// `std::chrono::duration`.
4355template <typename UnitT, typename RepT>
4356class QuantityPoint;
4357
4358template <typename UnitT>
4359struct QuantityPointMaker;
4360
4361// Make a Quantity of the given Unit, which has this value as measured in the Unit.
4362template <typename UnitT, typename T>
4363constexpr auto make_quantity_point(T value) {
4364 return QuantityPointMaker<UnitT>{}(value);
4365}
4366
4367// Trait to check whether two QuantityPoint types are exactly equivalent.
4368template <typename P1, typename P2>
4370
4371namespace detail {
4372template <typename TargetRep, typename U1, typename U2>
4373struct OriginDisplacementFitsIn;
4374} // namespace detail
4375
4376// QuantityPoint implementation and API elaboration.
4377template <typename UnitT, typename RepT>
4379 // Q: When should we enable IMPLICIT construction from another QuantityPoint type?
4380 // A: EXACTLY WHEN our own Diff type can be IMPLICITLY constructed from the SUM of the target's
4381 // Diff type, and the offset between our Units' zero points.
4382 //
4383 // In other words, there are two ways to fail implicit convertibility.
4384 //
4385 // 1. Their Diff type might not work with our Rep. Examples:
4386 // BAD: QuantityPoint<Milli<Meters>, int> -> QuantityPoint<Meters, int>
4387 // OK : QuantityPoint<Kilo<Meters> , int> -> QuantityPoint<Meters, int>
4388 //
4389 // 2. Their zero point might be offset from ours by a non-representable amount. Examples:
4390 // BAD: QuantityPoint<Celsius, int> -> QuantityPoint<Kelvins, int>
4391 // OK : QuantityPoint<Celsius, int> -> QuantityPoint<Kelvins, double>
4392 // OK : QuantityPoint<Celsius, int> -> QuantityPoint<Milli<Kelvins>, int>
4393 template <typename OtherUnit, typename OtherRep>
4394 static constexpr bool should_enable_implicit_construction_from() {
4395 return std::is_convertible<
4396 decltype(std::declval<typename QuantityPoint<OtherUnit, OtherRep>::Diff>() +
4397 origin_displacement(UnitT{}, OtherUnit{})),
4398 QuantityPoint::Diff>::value;
4399 }
4400
4401 // This machinery exists to give us a conditionally explicit constructor, using SFINAE to select
4402 // the explicit or implicit version (https://stackoverflow.com/a/26949793/15777264). If we had
4403 // C++20, we could use the `explicit(bool)` feature, making this code simpler and faster.
4404 template <bool ImplicitOk, typename OtherUnit, typename OtherRep>
4405 using EnableIfImplicitOkIs = std::enable_if_t<
4406 ImplicitOk ==
4407 QuantityPoint::should_enable_implicit_construction_from<OtherUnit, OtherRep>()>;
4408
4409 public:
4410 using Rep = RepT;
4411 using Unit = UnitT;
4412 static constexpr Unit unit{};
4413 using Diff = Quantity<Unit, Rep>;
4414
4415 // The default constructor produces a QuantityPoint in a valid but contractually unspecified
4416 // state. It exists to give you an object you can assign to. The main motivating factor for
4417 // including this is to support `std::atomic`, which requires its types to be
4418 // default-constructible.
4419 constexpr QuantityPoint() noexcept : x_{ZERO} {}
4420
4421 template <typename OtherUnit,
4422 typename OtherRep,
4423 typename Enable = EnableIfImplicitOkIs<true, OtherUnit, OtherRep>>
4424 constexpr QuantityPoint(QuantityPoint<OtherUnit, OtherRep> other) // NOLINT(runtime/explicit)
4425 : QuantityPoint{other.template as<Rep>(unit)} {}
4426
4427 template <typename OtherUnit,
4428 typename OtherRep,
4429 typename Enable = EnableIfImplicitOkIs<false, OtherUnit, OtherRep>,
4430 typename ThisUnusedTemplateParameterDistinguishesUsFromTheAboveConstructor = void>
4431 // Deleted: use `.as<NewRep>(new_unit)` to force a cast.
4432 constexpr explicit QuantityPoint(QuantityPoint<OtherUnit, OtherRep> other) = delete;
4433
4434 // The notion of "0" is *not* unambiguous for point types, because different scales can make
4435 // different decisions about what point is labeled as "0".
4436 constexpr QuantityPoint(Zero) = delete;
4437
4438 template <typename NewRep,
4439 typename NewUnit,
4440 typename = std::enable_if_t<IsUnit<NewUnit>::value>>
4441 constexpr auto as(NewUnit u) const {
4442 return make_quantity_point<NewUnit>(this->template in<NewRep>(u));
4443 }
4444
4445 template <typename NewUnit, typename = std::enable_if_t<IsUnit<NewUnit>::value>>
4446 constexpr auto as(NewUnit u) const {
4447 return make_quantity_point<NewUnit>(in(u));
4448 }
4449
4450 template <typename NewRep,
4451 typename NewUnit,
4452 typename = std::enable_if_t<IsUnit<NewUnit>::value>>
4453 constexpr NewRep in(NewUnit u) const {
4454 return (rep_cast<NewRep>(x_) - rep_cast<NewRep>(OriginDisplacement<Unit, NewUnit>::value()))
4455 .template in<NewRep>(u);
4456 }
4457
4458 template <typename NewUnit, typename = std::enable_if_t<IsUnit<NewUnit>::value>>
4459 constexpr Rep in(NewUnit u) const {
4461 "Cannot represent origin displacement in desired Rep");
4462
4463 // `rep_cast` is needed because if these are integral types, their difference might become a
4464 // different type due to integer promotion.
4465 return rep_cast<Rep>(x_ + rep_cast<Rep>(OriginDisplacement<NewUnit, Unit>::value())).in(u);
4466 }
4467
4468 // Overloads for passing a QuantityPointMaker.
4469 //
4470 // This is the "magic" that lets us write things like `position.in(meters_pt)`, instead of just
4471 // `position.in(Meters{})`.
4472 template <typename NewRep, typename NewUnit>
4473 constexpr auto as(QuantityPointMaker<NewUnit>) const {
4474 return as<NewRep>(NewUnit{});
4475 }
4476 template <typename NewUnit>
4477 constexpr auto as(QuantityPointMaker<NewUnit>) const {
4478 return as(NewUnit{});
4479 }
4480 template <typename NewRep, typename NewUnit>
4481 constexpr NewRep in(QuantityPointMaker<NewUnit>) const {
4482 return in<NewRep>(NewUnit{});
4483 }
4484 template <typename NewUnit>
4485 constexpr Rep in(QuantityPointMaker<NewUnit>) const {
4486 return in(NewUnit{});
4487 }
4488
4489 // "Old-style" overloads with <U, R> template parameters, and no function parameters.
4490 //
4491 // Matches the syntax from the CppCon 2021 talk, and legacy Aurora usage.
4492 template <typename U>
4493 [[deprecated(
4494 "Do not write `.as<YourUnits>()`; write `.as(your_units)` instead.")]] constexpr auto
4495 as() const -> decltype(as(U{})) {
4496 return as(U{});
4497 }
4498 template <typename U, typename R, typename = std::enable_if_t<IsUnit<U>::value>>
4499 [[deprecated(
4500 "Do not write `.as<YourUnits, T>()`; write `.as<T>(your_units)` instead.")]] constexpr auto
4501 as() const {
4502 return as<R>(U{});
4503 }
4504 template <typename U>
4505 [[deprecated(
4506 "Do not write `.in<YourUnits>()`; write `.in(your_units)` instead.")]] constexpr auto
4507 in() const -> decltype(in(U{})) {
4508 return in(U{});
4509 }
4510 template <typename U, typename R, typename = std::enable_if_t<IsUnit<U>::value>>
4511 [[deprecated(
4512 "Do not write `.in<YourUnits, T>()`; write `.in<T>(your_units)` instead.")]] constexpr auto
4513 in() const {
4514 return in<R>(U{});
4515 }
4516
4517 // "Forcing" conversions, which explicitly ignore safety checks for overflow and truncation.
4518 template <typename NewUnit>
4519 constexpr auto coerce_as(NewUnit) const {
4520 // Usage example: `p.coerce_as(new_units)`.
4521 return as<Rep>(NewUnit{});
4522 }
4523 template <typename NewRep, typename NewUnit>
4524 constexpr auto coerce_as(NewUnit) const {
4525 // Usage example: `p.coerce_as<T>(new_units)`.
4526 return as<NewRep>(NewUnit{});
4527 }
4528 template <typename NewUnit>
4529 constexpr auto coerce_in(NewUnit) const {
4530 // Usage example: `p.coerce_in(new_units)`.
4531 return in<Rep>(NewUnit{});
4532 }
4533 template <typename NewRep, typename NewUnit>
4534 constexpr auto coerce_in(NewUnit) const {
4535 // Usage example: `p.coerce_in<T>(new_units)`.
4536 return in<NewRep>(NewUnit{});
4537 }
4538
4539 // Direct access to the underlying value member, with any Point-equivalent Unit.
4540 //
4541 // Mutable access, QuantityPointMaker input.
4542 template <typename U>
4543 Rep &data_in(const QuantityPointMaker<U> &) {
4545 "Can only access value via Point-equivalent unit");
4546 return x_.data_in(QuantityMaker<U>{});
4547 }
4548 // Mutable access, Unit input.
4549 template <typename U>
4550 Rep &data_in(const U &) {
4551 return data_in(QuantityPointMaker<U>{});
4552 }
4553 // Const access, QuantityPointMaker input.
4554 template <typename U>
4555 const Rep &data_in(const QuantityPointMaker<U> &) const {
4557 "Can only access value via Point-equivalent unit");
4558 return x_.data_in(QuantityMaker<U>{});
4559 }
4560 // Const access, Unit input.
4561 template <typename U>
4562 const Rep &data_in(const U &) const {
4563 return data_in(QuantityPointMaker<U>{});
4564 }
4565
4566 // Comparison operators.
4567 constexpr friend bool operator==(QuantityPoint a, QuantityPoint b) { return a.x_ == b.x_; }
4568 constexpr friend bool operator!=(QuantityPoint a, QuantityPoint b) { return a.x_ != b.x_; }
4569 constexpr friend bool operator>=(QuantityPoint a, QuantityPoint b) { return a.x_ >= b.x_; }
4570 constexpr friend bool operator>(QuantityPoint a, QuantityPoint b) { return a.x_ > b.x_; }
4571 constexpr friend bool operator<=(QuantityPoint a, QuantityPoint b) { return a.x_ <= b.x_; }
4572 constexpr friend bool operator<(QuantityPoint a, QuantityPoint b) { return a.x_ < b.x_; }
4573
4574 // Subtraction between two QuantityPoint types.
4575 constexpr friend Diff operator-(QuantityPoint a, QuantityPoint b) { return a.x_ - b.x_; }
4576
4577 // Left and right addition of a Diff.
4578 constexpr friend auto operator+(Diff d, QuantityPoint p) { return QuantityPoint{d + p.x_}; }
4579 constexpr friend auto operator+(QuantityPoint p, Diff d) { return QuantityPoint{p.x_ + d}; }
4580
4581 // Right subtraction of a Diff.
4582 constexpr friend auto operator-(QuantityPoint p, Diff d) { return QuantityPoint{p.x_ - d}; }
4583
4584 // Short-hand addition assignment.
4585 constexpr QuantityPoint &operator+=(Diff diff) {
4586 x_ += diff;
4587 return *this;
4588 }
4589
4590 // Short-hand subtraction assignment.
4591 constexpr QuantityPoint &operator-=(Diff diff) {
4592 x_ -= diff;
4593 return *this;
4594 }
4595
4596 // Permit this factory functor to access our private constructor.
4597 //
4598 // We allow this because it explicitly names the unit at the callsite, even if people refer to
4599 // this present Quantity type by an alias that omits the unit. This preserves Unit Safety and
4600 // promotes callsite readability.
4601 friend struct QuantityPointMaker<Unit>;
4602
4603 private:
4604 constexpr explicit QuantityPoint(Diff x) : x_{x} {}
4605
4606 Diff x_;
4607};
4608
4609template <typename Unit>
4611 static constexpr auto unit = Unit{};
4612
4613 template <typename T>
4614 constexpr auto operator()(T value) const {
4615 return QuantityPoint<Unit, T>{make_quantity<Unit>(value)};
4616 }
4617
4618 template <typename... BPs>
4619 constexpr auto operator*(Magnitude<BPs...> m) const {
4620 return QuantityPointMaker<decltype(unit * m)>{};
4621 }
4622
4623 template <typename... BPs>
4624 constexpr auto operator/(Magnitude<BPs...> m) const {
4625 return QuantityPointMaker<decltype(unit / m)>{};
4626 }
4627};
4628
4629// Type trait to detect whether two QuantityPoint types are equivalent.
4630//
4631// In this library, QuantityPoint types are "equivalent" exactly when they use the same Rep, and are
4632// based on point-equivalent units.
4633template <typename U1, typename U2, typename R1, typename R2>
4635 : stdx::conjunction<std::is_same<R1, R2>, AreUnitsPointEquivalent<U1, U2>> {};
4636
4637// Cast QuantityPoint to a different underlying type.
4638template <typename NewRep, typename Unit, typename Rep>
4639constexpr auto rep_cast(QuantityPoint<Unit, Rep> q) {
4640 return q.template as<NewRep>(Unit{});
4641}
4642
4643//
4644// QuantityPoint aliases to set a particular Rep.
4645//
4646// This presents a less cumbersome interface for end users.
4647//
4648template <typename UnitT>
4649using QuantityPointD = QuantityPoint<UnitT, double>;
4650template <typename UnitT>
4651using QuantityPointF = QuantityPoint<UnitT, float>;
4652template <typename UnitT>
4653using QuantityPointI = QuantityPoint<UnitT, int>;
4654template <typename UnitT>
4655using QuantityPointU = QuantityPoint<UnitT, unsigned int>;
4656template <typename UnitT>
4657using QuantityPointI32 = QuantityPoint<UnitT, int32_t>;
4658template <typename UnitT>
4659using QuantityPointU32 = QuantityPoint<UnitT, uint32_t>;
4660template <typename UnitT>
4661using QuantityPointI64 = QuantityPoint<UnitT, int64_t>;
4662template <typename UnitT>
4663using QuantityPointU64 = QuantityPoint<UnitT, uint64_t>;
4664
4665namespace detail {
4666template <typename X, typename Y, typename Func>
4667constexpr auto using_common_point_unit(X x, Y y, Func f) {
4668 using R = std::common_type_t<typename X::Rep, typename Y::Rep>;
4669 constexpr auto u = CommonPointUnitT<typename X::Unit, typename Y::Unit>{};
4670 return f(rep_cast<R>(x).as(u), rep_cast<R>(y).as(u));
4671}
4672} // namespace detail
4673
4674// Comparison functions for compatible QuantityPoint types.
4675template <typename U1, typename U2, typename R1, typename R2>
4676constexpr auto operator<(QuantityPoint<U1, R1> p1, QuantityPoint<U2, R2> p2) {
4677 return detail::using_common_point_unit(p1, p2, detail::less);
4678}
4679template <typename U1, typename U2, typename R1, typename R2>
4680constexpr auto operator>(QuantityPoint<U1, R1> p1, QuantityPoint<U2, R2> p2) {
4681 return detail::using_common_point_unit(p1, p2, detail::greater);
4682}
4683template <typename U1, typename U2, typename R1, typename R2>
4684constexpr auto operator<=(QuantityPoint<U1, R1> p1, QuantityPoint<U2, R2> p2) {
4685 return detail::using_common_point_unit(p1, p2, detail::less_equal);
4686}
4687template <typename U1, typename U2, typename R1, typename R2>
4688constexpr auto operator>=(QuantityPoint<U1, R1> p1, QuantityPoint<U2, R2> p2) {
4689 return detail::using_common_point_unit(p1, p2, detail::greater_equal);
4690}
4691template <typename U1, typename U2, typename R1, typename R2>
4692constexpr auto operator==(QuantityPoint<U1, R1> p1, QuantityPoint<U2, R2> p2) {
4693 return detail::using_common_point_unit(p1, p2, detail::equal);
4694}
4695template <typename U1, typename U2, typename R1, typename R2>
4696constexpr auto operator!=(QuantityPoint<U1, R1> p1, QuantityPoint<U2, R2> p2) {
4697 return detail::using_common_point_unit(p1, p2, detail::not_equal);
4698}
4699
4700namespace detail {
4701// Another subtlety arises when we mix QuantityPoint and Quantity in adding or subtracting. We
4702// actually don't want to use `CommonPointUnitT`, because this is too restrictive if the units have
4703// different origins. Imagine adding a `Quantity<Kelvins>` to a `QuantityPoint<Celsius>`---we
4704// wouldn't want this to subdivide the unit of measure to satisfy an additive relative offset which
4705// we will never actually use!
4706//
4707// The solution is to set the (unused!) origin of the `Quantity` unit to the same as the
4708// `QuantityPoint` unit. Once we do, everything flows simply from there.
4709//
4710// This utility should be used for every overload below which combines a `QuantityPoint` with a
4711// `Quantity`.
4712template <typename Target, typename U>
4713constexpr auto borrow_origin(U u) {
4714 return Target{} * unit_ratio(u, Target{});
4715}
4716} // namespace detail
4717
4718// Addition and subtraction functions for compatible QuantityPoint types.
4719template <typename UnitP, typename UnitQ, typename RepP, typename RepQ>
4720constexpr auto operator+(QuantityPoint<UnitP, RepP> p, Quantity<UnitQ, RepQ> q) {
4721 constexpr auto new_unit_q = detail::borrow_origin<UnitP>(UnitQ{});
4722 return detail::using_common_point_unit(p, q.as(new_unit_q), detail::plus);
4723}
4724template <typename UnitQ, typename UnitP, typename RepQ, typename RepP>
4725constexpr auto operator+(Quantity<UnitQ, RepQ> q, QuantityPoint<UnitP, RepP> p) {
4726 constexpr auto new_unit_q = detail::borrow_origin<UnitP>(UnitQ{});
4727 return detail::using_common_point_unit(q.as(new_unit_q), p, detail::plus);
4728}
4729template <typename UnitP, typename UnitQ, typename R1, typename RepQ>
4730constexpr auto operator-(QuantityPoint<UnitP, R1> p, Quantity<UnitQ, RepQ> q) {
4731 constexpr auto new_unit_q = detail::borrow_origin<UnitP>(UnitQ{});
4732 return detail::using_common_point_unit(p, q.as(new_unit_q), detail::minus);
4733}
4734template <typename U1, typename U2, typename R1, typename R2>
4735constexpr auto operator-(QuantityPoint<U1, R1> p1, QuantityPoint<U2, R2> p2) {
4736 return detail::using_common_point_unit(p1, p2, detail::minus);
4737}
4738
4739namespace detail {
4740
4741template <typename TargetRep, typename U, typename R>
4742constexpr bool underlying_value_in_range(Quantity<U, R> q) {
4743 return stdx::in_range<TargetRep>(q.in(U{}));
4744}
4745
4746template <typename TargetRep>
4747constexpr bool underlying_value_in_range(Zero) {
4748 return true;
4749}
4750
4751template <typename TargetRep, typename U1, typename U2>
4753 : std::conditional_t<std::is_integral<TargetRep>::value,
4754 stdx::bool_constant<underlying_value_in_range<TargetRep>(
4755 OriginDisplacement<U1, U2>::value())>,
4756 std::true_type> {};
4757} // namespace detail
4758} // namespace au
4759
4760
4761namespace au {
4762
4763//
4764// A representation of the symbol for a unit.
4765//
4766// To use, create an instance variable templated on a unit, and make the instance variable's name
4767// the symbol to represent. For example:
4768//
4769// constexpr auto m = SymbolFor<Meters>{};
4770//
4771template <typename Unit>
4773 detail::ScalesQuantity<SymbolFor, Unit>,
4774 detail::ComposesWith<SymbolFor, Unit, SymbolFor, SymbolFor> {};
4775
4776//
4777// Create a unit symbol using the more fluent APIs that unit slots make possible. For example:
4778//
4779// constexpr auto mps = symbol_for(meters / second);
4780//
4781// This is generally easier to work with and makes code that is easier to read, at the cost of being
4782// (very slightly) slower to compile.
4783//
4784template <typename UnitSlot>
4785constexpr auto symbol_for(UnitSlot) {
4787}
4788
4789// Support using symbols in unit slot APIs (e.g., `v.in(m / s)`).
4790template <typename U>
4792
4793} // namespace au
4794
4795
4796namespace au {
4797
4798template <template <class U> class Prefix>
4800 // Applying a Prefix to a Unit instance, creates an instance of the Prefixed Unit.
4801 template <typename U>
4802 constexpr auto operator()(U) const {
4803 return Prefix<U>{};
4804 }
4805
4806 // Applying a Prefix to a QuantityMaker instance, creates a maker for the Prefixed Unit.
4807 template <typename U>
4808 constexpr auto operator()(QuantityMaker<U>) const {
4809 return QuantityMaker<Prefix<U>>{};
4810 }
4811
4812 // Applying a Prefix to a QuantityPointMaker instance, changes it to make the Prefixed Unit.
4813 template <typename U>
4814 constexpr auto operator()(QuantityPointMaker<U>) const {
4816 }
4817
4818 // Applying a Prefix to a SingularNameFor instance, creates a singularly-named instance of the
4819 // Prefixed Unit.
4820 template <typename U>
4821 constexpr auto operator()(SingularNameFor<U>) const {
4822 return SingularNameFor<Prefix<U>>{};
4823 }
4824
4825 // Applying a Prefix to a SymbolFor instance, creates a symbolically-named instance of the
4826 // Prefixed unit.
4827 template <typename U>
4828 constexpr auto operator()(SymbolFor<U>) const {
4829 return SymbolFor<Prefix<U>>{};
4830 }
4831};
4832
4834// SI Prefixes.
4835
4836template <typename U>
4837struct Quetta : decltype(U{} * pow<30>(mag<10>())) {
4838 static constexpr detail::ExtendedLabel<1, U> label = detail::concatenate("Q", unit_label<U>());
4839};
4840template <typename U>
4842constexpr auto quetta = PrefixApplier<Quetta>{};
4843
4844template <typename U>
4845struct Ronna : decltype(U{} * pow<27>(mag<10>())) {
4846 static constexpr detail::ExtendedLabel<1, U> label = detail::concatenate("R", unit_label<U>());
4847};
4848template <typename U>
4850constexpr auto ronna = PrefixApplier<Ronna>{};
4851
4852template <typename U>
4853struct Yotta : decltype(U{} * pow<24>(mag<10>())) {
4854 static constexpr detail::ExtendedLabel<1, U> label = detail::concatenate("Y", unit_label<U>());
4855};
4856template <typename U>
4858constexpr auto yotta = PrefixApplier<Yotta>{};
4859
4860template <typename U>
4861struct Zetta : decltype(U{} * pow<21>(mag<10>())) {
4862 static constexpr detail::ExtendedLabel<1, U> label = detail::concatenate("Z", unit_label<U>());
4863};
4864template <typename U>
4866constexpr auto zetta = PrefixApplier<Zetta>{};
4867
4868template <typename U>
4869struct Exa : decltype(U{} * pow<18>(mag<10>())) {
4870 static constexpr detail::ExtendedLabel<1, U> label = detail::concatenate("E", unit_label<U>());
4871};
4872template <typename U>
4874constexpr auto exa = PrefixApplier<Exa>{};
4875
4876template <typename U>
4877struct Peta : decltype(U{} * pow<15>(mag<10>())) {
4878 static constexpr detail::ExtendedLabel<1, U> label = detail::concatenate("P", unit_label<U>());
4879};
4880template <typename U>
4882constexpr auto peta = PrefixApplier<Peta>{};
4883
4884template <typename U>
4885struct Tera : decltype(U{} * pow<12>(mag<10>())) {
4886 static constexpr detail::ExtendedLabel<1, U> label = detail::concatenate("T", unit_label<U>());
4887};
4888template <typename U>
4890constexpr auto tera = PrefixApplier<Tera>{};
4891
4892template <typename U>
4893struct Giga : decltype(U{} * pow<9>(mag<10>())) {
4894 static constexpr detail::ExtendedLabel<1, U> label = detail::concatenate("G", unit_label<U>());
4895};
4896template <typename U>
4898constexpr auto giga = PrefixApplier<Giga>{};
4899
4900template <typename U>
4901struct Mega : decltype(U{} * pow<6>(mag<10>())) {
4902 static constexpr detail::ExtendedLabel<1, U> label = detail::concatenate("M", unit_label<U>());
4903};
4904template <typename U>
4906constexpr auto mega = PrefixApplier<Mega>{};
4907
4908template <typename U>
4909struct Kilo : decltype(U{} * pow<3>(mag<10>())) {
4910 static constexpr detail::ExtendedLabel<1, U> label = detail::concatenate("k", unit_label<U>());
4911};
4912template <typename U>
4914constexpr auto kilo = PrefixApplier<Kilo>{};
4915
4916template <typename U>
4917struct Hecto : decltype(U{} * pow<2>(mag<10>())) {
4918 static constexpr detail::ExtendedLabel<1, U> label = detail::concatenate("h", unit_label<U>());
4919};
4920template <typename U>
4922constexpr auto hecto = PrefixApplier<Hecto>{};
4923
4924template <typename U>
4925struct Deka : decltype(U{} * pow<1>(mag<10>())) {
4926 static constexpr detail::ExtendedLabel<2, U> label = detail::concatenate("da", unit_label<U>());
4927};
4928template <typename U>
4930constexpr auto deka = PrefixApplier<Deka>{};
4931
4932template <typename U>
4933struct Deci : decltype(U{} * pow<-1>(mag<10>())) {
4934 static constexpr detail::ExtendedLabel<1, U> label = detail::concatenate("d", unit_label<U>());
4935};
4936template <typename U>
4938constexpr auto deci = PrefixApplier<Deci>{};
4939
4940template <typename U>
4941struct Centi : decltype(U{} * pow<-2>(mag<10>())) {
4942 static constexpr detail::ExtendedLabel<1, U> label = detail::concatenate("c", unit_label<U>());
4943};
4944template <typename U>
4946constexpr auto centi = PrefixApplier<Centi>{};
4947
4948template <typename U>
4949struct Milli : decltype(U{} * pow<-3>(mag<10>())) {
4950 static constexpr detail::ExtendedLabel<1, U> label = detail::concatenate("m", unit_label<U>());
4951};
4952template <typename U>
4954constexpr auto milli = PrefixApplier<Milli>{};
4955
4956template <typename U>
4957struct Micro : decltype(U{} * pow<-6>(mag<10>())) {
4958 static constexpr detail::ExtendedLabel<1, U> label = detail::concatenate("u", unit_label<U>());
4959};
4960template <typename U>
4962constexpr auto micro = PrefixApplier<Micro>{};
4963
4964template <typename U>
4965struct Nano : decltype(U{} * pow<-9>(mag<10>())) {
4966 static constexpr detail::ExtendedLabel<1, U> label = detail::concatenate("n", unit_label<U>());
4967};
4968template <typename U>
4970constexpr auto nano = PrefixApplier<Nano>{};
4971
4972template <typename U>
4973struct Pico : decltype(U{} * pow<-12>(mag<10>())) {
4974 static constexpr detail::ExtendedLabel<1, U> label = detail::concatenate("p", unit_label<U>());
4975};
4976template <typename U>
4978constexpr auto pico = PrefixApplier<Pico>{};
4979
4980template <typename U>
4981struct Femto : decltype(U{} * pow<-15>(mag<10>())) {
4982 static constexpr detail::ExtendedLabel<1, U> label = detail::concatenate("f", unit_label<U>());
4983};
4984template <typename U>
4986constexpr auto femto = PrefixApplier<Femto>{};
4987
4988template <typename U>
4989struct Atto : decltype(U{} * pow<-18>(mag<10>())) {
4990 static constexpr detail::ExtendedLabel<1, U> label = detail::concatenate("a", unit_label<U>());
4991};
4992template <typename U>
4994constexpr auto atto = PrefixApplier<Atto>{};
4995
4996template <typename U>
4997struct Zepto : decltype(U{} * pow<-21>(mag<10>())) {
4998 static constexpr detail::ExtendedLabel<1, U> label = detail::concatenate("z", unit_label<U>());
4999};
5000template <typename U>
5002constexpr auto zepto = PrefixApplier<Zepto>{};
5003
5004template <typename U>
5005struct Yocto : decltype(U{} * pow<-24>(mag<10>())) {
5006 static constexpr detail::ExtendedLabel<1, U> label = detail::concatenate("y", unit_label<U>());
5007};
5008template <typename U>
5010constexpr auto yocto = PrefixApplier<Yocto>{};
5011
5012template <typename U>
5013struct Ronto : decltype(U{} * pow<-27>(mag<10>())) {
5014 static constexpr detail::ExtendedLabel<1, U> label = detail::concatenate("r", unit_label<U>());
5015};
5016template <typename U>
5018constexpr auto ronto = PrefixApplier<Ronto>{};
5019
5020template <typename U>
5021struct Quecto : decltype(U{} * pow<-30>(mag<10>())) {
5022 static constexpr detail::ExtendedLabel<1, U> label = detail::concatenate("q", unit_label<U>());
5023};
5024template <typename U>
5026constexpr auto quecto = PrefixApplier<Quecto>{};
5027
5029// Binary Prefixes.
5030
5031template <typename U>
5032struct Yobi : decltype(U{} * pow<80>(mag<2>())) {
5033 static constexpr detail::ExtendedLabel<2, U> label = detail::concatenate("Yi", unit_label<U>());
5034};
5035template <typename U>
5037constexpr auto yobi = PrefixApplier<Yobi>{};
5038
5039template <typename U>
5040struct Zebi : decltype(U{} * pow<70>(mag<2>())) {
5041 static constexpr detail::ExtendedLabel<2, U> label = detail::concatenate("Zi", unit_label<U>());
5042};
5043template <typename U>
5045constexpr auto zebi = PrefixApplier<Zebi>{};
5046
5047template <typename U>
5048struct Exbi : decltype(U{} * pow<60>(mag<2>())) {
5049 static constexpr detail::ExtendedLabel<2, U> label = detail::concatenate("Ei", unit_label<U>());
5050};
5051template <typename U>
5053constexpr auto exbi = PrefixApplier<Exbi>{};
5054
5055template <typename U>
5056struct Pebi : decltype(U{} * pow<50>(mag<2>())) {
5057 static constexpr detail::ExtendedLabel<2, U> label = detail::concatenate("Pi", unit_label<U>());
5058};
5059template <typename U>
5061constexpr auto pebi = PrefixApplier<Pebi>{};
5062
5063template <typename U>
5064struct Tebi : decltype(U{} * pow<40>(mag<2>())) {
5065 static constexpr detail::ExtendedLabel<2, U> label = detail::concatenate("Ti", unit_label<U>());
5066};
5067template <typename U>
5069constexpr auto tebi = PrefixApplier<Tebi>{};
5070
5071template <typename U>
5072struct Gibi : decltype(U{} * pow<30>(mag<2>())) {
5073 static constexpr detail::ExtendedLabel<2, U> label = detail::concatenate("Gi", unit_label<U>());
5074};
5075template <typename U>
5077constexpr auto gibi = PrefixApplier<Gibi>{};
5078
5079template <typename U>
5080struct Mebi : decltype(U{} * pow<20>(mag<2>())) {
5081 static constexpr detail::ExtendedLabel<2, U> label = detail::concatenate("Mi", unit_label<U>());
5082};
5083template <typename U>
5085constexpr auto mebi = PrefixApplier<Mebi>{};
5086
5087template <typename U>
5088struct Kibi : decltype(U{} * pow<10>(mag<2>())) {
5089 static constexpr detail::ExtendedLabel<2, U> label = detail::concatenate("Ki", unit_label<U>());
5090};
5091template <typename U>
5093constexpr auto kibi = PrefixApplier<Kibi>{};
5094
5095} // namespace au
5096
5097
5098namespace au {
5099
5100// DO NOT follow this pattern to define your own units. This is for library-defined units.
5101// Instead, follow instructions at (https://aurora-opensource.github.io/au/main/howto/new-units/).
5102template <typename T>
5104 static constexpr const char label[] = "rad";
5105};
5106template <typename T>
5107constexpr const char RadiansLabel<T>::label[];
5108struct Radians : UnitImpl<Angle>, RadiansLabel<void> {
5109 using RadiansLabel<void>::label;
5110};
5111constexpr auto radian = SingularNameFor<Radians>{};
5112constexpr auto radians = QuantityMaker<Radians>{};
5113
5114namespace symbols {
5115constexpr auto rad = SymbolFor<Radians>{};
5116}
5117} // namespace au
5118
5119
5120namespace au {
5121
5122//
5123// A monovalue type to represent a constant value, including its units, if any.
5124//
5125// Users can multiply or divide `Constant` instances by raw numbers or `Quantity` instances, and it
5126// will perform symbolic arithmetic at compile time without affecting the stored numeric value.
5127// `Constant` also composes with other constants, and with `QuantityMaker` and other related types.
5128//
5129// Although `Constant` does not have any specific numeric type associated with it (as opposed to
5130// `Quantity`), it can easily convert to any appropriate `Quantity` type, with any rep. Unlike
5131// `Quantity`, these conversions support _exact_ safety checks, so that every conversion producing a
5132// correctly representable value will succeed, and every unrepresentable conversion will fail.
5133//
5134template <typename Unit>
5136 detail::ScalesQuantity<Constant, Unit>,
5137 detail::ComposesWith<Constant, Unit, Constant, Constant>,
5138 detail::ComposesWith<Constant, Unit, QuantityMaker, QuantityMaker>,
5139 detail::ComposesWith<Constant, Unit, SingularNameFor, SingularNameFor>,
5140 detail::CanScaleByMagnitude<Constant, Unit> {
5141 // Convert this constant to a Quantity of the given rep.
5142 template <typename T>
5143 constexpr auto as() const {
5144 return make_quantity<Unit>(static_cast<T>(1));
5145 }
5146
5147 // Convert this constant to a Quantity of the given unit and rep, ignoring safety checks.
5148 template <typename T, typename OtherUnit>
5149 constexpr auto coerce_as(OtherUnit u) const {
5150 return as<T>().coerce_as(u);
5151 }
5152
5153 // Convert this constant to a Quantity of the given unit and rep.
5154 template <typename T, typename OtherUnit>
5155 constexpr auto as(OtherUnit u) const {
5156 static_assert(can_store_value_in<T>(u), "Cannot represent constant in this unit/rep");
5157 return coerce_as<T>(u);
5158 }
5159
5160 // Get the value of this constant in the given unit and rep, ignoring safety checks.
5161 template <typename T, typename OtherUnit>
5162 constexpr auto coerce_in(OtherUnit u) const {
5163 return as<T>().coerce_in(u);
5164 }
5165
5166 // Get the value of this constant in the given unit and rep.
5167 template <typename T, typename OtherUnit>
5168 constexpr auto in(OtherUnit u) const {
5169 static_assert(can_store_value_in<T>(u), "Cannot represent constant in this unit/rep");
5170 return coerce_in<T>(u);
5171 }
5172
5173 // Implicitly convert to any quantity type which passes safety checks.
5174 template <typename U, typename R>
5175 constexpr operator Quantity<U, R>() const {
5176 return as<R>(U{});
5177 }
5178
5179 // Static function to check whether this constant can be exactly-represented in the given rep
5180 // `T` and unit `OtherUnit`.
5181 template <typename T, typename OtherUnit>
5182 static constexpr bool can_store_value_in(OtherUnit other) {
5183 return representable_in<T>(unit_ratio(Unit{}, other));
5184 }
5185
5186 // Implicitly convert to type with an exactly corresponding quantity that passes safety checks.
5187 template <
5188 typename T,
5189 typename = std::enable_if_t<can_store_value_in<typename CorrespondingQuantity<T>::Rep>(
5190 typename CorrespondingQuantity<T>::Unit{})>>
5191 constexpr operator T() const {
5192 return as<typename CorrespondingQuantity<T>::Rep>(
5194 }
5195};
5196
5197// Make a constant from the given unit.
5198//
5199// Note that the argument is a _unit slot_, and thus can also accept things like `QuantityMaker` and
5200// `SymbolFor` in addition to regular units.
5201template <typename UnitSlot>
5202constexpr Constant<AssociatedUnitT<UnitSlot>> make_constant(UnitSlot) {
5203 return {};
5204}
5205
5206// Support using `Constant` in a unit slot.
5207template <typename Unit>
5209
5210} // namespace au
5211
5212
5213namespace au {
5214
5215// DO NOT follow this pattern to define your own units. This is for library-defined units.
5216// Instead, follow instructions at (https://aurora-opensource.github.io/au/main/howto/new-units/).
5217template <typename T>
5219 static constexpr const char label[] = "s";
5220};
5221template <typename T>
5222constexpr const char SecondsLabel<T>::label[];
5223struct Seconds : UnitImpl<Time>, SecondsLabel<void> {
5224 using SecondsLabel<void>::label;
5225};
5226constexpr auto second = SingularNameFor<Seconds>{};
5227constexpr auto seconds = QuantityMaker<Seconds>{};
5228
5229namespace symbols {
5230constexpr auto s = SymbolFor<Seconds>{};
5231}
5232} // namespace au
5233
5234
5235
5236namespace au {
5237
5238// Streaming output support for Quantity types.
5239template <typename U, typename R>
5240std::ostream &operator<<(std::ostream &out, const Quantity<U, R> &q) {
5241 // In the case that the Rep is a type that resolves to 'char' (e.g. int8_t),
5242 // the << operator will match the implementation that takes a character
5243 // literal. Using the unary + operator will trigger an integer promotion on
5244 // the operand, which will then match an appropriate << operator that will
5245 // output the integer representation.
5246 out << +q.in(U{}) << " " << unit_label(U{});
5247 return out;
5248}
5249
5250// Streaming output support for QuantityPoint types.
5251template <typename U, typename R>
5252std::ostream &operator<<(std::ostream &out, const QuantityPoint<U, R> &p) {
5253 out << "@(" << (p - rep_cast<R>(make_quantity_point<U>(0))) << ")";
5254 return out;
5255}
5256
5257// Streaming output support for Zero. (Useful for printing in unit test failures.)
5258inline std::ostream &operator<<(std::ostream &out, Zero) {
5259 out << "0";
5260 return out;
5261}
5262
5263} // namespace au
5264
5265
5266namespace au {
5267
5268// DO NOT follow this pattern to define your own units. This is for library-defined units.
5269// Instead, follow instructions at (https://aurora-opensource.github.io/au/main/howto/new-units/).
5270template <typename T>
5272 static constexpr const char label[] = "m";
5273};
5274template <typename T>
5275constexpr const char MetersLabel<T>::label[];
5276struct Meters : UnitImpl<Length>, MetersLabel<void> {
5277 using MetersLabel<void>::label;
5278};
5279constexpr auto meter = SingularNameFor<Meters>{};
5280constexpr auto meters = QuantityMaker<Meters>{};
5281constexpr auto meters_pt = QuantityPointMaker<Meters>{};
5282
5283namespace symbols {
5284constexpr auto m = SymbolFor<Meters>{};
5285}
5286} // namespace au
5287
5288
5289
5290namespace au {
5291
5292// If we don't provide these, then unqualified uses of `sin()`, etc. from <cmath> will break. Name
5293// Lookup will stop once it hits `::au::sin()`, hiding the `::sin()` overload in the global
5294// namespace. To learn more about Name Lookup, see this article (https://abseil.io/tips/49).
5295using std::abs;
5296using std::copysign;
5297using std::cos;
5298using std::fmod;
5299using std::isnan;
5300using std::max;
5301using std::min;
5302using std::remainder;
5303using std::sin;
5304using std::sqrt;
5305using std::tan;
5306
5307namespace detail {
5308
5309// This utility handles converting Quantity to Radians in a uniform way, while also giving a more
5310// direct error message via the static_assert if users make a coding error and pass the wrong type.
5311template <typename U, typename R>
5312auto in_radians(Quantity<U, R> q) {
5313 static_assert(HasSameDimension<U, Radians>{},
5314 "Can only use trig functions with Angle-dimensioned Quantity instances");
5315
5316 // The standard library trig functions handle types as follows:
5317 // - For floating point inputs, return the same type as the input.
5318 // - For integral inputs, cast to a `double` and return a `double`.
5319 // See, for instance: https://en.cppreference.com/w/cpp/numeric/math/sin
5320 using PromotedT = std::conditional_t<std::is_floating_point<R>::value, R, double>;
5321
5322 return q.template in<PromotedT>(radians);
5323}
5324
5325template <typename T>
5326constexpr T int_pow_impl(T x, int exp) {
5327 if (exp < 0) {
5328 return T{1} / int_pow_impl(x, -exp);
5329 }
5330
5331 if (exp == 0) {
5332 return T{1};
5333 }
5334
5335 if (exp % 2 == 1) {
5336 return x * int_pow_impl(x, exp - 1);
5337 }
5338
5339 const auto root = int_pow_impl(x, exp / 2);
5340 return root * root;
5341}
5342
5343// Rounding a Quantity by a function `f()` (where `f` could be `std::round`, `std::ceil`, or
5344// `std::floor`) can require _two_ steps: unit conversion, and type conversion. The unit conversion
5345// risks truncating the value if R is an integral type! To prevent this, when we do the unit
5346// conversion, we use "whatever Rep `f()` would produce," because that is always a floating point
5347// type.
5348//
5349// This risks breaking the correspondence between the Rep of our Quantity, and the output type of
5350// `f()`. For that correspondence to be _preserved_, we would need to make sure that
5351// `f(RoundingRepT{})` returns the same type as `f(R{})`. We believe this is always the case based
5352// on the documentation:
5353//
5354// https://en.cppreference.com/w/cpp/numeric/math/round
5355// https://en.cppreference.com/w/cpp/numeric/math/floor
5356// https://en.cppreference.com/w/cpp/numeric/math/ceil
5357//
5358// Both of these assumptions---that our RoundingRep is floating point, and that it doesn't change
5359// the output Rep type---we verify via `static_assert`.
5360template <typename Q, typename RoundingUnits>
5362template <typename Q, typename RoundingUnits>
5363using RoundingRepT = typename RoundingRep<Q, RoundingUnits>::type;
5364template <typename U, typename R, typename RoundingUnits>
5365struct RoundingRep<Quantity<U, R>, RoundingUnits> {
5366 using type = decltype(std::round(R{}));
5367
5368 // Test our floating point assumption.
5369 static_assert(std::is_floating_point<type>::value, "");
5370
5371 // Test our type identity assumption, for every function which is a client of this utility.
5372 static_assert(std::is_same<decltype(std::round(type{})), decltype(std::round(R{}))>::value, "");
5373 static_assert(std::is_same<decltype(std::floor(type{})), decltype(std::floor(R{}))>::value, "");
5374 static_assert(std::is_same<decltype(std::ceil(type{})), decltype(std::ceil(R{}))>::value, "");
5375};
5376} // namespace detail
5377
5378// The absolute value of a Quantity.
5379template <typename U, typename R>
5380auto abs(Quantity<U, R> q) {
5381 return make_quantity<U>(std::abs(q.in(U{})));
5382}
5383
5384// Wrapper for std::acos() which returns strongly typed angle quantity.
5385template <typename T>
5386auto arccos(T x) {
5387 return radians(std::acos(x));
5388}
5389
5390// Wrapper for std::asin() which returns strongly typed angle quantity.
5391template <typename T>
5392auto arcsin(T x) {
5393 return radians(std::asin(x));
5394}
5395
5396// Wrapper for std::atan() which returns strongly typed angle quantity.
5397template <typename T>
5398auto arctan(T x) {
5399 return radians(std::atan(x));
5400}
5401
5402// Wrapper for std::atan2() which returns strongly typed angle quantity.
5403template <typename T, typename U>
5404auto arctan2(T y, U x) {
5405 return radians(std::atan2(y, x));
5406}
5407
5408// arctan2() overload which supports same-dimensioned Quantity types.
5409template <typename U1, typename R1, typename U2, typename R2>
5410auto arctan2(Quantity<U1, R1> y, Quantity<U2, R2> x) {
5411 constexpr auto common_unit = CommonUnitT<U1, U2>{};
5412 return arctan2(y.in(common_unit), x.in(common_unit));
5413}
5414
5415// Clamp the first quantity to within the range of the second two.
5416template <typename UV, typename ULo, typename UHi, typename RV, typename RLo, typename RHi>
5417constexpr auto clamp(Quantity<UV, RV> v, Quantity<ULo, RLo> lo, Quantity<UHi, RHi> hi) {
5418 using U = CommonUnitT<UV, ULo, UHi>;
5419 using R = std::common_type_t<RV, RLo, RHi>;
5420 using ResultT = Quantity<U, R>;
5421 return (v < lo) ? ResultT{lo} : (hi < v) ? ResultT{hi} : ResultT{v};
5422}
5423
5424// `clamp` overloads for when either boundary is `Zero`.
5425//
5426// NOTE: these will not work if _both_ boundaries are `Zero`, or if the quantity being clamped is
5427// `Zero`. We do not think these use cases are very useful, but we're open to revisiting this if we
5428// receive a persuasive argument otherwise.
5429template <typename UV, typename UHi, typename RV, typename RHi>
5430constexpr auto clamp(Quantity<UV, RV> v, Zero z, Quantity<UHi, RHi> hi) {
5431 using U = CommonUnitT<UV, UHi>;
5432 using R = std::common_type_t<RV, RHi>;
5433 using ResultT = Quantity<U, R>;
5434 return (v < z) ? ResultT{z} : (hi < v) ? ResultT{hi} : ResultT{v};
5435}
5436template <typename UV, typename ULo, typename RV, typename RLo>
5437constexpr auto clamp(Quantity<UV, RV> v, Quantity<ULo, RLo> lo, Zero z) {
5438 using U = CommonUnitT<UV, ULo>;
5439 using R = std::common_type_t<RV, RLo>;
5440 using ResultT = Quantity<U, R>;
5441 return (v < lo) ? ResultT{lo} : (z < v) ? ResultT{z} : ResultT{v};
5442}
5443
5444// Clamp the first point to within the range of the second two.
5445template <typename UV, typename ULo, typename UHi, typename RV, typename RLo, typename RHi>
5446constexpr auto clamp(QuantityPoint<UV, RV> v,
5447 QuantityPoint<ULo, RLo> lo,
5448 QuantityPoint<UHi, RHi> hi) {
5449 using U = CommonPointUnitT<UV, ULo, UHi>;
5450 using R = std::common_type_t<RV, RLo, RHi>;
5451 using ResultT = QuantityPoint<U, R>;
5452 return (v < lo) ? ResultT{lo} : (hi < v) ? ResultT{hi} : ResultT{v};
5453}
5454
5455// Copysign where the magnitude has units.
5456template <typename U, typename R, typename T>
5457constexpr auto copysign(Quantity<U, R> mag, T sgn) {
5458 return make_quantity<U>(std::copysign(mag.in(U{}), sgn));
5459}
5460
5461// Copysign where the sign has units.
5462template <typename T, typename U, typename R>
5463constexpr auto copysign(T mag, Quantity<U, R> sgn) {
5464 return std::copysign(mag, sgn.in(U{}));
5465}
5466
5467// Copysign where both the magnitude and sign have units (disambiguates between the above).
5468template <typename U1, typename R1, typename U2, typename R2>
5469constexpr auto copysign(Quantity<U1, R1> mag, Quantity<U2, R2> sgn) {
5470 return make_quantity<U1>(std::copysign(mag.in(U1{}), sgn.in(U2{})));
5471}
5472
5473// Wrapper for std::cos() which accepts a strongly typed angle quantity.
5474template <typename U, typename R>
5475auto cos(Quantity<U, R> q) {
5476 return std::cos(detail::in_radians(q));
5477}
5478
5479// The floating point remainder of two values of the same dimension.
5480template <typename U1, typename R1, typename U2, typename R2>
5481auto fmod(Quantity<U1, R1> q1, Quantity<U2, R2> q2) {
5482 using U = CommonUnitT<U1, U2>;
5483 using R = decltype(std::fmod(R1{}, R2{}));
5484 return make_quantity<U>(std::fmod(q1.template in<R>(U{}), q2.template in<R>(U{})));
5485}
5486
5487// Raise a Quantity to an integer power.
5488template <int Exp, typename U, typename R>
5489constexpr auto int_pow(Quantity<U, R> q) {
5490 static_assert((!std::is_integral<R>::value) || (Exp >= 0),
5491 "Negative exponent on integral represented units are not supported.");
5492
5493 return make_quantity<UnitPowerT<U, Exp>>(detail::int_pow_impl(q.in(U{}), Exp));
5494}
5495
5496//
5497// The value of the "smart" inverse of a Quantity, in a given destination Unit and Rep.
5498//
5499// This is the "explicit Rep" format, which is semantically equivalent to a `static_cast`.
5500//
5501template <typename TargetRep, typename TargetUnits, typename U, typename R>
5502constexpr auto inverse_in(TargetUnits target_units, Quantity<U, R> q) {
5503 using Rep = std::common_type_t<TargetRep, R>;
5504 constexpr auto UNITY = make_constant(UnitProductT<>{});
5505 return static_cast<TargetRep>(UNITY.in<Rep>(associated_unit(target_units) * U{}) / q.in(U{}));
5506}
5507
5508//
5509// The value of the "smart" inverse of a Quantity, in a given destination unit.
5510//
5511// By "smart", we mean that, e.g., you can convert an integral Quantity of Kilo<Hertz> to an
5512// integral Quantity of Nano<Seconds>, without ever leaving the integral domain. (Under the hood,
5513// in this case, the library will know to divide into 1'000'000 instead of dividing into 1.)
5514//
5515template <typename TargetUnits, typename U, typename R>
5516constexpr auto inverse_in(TargetUnits target_units, Quantity<U, R> q) {
5517 // The policy here is similar to our overflow policy, in that we try to avoid "bad outcomes"
5518 // when users store values less than 1000. (The thinking, here as there, is that values _more_
5519 // than 1000 would tend to be stored in the next SI-prefixed unit up, e.g., 1 km instead of 1000
5520 // m.)
5521 //
5522 // The "bad outcome" here is a lossy conversion. Since we're mainly worried about the integral
5523 // domain (because floating point numbers are already pretty well behaved), this means that:
5524 //
5525 // inverse_in(a, inverse_as(b, a(n)))
5526 //
5527 // should be the identity for all n <= 1000. For this to be true, we need a threshold of
5528 // (1'000 ^ 2) = 1'000'000.
5529 //
5530 // (An extreme instance of this kind of lossiness would be the inverse of a nonzero value
5531 // getting represented as 0, which would happen for values over the threshold.)
5532
5533 // This will fail at compile time for types that can't hold 1'000'000.
5534 constexpr R threshold = 1'000'000;
5535
5536 constexpr auto UNITY = make_constant(UnitProductT<>{});
5537
5538 static_assert(
5539 UNITY.in<R>(associated_unit(target_units) * U{}) >= threshold ||
5541 "Dangerous inversion risking truncation to 0; must supply explicit Rep if truly desired");
5542
5543 // Having passed safety checks (at compile time!), we can delegate to the explicit-Rep version.
5544 return inverse_in<R>(target_units, q);
5545}
5546
5547//
5548// The "smart" inverse of a Quantity, in a given destination unit.
5549//
5550// (See `inverse_in()` comment above for how this inverse is "smart".)
5551//
5552template <typename TargetUnits, typename U, typename R>
5553constexpr auto inverse_as(TargetUnits target_units, Quantity<U, R> q) {
5554 return make_quantity<AssociatedUnitT<TargetUnits>>(inverse_in(target_units, q));
5555}
5556
5557//
5558// The "smart" inverse of a Quantity, in a given destination Unit and Rep.
5559//
5560// This is the "explicit Rep" format, which is semantically equivalent to a `static_cast`.
5561//
5562template <typename TargetRep, typename TargetUnits, typename U, typename R>
5563constexpr auto inverse_as(TargetUnits target_units, Quantity<U, R> q) {
5564 return make_quantity<AssociatedUnitT<TargetUnits>>(inverse_in<TargetRep>(target_units, q));
5565}
5566
5567//
5568// Check whether the value stored is "not a number" (NaN).
5569//
5570template <typename U, typename R>
5571constexpr bool isnan(Quantity<U, R> q) {
5572 return std::isnan(q.in(U{}));
5573}
5574
5575// The maximum of two values of the same dimension.
5576//
5577// Unlike std::max, returns by value rather than by reference, because the types might differ.
5578template <typename U1, typename U2, typename R1, typename R2>
5579auto max(Quantity<U1, R1> q1, Quantity<U2, R2> q2) {
5580 return detail::using_common_type(q1, q2, [](auto a, auto b) { return std::max(a, b); });
5581}
5582
5583// Overload to resolve ambiguity with `std::max` for identical `Quantity` types.
5584template <typename U, typename R>
5585auto max(Quantity<U, R> a, Quantity<U, R> b) {
5586 return std::max(a, b);
5587}
5588
5589// The maximum of two point values of the same dimension.
5590//
5591// Unlike std::max, returns by value rather than by reference, because the types might differ.
5592template <typename U1, typename U2, typename R1, typename R2>
5593auto max(QuantityPoint<U1, R1> p1, QuantityPoint<U2, R2> p2) {
5594 return detail::using_common_point_unit(p1, p2, [](auto a, auto b) { return std::max(a, b); });
5595}
5596
5597// Overload to resolve ambiguity with `std::max` for identical `QuantityPoint` types.
5598template <typename U, typename R>
5599auto max(QuantityPoint<U, R> a, QuantityPoint<U, R> b) {
5600 return std::max(a, b);
5601}
5602
5603// `max` overloads for when Zero is one of the arguments.
5604//
5605// NOTE: these will not work if _both_ arguments are `Zero`, but we don't plan to support this
5606// unless we find a compelling use case.
5607template <typename T>
5608auto max(Zero z, T x) {
5610 "Cannot compare type to abstract notion Zero");
5611 return std::max(T{z}, x);
5612}
5613template <typename T>
5614auto max(T x, Zero z) {
5616 "Cannot compare type to abstract notion Zero");
5617 return std::max(x, T{z});
5618}
5619
5620// The minimum of two values of the same dimension.
5621//
5622// Unlike std::min, returns by value rather than by reference, because the types might differ.
5623template <typename U1, typename U2, typename R1, typename R2>
5624auto min(Quantity<U1, R1> q1, Quantity<U2, R2> q2) {
5625 return detail::using_common_type(q1, q2, [](auto a, auto b) { return std::min(a, b); });
5626}
5627
5628// Overload to resolve ambiguity with `std::min` for identical `Quantity` types.
5629template <typename U, typename R>
5630auto min(Quantity<U, R> a, Quantity<U, R> b) {
5631 return std::min(a, b);
5632}
5633
5634// The minimum of two point values of the same dimension.
5635//
5636// Unlike std::min, returns by value rather than by reference, because the types might differ.
5637template <typename U1, typename U2, typename R1, typename R2>
5638auto min(QuantityPoint<U1, R1> p1, QuantityPoint<U2, R2> p2) {
5639 return detail::using_common_point_unit(p1, p2, [](auto a, auto b) { return std::min(a, b); });
5640}
5641
5642// Overload to resolve ambiguity with `std::min` for identical `QuantityPoint` types.
5643template <typename U, typename R>
5644auto min(QuantityPoint<U, R> a, QuantityPoint<U, R> b) {
5645 return std::min(a, b);
5646}
5647
5648// `min` overloads for when Zero is one of the arguments.
5649//
5650// NOTE: these will not work if _both_ arguments are `Zero`, but we don't plan to support this
5651// unless we find a compelling use case.
5652template <typename T>
5653auto min(Zero z, T x) {
5655 "Cannot compare type to abstract notion Zero");
5656 return std::min(T{z}, x);
5657}
5658template <typename T>
5659auto min(T x, Zero z) {
5661 "Cannot compare type to abstract notion Zero");
5662 return std::min(x, T{z});
5663}
5664
5665// The (zero-centered) floating point remainder of two values of the same dimension.
5666template <typename U1, typename R1, typename U2, typename R2>
5667auto remainder(Quantity<U1, R1> q1, Quantity<U2, R2> q2) {
5668 using U = CommonUnitT<U1, U2>;
5669 using R = decltype(std::remainder(R1{}, R2{}));
5670 return make_quantity<U>(std::remainder(q1.template in<R>(U{}), q2.template in<R>(U{})));
5671}
5672
5673//
5674// Round the value of this Quantity to the nearest integer in the given units.
5675//
5676// This is the "Unit-only" format (i.e., `round_in(rounding_units, q)`).
5677//
5678template <typename RoundingUnits, typename U, typename R>
5679auto round_in(RoundingUnits rounding_units, Quantity<U, R> q) {
5680 using OurRoundingRep = detail::RoundingRepT<Quantity<U, R>, RoundingUnits>;
5681 return std::round(q.template in<OurRoundingRep>(rounding_units));
5682}
5683
5684//
5685// Round the value of this Quantity to the nearest integer in the given units, returning OutputRep.
5686//
5687// This is the "Explicit-Rep" format (e.g., `round_in<int>(rounding_units, q)`).
5688//
5689template <typename OutputRep, typename RoundingUnits, typename U, typename R>
5690auto round_in(RoundingUnits rounding_units, Quantity<U, R> q) {
5691 return static_cast<OutputRep>(round_in(rounding_units, q));
5692}
5693
5694//
5695// The integral-valued Quantity, in this unit, nearest to the input.
5696//
5697// This is the "Unit-only" format (i.e., `round_as(rounding_units, q)`).
5698//
5699template <typename RoundingUnits, typename U, typename R>
5700auto round_as(RoundingUnits rounding_units, Quantity<U, R> q) {
5701 return make_quantity<AssociatedUnitT<RoundingUnits>>(round_in(rounding_units, q));
5702}
5703
5704//
5705// The integral-valued Quantity, in this unit, nearest to the input, using the specified OutputRep.
5706//
5707// This is the "Explicit-Rep" format (e.g., `round_as<float>(rounding_units, q)`).
5708//
5709template <typename OutputRep, typename RoundingUnits, typename U, typename R>
5710auto round_as(RoundingUnits rounding_units, Quantity<U, R> q) {
5711 return make_quantity<AssociatedUnitT<RoundingUnits>>(round_in<OutputRep>(rounding_units, q));
5712}
5713
5714//
5715// Return the largest integral value in `rounding_units` which is not greater than `q`.
5716//
5717// This is the "Unit-only" format (i.e., `floor_in(rounding_units, q)`).
5718//
5719template <typename RoundingUnits, typename U, typename R>
5720auto floor_in(RoundingUnits rounding_units, Quantity<U, R> q) {
5721 using OurRoundingRep = detail::RoundingRepT<Quantity<U, R>, RoundingUnits>;
5722 return std::floor(q.template in<OurRoundingRep>(rounding_units));
5723}
5724
5725//
5726// Return `OutputRep` with largest integral value in `rounding_units` which is not greater than `q`.
5727//
5728// This is the "Explicit-Rep" format (e.g., `floor_in<int>(rounding_units, q)`).
5729//
5730template <typename OutputRep, typename RoundingUnits, typename U, typename R>
5731auto floor_in(RoundingUnits rounding_units, Quantity<U, R> q) {
5732 return static_cast<OutputRep>(floor_in(rounding_units, q));
5733}
5734
5735//
5736// The largest integral-valued Quantity, in this unit, not greater than the input.
5737//
5738// This is the "Unit-only" format (i.e., `floor_as(rounding_units, q)`).
5739//
5740template <typename RoundingUnits, typename U, typename R>
5741auto floor_as(RoundingUnits rounding_units, Quantity<U, R> q) {
5742 return make_quantity<AssociatedUnitT<RoundingUnits>>(floor_in(rounding_units, q));
5743}
5744
5745//
5746// The largest integral-valued Quantity, in this unit, not greater than the input, using the
5747// specified `OutputRep`.
5748//
5749// This is the "Explicit-Rep" format (e.g., `floor_as<float>(rounding_units, q)`).
5750//
5751template <typename OutputRep, typename RoundingUnits, typename U, typename R>
5752auto floor_as(RoundingUnits rounding_units, Quantity<U, R> q) {
5753 return make_quantity<AssociatedUnitT<RoundingUnits>>(floor_in<OutputRep>(rounding_units, q));
5754}
5755
5756//
5757// Return the smallest integral value in `rounding_units` which is not less than `q`.
5758//
5759// This is the "Unit-only" format (i.e., `ceil_in(rounding_units, q)`).
5760//
5761template <typename RoundingUnits, typename U, typename R>
5762auto ceil_in(RoundingUnits rounding_units, Quantity<U, R> q) {
5763 using OurRoundingRep = detail::RoundingRepT<Quantity<U, R>, RoundingUnits>;
5764 return std::ceil(q.template in<OurRoundingRep>(rounding_units));
5765}
5766
5767//
5768// Return the smallest integral value in `rounding_units` which is not less than `q`.
5769//
5770// This is the "Explicit-Rep" format (e.g., `ceil_in<int>(rounding_units, q)`).
5771//
5772template <typename OutputRep, typename RoundingUnits, typename U, typename R>
5773auto ceil_in(RoundingUnits rounding_units, Quantity<U, R> q) {
5774 return static_cast<OutputRep>(ceil_in(rounding_units, q));
5775}
5776
5777//
5778// The smallest integral-valued Quantity, in this unit, not less than the input.
5779//
5780// This is the "Unit-only" format (i.e., `ceil_as(rounding_units, q)`).
5781//
5782template <typename RoundingUnits, typename U, typename R>
5783auto ceil_as(RoundingUnits rounding_units, Quantity<U, R> q) {
5784 return make_quantity<AssociatedUnitT<RoundingUnits>>(ceil_in(rounding_units, q));
5785}
5786
5787//
5788// The smallest integral-valued Quantity, in this unit, not less than the input, using the specified
5789// `OutputRep`.
5790//
5791// This is the "Explicit-Rep" format (e.g., `ceil_as<float>(rounding_units, q)`).
5792//
5793template <typename OutputRep, typename RoundingUnits, typename U, typename R>
5794auto ceil_as(RoundingUnits rounding_units, Quantity<U, R> q) {
5795 return make_quantity<AssociatedUnitT<RoundingUnits>>(ceil_in<OutputRep>(rounding_units, q));
5796}
5797
5798// Wrapper for std::sin() which accepts a strongly typed angle quantity.
5799template <typename U, typename R>
5800auto sin(Quantity<U, R> q) {
5801 return std::sin(detail::in_radians(q));
5802}
5803
5804// Wrapper for std::sqrt() which handles Quantity types.
5805template <typename U, typename R>
5806auto sqrt(Quantity<U, R> q) {
5807 return make_quantity<UnitPowerT<U, 1, 2>>(std::sqrt(q.in(U{})));
5808}
5809
5810// Wrapper for std::tan() which accepts a strongly typed angle quantity.
5811template <typename U, typename R>
5812auto tan(Quantity<U, R> q) {
5813 return std::tan(detail::in_radians(q));
5814}
5815
5816} // namespace au
5817
5818namespace std {
5827template <typename U, typename R>
5828struct numeric_limits<au::Quantity<U, R>> {
5829 // To validily extent std::numeric_limits<T>, we must define all members declared static
5830 // constexpr in the primary template, in such a way that they are usable as integral constant
5831 // expressions.
5832 //
5833 // Source for rule: https://en.cppreference.com/w/cpp/language/extending_std
5834 // List of members: https://en.cppreference.com/w/cpp/types/numeric_limits
5835 static constexpr bool is_specialized = true;
5836 static constexpr bool is_integer = numeric_limits<R>::is_integer;
5837 static constexpr bool is_signed = numeric_limits<R>::is_signed;
5838 static constexpr bool is_exact = numeric_limits<R>::is_exact;
5839 static constexpr bool has_infinity = numeric_limits<R>::has_infinity;
5840 static constexpr bool has_quiet_NaN = numeric_limits<R>::has_quiet_NaN;
5841 static constexpr bool has_signaling_NaN = numeric_limits<R>::has_signaling_NaN;
5842 static constexpr bool has_denorm = numeric_limits<R>::has_denorm;
5843 static constexpr bool has_denorm_loss = numeric_limits<R>::has_denorm_loss;
5844 static constexpr float_round_style round_style = numeric_limits<R>::round_style;
5845 static constexpr bool is_iec559 = numeric_limits<R>::is_iec559;
5846 static constexpr bool is_bounded = numeric_limits<R>::is_bounded;
5847 static constexpr bool is_modulo = numeric_limits<R>::is_modulo;
5848 static constexpr int digits = numeric_limits<R>::digits;
5849 static constexpr int digits10 = numeric_limits<R>::digits10;
5850 static constexpr int max_digits10 = numeric_limits<R>::max_digits10;
5851 static constexpr int radix = numeric_limits<R>::radix;
5852 static constexpr int min_exponent = numeric_limits<R>::min_exponent;
5853 static constexpr int min_exponent10 = numeric_limits<R>::min_exponent10;
5854 static constexpr int max_exponent = numeric_limits<R>::max_exponent;
5855 static constexpr int max_exponent10 = numeric_limits<R>::max_exponent10;
5856 static constexpr bool traps = numeric_limits<R>::traps;
5857 static constexpr bool tinyness_before = numeric_limits<R>::tinyness_before;
5858
5859 static constexpr au::Quantity<U, R> max() {
5860 return au::make_quantity<U>(std::numeric_limits<R>::max());
5861 }
5862
5863 static constexpr au::Quantity<U, R> lowest() {
5864 return au::make_quantity<U>(std::numeric_limits<R>::lowest());
5865 }
5866
5867 static constexpr au::Quantity<U, R> min() {
5868 return au::make_quantity<U>(std::numeric_limits<R>::min());
5869 }
5870
5871 static constexpr au::Quantity<U, R> epsilon() {
5872 return au::make_quantity<U>(std::numeric_limits<R>::epsilon());
5873 }
5874
5875 static constexpr au::Quantity<U, R> round_error() {
5876 return au::make_quantity<U>(std::numeric_limits<R>::round_error());
5877 }
5878
5879 static constexpr au::Quantity<U, R> infinity() {
5880 return au::make_quantity<U>(std::numeric_limits<R>::infinity());
5881 }
5882
5883 static constexpr au::Quantity<U, R> quiet_NaN() {
5884 return au::make_quantity<U>(std::numeric_limits<R>::quiet_NaN());
5885 }
5886
5887 static constexpr au::Quantity<U, R> signaling_NaN() {
5888 return au::make_quantity<U>(std::numeric_limits<R>::signaling_NaN());
5889 }
5890
5891 static constexpr au::Quantity<U, R> denorm_min() {
5892 return au::make_quantity<U>(std::numeric_limits<R>::denorm_min());
5893 }
5894};
5895
5896// Specialize for cv-qualified Quantity types by inheriting from bare Quantity implementation.
5897template <typename U, typename R>
5898struct numeric_limits<const au::Quantity<U, R>> : numeric_limits<au::Quantity<U, R>> {};
5899template <typename U, typename R>
5900struct numeric_limits<volatile au::Quantity<U, R>> : numeric_limits<au::Quantity<U, R>> {};
5901template <typename U, typename R>
5902struct numeric_limits<const volatile au::Quantity<U, R>> : numeric_limits<au::Quantity<U, R>> {};
5903
5904template <typename U, typename R>
5905constexpr bool numeric_limits<au::Quantity<U, R>>::is_specialized;
5906
5907template <typename U, typename R>
5908constexpr bool numeric_limits<au::Quantity<U, R>>::is_integer;
5909
5910template <typename U, typename R>
5911constexpr bool numeric_limits<au::Quantity<U, R>>::is_signed;
5912
5913template <typename U, typename R>
5914constexpr bool numeric_limits<au::Quantity<U, R>>::is_exact;
5915
5916template <typename U, typename R>
5917constexpr bool numeric_limits<au::Quantity<U, R>>::has_infinity;
5918
5919template <typename U, typename R>
5920constexpr bool numeric_limits<au::Quantity<U, R>>::has_quiet_NaN;
5921
5922template <typename U, typename R>
5923constexpr bool numeric_limits<au::Quantity<U, R>>::has_signaling_NaN;
5924
5925template <typename U, typename R>
5926constexpr bool numeric_limits<au::Quantity<U, R>>::has_denorm;
5927
5928template <typename U, typename R>
5929constexpr bool numeric_limits<au::Quantity<U, R>>::has_denorm_loss;
5930
5931template <typename U, typename R>
5932constexpr float_round_style numeric_limits<au::Quantity<U, R>>::round_style;
5933
5934template <typename U, typename R>
5935constexpr bool numeric_limits<au::Quantity<U, R>>::is_iec559;
5936
5937template <typename U, typename R>
5938constexpr bool numeric_limits<au::Quantity<U, R>>::is_bounded;
5939
5940template <typename U, typename R>
5941constexpr bool numeric_limits<au::Quantity<U, R>>::is_modulo;
5942
5943template <typename U, typename R>
5944constexpr int numeric_limits<au::Quantity<U, R>>::digits;
5945
5946template <typename U, typename R>
5947constexpr int numeric_limits<au::Quantity<U, R>>::digits10;
5948
5949template <typename U, typename R>
5950constexpr int numeric_limits<au::Quantity<U, R>>::max_digits10;
5951
5952template <typename U, typename R>
5953constexpr int numeric_limits<au::Quantity<U, R>>::radix;
5954
5955template <typename U, typename R>
5956constexpr int numeric_limits<au::Quantity<U, R>>::min_exponent;
5957
5958template <typename U, typename R>
5959constexpr int numeric_limits<au::Quantity<U, R>>::min_exponent10;
5960
5961template <typename U, typename R>
5962constexpr int numeric_limits<au::Quantity<U, R>>::max_exponent;
5963
5964template <typename U, typename R>
5965constexpr int numeric_limits<au::Quantity<U, R>>::max_exponent10;
5966
5967template <typename U, typename R>
5968constexpr bool numeric_limits<au::Quantity<U, R>>::traps;
5969
5970template <typename U, typename R>
5971constexpr bool numeric_limits<au::Quantity<U, R>>::tinyness_before;
5972
5973} // namespace std
5974
5975
5976
5977namespace au {
5978
5979// Define 1:1 mapping between duration types of chrono library and our library.
5980template <typename RepT, typename Period>
5981struct CorrespondingQuantity<std::chrono::duration<RepT, Period>> {
5982 using Unit = decltype(Seconds{} * (mag<Period::num>() / mag<Period::den>()));
5983 using Rep = RepT;
5984
5986
5987 static constexpr Rep extract_value(ChronoDuration d) { return d.count(); }
5988 static constexpr ChronoDuration construct_from_value(Rep x) { return ChronoDuration{x}; }
5989};
5990
5991// Convert any Au duration quantity to an equivalent `std::chrono::duration`.
5992template <typename U, typename R>
5993constexpr auto as_chrono_duration(Quantity<U, R> dt) {
5994 constexpr auto ratio = unit_ratio(U{}, seconds);
5995 static_assert(is_rational(ratio), "Cannot convert to chrono::duration with non-rational ratio");
5996 return std::chrono::duration<R,
5997 std::ratio<get_value<std::intmax_t>(numerator(ratio)),
5998 get_value<std::intmax_t>(denominator(ratio))>>{dt};
5999}
6000
6001} // namespace au
6002
6003
6004namespace au {
6005
6006// DO NOT follow this pattern to define your own units. This is for library-defined units.
6007// Instead, follow instructions at (https://aurora-opensource.github.io/au/main/howto/new-units/).
6008template <typename T>
6010 static constexpr const char label[] = "in";
6011};
6012template <typename T>
6013constexpr const char InchesLabel<T>::label[];
6014struct Inches : decltype(Centi<Meters>{} * mag<254>() / mag<100>()), InchesLabel<void> {
6015 using InchesLabel<void>::label;
6016};
6017constexpr auto inch = SingularNameFor<Inches>{};
6018constexpr auto inches = QuantityMaker<Inches>{};
6019
6020namespace symbols {
6021constexpr auto in = SymbolFor<Inches>{};
6022}
6023} // namespace au
6024
6025
6026
6027namespace au {
6028
6029// DO NOT follow this pattern to define your own units. This is for library-defined units.
6030// Instead, follow instructions at (https://aurora-opensource.github.io/au/main/howto/new-units/).
6031template <typename T>
6033 static constexpr const char label[] = "ft";
6034};
6035template <typename T>
6036constexpr const char FeetLabel<T>::label[];
6037struct Feet : decltype(Inches{} * mag<12>()), FeetLabel<void> {
6038 using FeetLabel<void>::label;
6039};
6040constexpr auto foot = SingularNameFor<Feet>{};
6041constexpr auto feet = QuantityMaker<Feet>{};
6042
6043namespace symbols {
6044constexpr auto ft = SymbolFor<Feet>{};
6045}
6046} // namespace au
6047
6048
6049namespace au {
6050
6051// DO NOT follow this pattern to define your own units. This is for library-defined units.
6052// Instead, follow instructions at (https://aurora-opensource.github.io/au/main/howto/new-units/).
6053template <typename T>
6055 static constexpr const char label[] = "mi";
6056};
6057template <typename T>
6058constexpr const char MilesLabel<T>::label[];
6059struct Miles : decltype(Feet{} * mag<5'280>()), MilesLabel<void> {
6060 using MilesLabel<void>::label;
6061};
6062constexpr auto mile = SingularNameFor<Miles>{};
6063constexpr auto miles = QuantityMaker<Miles>{};
6064
6065namespace symbols {
6066constexpr auto mi = SymbolFor<Miles>{};
6067}
6068} // namespace au
STL namespace.
The HikoGUI namespace.
Definition array_generic.hpp:20
Definition au.hh:50
Definition au.hh:88
Definition au.hh:98
Definition au.hh:106
Definition au.hh:114
Definition au.hh:118
Definition au.hh:228
Definition au.hh:317
Definition au.hh:350
Definition au.hh:499
Definition au.hh:515
Definition au.hh:638
Definition au.hh:670
Definition au.hh:678
Definition au.hh:686
Definition au.hh:694
Definition au.hh:702
Definition au.hh:710
Definition au.hh:722
Definition au.hh:730
Definition au.hh:747
Definition au.hh:972
Definition au.hh:983
Definition au.hh:794
Definition au.hh:800
Definition au.hh:809
Definition au.hh:818
Definition au.hh:829
Definition au.hh:834
Definition au.hh:840
Definition au.hh:853
Definition au.hh:860
Definition au.hh:867
Definition au.hh:873
Definition au.hh:892
Definition au.hh:898
Definition au.hh:1263
Definition au.hh:915
Definition au.hh:923
Definition au.hh:927
Definition au.hh:1359
Definition au.hh:1473
Definition au.hh:952
Definition au.hh:962
Definition au.hh:1074
Definition au.hh:1080
Definition au.hh:1254
Definition au.hh:1316
Definition au.hh:1404
Definition au.hh:1419
Definition au.hh:1535
Definition au.hh:1540
Definition au.hh:1896
Definition au.hh:2295
Definition au.hh:3010
Definition au.hh:2727
Definition au.hh:2690
Definition au.hh:2399
Definition au.hh:2536
Definition au.hh:2623
Definition au.hh:2738
Definition au.hh:2860
Definition au.hh:2920
Definition au.hh:2926
Definition au.hh:2935
Definition au.hh:2941
Definition au.hh:2953
Definition au.hh:2967
Definition au.hh:3061
Definition au.hh:3064
Definition au.hh:3070
Definition au.hh:3078
Definition au.hh:3893
Definition au.hh:3518
Definition au.hh:3481
Definition au.hh:4209
Definition au.hh:4245
Definition au.hh:4269
Definition au.hh:4304
Definition au.hh:4310
Definition au.hh:4378
Definition au.hh:4610
Definition au.hh:4774
Definition au.hh:4799
Definition au.hh:4837
Definition au.hh:4845
Definition au.hh:4853
Definition au.hh:4861
Definition au.hh:4869
Definition au.hh:4877
Definition au.hh:4885
Definition au.hh:4893
Definition au.hh:4901
Definition au.hh:4909
Definition au.hh:4917
Definition au.hh:4925
Definition au.hh:4933
Definition au.hh:4941
Definition au.hh:4949
Definition au.hh:4957
Definition au.hh:4965
Definition au.hh:4973
Definition au.hh:4981
Definition au.hh:4989
Definition au.hh:4997
Definition au.hh:5005
Definition au.hh:5013
Definition au.hh:5021
Definition au.hh:5032
Definition au.hh:5040
Definition au.hh:5048
Definition au.hh:5056
Definition au.hh:5064
Definition au.hh:5072
Definition au.hh:5080
Definition au.hh:5088
Definition au.hh:5103
Definition au.hh:5108
Definition au.hh:5140
Definition au.hh:5218
Definition au.hh:5223
Definition au.hh:5271
Definition au.hh:5276
Definition au.hh:5361
Definition au.hh:6009
Definition au.hh:6014
Definition au.hh:6032
Definition au.hh:6037
Definition au.hh:6054
Definition au.hh:6059
T acos(T... args)
T asin(T... args)
T atan2(T... args)
T atan(T... args)
T cbrt(T... args)
T ceil(T... args)
T copysign(T... args)
T cos(T... args)
T denorm_min(T... args)
T epsilon(T... args)
T exp(T... args)
T floor(T... args)
T fmod(T... args)
T infinity(T... args)
T isnan(T... args)
T lowest(T... args)
T max(T... args)
T min(T... args)
T operator>=(T... args)
T pow(T... args)
T quiet_NaN(T... args)
T remainder(T... args)
T round_error(T... args)
T round(T... args)
T signaling_NaN(T... args)
T sin(T... args)
T sqrt(T... args)
T tan(T... args)