164template<
typename T,
template<
typename...>
class Template>
165struct _is_specialization_of : std::false_type {};
167template<
template<
typename...>
class Template,
typename... Args>
168struct _is_specialization_of<Template<Args...>, Template> : std::true_type {};
170template<
typename T,
template<
typename...>
class Template>
171inline constexpr bool _is_specialization_of_v = _is_specialization_of<T, Template>::value;
174struct _is_ratio : std::false_type {};
176template<std::
intmax_t Num, std::
intmax_t Denom>
177struct _is_ratio<
std::ratio<Num, Denom>> : std::true_type {};
179consteval intmax_t _gcd(intmax_t m, intmax_t n)
noexcept {
181 intmax_t rem = m % n;
190constexpr T _runtime_gcd(T m, T n)
noexcept {
208struct _ratio_quotient {
215constexpr bool _product_fits(intmax_t a, intmax_t b) {
216 return a >= 0 && b >= 0 && (a == 0 || b <= std::numeric_limits<intmax_t>::max() / a);
220constexpr intmax_t _abs_ratio_term(intmax_t n) {
221 return n < 0 ? -n : n;
224template<
typename R1,
typename R2>
225inline constexpr _ratio_quotient _safe_ratio_divide = [] {
226 if constexpr (R2::num == 0) {
227 return _ratio_quotient{0, 1,
false};
231 constexpr intmax_t g1 = _gcd(_abs_ratio_term(R1::num), _abs_ratio_term(R2::num));
232 constexpr intmax_t g2 = _gcd(R1::den, R2::den);
233 constexpr intmax_t a = R1::num / g1;
234 constexpr intmax_t b = R2::den / g2;
235 constexpr intmax_t c = R1::den / g2;
236 constexpr intmax_t d = R2::num / g1;
237 if constexpr (_product_fits(_abs_ratio_term(a), b) && _product_fits(c, _abs_ratio_term(d))) {
238 constexpr intmax_t num = d < 0 ? -(a * b) : a * b;
239 constexpr intmax_t den = c * _abs_ratio_term(d);
240 constexpr intmax_t g = _gcd(_abs_ratio_term(num), den);
241 return _ratio_quotient{num / g, den / g,
true};
243 return _ratio_quotient{0, 1,
false};
250constexpr bool _difference_fits(intmax_t a, intmax_t b) {
251 constexpr intmax_t max = std::numeric_limits<intmax_t>::max();
252 return b >= 0 ? a >= -max + b : a <= max + b;
256consteval _ratio_quotient _reduced_quotient(intmax_t a, intmax_t b) {
257 const intmax_t g = _gcd(_abs_ratio_term(a), _abs_ratio_term(b));
258 const intmax_t sign = (a < 0) != (b < 0) ? -1 : 1;
259 return {sign * (_abs_ratio_term(a) / g), _abs_ratio_term(b) / g,
true};
264consteval intmax_t _cancelling_gcd(intmax_t m, intmax_t n) {
265 const intmax_t g = _gcd(_abs_ratio_term(m), _abs_ratio_term(n));
266 return g == 0 ? 1 : g;
273template<
typename R1,
typename R2>
274inline constexpr _ratio_quotient _safe_ratio_subtract = [] {
275 constexpr intmax_t gn = _cancelling_gcd(R1::num, R2::num);
276 constexpr intmax_t gd = _gcd(R1::den, R2::den);
277 constexpr intmax_t x1 = R1::num / gn;
278 constexpr intmax_t y1 = R2::den / gd;
279 constexpr intmax_t x2 = R2::num / gn;
280 constexpr intmax_t y2 = R1::den / gd;
281 if constexpr (!_product_fits(_abs_ratio_term(x1), y1) || !_product_fits(_abs_ratio_term(x2), y2)) {
282 return _ratio_quotient{0, 1,
false};
283 }
else if constexpr (!_difference_fits(x1 * y1, x2 * y2)) {
284 return _ratio_quotient{0, 1,
false};
287 constexpr _ratio_quotient a = _reduced_quotient(gn, R1::den / gd);
288 constexpr _ratio_quotient b = _reduced_quotient(x1 * y1 - x2 * y2, R2::den);
289 constexpr intmax_t g1 = _cancelling_gcd(a.num, b.den);
290 constexpr intmax_t g2 = _cancelling_gcd(a.den, b.num);
291 constexpr intmax_t n1 = a.num / g1;
292 constexpr intmax_t n2 = b.num / g2;
293 constexpr intmax_t d1 = b.den / g1;
294 constexpr intmax_t d2 = a.den / g2;
295 if constexpr (!_product_fits(_abs_ratio_term(n1), _abs_ratio_term(n2)) || !_product_fits(d1, d2)) {
296 return _ratio_quotient{0, 1,
false};
298 return _reduced_quotient(n1 * n2, d1 * d2);
305template<
typename From,
typename To>
306concept _harmonic_precision = _safe_ratio_divide<From, To>.fits && _safe_ratio_divide<From, To>.den == 1;
313template<
typename FromRep,
typename FromPrecision,
typename ToRep,
typename ToPrecision>
314concept _lossless_conversion =
319 while (
n >= 10 &&
n % 10 == 0) {
334template<
typename Precision>
338constexpr double _pow10(
int k) {
340 for (
int i = 0;
i < (
k < 0 ? -
k :
k); ++
i) {
355template<std::
floating_po
int T>
357 std::string
s(32,
'\0');
359 const auto result = std::to_chars(
s.data(),
s.data() +
s.size(),
v, std::chars_format::fixed);
360 if (
result.ec == std::errc()) {
361 s.resize(
static_cast<std::size_t
>(
result.ptr -
s.data()));
364 s.resize(
s.size() * 2);
370template<
typename Rep>
372 if constexpr (std::floating_point<Rep>) {
375 return std::to_string(
r);
379#if CONFIG_QUANTA_STD_FORMAT
381template<
typename OutputIt>
383 for (; *
s !=
'\0'; ++
s) {
398#if defined(__cpp_exceptions) && __cpp_exceptions
399 throw std::format_error(
what);
411#ifdef __SIZEOF_INT128__
423template<
typename Common,
typename Fallback = Common>
425#ifdef __SIZEOF_INT128__
435template<
typename Rep1,
typename Rep2>
437 std::is_unsigned_v<Rep1> && std::is_unsigned_v<Rep2>,
447 unsigned long long value;
452template<
char...
Chars>
455 constexpr std::size_t
n =
sizeof...(Chars);
461 }
else if (
n > 2 &&
chars[0] ==
'0' && (
chars[1] ==
'b' ||
chars[1] ==
'B')) {
464 }
else if (
n > 1 &&
chars[0] ==
'0') {
474 }
else if (
c >=
'0' &&
c <=
'9') {
475 digit =
static_cast<unsigned>(
c -
'0');
476 }
else if (
c >=
'a' &&
c <=
'f') {
477 digit =
static_cast<unsigned>(
c -
'a' + 10);
478 }
else if (
c >=
'A' &&
c <=
'F') {
479 digit =
static_cast<unsigned>(
c -
'A' + 10);
487 if (
result.value > (std::numeric_limits<unsigned long long>::max() -
digit) /
base) {
498 static_assert(
parsed.valid,
"invalid digit");
499 static_assert(!
parsed.overflow,
"literal value cannot be represented by unsigned long long");
503 "literal value cannot be represented by the quantity type"
511template<
typename Ratio,
typename Rep>
513 double v =
static_cast<double>(count);
514 if constexpr (Ratio::num != 1) {
515 v *=
static_cast<double>(Ratio::num);
517 if constexpr (Ratio::den != 1) {
518 v /=
static_cast<double>(Ratio::den);
525template<
typename Precision>
560template<std::
size_t N>
562 for (std::size_t
i =
N;
i-- > 1;) {
564 if (
v >= 1.0 ||
v <= -1.0) {
575template<
typename Precision1,
typename Precision2>
577 _gcd(Precision1::num, Precision2::num),
578 (Precision1::den /
_gcd(Precision1::den, Precision2::den)) * Precision2::den>;
585template<
typename FromRep,
typename ToRep,
typename Factor,
typename Common>
587 (Factor::num == 1 && Factor::den == 1 &&
requires(
FromRep (&count)()) {
static_cast<ToRep>(count()); }) ||
588 (Factor::num != 1 && Factor::den == 1 &&
requires(
FromRep (&count)()) {
589 static_cast<ToRep>(
static_cast<Common>(count()) *
static_cast<Common>(Factor::num));
590 }) || (Factor::num == 1 && Factor::den != 1 &&
requires(
FromRep (&count)()) {
591 static_cast<ToRep>(
static_cast<Common>(count()) /
static_cast<Common>(Factor::den));
592 }) || (Factor::num != 1 && Factor::den != 1 &&
requires(
FromRep (&count)()) {
594 static_cast<Common>(count()) *
static_cast<Common>(Factor::num) /
static_cast<Common>(Factor::den)
598template<
typename FromRep,
typename ToRep,
typename Factor>
599concept _cast_through_common =
requires {
typename std::common_type<ToRep, FromRep, intmax_t>::type; } &&
612template<
typename From,
typename To>
615 ((std::is_integral_v<typename From::rep> && std::is_integral_v<typename To::rep>) ||
628template<
typename To,
typename From>
631 if constexpr (std::is_same_v<To, From>) {
634 using rep =
typename From::rep;
635 using to_rep =
typename To::rep;
636 using cf = std::ratio_divide<typename From::precision, typename To::precision>;
639 if constexpr (std::is_integral_v<rep> && std::is_integral_v<to_rep>) {
642 if constexpr (cf::den == 1 && cf::num == 1) {
643 return To(
static_cast<to_rep>(
q.count()));
644 }
else if constexpr (cf::den == 1) {
647 }
else if constexpr (cf::num == 1) {
663 using cr = std::common_type_t<to_rep, rep, intmax_t>;
664 if constexpr (cf::den == 1 && cf::num == 1) {
665 return To(
static_cast<to_rep>(
q.count()));
666 }
else if constexpr (cf::den == 1) {
667 return To(
static_cast<to_rep>(
static_cast<cr>(
q.count()) *
static_cast<cr>(cf::num)));
668 }
else if constexpr (cf::num == 1) {
669 return To(
static_cast<to_rep>(
static_cast<cr>(
q.count()) /
static_cast<cr>(cf::den)));
673 static_cast<cr>(
q.count()) *
static_cast<cr>(cf::num) /
static_cast<cr>(cf::den)
684template<
typename From,
typename To>
698template<
typename Q1,
typename Q2>
700 using rep1 =
typename Q1::rep;
701 using rep2 =
typename Q2::rep;
703 if constexpr (std::is_integral_v<rep1> && std::is_integral_v<rep2>) {
706 using scale1 = std::ratio_divide<typename Q1::precision, cp>;
707 using scale2 = std::ratio_divide<typename Q2::precision, cp>;
708 static_assert(scale1::den == 1 && scale2::den == 1);
710 const wide x =
static_cast<wide>(
a.count()) *
static_cast<wide>(scale1::num);
711 const wide y =
static_cast<wide>(
b.count()) *
static_cast<wide>(scale2::num);
712 return x <
y ? -1 :
x >
y ? 1 : 0;
715 return c < 0 ? -1 :
c > 0 ? 1 : 0;
721template<
typename To,
typename From>
723 using to_rep =
typename To::rep;
725 if constexpr (std::is_integral_v<to_rep>) {
736template<
typename To,
typename From>
738 using to_rep =
typename To::rep;
740 if constexpr (std::is_integral_v<to_rep>) {
752template<
typename To,
typename From,
typename Floor>
754 using to_rep =
typename To::rep;
755 using from_rep =
typename From::rep;
757 if constexpr (std::is_integral_v<to_rep> && std::is_integral_v<from_rep>) {
766 using from_scale = std::ratio_divide<typename From::precision, cp>;
767 using to_scale = std::ratio_divide<typename To::precision, cp>;
768 static_assert(from_scale::den == 1 && to_scale::den == 1);
770 constexpr bool is_signed = !(std::is_unsigned_v<from_rep> && std::is_unsigned_v<to_rep>);
771 const wide scale =
static_cast<wide>(from_scale::num);
775 const wide count =
static_cast<wide>(
q.count());
794 }
else if constexpr (std::is_integral_v<to_rep>) {
818 return q >= Q::zero() ?
q :
Q(-
q);
821#if CONFIG_QUANTA_STD_FORMAT
831 auto it =
ctx.begin();
832 if (
it ==
ctx.end()) {
833 return _unit_form::own;
835 for (
const char*
m =
markers; *
m !=
'\0'; ++
m) {
837 ctx.advance_to(++
it);
838 return *
m ==
'p' ? _unit_form::power : *
m ==
'b' ? _unit_form::base : _unit_form::scaled;
841 return _unit_form::own;
860template<
typename Traits>
863 std::formatter<double> _num;
864 bool _has_spec =
false;
865 _unit_form _form = _unit_form::own;
868 constexpr auto parse(std::format_parse_context& ctx) {
869 _form = _parse_unit_marker(ctx, Traits::markers);
870 auto it = ctx.begin();
871 if (it == ctx.end() || *it ==
'}') {
872 if constexpr (!Traits::has_own_unit) {
873 if (_form == _unit_form::own) {
874 Traits::no_own_unit_error();
880 return _num.parse(ctx);
883 template<
typename Q,
typename FormatContext>
884 auto format_quantity(
const Q& q, FormatContext& ctx)
const {
885 if constexpr (Traits::has_own_unit) {
886 if (_form == _unit_form::own || _form == _unit_form::power) {
887 const auto unit = Traits::own_unit(_form == _unit_form::power);
888 auto out = ctx.out();
890 out = _num.format(
static_cast<double>(q.count()), ctx);
891 }
else if constexpr (std::floating_point<typename Q::rep>) {
892 out = _format_append(out, _fixed_string(q.count()).c_str());
894 out = std::format_to(out,
"{}", q.count());
896 return Traits::append_own_unit(out, unit);
900 const auto d = _form == _unit_form::scaled ? Traits::scaled_display(q) : Traits::base_display(q);
901 auto out = _has_spec ? _num.format(d.value, ctx) : _format_append(ctx.out(), _fixed_string(d.value).c_str());
902 return Traits::append_label(out, d);