frequency: frequency/frequency.hpp Source File
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;
117concept not_frequency = !_is_specialization_of_v<Rep, frequency>;
128concept duration_like = requires(T t) {
144concept distance_like = requires(T t) {
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 {
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");
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>)
343 constexpr rep count() const { return _r; }
441 template<duration_like Duration>
447 using cf = std::ratio_divide<period_ratio, duration_period>;
450 if (_r == rep(0)) {
455 if constexpr (std::is_integral_v<rep> && std::is_integral_v<duration_rep>) {
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) {
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))));
503 static_cast<cr>(cf::num) / (static_cast<double>(_r) * static_cast<cr>(cf::den))
564 template<typename T = double>
567 if constexpr (std::is_integral_v<rep>) {
568 return frequency(static_cast<rep>(std::round(static_cast<double>(_r) * multiplier)));
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)));
619 template<typename T = double>
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>
663 if (_r == rep(0)) {
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>
711 if (_r == rep(0)) {
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);
732 rep _r{};
748template<typename ToFreq, typename Rep, typename Precision>
750 if constexpr (std::is_same_v<ToFreq, frequency<Rep, Precision>>) {
751 return f;
753 using to_rep = typename ToFreq::rep;
754 using to_precision = typename ToFreq::precision;
755 using cf = std::ratio_divide<Precision, to_precision>;
758 if constexpr (std::is_integral_v<Rep> && std::is_integral_v<to_rep>) {
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>>
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>>
1023template<typename Rep1, typename Precision1, typename Rep2, typename Precision2>
1031template<typename Rep1, typename Precision, typename Rep2>
1032 requires not_frequency<Rep2> && std::convertible_to<const Rep2&, std::common_type_t<Rep1, Rep2>> &&
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>
1131struct common_type<freq::frequency<Rep1, Precision1>, freq::frequency<Rep2, 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>
1201 static constexpr unsigned long long value = Value;
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;
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"
1242 return detail::check_overflow<freq::hertz, 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.