RIT VEXU Core API
Loading...
Searching...
No Matches
units.h
1#pragma once
2
3#include <ostream>
4#include <sstream>
5#include <string>
6#include <type_traits>
7
8#include "gcem.hpp"
9
10namespace units {
11
12// Terminology
13// Quantity is a type like Length or Time
14// Unit is a representation of a Quantity like inches or seconds
15// Quantities are defined by their Dimensions, the exponents of the base SI
16// units here we allow algebra on those dimensions so arithmetic can derive
17// result quantities
18
19template <int M, int L, int Ti, int I, int Th, int N, int J, int A>
20struct dim {
21 static constexpr int mass = M;
22 static constexpr int length = L;
23 static constexpr int time = Ti;
24 static constexpr int current = I;
25 static constexpr int temperature = Th;
26 static constexpr int amount = N;
27 static constexpr int luminous_intensity = J;
28 static constexpr int angle = A;
29};
30
31template <class LeftDim, class RightDim>
32struct dim_add {
33 typedef dim<LeftDim::mass + RightDim::mass, LeftDim::length + RightDim::length, LeftDim::time + RightDim::time,
34 LeftDim::current + RightDim::current, LeftDim::temperature + RightDim::temperature,
35 LeftDim::amount + RightDim::amount, LeftDim::luminous_intensity + RightDim::luminous_intensity,
36 LeftDim::angle + RightDim::angle>
37 type;
38};
39
40template <class LeftDim, class RightDim>
41struct dim_sub {
42 typedef dim<LeftDim::mass - RightDim::mass, LeftDim::length - RightDim::length, LeftDim::time - RightDim::time,
43 LeftDim::current - RightDim::current, LeftDim::temperature - RightDim::temperature,
44 LeftDim::amount - RightDim::amount, LeftDim::luminous_intensity - RightDim::luminous_intensity,
45 LeftDim::angle - RightDim::angle>
46 type;
47};
48
49typedef dim<0, 0, 0, 0, 0, 0, 0, 0> dim_none;
50
51// Unit metadata
52// adapts unit tags into conversion factors, symbols, names, and defaults
53
54template <class UnitTag>
55struct unit_traits {
56 typedef typename UnitTag::dimension dimension;
57 static constexpr double scale_num = UnitTag::scale_num;
58 static constexpr double scale_den = UnitTag::scale_den;
59 static constexpr bool is_affine = UnitTag::is_affine;
60};
61
62template <class UnitTag>
63struct unit_text {
64 static const char* symbol() { return UnitTag::symbol(); }
65 static const char* name() { return UnitTag::name(); }
66};
67
68template <class Quantity>
69struct default_unit_for_quantity;
70
71template <class Quantity>
72struct dimension_for_quantity;
73
74// Quantity lookup
75// maps dim to the named quantity, or anonymous quantity if one does not exist
76
77template <class Dim>
78class anonymous_quantity;
79
80template <class Dim>
81anonymous_quantity<Dim> quantity_for_dimension_probe(Dim*, ...);
82
83template <class Dim>
84struct quantity_for_dimension {
85 typedef decltype(quantity_for_dimension_probe(static_cast<Dim*>(0), 0)) type;
86};
87
88template <class Quantity>
89struct is_quantity : std::false_type {};
90
91// Result constraints for quantity operators and math functions
92// determines what functions are enabled... i.e. length + time doesn't exist but
93// length + length does
94
95template <class Left, class Right, bool Enabled = is_quantity<Left>::value && is_quantity<Right>::value>
96struct same_dimension : std::false_type {};
97
98template <class Left, class Right>
99struct same_dimension<Left, Right, true> : std::is_same<typename Left::dimension, typename Right::dimension> {};
100
101template <bool Condition, class Type>
102using enable_if_t = typename std::enable_if<Condition, Type>::type;
103
104template <class Quantity>
105using quantity_result_t = enable_if_t<is_quantity<Quantity>::value, Quantity>;
106
107template <class Left, class Right>
108using same_dimension_result_t =
109 enable_if_t<same_dimension<Left, Right>::value, typename quantity_for_dimension<typename Left::dimension>::type>;
110
111template <class Left, class Right>
112using dim_add_result_t = enable_if_t<
113 is_quantity<Left>::value && is_quantity<Right>::value,
114 typename quantity_for_dimension<typename dim_add<typename Left::dimension, typename Right::dimension>::type>::type>;
115
116template <class Left, class Right>
117using dim_sub_result_t = enable_if_t<
118 is_quantity<Left>::value && is_quantity<Right>::value,
119 typename quantity_for_dimension<typename dim_sub<typename Left::dimension, typename Right::dimension>::type>::type>;
120
121template <class Quantity>
122using inverse_quantity_result_t =
123 enable_if_t<is_quantity<Quantity>::value,
124 typename quantity_for_dimension<typename dim_sub<dim_none, typename Quantity::dimension>::type>::type>;
125
126template <class Left, class Right>
127using comparison_result_t = enable_if_t<same_dimension<Left, Right>::value, bool>;
128
129template <int Factor, class Dim>
130struct dim_scale {
131 typedef dim<Dim::mass * Factor, Dim::length * Factor, Dim::time * Factor, Dim::current * Factor,
132 Dim::temperature * Factor, Dim::amount * Factor, Dim::luminous_intensity * Factor, Dim::angle * Factor>
133 type;
134};
135
136template <class Dim, int Divisor>
137struct dim_divisible_by
138 : std::integral_constant<bool, Dim::mass % Divisor == 0 && Dim::length % Divisor == 0 && Dim::time % Divisor == 0 &&
139 Dim::current % Divisor == 0 && Dim::temperature % Divisor == 0 &&
140 Dim::amount % Divisor == 0 && Dim::luminous_intensity % Divisor == 0 &&
141 Dim::angle % Divisor == 0> {};
142
143template <class Dim, int Divisor>
144struct dim_divide {
145 typedef dim<Dim::mass / Divisor, Dim::length / Divisor, Dim::time / Divisor, Dim::current / Divisor,
146 Dim::temperature / Divisor, Dim::amount / Divisor, Dim::luminous_intensity / Divisor,
147 Dim::angle / Divisor>
148 type;
149};
150
151template <class Quantity, int Factor>
152using scaled_dimension_t = typename dim_scale<Factor, typename Quantity::dimension>::type;
153template <class Quantity, int Divisor>
154using divided_dimension_t = typename dim_divide<typename Quantity::dimension, Divisor>::type;
155template <class Quantity, int Factor>
156using scaled_quantity_t = typename quantity_for_dimension<scaled_dimension_t<Quantity, Factor> >::type;
157template <class Quantity, int Divisor>
158using divided_quantity_t = typename quantity_for_dimension<divided_dimension_t<Quantity, Divisor> >::type;
159
160template <class Quantity, int Factor, bool = is_quantity<Quantity>::value>
161struct can_scale_quantity_impl : std::false_type {};
162template <class Quantity, int Factor>
163struct can_scale_quantity_impl<Quantity, Factor, true> : std::true_type {};
164template <class Quantity, int Divisor, bool = is_quantity<Quantity>::value>
165struct can_root_quantity_impl : std::false_type {};
166template <class Quantity, int Divisor>
167struct can_root_quantity_impl<Quantity, Divisor, true> : dim_divisible_by<typename Quantity::dimension, Divisor> {};
168template <class Quantity, int Factor>
169struct can_scale_quantity : can_scale_quantity_impl<Quantity, Factor> {};
170template <class Quantity, int Divisor>
171struct can_root_quantity : can_root_quantity_impl<Quantity, Divisor> {};
172template <class Quantity>
173using quantity_bool_result_t = enable_if_t<is_quantity<Quantity>::value, bool>;
174template <class Quantity, int Power>
175using power_result_t = enable_if_t<can_scale_quantity<Quantity, Power>::value, scaled_quantity_t<Quantity, Power> >;
176template <class Quantity, int Divisor>
177using root_result_t = enable_if_t<can_root_quantity<Quantity, Divisor>::value, divided_quantity_t<Quantity, Divisor> >;
178
179// Quantity storage
180// stores canonical values and provides conversion and compound operators
181
182template <class Derived, class Dim>
183class quantity_base {
184 public:
185 typedef Dim dimension;
186
187 constexpr explicit quantity_base(double canonical_value = 0.0) : canonical_value_(canonical_value) {}
188
189 template <class OtherQuantity>
190 constexpr quantity_base(const OtherQuantity& other) : canonical_value_(other.canonical_value()) {}
191
192 constexpr double canonical_value() const { return canonical_value_; }
193
194 template <class UnitTag>
195 constexpr double as() const {
196 static_assert(std::is_same<typename unit_traits<UnitTag>::dimension, Dim>::value, "unit dimension mismatch");
197 return canonical_value_ * unit_traits<UnitTag>::scale_den / unit_traits<UnitTag>::scale_num;
198 }
199
200 static constexpr Derived from_canonical(double value) { return Derived(value); }
201
202 template <class UnitTag>
203 static constexpr Derived from(double value) {
204 return Derived(to_canonical<UnitTag>(value));
205 }
206
207 protected:
208 template <class UnitTag>
209 static constexpr double to_canonical(double value) {
210 static_assert(std::is_same<typename unit_traits<UnitTag>::dimension, Dim>::value, "unit dimension mismatch");
211 return value * unit_traits<UnitTag>::scale_num / unit_traits<UnitTag>::scale_den;
212 }
213
214 void set_canonical_value(double canonical_value) { canonical_value_ = canonical_value; }
215
216 public:
217 template <class OtherQuantity>
218 Derived& operator+=(const OtherQuantity& other) {
219 set_canonical_value(canonical_value() + other.canonical_value());
220 return static_cast<Derived&>(*this);
221 }
222
223 template <class OtherQuantity>
224 Derived& operator-=(const OtherQuantity& other) {
225 set_canonical_value(canonical_value() - other.canonical_value());
226 return static_cast<Derived&>(*this);
227 }
228
229 Derived& operator*=(double scalar) {
230 set_canonical_value(canonical_value() * scalar);
231 return static_cast<Derived&>(*this);
232 }
233
234 Derived& operator/=(double scalar) {
235 set_canonical_value(canonical_value() / scalar);
236 return static_cast<Derived&>(*this);
237 }
238
239 Derived& operator++() {
240 set_canonical_value(canonical_value() + 1.0);
241 return static_cast<Derived&>(*this);
242 }
243
244 Derived operator++(int) {
245 Derived copy = static_cast<Derived&>(*this);
246 ++(*this);
247 return copy;
248 }
249
250 Derived& operator--() {
251 set_canonical_value(canonical_value() - 1.0);
252 return static_cast<Derived&>(*this);
253 }
254
255 Derived operator--(int) {
256 Derived copy = static_cast<Derived&>(*this);
257 --(*this);
258 return copy;
259 }
260
261 private:
262 double canonical_value_;
263};
264
265template <class Derived, class Dim>
266using semantic_quantity_base = quantity_base<Derived, Dim>;
267
268template <class Dim>
269class anonymous_quantity : public quantity_base<anonymous_quantity<Dim>, Dim> {
270 public:
271 typedef quantity_base<anonymous_quantity<Dim>, Dim> base_type;
272 typedef Dim dimension;
273
274 constexpr explicit anonymous_quantity(double canonical_value = 0.0) : base_type(canonical_value) {}
275
276 template <class OtherQuantity>
277 constexpr anonymous_quantity(const OtherQuantity& other,
278 enable_if_t<same_dimension<anonymous_quantity<Dim>, OtherQuantity>::value, int> = 0)
279 : base_type(other.canonical_value()) {}
280
281 static constexpr anonymous_quantity from_canonical(double value) { return anonymous_quantity(value); }
282};
283
284template <class Dim>
285struct is_quantity<anonymous_quantity<Dim> > : std::true_type {};
286
287// Arithmetic operators
288// combines as canonical values, and makes sure to return the right dims
289
290template <class Left, class Right>
291constexpr same_dimension_result_t<Left, Right> operator+(const Left& left, const Right& right) {
292 return same_dimension_result_t<Left, Right>::from_canonical(left.canonical_value() + right.canonical_value());
293}
294
295template <class Left, class Right>
296constexpr same_dimension_result_t<Left, Right> operator-(const Left& left, const Right& right) {
297 return same_dimension_result_t<Left, Right>::from_canonical(left.canonical_value() - right.canonical_value());
298}
299
300template <class Left, class Right>
301constexpr dim_add_result_t<Left, Right> operator*(const Left& left, const Right& right) {
302 return dim_add_result_t<Left, Right>::from_canonical(left.canonical_value() * right.canonical_value());
303}
304
305template <class Left, class Right>
306constexpr dim_sub_result_t<Left, Right> operator/(const Left& left, const Right& right) {
307 return dim_sub_result_t<Left, Right>::from_canonical(left.canonical_value() / right.canonical_value());
308}
309
310template <class Quantity>
311constexpr quantity_result_t<Quantity> operator*(const Quantity& quantity, double scalar) {
312 return Quantity::from_canonical(quantity.canonical_value() * scalar);
313}
314
315template <class Quantity>
316constexpr quantity_result_t<Quantity> operator*(double scalar, const Quantity& quantity) {
317 return Quantity::from_canonical(quantity.canonical_value() * scalar);
318}
319
320template <class Quantity>
321constexpr quantity_result_t<Quantity> operator/(const Quantity& quantity, double scalar) {
322 return Quantity::from_canonical(quantity.canonical_value() / scalar);
323}
324
325template <class Quantity>
326constexpr inverse_quantity_result_t<Quantity> operator/(double scalar, const Quantity& quantity) {
327 return inverse_quantity_result_t<Quantity>::from_canonical(scalar / quantity.canonical_value());
328}
329
330template <class Quantity>
331constexpr quantity_result_t<Quantity> operator-(const Quantity& quantity) {
332 return Quantity::from_canonical(-quantity.canonical_value());
333}
334
335template <class Quantity>
336constexpr quantity_result_t<Quantity> operator+(const Quantity& quantity) {
337 return quantity;
338}
339
340template <class Left, class Right>
341constexpr comparison_result_t<Left, Right> operator==(const Left& left, const Right& right) {
342 return left.canonical_value() == right.canonical_value();
343}
344
345template <class Left, class Right>
346constexpr comparison_result_t<Left, Right> operator!=(const Left& left, const Right& right) {
347 return !(left == right);
348}
349
350template <class Left, class Right>
351constexpr comparison_result_t<Left, Right> operator<(const Left& left, const Right& right) {
352 return left.canonical_value() < right.canonical_value();
353}
354
355template <class Left, class Right>
356constexpr comparison_result_t<Left, Right> operator<=(const Left& left, const Right& right) {
357 return left.canonical_value() <= right.canonical_value();
358}
359
360template <class Left, class Right>
361constexpr comparison_result_t<Left, Right> operator>(const Left& left, const Right& right) {
362 return left.canonical_value() > right.canonical_value();
363}
364
365template <class Left, class Right>
366constexpr comparison_result_t<Left, Right> operator>=(const Left& left, const Right& right) {
367 return left.canonical_value() >= right.canonical_value();
368}
369
370// Catalog defines quantities... Length, Time, etc, and gives them their units
371// number angle and temperature are special cases... not sure what to do with
372// angle/rotation2d yet, we will have both for now these files are not really
373// complete, they are included halfway through this file because they depend on
374// everything above I could not come up with a better way to do this, if you
375// open them your lsp will weep... but it works
376
377#include "core/units/catalog.h"
378#include "core/units/number.h"
379#include "core/units/angle.h"
380#include "core/units/temperature.h"
381
382// Math functions that respect quantities
383// These wrap gcem to be constexpr, when we upgrade to c++23 we can remove that
384// dependency
385
386template <class Quantity>
387constexpr quantity_result_t<Quantity> abs(const Quantity& quantity) {
388 return Quantity::from_canonical(gcem::abs(quantity.canonical_value()));
389}
390template <class Quantity>
391constexpr quantity_result_t<Quantity> min(const Quantity& left, const Quantity& right) {
392 return left < right ? left : right;
393}
394template <class Quantity>
395constexpr quantity_result_t<Quantity> max(const Quantity& left, const Quantity& right) {
396 return left < right ? right : left;
397}
398inline constexpr Number abs(const Number& value) { return Number(gcem::abs(value.value())); }
399inline constexpr Number min(const Number& left, const Number& right) {
400 return left.value() < right.value() ? left : right;
401}
402inline constexpr Number max(const Number& left, const Number& right) {
403 return left.value() < right.value() ? right : left;
404}
405template <class Quantity>
406inline constexpr Number sgn(const Quantity& quantity) {
407 return Number((quantity.canonical_value() > 0.0) - (quantity.canonical_value() < 0.0));
408}
409inline constexpr Number sgn(const Number& value) { return Number((value.value() > 0.0) - (value.value() < 0.0)); }
410template <int Power, class Quantity>
411constexpr power_result_t<Quantity, Power> pow(const Quantity& quantity) {
412 return scaled_quantity_t<Quantity, Power>::from_canonical(gcem::pow(quantity.canonical_value(), Power));
413}
414template <int Power>
415inline constexpr Number pow(const Number& value) {
416 return Number(gcem::pow(value.value(), Power));
417}
418template <typename Root, class Quantity>
419constexpr root_result_t<Quantity, Root::value> root(const Quantity& quantity) {
420 return divided_quantity_t<Quantity, Root::value>::from_canonical(
421 gcem::pow(quantity.canonical_value(), 1.0 / Root::value));
422}
423template <typename Root>
424inline constexpr Number root(const Number& value) {
425 return Number(gcem::pow(value.value(), 1.0 / Root::value));
426}
427template <class Quantity>
428constexpr root_result_t<Quantity, 2> sqrt(const Quantity& quantity) {
429 return root<std::integral_constant<int, 2> >(quantity);
430}
431inline constexpr Number sqrt(const Number& value) { return root<std::integral_constant<int, 2> >(value); }
432template <class Quantity>
433constexpr root_result_t<Quantity, 3> cbrt(const Quantity& quantity) {
434 return root<std::integral_constant<int, 3> >(quantity);
435}
436inline constexpr Number cbrt(const Number& value) { return root<std::integral_constant<int, 3> >(value); }
437template <class Quantity>
438constexpr quantity_result_t<Quantity> hypot(const Quantity& left, const Quantity& right) {
439 return Quantity::from_canonical(gcem::hypot(left.canonical_value(), right.canonical_value()));
440}
441inline constexpr Number hypot(const Number& left, const Number& right) {
442 return Number(gcem::hypot(left.value(), right.value()));
443}
444template <class Quantity>
445constexpr quantity_result_t<Quantity> mod(const Quantity& left, const Quantity& right) {
446 return Quantity::from_canonical(gcem::fmod(left.canonical_value(), right.canonical_value()));
447}
448inline constexpr Number mod(const Number& left, const Number& right) {
449 return Number(gcem::fmod(left.value(), right.value()));
450}
451template <class Quantity>
452constexpr quantity_result_t<Quantity> remainder(const Quantity& left, const Quantity& right) {
453 return Quantity::from_canonical(
454 left.canonical_value() - gcem::round(left.canonical_value() / right.canonical_value()) * right.canonical_value());
455}
456inline constexpr Number remainder(const Number& left, const Number& right) {
457 return Number(left.value() - gcem::round(left.value() / right.value()) * right.value());
458}
459template <class Quantity>
460constexpr quantity_result_t<Quantity> copysign(const Quantity& left, const Quantity& right) {
461 return Quantity::from_canonical(gcem::copysign(left.canonical_value(), right.canonical_value()));
462}
463inline constexpr Number copysign(const Number& left, const Number& right) {
464 return Number(gcem::copysign(left.value(), right.value()));
465}
466template <class Quantity>
467constexpr quantity_bool_result_t<Quantity> signbit(const Quantity& quantity) {
468 return gcem::signbit(quantity.canonical_value());
469}
470inline constexpr bool signbit(const Number& value) { return gcem::signbit(value.value()); }
471template <class Quantity>
472constexpr quantity_result_t<Quantity> clamp(const Quantity& value, const Quantity& low, const Quantity& high) {
473 return max(low, min(value, high));
474}
475inline constexpr Number clamp(const Number& value, const Number& low, const Number& high) {
476 return max(low, min(value, high));
477}
478template <class Quantity>
479constexpr quantity_result_t<Quantity> ceil(const Quantity& value, const Quantity& step) {
480 return Quantity::from_canonical(gcem::ceil(value.canonical_value() / step.canonical_value()) *
481 step.canonical_value());
482}
483template <class Quantity>
484constexpr quantity_result_t<Quantity> floor(const Quantity& value, const Quantity& step) {
485 return Quantity::from_canonical(gcem::floor(value.canonical_value() / step.canonical_value()) *
486 step.canonical_value());
487}
488template <class Quantity>
489constexpr quantity_result_t<Quantity> trunc(const Quantity& value, const Quantity& step) {
490 return Quantity::from_canonical(gcem::trunc(value.canonical_value() / step.canonical_value()) *
491 step.canonical_value());
492}
493template <class Quantity>
494constexpr quantity_result_t<Quantity> round(const Quantity& value, const Quantity& step) {
495 return Quantity::from_canonical(gcem::round(value.canonical_value() / step.canonical_value()) *
496 step.canonical_value());
497}
498inline constexpr Number ceil(const Number& value, const Number& step) {
499 return Number(gcem::ceil(value.value() / step.value()) * step.value());
500}
501inline constexpr Number floor(const Number& value, const Number& step) {
502 return Number(gcem::floor(value.value() / step.value()) * step.value());
503}
504inline constexpr Number trunc(const Number& value, const Number& step) {
505 return Number(gcem::trunc(value.value() / step.value()) * step.value());
506}
507inline constexpr Number round(const Number& value, const Number& step) {
508 return Number(gcem::round(value.value() / step.value()) * step.value());
509}
510template <class Quantity>
511constexpr quantity_result_t<Quantity> fma(const Quantity& x, double y, const Quantity& z) {
512 return Quantity::from_canonical(x.canonical_value() * y + z.canonical_value());
513}
514template <class Quantity>
515constexpr quantity_result_t<Quantity> fma(double x, const Quantity& y, const Quantity& z) {
516 return Quantity::from_canonical(x * y.canonical_value() + z.canonical_value());
517}
518inline constexpr Number fma(const Number& x, const Number& y, const Number& z) {
519 return Number(x.value() * y.value() + z.value());
520}
521template <class Quantity>
522inline constexpr power_result_t<Quantity, 2> square(const Quantity& quantity) {
523 return pow<2>(quantity);
524}
525inline constexpr Number square(const Number& value) { return pow<2>(value); }
526template <class Quantity>
527inline constexpr power_result_t<Quantity, 3> cube(const Quantity& quantity) {
528 return pow<3>(quantity);
529}
530inline constexpr Number cube(const Number& value) { return pow<3>(value); }
531
532template <class UnitTag, class Quantity>
533inline typename std::enable_if<is_quantity<Quantity>::value, double>::type unit_value_for_text(
534 const Quantity& quantity) {
535 return quantity.template as<UnitTag>();
536}
537
538// Formatting with names for strings or streams
539
540inline std::string humanize_identifier(const char* text) {
541 std::string result(text);
542 for (std::string::size_type i = 0; i < result.size(); ++i) {
543 if (result[i] == '_') result[i] = ' ';
544 }
545 return result;
546}
547
548template <class Quantity>
549inline std::string format_with_default_unit(const Quantity& quantity) {
550 typedef typename default_unit_for_quantity<Quantity>::type unit_t;
551 std::ostringstream stream;
552 stream << unit_value_for_text<unit_t>(quantity) << ' ' << unit_text<unit_t>::symbol();
553 return stream.str();
554}
555
556template <class UnitTag>
557inline const char* symbol() {
558 return unit_text<UnitTag>::symbol();
559}
560template <class UnitTag>
561inline std::string display_name() {
562 return humanize_identifier(unit_text<UnitTag>::name());
563}
564template <class UnitTag, class Quantity>
565inline std::string to_string_as(const Quantity& quantity) {
566 std::ostringstream stream;
567 stream << unit_value_for_text<UnitTag>(quantity) << ' ' << symbol<UnitTag>();
568 return stream.str();
569}
570inline std::string to_string(const Number& number) {
571 std::ostringstream stream;
572 stream << number.value();
573 return stream.str();
574}
575template <class Quantity>
576inline
577 typename std::enable_if<is_quantity<Quantity>::value && !std::is_same<Quantity, Number>::value, std::string>::type
578 to_string(const Quantity& quantity) {
579 return format_with_default_unit(quantity);
580}
581inline std::string to_string(const Temperature& quantity) { return format_with_default_unit(quantity); }
582inline std::string to_string(const TemperatureDelta& quantity) { return format_with_default_unit(quantity); }
583inline std::ostream& operator<<(std::ostream& stream, const Number& number) {
584 stream << number.value();
585 return stream;
586}
587template <class Quantity>
588inline
589 typename std::enable_if<is_quantity<Quantity>::value && !std::is_same<Quantity, Number>::value, std::ostream&>::type
590 operator<<(std::ostream& stream, const Quantity& quantity) {
591 stream << to_string(quantity);
592 return stream;
593}
594inline std::ostream& operator<<(std::ostream& stream, const Temperature& quantity) {
595 stream << to_string(quantity);
596 return stream;
597}
598inline std::ostream& operator<<(std::ostream& stream, const TemperatureDelta& quantity) {
599 stream << to_string(quantity);
600 return stream;
601}
602
603// Compatibility literals not in the catalog because they don't match the name,
604// the intent is that they're more obvious to think about volt per meter per
605// second as opposed to volt second per meter... one obviously gives you volts
606// when multiplied by m/s, the latter is less obvious
607
608namespace literals {
609constexpr LinearDerivativeGain operator""_VpMPs(long double value) {
610 return LinearDerivativeGain::from<volt_second_per_meter_t>(static_cast<double>(value));
611}
612constexpr LinearDerivativeGain operator""_VpMPs(unsigned long long value) {
613 return LinearDerivativeGain::from<volt_second_per_meter_t>(static_cast<double>(value));
614}
615constexpr LinearDerivativeGain operator""_VpInPs(long double value) {
616 return LinearDerivativeGain::from<volt_second_per_inch_t>(static_cast<double>(value));
617}
618constexpr LinearDerivativeGain operator""_VpInPs(unsigned long long value) {
619 return LinearDerivativeGain::from<volt_second_per_inch_t>(static_cast<double>(value));
620}
621constexpr LinearVelocityDerivativeGain operator""_VpMPs2(long double value) {
622 return LinearVelocityDerivativeGain::from<volt_second_squared_per_meter_t>(static_cast<double>(value));
623}
624constexpr LinearVelocityDerivativeGain operator""_VpMPs2(unsigned long long value) {
625 return LinearVelocityDerivativeGain::from<volt_second_squared_per_meter_t>(static_cast<double>(value));
626}
627constexpr LinearVelocityDerivativeGain operator""_VpInPs2(long double value) {
628 return LinearVelocityDerivativeGain::from<volt_second_squared_per_inch_t>(static_cast<double>(value));
629}
630constexpr LinearVelocityDerivativeGain operator""_VpInPs2(unsigned long long value) {
631 return LinearVelocityDerivativeGain::from<volt_second_squared_per_inch_t>(static_cast<double>(value));
632}
633} // namespace literals
634
635} // namespace units
636
637using namespace units;
638using namespace units::literals;