frequency 1.0.0
Type-safe frequency handling library modeled after std::chrono
Loading...
Searching...
No Matches
frequency.hpp
Go to the documentation of this file.
1#pragma once
2
8#include <cmath>
9#include <compare>
10#include <concepts>
11#include <cstdint>
12#include <limits>
13#include <ratio>
14#include <string>
15#ifndef CONFIG_FREQUENCY_STD_FORMAT
16#if __has_include(<format>) && defined(__cpp_lib_format)
17#define CONFIG_FREQUENCY_STD_FORMAT 1
18#else
19#define CONFIG_FREQUENCY_STD_FORMAT 0
20#endif
21#endif
22
23#if CONFIG_FREQUENCY_STD_FORMAT
24#include <format>
25#endif
26#include <assert.h>
27
88namespace freq {
89
90template<typename Rep, typename Precision = std::ratio<1>>
91class frequency;
92
97template<typename T>
98struct is_frequency : std::false_type {};
99
100template<typename Rep, typename Precision>
101struct is_frequency<frequency<Rep, Precision>> : std::true_type {};
102
103template<typename T>
105
107template<typename T, template<typename...> class Template>
108struct _is_specialization_of : std::false_type {};
109
110template<template<typename...> class Template, typename... Args>
111struct _is_specialization_of<Template<Args...>, Template> : std::true_type {};
112
113template<typename T, template<typename...> class Template>
114inline constexpr bool _is_specialization_of_v = _is_specialization_of<T, Template>::value;
115
116template<typename Rep>
117concept not_frequency = !_is_specialization_of_v<Rep, frequency>;
118
127template<typename T>
128concept duration_like = requires(T t) {
129 { t.count() };
130 typename T::period;
131 typename T::rep;
132};
133
143template<typename T>
144concept distance_like = requires(T t) {
145 { t.count() };
146 typename T::period;
147 typename T::rep;
148};
149
154template<typename Rep>
155struct frequency_values {
157 static constexpr Rep zero() noexcept { return Rep(0); }
158
160 static constexpr Rep max() noexcept { return std::numeric_limits<Rep>::max(); }
161
163 static constexpr Rep min() noexcept { return std::numeric_limits<Rep>::lowest(); }
164};
165
166template<typename T>
167struct _is_ratio : std::false_type {};
168
169template<std::intmax_t Num, std::intmax_t Denom>
170struct _is_ratio<std::ratio<Num, Denom>> : std::true_type {};
171
193template<typename T>
194struct treat_as_inexact : std::bool_constant<std::floating_point<T>> {};
195
196template<typename T>
197inline constexpr bool treat_as_inexact_v = treat_as_inexact<T>::value;
198
199consteval intmax_t _gcd(intmax_t m, intmax_t n) noexcept {
200 while (n != 0) {
201 intmax_t rem = m % n;
202 m = n;
203 n = rem;
204 }
205 return m;
206}
207
208// Runtime GCD for integer types (using Euclidean algorithm)
209template<typename T>
210constexpr T _runtime_gcd(T m, T n) noexcept {
211 if (m < 0) {
212 m = -m;
213 }
214 if (n < 0) {
215 n = -n;
216 }
217 while (n != 0) {
218 T rem = m % n;
219 m = n;
220 n = rem;
221 }
222 return m;
223}
224
225template<typename R1, typename R2>
226inline constexpr intmax_t _safe_ratio_divide_den = [] {
227 constexpr intmax_t g1 = _gcd(R1::num, R2::num);
228 constexpr intmax_t g2 = _gcd(R1::den, R2::den);
229 return (R1::den / g2) * (R2::num / g1);
230}();
231
232template<typename From, typename To>
233concept _harmonic_precision = _safe_ratio_divide_den<From, To> == 1;
280template<typename Rep, typename Precision>
282 static_assert(!is_frequency<Rep>::value, "rep cannot be a frequency::frequency");
283 static_assert(_is_ratio<Precision>::value, "precision must be a specialization of std::ratio");
284 static_assert(Precision::num > 0, "precision must be positive");
285
286public:
288 using rep = Rep;
290 using precision = typename Precision::type;
291
293 constexpr frequency() = default;
294 frequency(const frequency&) = default;
295
305 template<typename Rep2>
306 requires std::convertible_to<const Rep2&, rep> && (treat_as_inexact_v<rep> || !treat_as_inexact_v<Rep2>)
307 constexpr explicit frequency(const Rep2& r)
308 : _r(static_cast<rep>(r)) {}
309
320 template<typename Rep2, typename Precision2>
321 requires std::convertible_to<const Rep2&, rep> &&
322 (treat_as_inexact_v<rep> || (_harmonic_precision<Precision2, precision> && !treat_as_inexact_v<Rep2>))
323 constexpr frequency(const frequency<Rep2, Precision2>& f)
324 : _r(frequency_cast<frequency>(f).count()) {}
325
333 template<typename Rep2, typename Precision2>
334 requires(!std::is_same_v<frequency, frequency<Rep2, Precision2>>) && (!treat_as_inexact_v<rep>) &&
335 (!_harmonic_precision<Precision2, precision>)
336 constexpr explicit frequency(const frequency<Rep2, Precision2>& f)
338
339 ~frequency() = default;
340 frequency& operator=(const frequency&) = default;
341
343 constexpr rep count() const { return _r; }
344
348
352
353 constexpr frequency& operator++() {
354 ++_r;
355 return *this;
356 }
357
358 constexpr frequency operator++(int) { return frequency(_r++); }
359
360 constexpr frequency& operator--() {
361 --_r;
362 return *this;
363 }
364
365 constexpr frequency operator--(int) { return frequency(_r--); }
366
367 constexpr frequency& operator+=(const frequency& f) {
368 _r += f.count();
369 return *this;
370 }
371
372 constexpr frequency& operator-=(const frequency& f) {
373 _r -= f.count();
374 return *this;
375 }
376
377 constexpr frequency& operator*=(const rep& r) {
378 _r *= r;
379 return *this;
380 }
381
382 constexpr frequency& operator/=(const rep& r) {
383 _r /= r;
384 return *this;
385 }
386
387 constexpr frequency& operator%=(const rep& r)
388 requires(!treat_as_inexact_v<rep>)
389 {
390 _r %= r;
391 return *this;
392 }
393
394 constexpr frequency& operator%=(const frequency& f)
395 requires(!treat_as_inexact_v<rep>)
396 {
397 _r %= f.count();
398 return *this;
399 }
400
402 static constexpr frequency zero() noexcept { return frequency(frequency_values<rep>::zero()); }
403
405 static constexpr frequency min() noexcept { return frequency(frequency_values<rep>::min()); }
406
408 static constexpr frequency max() noexcept { return frequency(frequency_values<rep>::max()); }
409
441 template<duration_like Duration>
442 constexpr Duration period() const {
443 // Period ratio is the inverse of frequency precision
444 // For frequency<Rep, ratio<N,D>>, the period of 1 tick is ratio<D,N> seconds
445 using period_ratio = std::ratio_divide<std::ratio<1>, precision>;
446 using duration_period = typename Duration::period;
447 using cf = std::ratio_divide<period_ratio, duration_period>;
448 using duration_rep = typename Duration::rep;
449
450 if (_r == rep(0)) {
451 return Duration::max();
452 }
453
454 // Integer-only path when both types are integral
455 if constexpr (std::is_integral_v<rep> && std::is_integral_v<duration_rep>) {
456#ifdef __SIZEOF_INT128__
457 using cr = std::common_type_t<duration_rep, rep, intmax_t>;
458 using wider_t = std::conditional_t<std::is_signed_v<cr>, __int128, unsigned __int128>;
459#else
460 using wider_t = intmax_t;
461#endif
462
463 wider_t count = static_cast<wider_t>(_r);
464
465 if constexpr (cf::den == 1 && cf::num == 1) {
466 // period_ticks = 1 / freq_count (in the target duration units)
467 return Duration(static_cast<duration_rep>(1 / count));
468 } else if constexpr (cf::den == 1) {
469 // period_ticks = num / freq_count
470 // Use GCD: g = gcd(num, count), then (num/g) / (count/g)
471 wider_t num = static_cast<wider_t>(cf::num);
475 return Duration(static_cast<duration_rep>(reduced_num / reduced_count));
476 } else if constexpr (cf::num == 1) {
477 // period_ticks = 1 / (freq_count * den)
478 wider_t denom = count * static_cast<wider_t>(cf::den);
479 return Duration(static_cast<duration_rep>(1 / denom));
480 } else {
481 // period_ticks = num / (freq_count * den)
482 // Use GCD: g = gcd(num, count), then (num/g) / ((count/g) * den)
483 wider_t num = static_cast<wider_t>(cf::num);
487 wider_t denom = reduced_count * static_cast<wider_t>(cf::den);
488 return Duration(static_cast<duration_rep>(reduced_num / denom));
489 }
490 } else {
491 // Floating-point path
492 using cr = std::common_type_t<duration_rep, double>;
493
494 if constexpr (cf::den == 1 && cf::num == 1) {
495 return Duration(static_cast<duration_rep>(1.0 / static_cast<double>(_r)));
496 } else if constexpr (cf::den == 1) {
497 return Duration(static_cast<duration_rep>(static_cast<cr>(cf::num) / static_cast<double>(_r)));
498 } else if constexpr (cf::num == 1) {
499 return Duration(static_cast<duration_rep>(1.0 / (static_cast<double>(_r) * static_cast<cr>(cf::den))));
500 } else {
501 return Duration(
502 static_cast<duration_rep>(
503 static_cast<cr>(cf::num) / (static_cast<double>(_r) * static_cast<cr>(cf::den))
504 )
505 );
506 }
507 }
508 }
509
522 constexpr frequency harmonic(unsigned int n) const { return *this * n; }
523
536 constexpr frequency subharmonic(unsigned int n) const {
537 assert(n > 0 && "subharmonic divisor must be positive");
538 return *this / n;
539 }
540
564 template<typename T = double>
566 double multiplier = std::pow(2.0, static_cast<double>(octaves));
567 if constexpr (std::is_integral_v<rep>) {
568 return frequency(static_cast<rep>(std::round(static_cast<double>(_r) * multiplier)));
569 } else {
570 return frequency(static_cast<rep>(static_cast<double>(_r) * multiplier));
571 }
572 }
573
598 template<typename T = double>
600 double multiplier = std::pow(2.0, static_cast<double>(semitones) / 12.0);
601 if constexpr (std::is_integral_v<rep>) {
602 return frequency(static_cast<rep>(std::round(static_cast<double>(_r) * multiplier)));
603 } else {
604 return frequency(static_cast<rep>(static_cast<double>(_r) * multiplier));
605 }
606 }
607
619 template<typename T = double>
621 return static_cast<T>(std::log2(static_cast<double>(_r) / static_cast<double>(other._r)));
622 }
623
635 template<typename T = double>
637 return static_cast<T>(12.0 * std::log2(static_cast<double>(_r) / static_cast<double>(other._r)));
638 }
639
657 template<distance_like Distance, duration_like Duration>
659 // Get the period of this frequency
660 using period_rep = double;
661 using period_ratio = std::ratio_divide<std::ratio<1>, precision>;
662
663 if (_r == rep(0)) {
664 return Distance::max();
665 }
666
667 // Calculate period in our internal representation (1/frequency)
668 period_rep wave_period = 1.0 / static_cast<double>(_r);
669
670 // Convert to common time representation
671 using duration_period = typename Duration::period;
672 using time_cf = std::ratio_divide<period_ratio, duration_period>;
673 using time_cr = std::common_type_t<period_rep, typename Duration::rep, double>;
674
675 // Calculate period in time_per_unit_distance units
677 if constexpr (time_cf::den == 1 && time_cf::num == 1) {
679 } else if constexpr (time_cf::den == 1) {
680 period_in_duration_units = static_cast<time_cr>(wave_period) * static_cast<time_cr>(time_cf::num);
681 } else if constexpr (time_cf::num == 1) {
682 period_in_duration_units = static_cast<time_cr>(wave_period) / static_cast<time_cr>(time_cf::den);
683 } else {
684 period_in_duration_units = static_cast<time_cr>(wave_period) * static_cast<time_cr>(time_cf::num) /
685 static_cast<time_cr>(time_cf::den);
686 }
687
688 // Wavelength = period / time_per_unit_distance
689 double wavelength_count = period_in_duration_units / static_cast<double>(time_per_unit_distance.count());
690
691 return Distance(static_cast<typename Distance::rep>(wavelength_count));
692 }
693
709 template<distance_like Distance>
710 constexpr Distance wavelength(double velocity = 299792458.0) const {
711 if (_r == rep(0)) {
712 return Distance::max();
713 }
714
715 // Convert frequency to Hz
716 double freq_hz =
717 static_cast<double>(_r) * static_cast<double>(precision::num) / static_cast<double>(precision::den);
718
719 // Calculate wavelength in meters: wavelength = velocity / frequency
721
722 // Convert from meters to the target distance type's units
723 // Distance::period represents the ratio of the distance unit to meters
724 using distance_period = typename Distance::period;
725 double wavelength_in_units =
726 wavelength_meters * static_cast<double>(distance_period::den) / static_cast<double>(distance_period::num);
727
728 return Distance(static_cast<typename Distance::rep>(wavelength_in_units));
729 }
730
731private:
732 rep _r{};
733};
734
748template<typename ToFreq, typename Rep, typename Precision>
750 if constexpr (std::is_same_v<ToFreq, frequency<Rep, Precision>>) {
751 return f;
752 } else {
753 using to_rep = typename ToFreq::rep;
754 using to_precision = typename ToFreq::precision;
755 using cf = std::ratio_divide<Precision, to_precision>;
756
757 // Use wider intermediate type for integer-to-integer conversions
758 if constexpr (std::is_integral_v<Rep> && std::is_integral_v<to_rep>) {
759#ifdef __SIZEOF_INT128__
760 using cr = std::common_type_t<to_rep, Rep, intmax_t>;
761 using wider_t = std::conditional_t<std::is_signed_v<cr>, __int128, unsigned __int128>;
762#else
763 using wider_t = intmax_t;
764#endif
765
766 if constexpr (cf::den == 1 && cf::num == 1) {
767 return ToFreq(static_cast<to_rep>(f.count()));
768 } else if constexpr (cf::den == 1) {
769 wider_t result = static_cast<wider_t>(f.count()) * static_cast<wider_t>(cf::num);
770 return ToFreq(static_cast<to_rep>(result));
771 } else if constexpr (cf::num == 1) {
772 wider_t result = static_cast<wider_t>(f.count()) / static_cast<wider_t>(cf::den);
773 return ToFreq(static_cast<to_rep>(result));
774 } else {
775 // Use GCD to reduce operands: count * num / den
776 // Compute g = gcd(count, den), then (count/g) * num / (den/g)
777 wider_t count = static_cast<wider_t>(f.count());
778 wider_t den = static_cast<wider_t>(cf::den);
779 wider_t g = _runtime_gcd(count, den);
780 wider_t reduced_count = count / g;
782 wider_t result = reduced_count * static_cast<wider_t>(cf::num) / reduced_den;
783 return ToFreq(static_cast<to_rep>(result));
784 }
785 } else {
786 // Floating-point path
787 using cr = std::common_type_t<to_rep, Rep, intmax_t>;
788 if constexpr (cf::den == 1 && cf::num == 1) {
789 return ToFreq(static_cast<to_rep>(f.count()));
790 } else if constexpr (cf::den == 1) {
791 return ToFreq(static_cast<to_rep>(static_cast<cr>(f.count()) * static_cast<cr>(cf::num)));
792 } else if constexpr (cf::num == 1) {
793 return ToFreq(static_cast<to_rep>(static_cast<cr>(f.count()) / static_cast<cr>(cf::den)));
794 } else {
795 return ToFreq(
796 static_cast<to_rep>(
797 static_cast<cr>(f.count()) * static_cast<cr>(cf::num) / static_cast<cr>(cf::den)
798 )
799 );
800 }
801 }
802 }
803}
804
824template<typename ToFreq, typename Rep, typename Precision>
826 using to_rep = typename ToFreq::rep;
828
829 if constexpr (std::is_integral_v<Rep> && std::is_integral_v<to_rep>) {
830 if (result > f) {
831 return ToFreq(result.count() - to_rep(1));
832 }
833 }
834
835 return result;
836}
837
857template<typename ToFreq, typename Rep, typename Precision>
859 using to_rep = typename ToFreq::rep;
861
862 if constexpr (std::is_integral_v<Rep> && std::is_integral_v<to_rep>) {
863 if (result < f) {
864 return ToFreq(result.count() + to_rep(1));
865 }
866 }
867
868 return result;
869}
870
893template<typename ToFreq, typename Rep, typename Precision>
895 using to_rep = typename ToFreq::rep;
896
897 if constexpr (std::is_integral_v<Rep> && std::is_integral_v<to_rep>) {
901
902 auto diff_lower = f - lower;
903 auto diff_upper = upper - f;
904
905 if (diff_lower < diff_upper) {
906 return lower;
907 } else if (diff_lower > diff_upper) {
908 return upper;
909 } else {
910 // Tie: round to even
911 return (lower.count() % to_rep(2) == to_rep(0)) ? lower : upper;
912 }
913 } else {
915 }
916}
917
953template<typename Rep1, typename Precision1, typename Rep2, typename Precision2>
955 -> std::common_type_t<frequency<Rep1, Precision1>, frequency<Rep2, Precision2>> {
956 using cf = std::common_type_t<frequency<Rep1, Precision1>, frequency<Rep2, Precision2>>;
957 return abs(cf(f1) - cf(f2));
958}
959
975template<typename Rep, typename Precision>
979
981template<typename Rep1, typename Precision1, typename Rep2, typename Precision2>
983 -> std::common_type_t<frequency<Rep1, Precision1>, frequency<Rep2, Precision2>> {
984 using cf = std::common_type_t<frequency<Rep1, Precision1>, frequency<Rep2, Precision2>>;
985 return cf(cf(lhs).count() + cf(rhs).count());
986}
987
989template<typename Rep1, typename Precision1, typename Rep2, typename Precision2>
991 -> std::common_type_t<frequency<Rep1, Precision1>, frequency<Rep2, Precision2>> {
992 using cf = std::common_type_t<frequency<Rep1, Precision1>, frequency<Rep2, Precision2>>;
993 return cf(cf(lhs).count() - cf(rhs).count());
994}
995
997template<typename Rep1, typename Precision, typename Rep2>
998 requires not_frequency<Rep2> && std::convertible_to<const Rep2&, std::common_type_t<Rep1, Rep2>>
999constexpr auto operator*(const frequency<Rep1, Precision>& f, const Rep2& r)
1002 return cf(cf(f).count() * r);
1003}
1004
1006template<typename Rep1, typename Rep2, typename Precision>
1007 requires not_frequency<Rep1> && std::convertible_to<const Rep1&, std::common_type_t<Rep1, Rep2>>
1008constexpr auto operator*(const Rep1& r, const frequency<Rep2, Precision>& f)
1010 return f * r;
1011}
1012
1014template<typename Rep1, typename Precision, typename Rep2>
1015 requires not_frequency<Rep2> && std::convertible_to<const Rep2&, std::common_type_t<Rep1, Rep2>>
1016constexpr auto operator/(const frequency<Rep1, Precision>& f, const Rep2& s)
1019 return cf(cf(f).count() / s);
1020}
1021
1023template<typename Rep1, typename Precision1, typename Rep2, typename Precision2>
1025 -> std::common_type_t<Rep1, Rep2> {
1026 using cf = std::common_type_t<frequency<Rep1, Precision1>, frequency<Rep2, Precision2>>;
1027 return cf(lhs).count() / cf(rhs).count();
1028}
1029
1031template<typename Rep1, typename Precision, typename Rep2>
1032 requires not_frequency<Rep2> && std::convertible_to<const Rep2&, std::common_type_t<Rep1, Rep2>> &&
1034constexpr auto operator%(const frequency<Rep1, Precision>& f, const Rep2& s)
1037 return cf(cf(f).count() % s);
1038}
1039
1041template<typename Rep1, typename Precision1, typename Rep2, typename Precision2>
1043constexpr auto operator%(const frequency<Rep1, Precision1>& lhs, const frequency<Rep2, Precision2>& rhs)
1044 -> std::common_type_t<frequency<Rep1, Precision1>, frequency<Rep2, Precision2>> {
1045 using cf = std::common_type_t<frequency<Rep1, Precision1>, frequency<Rep2, Precision2>>;
1046 return cf(cf(lhs).count() % cf(rhs).count());
1047}
1048
1049template<typename Rep1, typename Precision1, typename Rep2, typename Precision2>
1051 using ct = std::common_type_t<frequency<Rep1, Precision1>, frequency<Rep2, Precision2>>;
1052 return ct(lhs).count() == ct(rhs).count();
1053}
1054
1055template<typename Rep1, typename Precision1, typename Rep2, typename Precision2>
1056 requires std::three_way_comparable<std::common_type_t<Rep1, Rep2>>
1058 using ct = std::common_type_t<frequency<Rep1, Precision1>, frequency<Rep2, Precision2>>;
1059 return ct(lhs).count() <=> ct(rhs).count();
1060}
1061
1099
// end of FrequencyTypes group
1101
1102inline std::string to_string(millihertz f) {
1103 return std::to_string(f.count()) + "mHz";
1104}
1105
1106inline std::string to_string(hertz f) {
1107 return std::to_string(f.count()) + "Hz";
1108}
1109
1110inline std::string to_string(kilohertz f) {
1111 return std::to_string(f.count()) + "kHz";
1112}
1113
1114inline std::string to_string(megahertz f) {
1115 return std::to_string(f.count()) + "MHz";
1116}
1117
1118inline std::string to_string(gigahertz f) {
1119 return std::to_string(f.count()) + "GHz";
1120}
1121
1122inline std::string to_string(terahertz f) {
1123 return std::to_string(f.count()) + "THz";
1124}
1125
1126} // namespace freq
1127
1128namespace std {
1129
1130template<typename Rep1, typename Precision1, typename Rep2, typename Precision2>
1131struct common_type<freq::frequency<Rep1, Precision1>, freq::frequency<Rep2, Precision2>> {
1132private:
1133 using common_precision = std::ratio<
1134 freq::_gcd(Precision1::num, Precision2::num),
1135 (Precision1::den / freq::_gcd(Precision1::den, Precision2::den)) * Precision2::den>;
1136
1137public:
1138 using type = freq::frequency<std::common_type_t<Rep1, Rep2>, common_precision>;
1139};
1140
1141#if CONFIG_FREQUENCY_STD_FORMAT
1142template<>
1143struct formatter<freq::millihertz> {
1144 constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
1145
1146 auto format(freq::millihertz f, format_context& ctx) const { return std::format_to(ctx.out(), "{}mHz", f.count()); }
1147};
1148
1149template<>
1150struct formatter<freq::hertz> {
1151 constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
1152
1153 auto format(freq::hertz f, format_context& ctx) const { return std::format_to(ctx.out(), "{}Hz", f.count()); }
1154};
1155
1156template<>
1157struct formatter<freq::kilohertz> {
1158 constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
1159
1160 auto format(freq::kilohertz f, format_context& ctx) const { return std::format_to(ctx.out(), "{}kHz", f.count()); }
1161};
1162
1163template<>
1164struct formatter<freq::megahertz> {
1165 constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
1166
1167 auto format(freq::megahertz f, format_context& ctx) const { return std::format_to(ctx.out(), "{}MHz", f.count()); }
1168};
1169
1170template<>
1171struct formatter<freq::gigahertz> {
1172 constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
1173
1174 auto format(freq::gigahertz f, format_context& ctx) const { return std::format_to(ctx.out(), "{}GHz", f.count()); }
1175};
1176
1177template<>
1178struct formatter<freq::terahertz> {
1179 constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
1180
1181 auto format(freq::terahertz f, format_context& ctx) const { return std::format_to(ctx.out(), "{}THz", f.count()); }
1182};
1183#endif
1184
1185} // namespace std
1186
1192namespace detail {
1193
1194template<unsigned long long Value, unsigned long long Power>
1195struct pow10 {
1196 static constexpr unsigned long long value = 10 * pow10<Value, Power - 1>::value;
1197};
1198
1199template<unsigned long long Value>
1200struct pow10<Value, 0> {
1201 static constexpr unsigned long long value = Value;
1202};
1203
1204template<char... Digits>
1205struct parse_int;
1206
1207template<char D, char... Rest>
1208struct parse_int<D, Rest...> {
1209 static_assert(D >= '0' && D <= '9', "invalid digit");
1210 static constexpr unsigned long long value = pow10<D - '0', sizeof...(Rest)>::value + parse_int<Rest...>::value;
1211};
1212
1213template<char D>
1214struct parse_int<D> {
1215 static_assert(D >= '0' && D <= '9', "invalid digit");
1216 static constexpr unsigned long long value = D - '0';
1217};
1218
1219template<typename Freq, char... Digits>
1220constexpr Freq check_overflow() {
1221 using parsed = parse_int<Digits...>;
1222 constexpr typename Freq::rep repval = parsed::value;
1223 static_assert(
1224 repval >= 0 && static_cast<unsigned long long>(repval) == parsed::value,
1225 "literal value cannot be represented by frequency type"
1226 );
1227 return Freq(repval);
1228}
1229
1230} // namespace detail
1234template<char... Digits>
1235constexpr freq::millihertz operator""_mHz() {
1236 return detail::check_overflow<freq::millihertz, Digits...>();
1237}
1238
1240template<char... Digits>
1241constexpr freq::hertz operator""_Hz() {
1242 return detail::check_overflow<freq::hertz, Digits...>();
1243}
1244
1246template<char... Digits>
1247constexpr freq::kilohertz operator""_kHz() {
1248 return detail::check_overflow<freq::kilohertz, Digits...>();
1249}
1250
1252template<char... Digits>
1253constexpr freq::megahertz operator""_MHz() {
1254 return detail::check_overflow<freq::megahertz, Digits...>();
1255}
1256
1258template<char... Digits>
1259constexpr freq::gigahertz operator""_GHz() {
1260 return detail::check_overflow<freq::gigahertz, Digits...>();
1261}
1262
1264template<char... Digits>
1265constexpr freq::terahertz operator""_THz() {
1266 return detail::check_overflow<freq::terahertz, Digits...>();
1267}
1268
1269} // namespace frequency_literals
A frequency value with a representation and precision.
constexpr Duration period() const
Returns the period of this frequency as a duration.
frequency semitone_shift(T semitones) const
Returns this frequency shifted by a number of semitones.
constexpr frequency & operator++()
static constexpr frequency max() noexcept
Returns the maximum representable frequency.
constexpr frequency & operator%=(const rep &r)
constexpr frequency(const Rep2 &r)
Constructs from a tick count.
constexpr frequency< typename std::common_type< rep >::type, precision > operator-() const
constexpr frequency(const frequency< Rep2, Precision2 > &f)
Explicit constructor for lossy precision conversions.
constexpr frequency & operator-=(const frequency &f)
Rep rep
The representation type.
T octaves_from(const frequency &other) const
Calculates the interval in octaves between this frequency and another.
constexpr Distance wavelength(const Duration &time_per_unit_distance) const
Calculates the wavelength for this frequency.
constexpr frequency & operator+=(const frequency &f)
static constexpr frequency min() noexcept
Returns the minimum representable frequency.
constexpr frequency()=default
Constructs a zero frequency.
static constexpr frequency zero() noexcept
Returns a zero frequency.
constexpr frequency subharmonic(unsigned int n) const
Returns the nth subharmonic of this frequency.
frequency(const frequency &)=default
T semitones_from(const frequency &other) const
Calculates the interval in semitones between this frequency and another.
frequency & operator=(const frequency &)=default
constexpr frequency & operator/=(const rep &r)
constexpr frequency< typename std::common_type< rep >::type, precision > operator+() const
typename Precision::type precision
The precision as a std::ratio.
frequency octave_shift(T octaves) const
Returns this frequency shifted by a number of octaves.
constexpr frequency operator++(int)
constexpr frequency operator--(int)
constexpr rep count() const
Returns the tick count.
constexpr frequency & operator--()
constexpr frequency & operator%=(const frequency &f)
constexpr frequency harmonic(unsigned int n) const
Returns the nth harmonic of this frequency.
~frequency()=default
constexpr frequency & operator*=(const rep &r)
constexpr Distance wavelength(double velocity=299792458.0) const
Calculates the wavelength for this frequency given a propagation velocity.
frequency< int64_t, std::tera > terahertz
Frequency with 1,000,000,000,000 Hz (terahertz) precision.
frequency< int64_t, std::mega > megahertz
Frequency with 1,000,000 Hz (megahertz) precision.
frequency< int64_t, std::kilo > kilohertz
Frequency with 1000 Hz (kilohertz) precision.
frequency< int64_t, std::giga > gigahertz
Frequency with 1,000,000,000 Hz (gigahertz) precision.
frequency< int64_t, std::milli > millihertz
Frequency with 0.001 Hz (millihertz) precision.
frequency< int64_t > hertz
Frequency with 1 Hz precision.
Frequency types and utilities.
Definition frequency.hpp:88
constexpr bool operator==(const frequency< Rep1, Precision1 > &lhs, const frequency< Rep2, Precision2 > &rhs)
constexpr auto operator<=>(const frequency< Rep1, Precision1 > &lhs, const frequency< Rep2, Precision2 > &rhs)
constexpr auto operator/(const frequency< Rep1, Precision > &f, const Rep2 &s) -> frequency< std::common_type_t< Rep1, Rep2 >, Precision >
Divides a frequency by a scalar.
constexpr ToFreq ceil(const frequency< Rep, Precision > &f)
Converts a frequency to the target type, rounding toward positive infinity.
constexpr ToFreq frequency_cast(const frequency< Rep, Precision > &f)
Converts a frequency to a different precision or representation.
constexpr auto beat(const frequency< Rep1, Precision1 > &f1, const frequency< Rep2, Precision2 > &f2) -> std::common_type_t< frequency< Rep1, Precision1 >, frequency< Rep2, Precision2 > >
Calculates the beat frequency between two frequencies.
constexpr auto operator-(const frequency< Rep1, Precision1 > &lhs, const frequency< Rep2, Precision2 > &rhs) -> std::common_type_t< frequency< Rep1, Precision1 >, frequency< Rep2, Precision2 > >
Returns the difference of two frequencies.
constexpr auto operator*(const frequency< Rep1, Precision > &f, const Rep2 &r) -> frequency< std::common_type_t< Rep1, Rep2 >, Precision >
Multiplies a frequency by a scalar.
std::string to_string(millihertz f)
constexpr ToFreq round(const frequency< Rep, Precision > &f)
Converts a frequency to the target type, rounding to nearest (ties to even).
constexpr bool is_frequency_v
constexpr ToFreq floor(const frequency< Rep, Precision > &f)
Converts a frequency to the target type, rounding toward negative infinity.
constexpr auto operator+(const frequency< Rep1, Precision1 > &lhs, const frequency< Rep2, Precision2 > &rhs) -> std::common_type_t< frequency< Rep1, Precision1 >, frequency< Rep2, Precision2 > >
Returns the sum of two frequencies.
constexpr frequency< Rep, Precision > abs(const frequency< Rep, Precision > &f)
Returns the absolute value of a frequency.
User-defined literals for frequency types.
Trait to detect frequency specializations.
Definition frequency.hpp:98