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>>
1104template<
typename Ratio>
1106 static constexpr const char*
value =
nullptr;
1110 static constexpr const char*
value =
"f";
1114 static constexpr const char*
value =
"p";
1118 static constexpr const char*
value =
"n";
1122 static constexpr const char*
value =
"ยต";
1126 static constexpr const char*
value =
"m";
1130 static constexpr const char*
value =
"";
1134 static constexpr const char*
value =
"k";
1138 static constexpr const char*
value =
"M";
1142 static constexpr const char*
value =
"G";
1146 static constexpr const char*
value =
"T";
1148template<
typename Ratio>
1149inline constexpr const char*
_si_prefix_v = _si_prefix<typename Ratio::type>::value;
1152template<
typename OutputIt>
1154 for (; *
s !=
'\0'; ++
s) {
1165template<
typename Rep,
typename Precision>
1177template<
typename Rep1,
typename Precision1,
typename Rep2,
typename Precision2>
1180 using common_precision = std::ratio<
1181 freq::_gcd(Precision1::num, Precision2::num),
1182 (Precision1::den /
freq::_gcd(Precision1::den, Precision2::den)) * Precision2::den>;
1188#if CONFIG_FREQUENCY_STD_FORMAT
1192template<
typename Rep,
typename Precision>
1193struct formatter<
freq::frequency<Rep, Precision>> {
1197 std::formatter<double> _num;
1198 bool _has_spec =
false;
1201 constexpr auto parse(format_parse_context& ctx) {
1202 auto it = ctx.begin();
1203 if (it == ctx.end() || *it ==
'}') {
1204 if (_prefix ==
nullptr) {
1205 throw format_error(
"frequency: precision has no SI prefix; use an explicit format spec");
1210 return _num.parse(ctx);
1213 template<
typename FormatContext>
1216 double hz =
static_cast<double>(f.
count()) * _precision::num / _precision::den;
1217 auto out = _num.format(hz, ctx);
1220 auto out = std::format_to(ctx.out(),
"{}", f.
count());
1236template<
unsigned long long Value,
unsigned long long Power>
1238 static constexpr unsigned long long value = 10 * pow10<Value, Power - 1>::value;
1241template<
unsigned long long Value>
1242struct pow10<Value, 0> {
1243 static constexpr unsigned long long value = Value;
1246template<
char... Digits>
1249template<
char D,
char... Rest>
1250struct parse_int<D, Rest...> {
1251 static_assert(D >=
'0' && D <=
'9',
"invalid digit");
1252 static constexpr unsigned long long value = pow10<D -
'0',
sizeof...(Rest)>::value + parse_int<Rest...>::value;
1256struct parse_int<D> {
1257 static_assert(D >=
'0' && D <=
'9',
"invalid digit");
1258 static constexpr unsigned long long value = D -
'0';
1261template<
typename Freq,
char... Digits>
1262constexpr Freq check_overflow() {
1263 using parsed = parse_int<Digits...>;
1264 constexpr typename Freq::rep repval = parsed::value;
1266 repval >= 0 &&
static_cast<unsigned long long>(repval) == parsed::value,
1267 "literal value cannot be represented by frequency type"
1269 return Freq(repval);
1276template<
char... Digits>
1282template<
char... Digits>
1284 return detail::check_overflow<
freq::hertz, Digits...>();
1288template<
char... Digits>
1294template<
char... Digits>
1300template<
char... Digits>
1306template<
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.