15#ifndef CONFIG_FREQUENCY_STD_FORMAT
16#if __has_include(<format>) && defined(__cpp_lib_format)
17#define CONFIG_FREQUENCY_STD_FORMAT 1
19#define CONFIG_FREQUENCY_STD_FORMAT 0
23#if CONFIG_FREQUENCY_STD_FORMAT
90template<
typename Rep,
typename Precision = std::ratio<1>>
100template<
typename Rep,
typename Precision>
107template<
typename T,
template<
typename...>
class Template>
108struct _is_specialization_of : std::false_type {};
110template<
template<
typename...>
class Template,
typename... Args>
111struct _is_specialization_of<Template<Args...>, Template> : std::true_type {};
113template<
typename T,
template<
typename...>
class Template>
114inline constexpr bool _is_specialization_of_v = _is_specialization_of<T, Template>::value;
116template<
typename Rep>
117concept not_frequency = !_is_specialization_of_v<Rep, frequency>;
128concept duration_like =
requires(T t) {
144concept distance_like =
requires(T t) {
154template<
typename Rep>
155struct frequency_values {
157 static constexpr Rep zero() noexcept {
return Rep(0); }
160 static constexpr Rep max() noexcept {
return std::numeric_limits<Rep>::max(); }
163 static constexpr Rep min() noexcept {
return std::numeric_limits<Rep>::lowest(); }
167struct _is_ratio : std::false_type {};
169template<std::
intmax_t Num, std::
intmax_t Denom>
170struct _is_ratio<
std::ratio<Num, Denom>> : std::true_type {};
194struct treat_as_inexact : std::bool_constant<std::floating_point<T>> {};
197inline constexpr bool treat_as_inexact_v = treat_as_inexact<T>::value;
199consteval intmax_t _gcd(intmax_t m, intmax_t n)
noexcept {
201 intmax_t rem = m % n;
210constexpr T _runtime_gcd(T m, T n)
noexcept {
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);
232template<
typename From,
typename To>
233concept _harmonic_precision = _safe_ratio_divide_den<From, To> == 1;
280template<
typename Rep,
typename Precision>
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");
305 template<
typename Rep2>
306 requires std::convertible_to<const Rep2&, rep> && (treat_as_inexact_v<rep> || !treat_as_inexact_v<Rep2>)
308 : _r(
static_cast<rep>(r)) {}
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>))
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>)
441 template<duration_like Duration>
447 using cf = std::ratio_divide<period_ratio, duration_period>;
451 return Duration::max();
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>;
465 if constexpr (cf::den == 1 && cf::num == 1) {
468 }
else if constexpr (cf::den == 1) {
476 }
else if constexpr (cf::num == 1) {
492 using cr = std::common_type_t<duration_rep, double>;
494 if constexpr (cf::den == 1 && cf::num == 1) {
496 }
else if constexpr (cf::den == 1) {
498 }
else if constexpr (cf::num == 1) {
503 static_cast<cr>(cf::num) / (
static_cast<double>(_r) *
static_cast<cr>(cf::den))
537 assert(
n > 0 &&
"subharmonic divisor must be positive");
564 template<
typename T =
double>
567 if constexpr (std::is_integral_v<rep>) {
598 template<
typename T =
double>
601 if constexpr (std::is_integral_v<rep>) {
619 template<
typename T =
double>
621 return static_cast<T>(std::log2(
static_cast<double>(_r) /
static_cast<double>(
other._r)));
635 template<
typename T =
double>
637 return static_cast<T>(12.0 * std::log2(
static_cast<double>(_r) /
static_cast<double>(
other._r)));
657 template<distance_like Distance, duration_like Duration>
664 return Distance::max();
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>;
677 if constexpr (time_cf::den == 1 && time_cf::num == 1) {
679 }
else if constexpr (time_cf::den == 1) {
681 }
else if constexpr (time_cf::num == 1) {
685 static_cast<time_cr>(time_cf::den);
709 template<distance_like Distance>
712 return Distance::max();
717 static_cast<double>(_r) *
static_cast<double>(precision::num) /
static_cast<double>(precision::den);
726 wavelength_meters *
static_cast<double>(distance_period::den) /
static_cast<double>(distance_period::num);
748template<
typename ToFreq,
typename Rep,
typename Precision>
750 if constexpr (std::is_same_v<ToFreq, frequency<Rep, Precision>>) {
753 using to_rep =
typename ToFreq::rep;
755 using cf = std::ratio_divide<Precision, to_precision>;
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>;
766 if constexpr (cf::den == 1 && cf::num == 1) {
768 }
else if constexpr (cf::den == 1) {
771 }
else if constexpr (cf::num == 1) {
787 using cr = std::common_type_t<to_rep, Rep, intmax_t>;
788 if constexpr (cf::den == 1 && cf::num == 1) {
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)));
797 static_cast<cr>(
f.count()) *
static_cast<cr>(cf::num) /
static_cast<cr>(cf::den)
824template<
typename ToFreq,
typename Rep,
typename Precision>
826 using to_rep =
typename ToFreq::rep;
829 if constexpr (std::is_integral_v<Rep> && std::is_integral_v<to_rep>) {
857template<
typename ToFreq,
typename Rep,
typename Precision>
859 using to_rep =
typename ToFreq::rep;
862 if constexpr (std::is_integral_v<Rep> && std::is_integral_v<to_rep>) {
893template<
typename ToFreq,
typename Rep,
typename Precision>
895 using to_rep =
typename ToFreq::rep;
897 if constexpr (std::is_integral_v<Rep> && std::is_integral_v<to_rep>) {
953template<
typename Rep1,
typename Precision1,
typename Rep2,
typename Precision2>
975template<
typename Rep,
typename Precision>
981template<
typename Rep1,
typename Precision1,
typename Rep2,
typename Precision2>
989template<
typename Rep1,
typename Precision1,
typename Rep2,
typename Precision2>
997template<
typename Rep1,
typename Precision,
typename Rep2>
998 requires not_frequency<Rep2> && std::convertible_to<const Rep2&, std::common_type_t<Rep1, Rep2>>
1002 return cf(
cf(
f).count() *
r);
1006template<
typename Rep1,
typename Rep2,
typename Precision>
1007 requires not_frequency<Rep1> && std::convertible_to<const Rep1&, std::common_type_t<Rep1, Rep2>>
1014template<
typename Rep1,
typename Precision,
typename Rep2>
1015 requires not_frequency<Rep2> && std::convertible_to<const Rep2&, std::common_type_t<Rep1, Rep2>>
1019 return cf(
cf(
f).count() /
s);
1023template<
typename Rep1,
typename Precision1,
typename Rep2,
typename Precision2>
1025 -> std::common_type_t<Rep1, Rep2> {
1031template<
typename Rep1,
typename Precision,
typename Rep2>
1032 requires not_frequency<Rep2> && std::convertible_to<const Rep2&, std::common_type_t<Rep1, Rep2>> &&
1037 return cf(
cf(
f).count() %
s);
1041template<
typename Rep1,
typename Precision1,
typename Rep2,
typename Precision2>
1049template<
typename Rep1,
typename Precision1,
typename Rep2,
typename Precision2>
1055template<
typename Rep1,
typename Precision1,
typename Rep2,
typename Precision2>
1056 requires std::three_way_comparable<std::common_type_t<Rep1, Rep2>>
1103 return std::to_string(
f.count()) +
"mHz";
1107 return std::to_string(
f.count()) +
"Hz";
1111 return std::to_string(
f.count()) +
"kHz";
1115 return std::to_string(
f.count()) +
"MHz";
1119 return std::to_string(
f.count()) +
"GHz";
1123 return std::to_string(
f.count()) +
"THz";
1130template<
typename Rep1,
typename Precision1,
typename Rep2,
typename Precision2>
1133 using common_precision = std::ratio<
1134 freq::_gcd(Precision1::num, Precision2::num),
1135 (Precision1::den /
freq::_gcd(Precision1::den, Precision2::den)) * Precision2::den>;
1141#if CONFIG_FREQUENCY_STD_FORMAT
1143struct formatter<
freq::millihertz> {
1144 constexpr auto parse(format_parse_context& ctx) {
return ctx.begin(); }
1146 auto format(
freq::millihertz f, format_context& ctx)
const {
return std::format_to(ctx.out(),
"{}mHz", f.
count()); }
1150struct formatter<
freq::hertz> {
1151 constexpr auto parse(format_parse_context& ctx) {
return ctx.begin(); }
1153 auto format(
freq::hertz f, format_context& ctx)
const {
return std::format_to(ctx.out(),
"{}Hz", f.
count()); }
1157struct formatter<
freq::kilohertz> {
1158 constexpr auto parse(format_parse_context& ctx) {
return ctx.begin(); }
1160 auto format(
freq::kilohertz f, format_context& ctx)
const {
return std::format_to(ctx.out(),
"{}kHz", f.
count()); }
1164struct formatter<
freq::megahertz> {
1165 constexpr auto parse(format_parse_context& ctx) {
return ctx.begin(); }
1167 auto format(
freq::megahertz f, format_context& ctx)
const {
return std::format_to(ctx.out(),
"{}MHz", f.
count()); }
1171struct formatter<
freq::gigahertz> {
1172 constexpr auto parse(format_parse_context& ctx) {
return ctx.begin(); }
1174 auto format(
freq::gigahertz f, format_context& ctx)
const {
return std::format_to(ctx.out(),
"{}GHz", f.
count()); }
1178struct formatter<
freq::terahertz> {
1179 constexpr auto parse(format_parse_context& ctx) {
return ctx.begin(); }
1181 auto format(
freq::terahertz f, format_context& ctx)
const {
return std::format_to(ctx.out(),
"{}THz", f.
count()); }
1194template<
unsigned long long Value,
unsigned long long Power>
1196 static constexpr unsigned long long value = 10 * pow10<Value, Power - 1>::value;
1199template<
unsigned long long Value>
1200struct pow10<Value, 0> {
1201 static constexpr unsigned long long value = Value;
1204template<
char... Digits>
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;
1214struct parse_int<D> {
1215 static_assert(D >=
'0' && D <=
'9',
"invalid digit");
1216 static constexpr unsigned long long value = D -
'0';
1219template<
typename Freq,
char... Digits>
1220constexpr Freq check_overflow() {
1221 using parsed = parse_int<Digits...>;
1222 constexpr typename Freq::rep repval = parsed::value;
1224 repval >= 0 &&
static_cast<unsigned long long>(repval) == parsed::value,
1225 "literal value cannot be represented by frequency type"
1227 return Freq(repval);
1234template<
char... Digits>
1240template<
char... Digits>
1242 return detail::check_overflow<
freq::hertz, Digits...>();
1246template<
char... Digits>
1252template<
char... Digits>
1258template<
char... Digits>
1264template<
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)
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.