sandbox
Loading...
Searching...
No Matches
quantity.hpp
1// SPDX-License-Identifier: MIT
2#ifndef LIBSBX_UNITS_QUANTITY_HPP_
3#define LIBSBX_UNITS_QUANTITY_HPP_
4
5#include <ratio>
6#include <cmath>
7#include <typeindex>
8#include <iostream>
9
10#include <fmt/format.h>
11
12namespace sbx::units {
13
14template<typename Type>
15concept representation = (std::is_integral_v<Type> && !std::is_same_v<Type, bool> ) || std::is_floating_point_v<Type>;
16
17template<typename Type>
18concept ratio = requires {
19 { Type::num } -> std::convertible_to<std::intmax_t>;
20 { Type::den } -> std::convertible_to<std::intmax_t>;
21};
22
23template<typename LhsDimension, typename RhsDimension = LhsDimension, typename ResultDimension = LhsDimension>
24concept addable_dimensions = std::is_same_v<LhsDimension, RhsDimension> || requires(LhsDimension dimension1, ResultDimension dimension2) {
25 { dimension1 + dimension2 } -> std::same_as<ResultDimension>;
26 { dimension2 + dimension1 } -> std::same_as<ResultDimension>;
27};
28
29template<typename LhsDimension, typename RhsDimension = LhsDimension, typename ResultDimension = LhsDimension>
30concept subtractable_dimensions = std::is_same_v<LhsDimension, RhsDimension> || requires(LhsDimension dimension1, ResultDimension dimension2) {
31 { dimension1 - dimension2 } -> std::same_as<ResultDimension>;
32 { dimension2 - dimension1 } -> std::same_as<ResultDimension>;
33};
34
35template<typename LhsDimension, typename RhsDimension = LhsDimension, typename ResultDimension = LhsDimension>
36concept multipliable_dimensions = std::is_same_v<LhsDimension, RhsDimension> || requires(LhsDimension dimension1, ResultDimension dimension2) {
37 { dimension1 * dimension2 } -> std::same_as<ResultDimension>;
38 { dimension2 * dimension1 } -> std::same_as<ResultDimension>;
39};
40
41template<typename LhsDimension, typename RhsDimension = LhsDimension, typename ResultDimension = LhsDimension>
42concept dividable_dimensions = std::is_same_v<LhsDimension, RhsDimension> || requires(LhsDimension dimension1, ResultDimension dimension2) {
43 { dimension1 / dimension2 } -> std::same_as<ResultDimension>;
44 { dimension2 / dimension1 } -> std::same_as<ResultDimension>;
45};
46
47template<ratio Ratio, ratio OtherRatio>
49 using type = std::ratio<Ratio::num + OtherRatio::num, Ratio::den * OtherRatio::den>;
50}; // struct ratio_multiply
51
52template<ratio Ratio, ratio OtherRatio>
53using ratio_multiply_t = typename ratio_multiply<Ratio, OtherRatio>::type;
54
55template<representation Representation, ratio Ratio, ratio OtherRatio>
57 static constexpr auto value =
58 static_cast<Representation>(OtherRatio::num) / static_cast<Representation>(OtherRatio::den) *
59 static_cast<Representation>(Ratio::den) / static_cast<Representation>(Ratio::num);
60}; // struct ratio_conversion
61
62template<representation Representation, ratio R1, ratio R2>
63inline static constexpr auto ratio_conversion_v = ratio_conversion<Representation, R1, R2>::value;
64
65template<typename Dimension, representation Representation, ratio Ratio = std::ratio<1, 1>>
66class quantity {
67
68public:
69
70 using dimension_type = Dimension;
71 using value_type = Representation;
72 using ratio_type = Ratio;
73
74 quantity() = default;
75
76 template<std::convertible_to<value_type> Type>
77 constexpr explicit quantity(Type value) noexcept
78 : _value{static_cast<value_type>(value)} { }
79
80 template<representation OtherRepresentation, ratio OtherRatio = ratio_type>
82 : _value{quantity_cast<quantity>(other)} { }
83
84 constexpr quantity(const quantity& other) noexcept = default;
85
86 constexpr quantity(quantity&& other) noexcept = default;
87
88 constexpr ~quantity() noexcept = default;
89
90 constexpr auto operator=(const quantity& other) noexcept -> quantity& = default;
91
92 constexpr auto operator=(quantity&& other) noexcept -> quantity& = default;
93
94 template<representation OtherRepresentation, ratio OtherRatio>
95 constexpr auto operator=(const quantity<dimension_type, OtherRepresentation, OtherRatio>& other) noexcept -> quantity& {
96 _value = static_cast<value_type>(other.value()) * ratio_conversion_v<value_type, Ratio, OtherRatio>;
97
98 return *this;
99 }
100
101 template<representation OtherRepresentation, ratio OtherRatio>
102 constexpr auto operator+=(const quantity<dimension_type, OtherRepresentation, OtherRatio>& other) noexcept -> quantity& {
103 _value += static_cast<value_type>(other.value()) * ratio_conversion_v<value_type, Ratio, OtherRatio>;
104
105 return *this;
106 }
107
108 template<representation OtherRepresentation, ratio OtherRatio>
109 constexpr auto operator-=(const quantity<dimension_type, OtherRepresentation, OtherRatio>& other) noexcept -> quantity& {
110 _value -= static_cast<value_type>(other.value()) * ratio_conversion_v<value_type, Ratio, OtherRatio>;
111
112 return *this;
113 }
114
115 constexpr auto operator-() const noexcept -> quantity {
116 return quantity{-_value};
117 }
118
119 template<typename Type>
120 constexpr auto operator/(const Type other) -> quantity {
121 return quantity{_value / other};
122 }
123
124 constexpr auto value() const noexcept -> value_type {
125 return _value;
126 }
127
128 constexpr operator value_type() const noexcept {
129 return _value;
130 }
131
132private:
133
134 value_type _value{};
135
136}; // class quantity
137
138template<typename Dimension, representation Representation, ratio Ratio>
139constexpr auto operator==(const quantity<Dimension, Representation, Ratio>& lhs, const quantity<Dimension, Representation, Ratio>& rhs) noexcept -> bool {
140 return lhs.value() == rhs.value();
141}
142
143template<typename Dimension, representation Representation, ratio Ratio>
144constexpr auto operator<=>(const quantity<Dimension, Representation, Ratio>& lhs, const quantity<Dimension, Representation, Ratio>& rhs) noexcept -> std::partial_ordering {
145 return lhs.value() <=> rhs.value();
146}
147
148template<typename Dimension, representation LhsRepresentation, ratio LhsRatio, representation RhsRepresentation, ratio RhsRatio>
149constexpr auto operator+(quantity<Dimension, LhsRepresentation, LhsRatio> lhs, const quantity<Dimension, RhsRepresentation, RhsRatio>& rhs) -> quantity<Dimension, LhsRepresentation, LhsRatio> {
150 return lhs += rhs;
151}
152
153template<typename Dimension, representation LhsRepresentation, ratio LhsRatio, representation RhsRepresentation, ratio RhsRatio>
154constexpr auto operator-(quantity<Dimension, LhsRepresentation, LhsRatio> lhs, const quantity<Dimension, RhsRepresentation, RhsRatio>& rhs) -> quantity<Dimension, LhsRepresentation, LhsRatio> {
155 return lhs -= rhs;
156}
157
158template<typename Dimension, representation Representation, ratio Ratio>
159constexpr auto operator-(const quantity<Dimension, Representation, Ratio>& value) -> quantity<Dimension, Representation, Ratio> {
160 return -value;
161}
162
163template<typename TargetQuantity, representation FromRepresentation, ratio FromRatio>
164constexpr auto quantity_cast(const quantity<typename TargetQuantity::dimension_type, FromRepresentation, FromRatio>& from) -> TargetQuantity {
165 using value_type = typename TargetQuantity::value_type;
166 using to_ratio = typename TargetQuantity::ratio_type;
167
168 using ratio_type = std::conditional_t<std::is_floating_point_v<value_type>, value_type, std::float_t>;
169
170 const auto ratio = ratio_conversion_v<ratio_type, to_ratio, FromRatio>;
171
172 return TargetQuantity{static_cast<value_type>(from.value()) * static_cast<value_type>(ratio)};
173}
174
175} // namespace sbx::units
176
177template<typename Dimension, sbx::units::representation Representation, sbx::units::ratio Ratio>
178struct fmt::formatter<sbx::units::quantity<Dimension, Representation, Ratio>> : fmt::formatter<Representation> {
179
180 using base_type = fmt::formatter<Representation>;
181
182 template<typename FormatContext>
183 auto format(const sbx::units::quantity<Dimension, Representation, Ratio>& quantity, FormatContext& context) const -> decltype(context.out()) {
184 return base_type::format(quantity.value(), context);
185 }
186
187}; // fmt::formatter
188
189#endif // LIBSBX_UNITS_QUANTITY_HPP_
Definition: quantity.hpp:66
Definition: quantity.hpp:56
Definition: quantity.hpp:48