1 #pragma once
2 #ifndef CATA_SRC_UNITS_H
3 #define CATA_SRC_UNITS_H
4
5 #include <cctype>
6 #include <algorithm>
7 #include <cmath>
8 #include <cstddef>
9 #include <limits>
10 #include <map>
11 #include <sstream>
12 #include <string>
13 #include <type_traits>
14 #include <utility>
15 #include <vector>
16
17 #include "json.h"
18 #include "math_defines.h"
19 #include "translations.h"
20 #include "units_fwd.h" // IWYU pragma: export
21
22 namespace units
23 {
24
25 template<typename V, typename U>
26 class quantity
27 {
28 public:
29 using value_type = V;
30 using unit_type = U;
31 using this_type = quantity<value_type, unit_type>;
32
33 /**
34 * Create an empty quantity - its @ref value_ is value initialized.
35 * It does not need an explicitly named unit, it's always 0: 0 l == 0 ml == 0 Ml.
36 */
quantity()37 constexpr quantity() : value_() {
38 }
39 /**
40 * Construct from value. This is supposed to be wrapped into a static
41 * function (e.g. `from_liter(int)` ) to provide context.
42 */
quantity(const value_type & v,unit_type)43 constexpr quantity( const value_type &v, unit_type ) : value_( v ) {
44 }
45 /**
46 * Conversion from other value type, e.g. from `quantity<int, foo>` to
47 * `quantity<float, foo>`. The unit type stays the same!
48 */
49 template<typename other_value_type>
50 // NOLINTNEXTLINE(google-explicit-constructor)
quantity(const quantity<other_value_type,unit_type> & other)51 constexpr quantity( const quantity<other_value_type, unit_type> &other ) :
52 value_( other.value() ) {
53 }
54
55 /**
56 * Access the raw dimensionless value. Use it in a properly named wrapper function only.
57 */
value()58 constexpr const value_type &value() const {
59 return value_;
60 }
61
62 /**
63 * The usual comparators, they compare the base value only.
64 */
65 /**@{*/
66 constexpr bool operator==( const this_type &rhs ) const {
67 return value_ == rhs.value_;
68 }
69 constexpr bool operator!=( const this_type &rhs ) const {
70 return !operator==( rhs );
71 }
72 constexpr bool operator<( const this_type &rhs ) const {
73 return value_ < rhs.value_;
74 }
75 constexpr bool operator>=( const this_type &rhs ) const {
76 return !operator<( rhs );
77 }
78 constexpr bool operator>( const this_type &rhs ) const {
79 return value_ > rhs.value_;
80 }
81 constexpr bool operator<=( const this_type &rhs ) const {
82 return !operator>( rhs );
83 }
84 /**@}*/
85
86 /**
87 * Addition and subtraction of quantities of the same unit type. Result is
88 * a quantity with the same unit as the input.
89 * Functions are templated to allow combining quantities with different `value_type`, e.g.
90 * \code
91 * quantity<int, foo> a = ...;
92 * quantity<double, foo> b = ...;
93 * auto sum = a + b;
94 * static_assert(std::is_same<decltype(sum), quantity<double, foo>>::value);
95 * \endcode
96 *
97 * Note that `+=` and `-=` accept any type as `value_type` for the other operand, but
98 * they convert this back to the type of the right hand, like in `int a; a += 0.4;`
99 * \code
100 * quantity<int, foo> a( 10, foo{} );
101 * quantity<double, foo> b( 0.5, foo{} );
102 * a += b;
103 * cata_assert( a == quantity<int, foo>( 10 + 0.5, foo{} ) );
104 * cata_assert( a == quantity<int, foo>( 10, foo{} ) );
105 * \endcode
106 */
107 /**@{*/
108 template<typename other_value_type>
109 constexpr quantity < decltype( std::declval<value_type>() + std::declval<other_value_type>() ),
110 unit_type >
111 operator+( const quantity<other_value_type, unit_type> &rhs ) const {
112 return { value_ + rhs.value(), unit_type{} };
113 }
114 template<typename other_value_type>
115 constexpr quantity < decltype( std::declval<value_type>() + std::declval<other_value_type>() ),
116 unit_type >
117 operator-( const quantity<other_value_type, unit_type> &rhs ) const {
118 return { value_ - rhs.value(), unit_type{} };
119 }
120
121 template<typename other_value_type>
122 this_type &operator+=( const quantity<other_value_type, unit_type> &rhs ) {
123 value_ += rhs.value();
124 return *this;
125 }
126 template<typename other_value_type>
127 this_type &operator-=( const quantity<other_value_type, unit_type> &rhs ) {
128 value_ -= rhs.value();
129 return *this;
130 }
131 /**@}*/
132
133 constexpr this_type operator-() const {
134 return this_type( -value_, unit_type{} );
135 }
136
137 void serialize( JsonOut &jsout ) const;
138 void deserialize( JsonIn &jsin );
139
140 private:
141 value_type value_;
142 };
143
144 template<typename V, typename U>
fabs(quantity<V,U> q)145 inline quantity<V, U> fabs( quantity<V, U> q )
146 {
147 return quantity<V, U>( std::fabs( q.value() ), U{} );
148 }
149
150 template<typename V, typename U>
fmod(quantity<V,U> num,quantity<V,U> den)151 inline quantity<V, U> fmod( quantity<V, U> num, quantity<V, U> den )
152 {
153 return quantity<V, U>( std::fmod( num.value(), den.value() ), U{} );
154 }
155
156 /**
157 * Multiplication and division with scalars. Result is a quantity with the same unit
158 * as the input.
159 * Functions are templated to allow scaling with different types:
160 * \code
161 * quantity<int, foo> a{ 10, foo{} };
162 * auto b = a * 4.52;
163 * static_assert(std::is_same<decltype(b), quantity<double, foo>>::value);
164 * \endcode
165 *
166 * Note that the result for `*=` and `/=` is calculated using the given types, but is
167 * implicitly converted back to `value_type` as it is stored in the operand.
168 * \code
169 * quantity<int, foo> a{ 10, foo{} };
170 * a *= 4.52;
171 * cata_assert( a == quantity<int, foo>( 10 * 4.52, foo{} ) );
172 * cata_assert( a != quantity<int, foo>( 10 * (int)4.52, foo{} ) );
173 * cata_assert( a == quantity<int, foo>( 45, foo{} ) );
174 * \endcode
175 *
176 * Division of a quantity with a quantity of the same unit yields a dimensionless
177 * scalar value, with the same type as the division of the contained `value_type`s:
178 * \code
179 * quantity<int, foo> a{ 10, foo{} };
180 * quantity<double, foo> b{ 20, foo{} };
181 * auto proportion = a / b;
182 * static_assert(std::is_same<decltype(proportion), double>::value);
183 * cata_assert( proportion == 10 / 20.0 );
184 * \endcode
185 *
186 */
187 /**@{*/
188 // the decltype in the result type ensures the returned type has the same scalar type
189 // as you would get when performing the operation directly:
190 // `int * double` => `double` and `char * int` => `int`
191 // st is scalar type (dimensionless)
192 // lvt is the value type (of a quantity) on the left side, rvt is the value type on the right side
193 // ut is unit type (same for left and right side)
194 // The enable_if ensures no ambiguity, the compiler may otherwise not be able to decide whether
195 // "quantity / scalar" or "quantity / other_quanity" is meant.
196
197 // scalar * quantity<foo, unit> == quantity<decltype(foo * scalar), unit>
198 template<typename lvt, typename ut, typename st, typename = typename std::enable_if<std::is_arithmetic<st>::value>::type>
199 inline constexpr quantity<decltype( std::declval<lvt>() * std::declval<st>() ), ut>
200 operator*( const st &factor, const quantity<lvt, ut> &rhs )
201 {
202 return { factor * rhs.value(), ut{} };
203 }
204
205 // same as above only with inverse order of operands: quantity * scalar
206 template<typename lvt, typename ut, typename st, typename = typename std::enable_if<std::is_arithmetic<st>::value>::type>
207 inline constexpr quantity<decltype( std::declval<st>() * std::declval<lvt>() ), ut>
208 operator*( const quantity<lvt, ut> &lhs, const st &factor )
209 {
210 return { lhs.value() *factor, ut{} };
211 }
212
213 // quantity<foo, unit> * quantity<bar, unit> is not supported
214 template<typename lvt, typename ut, typename rvt, typename = typename std::enable_if<std::is_arithmetic<lvt>::value>::type>
215 inline void operator*( quantity<lvt, ut>, quantity<rvt, ut> ) = delete;
216
217 // operator *=
218 template<typename lvt, typename ut, typename st, typename = typename std::enable_if<std::is_arithmetic<st>::value>::type>
219 inline quantity<lvt, ut> &
220 operator*=( quantity<lvt, ut> &lhs, const st &factor )
221 {
222 lhs = lhs * factor;
223 return lhs;
224 }
225
226 // and the revers of the multiplication above:
227 // quantity<foo, unit> / scalar == quantity<decltype(foo / scalar), unit>
228 template<typename lvt, typename ut, typename rvt, typename = typename std::enable_if<std::is_arithmetic<rvt>::value>::type>
229 inline constexpr quantity<decltype( std::declval<lvt>() * std::declval<rvt>() ), ut>
230 operator/( const quantity<lvt, ut> &lhs, const rvt &divisor )
231 {
232 return { lhs.value() / divisor, ut{} };
233 }
234
235 // scalar / quantity<foo, unit> is not supported
236 template<typename lvt, typename ut, typename rvt, typename = typename std::enable_if<std::is_arithmetic<lvt>::value>::type>
237 inline void operator/( lvt, quantity<rvt, ut> ) = delete;
238
239 // quantity<foo, unit> / quantity<bar, unit> == decltype(foo / bar)
240 template<typename lvt, typename ut, typename rvt>
241 inline constexpr decltype( std::declval<lvt>() / std::declval<rvt>() )
242 operator/( const quantity<lvt, ut> &lhs, const quantity<rvt, ut> &rhs )
243 {
244 return lhs.value() / rhs.value();
245 }
246
247 // operator /=
248 template<typename lvt, typename ut, typename st, typename = typename std::enable_if<std::is_arithmetic<st>::value>::type>
249 inline quantity<lvt, ut> &
250 operator/=( quantity<lvt, ut> &lhs, const st &divisor )
251 {
252 lhs = lhs / divisor;
253 return lhs;
254 }
255
256 // remainder:
257 // quantity<foo, unit> % scalar == quantity<decltype(foo % scalar), unit>
258 template<typename lvt, typename ut, typename rvt, typename = typename std::enable_if<std::is_arithmetic<rvt>::value>::type>
259 inline constexpr quantity < decltype( std::declval<lvt>() % std::declval<rvt>() ), ut >
260 operator%( const quantity<lvt, ut> &lhs, const rvt &divisor )
261 {
262 return { lhs.value() % divisor, ut{} };
263 }
264
265 // scalar % quantity<foo, unit> is not supported
266 template<typename lvt, typename ut, typename rvt, typename = typename std::enable_if<std::is_arithmetic<lvt>::value>::type>
267 inline void operator%( lvt, quantity<rvt, ut> ) = delete;
268
269 // quantity<foo, unit> % quantity<bar, unit> == decltype(foo % bar)
270 template<typename lvt, typename ut, typename rvt>
271 inline constexpr quantity < decltype( std::declval<lvt>() % std::declval<rvt>() ), ut >
272 operator%( const quantity<lvt, ut> &lhs, const quantity<rvt, ut> &rhs )
273 {
274 return { lhs.value() % rhs.value(), ut{} };
275 }
276
277 // operator %=
278 template<typename lvt, typename ut, typename st, typename = typename std::enable_if<std::is_arithmetic<st>::value>::type>
279 inline quantity<lvt, ut> &
280 operator%=( quantity<lvt, ut> &lhs, const st &divisor )
281 {
282 lhs = lhs % divisor;
283 return lhs;
284 }
285 template<typename lvt, typename ut, typename rvt>
286 inline quantity<lvt, ut> &
287 operator%=( quantity<lvt, ut> &lhs, const quantity<rvt, ut> &rhs )
288 {
289 lhs = lhs % rhs;
290 return lhs;
291 }
292 /**@}*/
293
294 const volume volume_min = units::volume( std::numeric_limits<units::volume::value_type>::min(),
295 units::volume::unit_type{} );
296
297 const volume volume_max = units::volume( std::numeric_limits<units::volume::value_type>::max(),
298 units::volume::unit_type{} );
299
300 template<typename value_type>
from_milliliter(const value_type v)301 inline constexpr quantity<value_type, volume_in_milliliter_tag> from_milliliter(
302 const value_type v )
303 {
304 return quantity<value_type, volume_in_milliliter_tag>( v, volume_in_milliliter_tag{} );
305 }
306
307 template<typename value_type>
from_liter(const value_type v)308 inline constexpr quantity<value_type, volume_in_milliliter_tag> from_liter( const value_type v )
309 {
310 return from_milliliter<value_type>( v * 1000 );
311 }
312
313 template<typename value_type>
to_milliliter(const quantity<value_type,volume_in_milliliter_tag> & v)314 inline constexpr value_type to_milliliter( const quantity<value_type, volume_in_milliliter_tag> &v )
315 {
316 return v / from_milliliter<value_type>( 1 );
317 }
318
to_liter(const volume & v)319 inline constexpr double to_liter( const volume &v )
320 {
321 return v.value() / 1000.0;
322 }
323
324 // Legacy conversions factor for old volume values.
325 // Don't use in new code! Use one of the from_* functions instead.
326 static constexpr volume legacy_volume_factor = from_milliliter( 250 );
327
328 const mass mass_min = units::mass( std::numeric_limits<units::mass::value_type>::min(),
329 units::mass::unit_type{} );
330
331 const mass mass_max = units::mass( std::numeric_limits<units::mass::value_type>::max(),
332 units::mass::unit_type{} );
333
334 template<typename value_type>
from_milligram(const value_type v)335 inline constexpr quantity<value_type, mass_in_milligram_tag> from_milligram(
336 const value_type v )
337 {
338 return quantity<value_type, mass_in_milligram_tag>( v, mass_in_milligram_tag{} );
339 }
340
341 template<typename value_type>
from_gram(const value_type v)342 inline constexpr quantity<value_type, mass_in_milligram_tag> from_gram(
343 const value_type v )
344 {
345 return from_milligram( v * 1000 );
346 }
347
348 template<typename value_type>
from_kilogram(const value_type v)349 inline constexpr quantity<value_type, mass_in_milligram_tag> from_kilogram(
350 const value_type v )
351 {
352 return from_gram( v * 1000 );
353 }
354
355 template<typename value_type>
to_milligram(const quantity<value_type,mass_in_milligram_tag> & v)356 inline constexpr value_type to_milligram( const quantity<value_type, mass_in_milligram_tag> &v )
357 {
358 return v.value();
359 }
360
361 template<typename value_type>
to_gram(const quantity<value_type,mass_in_milligram_tag> & v)362 inline constexpr value_type to_gram( const quantity<value_type, mass_in_milligram_tag> &v )
363 {
364 return v.value() / 1000.0;
365 }
366
to_kilogram(const mass & v)367 inline constexpr double to_kilogram( const mass &v )
368 {
369 return v.value() / 1000000.0;
370 }
371
372 const energy energy_min = units::energy( std::numeric_limits<units::energy::value_type>::min(),
373 units::energy::unit_type{} );
374
375 const energy energy_max = units::energy( std::numeric_limits<units::energy::value_type>::max(),
376 units::energy::unit_type{} );
377
378 template<typename value_type>
from_millijoule(const value_type v)379 inline constexpr quantity<value_type, energy_in_millijoule_tag> from_millijoule(
380 const value_type v )
381 {
382 return quantity<value_type, energy_in_millijoule_tag>( v, energy_in_millijoule_tag{} );
383 }
384
385 template<typename value_type>
from_joule(const value_type v)386 inline constexpr quantity<value_type, energy_in_millijoule_tag> from_joule( const value_type v )
387 {
388 const value_type max_energy_joules = std::numeric_limits<value_type>::max() / 1000;
389 // Check for overflow - if the energy provided is greater than max energy, then it
390 // if overflow when converted to millijoules
391 const value_type energy = v > max_energy_joules ? max_energy_joules : v;
392 return from_millijoule<value_type>( energy * 1000 );
393 }
394
395 template<typename value_type>
from_kilojoule(const value_type v)396 inline constexpr quantity<value_type, energy_in_millijoule_tag> from_kilojoule( const value_type v )
397 {
398 const value_type max_energy_joules = std::numeric_limits<value_type>::max() / 1000;
399 // This checks for value_type overflow - if the energy we are given in Joules is greater
400 // than the max energy in Joules, overflow will occur when it is converted to millijoules
401 // The value we are given is in kJ, multiply by 1000 to convert it to joules, for use in from_joule
402 value_type energy = v * 1000 > max_energy_joules ? max_energy_joules : v * 1000;
403 return from_joule<value_type>( energy );
404 }
405
406 template<typename value_type>
to_millijoule(const quantity<value_type,energy_in_millijoule_tag> & v)407 inline constexpr value_type to_millijoule( const quantity<value_type, energy_in_millijoule_tag> &v )
408 {
409 return v / from_millijoule<value_type>( 1 );
410 }
411
412 template<typename value_type>
to_joule(const quantity<value_type,energy_in_millijoule_tag> & v)413 inline constexpr value_type to_joule( const quantity<value_type, energy_in_millijoule_tag> &v )
414 {
415 return to_millijoule( v ) / 1000.0;
416 }
417
418 template<typename value_type>
to_kilojoule(const quantity<value_type,energy_in_millijoule_tag> & v)419 inline constexpr value_type to_kilojoule( const quantity<value_type, energy_in_millijoule_tag> &v )
420 {
421 return to_joule( v ) / 1000.0;
422 }
423
424 const money money_min = units::money( std::numeric_limits<units::money::value_type>::min(),
425 units::money::unit_type{} );
426
427 const money money_max = units::money( std::numeric_limits<units::money::value_type>::max(),
428 units::money::unit_type{} );
429
430 template<typename value_type>
from_cent(const value_type v)431 inline constexpr quantity<value_type, money_in_cent_tag> from_cent(
432 const value_type v )
433 {
434 return quantity<value_type, money_in_cent_tag>( v, money_in_cent_tag{} );
435 }
436
437 template<typename value_type>
from_usd(const value_type v)438 inline constexpr quantity<value_type, money_in_cent_tag> from_usd( const value_type v )
439 {
440 return from_cent<value_type>( v * 100 );
441 }
442
443 template<typename value_type>
from_kusd(const value_type v)444 inline constexpr quantity<value_type, money_in_cent_tag> from_kusd( const value_type v )
445 {
446 return from_usd<value_type>( v * 1000 );
447 }
448
449 template<typename value_type>
to_cent(const quantity<value_type,money_in_cent_tag> & v)450 inline constexpr value_type to_cent( const quantity<value_type, money_in_cent_tag> &v )
451 {
452 return v / from_cent<value_type>( 1 );
453 }
454
455 template<typename value_type>
to_usd(const quantity<value_type,money_in_cent_tag> & v)456 inline constexpr value_type to_usd( const quantity<value_type, money_in_cent_tag> &v )
457 {
458 return to_cent( v ) / 100.0;
459 }
460
461 template<typename value_type>
to_kusd(const quantity<value_type,money_in_cent_tag> & v)462 inline constexpr value_type to_kusd( const quantity<value_type, money_in_cent_tag> &v )
463 {
464 return to_usd( v ) / 1000.0;
465 }
466
467 const length length_min = units::length( std::numeric_limits<units::length::value_type>::min(),
468 units::length::unit_type{} );
469
470 const length length_max = units::length( std::numeric_limits<units::length::value_type>::max(),
471 units::length::unit_type{} );
472
473 template<typename value_type>
from_millimeter(const value_type v)474 inline constexpr quantity<value_type, length_in_millimeter_tag> from_millimeter(
475 const value_type v )
476 {
477 return quantity<value_type, length_in_millimeter_tag>( v, length_in_millimeter_tag{} );
478 }
479
480 template<typename value_type>
from_centimeter(const value_type v)481 inline constexpr quantity<value_type, length_in_millimeter_tag> from_centimeter(
482 const value_type v )
483 {
484 return from_millimeter<value_type>( v * 10 );
485 }
486
487 template<typename value_type>
from_meter(const value_type v)488 inline constexpr quantity<value_type, length_in_millimeter_tag> from_meter(
489 const value_type v )
490 {
491 return from_millimeter<value_type>( v * 1000 );
492 }
493
494 template<typename value_type>
from_kilometer(const value_type v)495 inline constexpr quantity<value_type, length_in_millimeter_tag> from_kilometer(
496 const value_type v )
497 {
498 return from_millimeter<value_type>( v * 1'000'000 );
499 }
500
501 template<typename value_type>
to_millimeter(const quantity<value_type,length_in_millimeter_tag> & v)502 inline constexpr value_type to_millimeter( const quantity<value_type, length_in_millimeter_tag> &v )
503 {
504 return v / from_millimeter<value_type>( 1 );
505 }
506
507 template<typename value_type>
to_centimeter(const quantity<value_type,length_in_millimeter_tag> & v)508 inline constexpr value_type to_centimeter( const quantity<value_type, length_in_millimeter_tag> &v )
509 {
510 return to_millimeter( v ) / 10.0;
511 }
512
513 template<typename value_type>
to_meter(const quantity<value_type,length_in_millimeter_tag> & v)514 inline constexpr value_type to_meter( const quantity<value_type, length_in_millimeter_tag> &v )
515 {
516 return to_millimeter( v ) / 1'000.0;
517 }
518
519 template<typename value_type>
520 inline constexpr value_type to_kilometer( const quantity<value_type, length_in_millimeter_tag> &v )
521 {
522 return to_millimeter( v ) / 1'000'000.0;
523 }
524
525 template<typename value_type>
526 inline constexpr quantity<value_type, angle_in_radians_tag> from_radians( const value_type v )
527 {
528 return quantity<value_type, angle_in_radians_tag>( v, angle_in_radians_tag{} );
529 }
530
531 inline constexpr double to_radians( const units::angle v )
532 {
533 return v.value();
534 }
535
536 template<typename value_type>
537 inline constexpr quantity<double, angle_in_radians_tag> from_degrees( const value_type v )
538 {
539 return from_radians( v * M_PI / 180 );
540 }
541
542 inline constexpr double to_degrees( const units::angle v )
543 {
544 return to_radians( v ) * 180 / M_PI;
545 }
546
547 template<typename value_type>
548 inline constexpr quantity<double, angle_in_radians_tag> from_arcmin( const value_type v )
549 {
550 return from_degrees( v / 60.0 );
551 }
552
553 inline constexpr double to_arcmin( const units::angle v )
554 {
555 return to_degrees( v ) * 60;
556 }
557
558 // converts a volume as if it were a cube to the length of one side
559 template<typename value_type>
560 inline constexpr quantity<value_type, length_in_millimeter_tag> default_length_from_volume(
561 const quantity<value_type, volume_in_milliliter_tag> &v )
562 {
563 return units::from_centimeter<int>(
564 std::round(
565 std::cbrt( units::to_milliliter( v ) ) ) );
566 }
567
568 // Streaming operators for debugging and tests
569 // (for UI output other functions should be used which render in the user's
570 // chosen units)
571 inline std::ostream &operator<<( std::ostream &o, mass_in_milligram_tag )
572 {
573 return o << "mg";
574 }
575
576 inline std::ostream &operator<<( std::ostream &o, volume_in_milliliter_tag )
577 {
578 return o << "ml";
579 }
580
581 inline std::ostream &operator<<( std::ostream &o, energy_in_millijoule_tag )
582 {
583 return o << "mJ";
584 }
585
586 inline std::ostream &operator<<( std::ostream &o, money_in_cent_tag )
587 {
588 return o << "cent";
589 }
590
591 inline std::ostream &operator<<( std::ostream &o, length_in_millimeter_tag )
592 {
593 return o << "mm";
594 }
595
596 inline std::ostream &operator<<( std::ostream &o, angle_in_radians_tag )
597 {
598 return o << "rad";
599 }
600
601 template<typename value_type, typename tag_type>
602 inline std::ostream &operator<<( std::ostream &o, const quantity<value_type, tag_type> &v )
603 {
604 return o << v.value() << tag_type{};
605 }
606
607 template<typename value_type, typename tag_type>
608 inline std::string quantity_to_string( const quantity<value_type, tag_type> &v )
609 {
610 std::ostringstream os;
611 os << v;
612 return os.str();
613 }
614
615 inline std::string display( const units::energy v )
616 {
617 const int kj = units::to_kilojoule( v );
618 const int j = units::to_joule( v );
619 // at least 1 kJ and there is no fraction
620 if( kj >= 1 && static_cast<float>( j ) / kj == 1000 ) {
621 return std::to_string( kj ) + ' ' + pgettext( "energy unit: kilojoule", "kJ" );
622 }
623 const int mj = units::to_millijoule( v );
624 // at least 1 J and there is no fraction
625 if( j >= 1 && static_cast<float>( mj ) / j == 1000 ) {
626 return std::to_string( j ) + ' ' + pgettext( "energy unit: joule", "J" );
627 }
628 return std::to_string( mj ) + ' ' + pgettext( "energy unit: millijoule", "mJ" );
629 }
630
631 } // namespace units
632
633 // Implicitly converted to volume, which has int as value_type!
634 inline constexpr units::volume operator"" _ml( const unsigned long long v )
635 {
636 return units::from_milliliter( v );
637 }
638
639 inline constexpr units::quantity<double, units::volume_in_milliliter_tag> operator"" _ml(
640 const long double v )
641 {
642 return units::from_milliliter( v );
643 }
644
645 // Implicitly converted to volume, which has int as value_type!
646 inline constexpr units::volume operator"" _liter( const unsigned long long v )
647 {
648 return units::from_milliliter( v * 1000 );
649 }
650
651 inline constexpr units::quantity<double, units::volume_in_milliliter_tag> operator"" _liter(
652 const long double v )
653 {
654 return units::from_milliliter( v * 1000 );
655 }
656
657 // Implicitly converted to mass, which has int as value_type!
658 inline constexpr units::mass operator"" _milligram( const unsigned long long v )
659 {
660 return units::from_milligram( v );
661 }
662 inline constexpr units::mass operator"" _gram( const unsigned long long v )
663 {
664 return units::from_gram( v );
665 }
666
667 inline constexpr units::mass operator"" _kilogram( const unsigned long long v )
668 {
669 return units::from_kilogram( v );
670 }
671
672 inline constexpr units::quantity<double, units::mass_in_milligram_tag> operator"" _milligram(
673 const long double v )
674 {
675 return units::from_milligram( v );
676 }
677
678 inline constexpr units::quantity<double, units::mass_in_milligram_tag> operator"" _gram(
679 const long double v )
680 {
681 return units::from_gram( v );
682 }
683
684 inline constexpr units::quantity<double, units::mass_in_milligram_tag> operator"" _kilogram(
685 const long double v )
686 {
687 return units::from_kilogram( v );
688 }
689
690 inline constexpr units::energy operator"" _mJ( const unsigned long long v )
691 {
692 return units::from_millijoule( v );
693 }
694
695 inline constexpr units::quantity<double, units::energy_in_millijoule_tag> operator"" _mJ(
696 const long double v )
697 {
698 return units::from_millijoule( v );
699 }
700
701 inline constexpr units::energy operator"" _J( const unsigned long long v )
702 {
703 return units::from_joule( v );
704 }
705
706 inline constexpr units::quantity<double, units::energy_in_millijoule_tag> operator"" _J(
707 const long double v )
708 {
709 return units::from_joule( v );
710 }
711
712 inline constexpr units::energy operator"" _kJ( const unsigned long long v )
713 {
714 return units::from_kilojoule( v );
715 }
716
717 inline constexpr units::quantity<double, units::energy_in_millijoule_tag> operator"" _kJ(
718 const long double v )
719 {
720 return units::from_kilojoule( v );
721 }
722
723 inline constexpr units::money operator"" _cent( const unsigned long long v )
724 {
725 return units::from_cent( v );
726 }
727
728 inline constexpr units::quantity<double, units::money_in_cent_tag> operator"" _cent(
729 const long double v )
730 {
731 return units::from_cent( v );
732 }
733
734 inline constexpr units::money operator"" _USD( const unsigned long long v )
735 {
736 return units::from_usd( v );
737 }
738
739 inline constexpr units::quantity<double, units::money_in_cent_tag> operator"" _USD(
740 const long double v )
741 {
742 return units::from_usd( v );
743 }
744
745 inline constexpr units::money operator"" _kUSD( const unsigned long long v )
746 {
747 return units::from_kusd( v );
748 }
749
750 inline constexpr units::quantity<double, units::money_in_cent_tag> operator"" _kUSD(
751 const long double v )
752 {
753 return units::from_kusd( v );
754 }
755
756 inline constexpr units::quantity<double, units::length_in_millimeter_tag> operator"" _mm(
757 const long double v )
758 {
759 return units::from_millimeter( v );
760 }
761
762 inline constexpr units::length operator"" _mm( const unsigned long long v )
763 {
764 return units::from_millimeter( v );
765 }
766
767 inline constexpr units::quantity<double, units::length_in_millimeter_tag> operator"" _cm(
768 const long double v )
769 {
770 return units::from_centimeter( v );
771 }
772
773 inline constexpr units::length operator"" _cm( const unsigned long long v )
774 {
775 return units::from_centimeter( v );
776 }
777
778 inline constexpr units::quantity<double, units::length_in_millimeter_tag> operator"" _meter(
779 const long double v )
780 {
781 return units::from_meter( v );
782 }
783
784 inline constexpr units::length operator"" _meter( const unsigned long long v )
785 {
786 return units::from_meter( v );
787 }
788
789 inline constexpr units::quantity<double, units::length_in_millimeter_tag> operator"" _km(
790 const long double v )
791 {
792 return units::from_kilometer( v );
793 }
794
795 inline constexpr units::length operator"" _km( const unsigned long long v )
796 {
797 return units::from_kilometer( v );
798 }
799
800 inline constexpr units::angle operator"" _radians( const long double v )
801 {
802 return units::from_radians( v );
803 }
804
805 inline constexpr units::angle operator"" _radians( const unsigned long long v )
806 {
807 return units::from_radians( v );
808 }
809
810 inline constexpr units::angle operator"" _pi_radians( const long double v )
811 {
812 return units::from_radians( v * M_PI );
813 }
814
815 inline constexpr units::angle operator"" _pi_radians( const unsigned long long v )
816 {
817 return units::from_radians( v * M_PI );
818 }
819
820 inline constexpr units::angle operator"" _degrees( const long double v )
821 {
822 return units::from_degrees( v );
823 }
824
825 inline constexpr units::angle operator"" _degrees( const unsigned long long v )
826 {
827 return units::from_degrees( v );
828 }
829
830 inline constexpr units::angle operator"" _arcmin( const long double v )
831 {
832 return units::from_arcmin( v );
833 }
834
835 inline constexpr units::angle operator"" _arcmin( const unsigned long long v )
836 {
837 return units::from_arcmin( v );
838 }
839
840 namespace units
841 {
842
sin(angle a)843 inline double sin( angle a )
844 {
845 return std::sin( to_radians( a ) );
846 }
847
cos(angle a)848 inline double cos( angle a )
849 {
850 return std::cos( to_radians( a ) );
851 }
852
tan(angle a)853 inline double tan( angle a )
854 {
855 return std::tan( to_radians( a ) );
856 }
857
atan2(double y,double x)858 inline units::angle atan2( double y, double x )
859 {
860 return from_radians( std::atan2( y, x ) );
861 }
862
863 static const std::vector<std::pair<std::string, energy>> energy_units = { {
864 { "mJ", 1_mJ },
865 { "J", 1_J },
866 { "kJ", 1_kJ },
867 }
868 };
869 static const std::vector<std::pair<std::string, mass>> mass_units = { {
870 { "mg", 1_milligram },
871 { "g", 1_gram },
872 { "kg", 1_kilogram },
873 }
874 };
875 static const std::vector<std::pair<std::string, money>> money_units = { {
876 { "cent", 1_cent },
877 { "USD", 1_USD },
878 { "kUSD", 1_kUSD },
879 }
880 };
881 static const std::vector<std::pair<std::string, volume>> volume_units = { {
882 { "ml", 1_ml },
883 { "L", 1_liter }
884 }
885 };
886 static const std::vector<std::pair<std::string, length>> length_units = { {
887 { "mm", 1_mm },
888 { "cm", 1_cm },
889 { "meter", 1_meter },
890 { "km", 1_km }
891 }
892 };
893 static const std::vector<std::pair<std::string, angle>> angle_units = { {
894 { "arcmin", 1_arcmin },
895 { "°", 1_degrees },
896 { "rad", 1_radians },
897 }
898 };
899 } // namespace units
900
901 template<typename T>
read_from_json_string(JsonIn & jsin,const std::vector<std::pair<std::string,T>> & units)902 T read_from_json_string( JsonIn &jsin, const std::vector<std::pair<std::string, T>> &units )
903 {
904 const size_t pos = jsin.tell();
905 size_t i = 0;
906 const auto error = [&]( const char *const msg ) {
907 jsin.seek( pos + i );
908 jsin.error( msg );
909 };
910
911 const std::string s = jsin.get_string();
912 // returns whether we are at the end of the string
913 const auto skip_spaces = [&]() {
914 while( i < s.size() && s[i] == ' ' ) {
915 ++i;
916 }
917 return i >= s.size();
918 };
919 const auto get_unit = [&]() {
920 if( skip_spaces() ) {
921 error( "invalid quantity string: missing unit" );
922 }
923 for( const auto &pair : units ) {
924 const std::string &unit = pair.first;
925 if( s.size() >= unit.size() + i && s.compare( i, unit.size(), unit ) == 0 ) {
926 i += unit.size();
927 return pair.second;
928 }
929 }
930 error( "invalid quantity string: unknown unit" );
931 // above always throws but lambdas cannot be marked [[noreturn]]
932 throw std::string( "Exceptionally impossible" );
933 };
934
935 if( skip_spaces() ) {
936 error( "invalid quantity string: empty string" );
937 }
938 T result{};
939 do {
940 int sign_value = +1;
941 if( s[i] == '-' ) {
942 sign_value = -1;
943 ++i;
944 } else if( s[i] == '+' ) {
945 ++i;
946 }
947 if( i >= s.size() || !isdigit( s[i] ) ) {
948 error( "invalid quantity string: number expected" );
949 }
950 int value = 0;
951 for( ; i < s.size() && isdigit( s[i] ); ++i ) {
952 value = value * 10 + ( s[i] - '0' );
953 }
954 result += sign_value * value * get_unit();
955 } while( !skip_spaces() );
956 return result;
957 }
958
959 template<typename T>
dump_to_json_string(T t,JsonOut & jsout,const std::vector<std::pair<std::string,T>> & units)960 void dump_to_json_string( T t, JsonOut &jsout,
961 const std::vector<std::pair<std::string, T>> &units )
962 {
963 // deduplicate unit strings and choose the shortest representations
964 std::map<T, std::string> sorted_units;
965 for( const auto &p : units ) {
966 const auto it = sorted_units.find( p.second );
967 if( it != sorted_units.end() ) {
968 if( p.first.length() < it->second.length() ) {
969 it->second = p.first;
970 }
971 } else {
972 sorted_units.emplace( p.second, p.first );
973 }
974 }
975 std::string str;
976 bool written = false;
977 for( auto it = sorted_units.rbegin(); it != sorted_units.rend(); ++it ) {
978 const int val = static_cast<int>( t / it->first );
979 if( val != 0 ) {
980 if( written ) {
981 str += ' ';
982 }
983 int tmp = val;
984 if( tmp < 0 ) {
985 str += '-';
986 tmp = -tmp;
987 }
988 const size_t val_beg = str.size();
989 while( tmp != 0 ) {
990 str += static_cast<char>( '0' + tmp % 10 );
991 tmp /= 10;
992 }
993 std::reverse( str.begin() + val_beg, str.end() );
994 str += ' ';
995 str += it->second;
996 written = true;
997 t -= it->first * val;
998 }
999 }
1000 if( str.empty() ) {
1001 str = "0 " + sorted_units.begin()->second;
1002 }
1003 jsout.write( str );
1004 }
1005
1006 #endif // CATA_SRC_UNITS_H
1007