16#ifndef CONFIG_FREQUENCY_STD_FORMAT
17#if __has_include(<format>) && defined(__cpp_lib_format)
18#define CONFIG_FREQUENCY_STD_FORMAT 1
20#define CONFIG_FREQUENCY_STD_FORMAT 0
24#if CONFIG_FREQUENCY_STD_FORMAT
91template<
typename Rep,
typename Precision = std::ratio<1>>
101template<
typename Rep,
typename Precision>
108template<
typename T,
template<
typename...>
class Template>
109struct _is_specialization_of : std::false_type {};
111template<
template<
typename...>
class Template,
typename... Args>
112struct _is_specialization_of<Template<Args...>, Template> : std::true_type {};
114template<
typename T,
template<
typename...>
class Template>
115inline constexpr bool _is_specialization_of_v = _is_specialization_of<T, Template>::value;
117template<
typename Rep>
118concept not_frequency = !_is_specialization_of_v<Rep, frequency>;
129concept duration_like =
requires(T t) {
145concept distance_like =
requires(T t) {
155template<
typename Rep>
156struct frequency_values {
158 static constexpr Rep zero() noexcept {
return Rep(0); }
161 static constexpr Rep max() noexcept {
return std::numeric_limits<Rep>::max(); }
164 static constexpr Rep min() noexcept {
return std::numeric_limits<Rep>::lowest(); }
168struct _is_ratio : std::false_type {};
170template<std::
intmax_t Num, std::
intmax_t Denom>
171struct _is_ratio<
std::ratio<Num, Denom>> : std::true_type {};
195struct treat_as_inexact : std::bool_constant<std::floating_point<T>> {};
198inline constexpr bool treat_as_inexact_v = treat_as_inexact<T>::value;
200consteval intmax_t _gcd(intmax_t m, intmax_t n)
noexcept {
202 intmax_t rem = m % n;
211constexpr T _runtime_gcd(T m, T n)
noexcept {
226template<
typename R1,
typename R2>
227inline constexpr intmax_t _safe_ratio_divide_den = [] {
228 constexpr intmax_t g1 = _gcd(R1::num, R2::num);
229 constexpr intmax_t g2 = _gcd(R1::den, R2::den);
230 return (R1::den / g2) * (R2::num / g1);
233template<
typename From,
typename To>
234concept _harmonic_precision = _safe_ratio_divide_den<From, To> == 1;
281template<
typename Rep,
typename Precision>
284 static_assert(_is_ratio<Precision>::value,
"precision must be a specialization of std::ratio");
285 static_assert(Precision::num > 0,
"precision must be positive");
306 template<
typename Rep2>
307 requires std::convertible_to<const Rep2&, rep> && (treat_as_inexact_v<rep> || !treat_as_inexact_v<Rep2>)
309 : _r(
static_cast<rep>(r)) {}
321 template<
typename Rep2,
typename Precision2>
322 requires std::convertible_to<const Rep2&, rep> &&
323 (treat_as_inexact_v<rep> || (_harmonic_precision<Precision2, precision> && !treat_as_inexact_v<Rep2>))
334 template<
typename Rep2,
typename Precision2>
335 requires(!std::is_same_v<frequency, frequency<Rep2, Precision2>>) && (!treat_as_inexact_v<rep>) &&
336 (!_harmonic_precision<Precision2, precision>)
442 template<duration_like Duration>
448 using cf = std::ratio_divide<period_ratio, duration_period>;
452 return Duration::max();
456 if constexpr (std::is_integral_v<rep> && std::is_integral_v<duration_rep>) {
457#ifdef __SIZEOF_INT128__
458 using cr = std::common_type_t<duration_rep, rep, intmax_t>;
466 if constexpr (cf::den == 1 && cf::num == 1) {
469 }
else if constexpr (cf::den == 1) {
477 }
else if constexpr (cf::num == 1) {
493 using cr = std::common_type_t<duration_rep, double>;
495 if constexpr (cf::den == 1 && cf::num == 1) {
497 }
else if constexpr (cf::den == 1) {
499 }
else if constexpr (cf::num == 1) {
504 static_cast<cr>(cf::num) / (
static_cast<double>(_r) *
static_cast<cr>(cf::den))
538 assert(
n > 0 &&
"subharmonic divisor must be positive");
565 template<
typename T =
double>
568 if constexpr (std::is_integral_v<rep>) {
599 template<
typename T =
double>
602 if constexpr (std::is_integral_v<rep>) {
620 template<
typename T =
double>
622 return static_cast<T>(std::log2(
static_cast<double>(_r) /
static_cast<double>(
other._r)));
636 template<
typename T =
double>
638 return static_cast<T>(12.0 * std::log2(
static_cast<double>(_r) /
static_cast<double>(
other._r)));
658 template<distance_like Distance, duration_like Duration>
665 return Distance::max();
673 using time_cf = std::ratio_divide<period_ratio, duration_period>;
674 using time_cr = std::common_type_t<period_rep, typename Duration::rep, double>;
678 if constexpr (time_cf::den == 1 && time_cf::num == 1) {
680 }
else if constexpr (time_cf::den == 1) {
682 }
else if constexpr (time_cf::num == 1) {
686 static_cast<time_cr>(time_cf::den);
710 template<distance_like Distance>
713 return Distance::max();
718 static_cast<double>(_r) *
static_cast<double>(precision::num) /
static_cast<double>(precision::den);
727 wavelength_meters *
static_cast<double>(distance_period::den) /
static_cast<double>(distance_period::num);
749template<
typename ToFreq,
typename Rep,
typename Precision>
751 if constexpr (std::is_same_v<ToFreq, frequency<Rep, Precision>>) {
754 using to_rep =
typename ToFreq::rep;
756 using cf = std::ratio_divide<Precision, to_precision>;
759 if constexpr (std::is_integral_v<Rep> && std::is_integral_v<to_rep>) {
760#ifdef __SIZEOF_INT128__
761 using cr = std::common_type_t<to_rep, Rep, intmax_t>;
767 if constexpr (cf::den == 1 && cf::num == 1) {
769 }
else if constexpr (cf::den == 1) {
772 }
else if constexpr (cf::num == 1) {
788 using cr = std::common_type_t<to_rep, Rep, intmax_t>;
789 if constexpr (cf::den == 1 && cf::num == 1) {
791 }
else if constexpr (cf::den == 1) {
792 return ToFreq(
static_cast<to_rep>(
static_cast<cr>(
f.count()) *
static_cast<cr>(cf::num)));
793 }
else if constexpr (cf::num == 1) {
794 return ToFreq(
static_cast<to_rep>(
static_cast<cr>(
f.count()) /
static_cast<cr>(cf::den)));
798 static_cast<cr>(
f.count()) *
static_cast<cr>(cf::num) /
static_cast<cr>(cf::den)
825template<
typename ToFreq,
typename Rep,
typename Precision>
827 using to_rep =
typename ToFreq::rep;
830 if constexpr (std::is_integral_v<Rep> && std::is_integral_v<to_rep>) {
858template<
typename ToFreq,
typename Rep,
typename Precision>
860 using to_rep =
typename ToFreq::rep;
863 if constexpr (std::is_integral_v<Rep> && std::is_integral_v<to_rep>) {
894template<
typename ToFreq,
typename Rep,
typename Precision>
896 using to_rep =
typename ToFreq::rep;
898 if constexpr (std::is_integral_v<Rep> && std::is_integral_v<to_rep>) {
954template<
typename Rep1,
typename Precision1,
typename Rep2,
typename Precision2>
976template<
typename Rep,
typename Precision>
982template<
typename Rep1,
typename Precision1,
typename Rep2,
typename Precision2>
990template<
typename Rep1,
typename Precision1,
typename Rep2,
typename Precision2>
998template<
typename Rep1,
typename Precision,
typename Rep2>
999 requires not_frequency<Rep2> && std::convertible_to<const Rep2&, std::common_type_t<Rep1, Rep2>>
1003 return cf(
cf(
f).count() *
r);
1007template<
typename Rep1,
typename Rep2,
typename Precision>
1008 requires not_frequency<Rep1> && std::convertible_to<const Rep1&, std::common_type_t<Rep1, Rep2>>
1015template<
typename Rep1,
typename Precision,
typename Rep2>
1016 requires not_frequency<Rep2> && std::convertible_to<const Rep2&, std::common_type_t<Rep1, Rep2>>
1020 return cf(
cf(
f).count() /
s);
1024template<
typename Rep1,
typename Precision1,
typename Rep2,
typename Precision2>
1026 -> std::common_type_t<Rep1, Rep2> {
1032template<
typename Rep1,
typename Precision,
typename Rep2>
1033 requires not_frequency<Rep2> && std::convertible_to<const Rep2&, std::common_type_t<Rep1, Rep2>> &&
1038 return cf(
cf(
f).count() %
s);
1042template<
typename Rep1,
typename Precision1,
typename Rep2,
typename Precision2>
1050template<
typename Rep1,
typename Precision1,
typename Rep2,
typename Precision2>
1056template<
typename Rep1,
typename Precision1,
typename Rep2,
typename Precision2>
1057 requires std::three_way_comparable<std::common_type_t<Rep1, Rep2>>
1105template<
typename Ratio>
1107 static constexpr const char*
value =
nullptr;
1111 static constexpr const char*
value =
"f";
1115 static constexpr const char*
value =
"p";
1119 static constexpr const char*
value =
"n";
1123 static constexpr const char*
value =
"µ";
1127 static constexpr const char*
value =
"m";
1131 static constexpr const char*
value =
"";
1135 static constexpr const char*
value =
"k";
1139 static constexpr const char*
value =
"M";
1143 static constexpr const char*
value =
"G";
1147 static constexpr const char*
value =
"T";
1149template<
typename Ratio>
1150inline constexpr const char*
_si_prefix_v = _si_prefix<typename Ratio::type>::value;
1153template<
typename OutputIt>
1155 for (; *
s !=
'\0'; ++
s) {
1161#if CONFIG_FREQUENCY_STD_FORMAT
1171#if defined(__cpp_exceptions) && __cpp_exceptions
1172 throw std::format_error(
what);
1185template<
typename Rep,
typename Precision>
1197template<
typename Rep1,
typename Precision1,
typename Rep2,
typename Precision2>
1200 using common_precision = std::ratio<
1201 freq::_gcd(Precision1::num, Precision2::num),
1202 (Precision1::den /
freq::_gcd(Precision1::den, Precision2::den)) * Precision2::den>;
1208#if CONFIG_FREQUENCY_STD_FORMAT
1212template<
typename Rep,
typename Precision>
1213struct formatter<
freq::frequency<Rep, Precision>> {
1217 std::formatter<double> _num;
1218 bool _has_spec =
false;
1221 constexpr auto parse(format_parse_context& ctx) {
1222 auto it = ctx.begin();
1223 if (it == ctx.end() || *it ==
'}') {
1224 if constexpr (_prefix ==
nullptr) {
1225 freq::_format_error(
"frequency: precision has no SI prefix; use an explicit format spec");
1230 return _num.parse(ctx);
1233 template<
typename FormatContext>
1236 double hz =
static_cast<double>(f.
count()) * _precision::num / _precision::den;
1237 auto out = _num.format(hz, ctx);
1240 auto out = std::format_to(ctx.out(),
"{}", f.
count());
1256template<
unsigned long long Value,
unsigned long long Power>
1258 static constexpr unsigned long long value = 10 * pow10<Value, Power - 1>::value;
1261template<
unsigned long long Value>
1262struct pow10<Value, 0> {
1263 static constexpr unsigned long long value = Value;
1266template<
char... Digits>
1269template<
char D,
char... Rest>
1270struct parse_int<D, Rest...> {
1271 static_assert(D >=
'0' && D <=
'9',
"invalid digit");
1272 static constexpr unsigned long long value = pow10<D -
'0',
sizeof...(Rest)>::value + parse_int<Rest...>::value;
1276struct parse_int<D> {
1277 static_assert(D >=
'0' && D <=
'9',
"invalid digit");
1278 static constexpr unsigned long long value = D -
'0';
1281template<
typename Freq,
char... Digits>
1282constexpr Freq check_overflow() {
1283 using parsed = parse_int<Digits...>;
1284 constexpr typename Freq::rep repval = parsed::value;
1286 repval >= 0 &&
static_cast<unsigned long long>(repval) == parsed::value,
1287 "literal value cannot be represented by frequency type"
1289 return Freq(repval);
1296template<
char... Digits>
1302template<
char... Digits>
1304 return detail::check_overflow<
freq::hertz, Digits...>();
1308template<
char... Digits>
1314template<
char... Digits>
1320template<
char... Digits>
1326template<
char... Digits>
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.
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.
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)
std::string to_string(const frequency< Rep, Precision > &f)
Renders a frequency as its exact count with a precision-qualified unit, e.g.
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.
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.