MUSX Document Model
Loading...
Searching...
No Matches
Fraction.h
1/*
2 * Copyright (C) 2025, Robert Patterson
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 * THE SOFTWARE.
21 */
22#pragma once
23
24#include <iostream>
25#include <numeric>
26#include <stdexcept>
27#include <limits>
28
29#include "musx/dom/Fundamentals.h"
30
31namespace musx {
32namespace util {
33
38class Fraction {
39private:
40 int m_numerator;
41 int m_denominator;
42
47 static constexpr std::pair<int, int> reduce(int num, int den) {
48 int gcd = std::gcd(num, den);
49 num /= gcd;
50 den /= gcd;
51
52 // Ensure denominator is always positive
53 if (den < 0) {
54 num = -num;
55 den = -den;
56 }
57
58 return {num, den};
59 }
60
61public:
69 Fraction(int num = 0, int den = 1)
70 {
71 if (den == 0) {
72 throw std::invalid_argument("Denominator cannot be zero.");
73 }
74
75 auto [n, d] = reduce(num, den);
76 m_numerator = n;
77 m_denominator = d;
78 }
79
84 static Fraction fromEdu(dom::Edu edu) { return Fraction(edu, EDU_PER_WHOLE_NOTE); }
85
89 static Fraction fromPercent(int percent) { return Fraction(percent, 100); }
90
95 constexpr int numerator() const { return m_numerator; }
96
101 constexpr int denominator() const { return m_denominator; }
102
107 constexpr int quotient() const {
108 return m_numerator / m_denominator;
109 }
110
117 return Fraction(m_numerator % m_denominator, m_denominator);
118 }
119
123 return Fraction(m_denominator, m_numerator);
124 }
125
129 constexpr dom::Edu calcEduDuration() const {
130 const int num = numerator() * EDU_PER_WHOLE_NOTE;
131 const int den = denominator();
132 const double div = double(num) / double(den);
133 return dom::Edu(static_cast<int>(div + (div >= 0.0 ? 0.5 : -0.5)));
134 }
135
139 constexpr double toDouble() const {
140 return double(m_numerator) / double(m_denominator);
141 }
142
149 Fraction operator+(const Fraction& other) const {
150 return Fraction(
151 m_numerator * other.m_denominator + other.m_numerator * m_denominator,
152 m_denominator * other.m_denominator
153 );
154 }
155
162 Fraction operator-(const Fraction& other) const {
163 return Fraction(
164 m_numerator * other.m_denominator - other.m_numerator * m_denominator,
165 m_denominator * other.m_denominator
166 );
167 }
168
175 Fraction operator*(const Fraction& other) const {
176 return Fraction(
177 m_numerator * other.m_numerator,
178 m_denominator * other.m_denominator
179 );
180 }
181
189 Fraction operator/(const Fraction& other) const {
190 return Fraction(
191 m_numerator * other.m_denominator,
192 m_denominator * other.m_numerator
193 );
194 }
195
202 Fraction& operator+=(const Fraction& other) {
203 *this = *this + other;
204 return *this;
205 }
206
213 Fraction& operator-=(const Fraction& other) {
214 *this = *this - other;
215 return *this;
216 }
217
224 Fraction& operator*=(const Fraction& other) {
225 *this = *this * other;
226 return *this;
227 }
228
235 Fraction& operator/=(const Fraction& other) {
236 *this = *this / other;
237 return *this;
238 }
239
245 constexpr bool operator==(const Fraction& other) const {
246 return m_numerator == other.m_numerator && m_denominator == other.m_denominator;
247 }
248
254 constexpr bool operator!=(const Fraction& other) const {
255 return !(*this == other);
256 }
257
263 constexpr bool operator<(const Fraction& other) const {
264 double lhs = static_cast<double>(m_numerator) / m_denominator;
265 double rhs = static_cast<double>(other.m_numerator) / other.m_denominator;
266 return lhs < rhs;
267 }
268
274 constexpr bool operator<=(const Fraction& other) const {
275 return *this < other || *this == other;
276 }
277
283 constexpr bool operator>(const Fraction& other) const {
284 return !(*this <= other);
285 }
286
292 constexpr bool operator>=(const Fraction& other) const {
293 return !(*this < other);
294 }
295
300 constexpr explicit operator bool() const {
301 return m_numerator != 0;
302 }
303
310 friend std::ostream& operator<<(std::ostream& os, const Fraction& frac) {
311 os << frac.m_numerator;
312 if (frac.m_denominator != 1) {
313 os << "/" << frac.m_denominator;
314 }
315 return os;
316 }
317
325 friend std::istream& operator>>(std::istream& is, Fraction& frac) {
326 int num, den;
327 char sep;
328 is >> num >> sep >> den;
329 if (sep != '/' || den == 0) {
330 throw std::invalid_argument("Invalid fraction format or zero m_denominator.");
331 }
332 frac = Fraction(num, den);
333 return is;
334 }
335};
336
337} // namespace util
338} // namespace musx
339
340#ifndef DOXYGEN_SHOULD_IGNORE_THIS
341#include <limits>
342
343namespace std {
344template <>
345class numeric_limits<musx::util::Fraction> {
346public:
347 static constexpr bool is_specialized = true;
348
349 // Smallest positive normalized value (not necessarily lowest)
350 static musx::util::Fraction min() noexcept {
351 return musx::util::Fraction(1, std::numeric_limits<int>::max());
352 }
353
354 // Largest representable positive fraction
355 static musx::util::Fraction max() noexcept {
356 return musx::util::Fraction(std::numeric_limits<int>::max(), 1);
357 }
358
359 // Most negative representable fraction
360 static musx::util::Fraction lowest() noexcept {
361 return musx::util::Fraction(std::numeric_limits<int>::lowest(), 1);
362 }
363
364 static constexpr int digits = std::numeric_limits<int>::digits;
365 static constexpr int digits10 = std::numeric_limits<int>::digits10;
366
367 static constexpr bool is_signed = true;
368 static constexpr bool is_integer = false;
369 static constexpr bool is_exact = true;
370 static constexpr bool has_infinity = false;
371 static constexpr bool has_quiet_NaN = false;
372 static constexpr bool has_signaling_NaN = false;
373
374 static musx::util::Fraction epsilon() noexcept {
375 return musx::util::Fraction(1, std::numeric_limits<int>::max());
376 }
377
378 static musx::util::Fraction round_error() noexcept {
379 return musx::util::Fraction(0);
380 }
381
382 static constexpr int radix = 2;
383
384 static musx::util::Fraction infinity() noexcept { return musx::util::Fraction(0); }
385 static musx::util::Fraction quiet_NaN() noexcept { return musx::util::Fraction(0); }
386 static musx::util::Fraction signaling_NaN() noexcept { return musx::util::Fraction(0); }
387
388 static constexpr bool is_iec559 = false;
389 static constexpr bool is_bounded = true;
390 static constexpr bool is_modulo = false;
391 static constexpr bool traps = true; // Because invalid construction throws
392 static constexpr bool tinyness_before = false;
393 static constexpr float_round_style round_style = round_indeterminate;
394};
395
396template <>
397struct hash<musx::util::Fraction>
398{
399 size_t operator()(const musx::util::Fraction& frac) const noexcept
400 {
401 // boost algorithm tailored to Fraction
402 size_t seed = std::hash<int>{}(frac.numerator());
403 seed ^= std::hash<int>{}(frac.denominator()) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
404 return seed;
405 }
406};
407
408} // namespace std
409#endif // DOXYGEN_SHOULD_IGNORE_THIS
410
A class to represent fractions with integer m_numerator and m_denominator, automatically reduced to s...
Definition Fraction.h:38
static Fraction fromPercent(int percent)
Constructs a Fraction from a percent (where 100 is 100%)
Definition Fraction.h:89
constexpr int quotient() const
Returns the integer (whole number) part of the fraction.
Definition Fraction.h:107
Fraction remainder() const
Returns the fractional part of the fraction.
Definition Fraction.h:116
constexpr double toDouble() const
Converts the fraction to floating point double.
Definition Fraction.h:139
constexpr int numerator() const
Gets the m_numerator of the fraction.
Definition Fraction.h:95
Fraction operator+(const Fraction &other) const
Adds two fractions.
Definition Fraction.h:149
Fraction(int num=0, int den=1)
Constructs a Fraction object.
Definition Fraction.h:69
constexpr bool operator<(const Fraction &other) const
Less-than comparison operator.
Definition Fraction.h:263
constexpr int denominator() const
Gets the m_denominator of the fraction.
Definition Fraction.h:101
Fraction reciprocal() const
Returns the reciprocal fraction.
Definition Fraction.h:122
constexpr bool operator<=(const Fraction &other) const
Less-than-or-equal-to comparison operator.
Definition Fraction.h:274
Fraction & operator+=(const Fraction &other)
Compound addition assignment operator.
Definition Fraction.h:202
constexpr dom::Edu calcEduDuration() const
Calculates duration as a fraction of a whole note.
Definition Fraction.h:129
Fraction operator*(const Fraction &other) const
Multiplies two fractions.
Definition Fraction.h:175
friend std::ostream & operator<<(std::ostream &os, const Fraction &frac)
Stream output operator.
Definition Fraction.h:310
static Fraction fromEdu(dom::Edu edu)
Constructs a Fraction from edu.
Definition Fraction.h:84
friend std::istream & operator>>(std::istream &is, Fraction &frac)
Stream input operator.
Definition Fraction.h:325
Fraction & operator*=(const Fraction &other)
Compound multiplication assignment operator.
Definition Fraction.h:224
Fraction & operator-=(const Fraction &other)
Compound subtraction assignment operator.
Definition Fraction.h:213
constexpr bool operator>(const Fraction &other) const
Greater-than comparison operator.
Definition Fraction.h:283
Fraction operator-(const Fraction &other) const
Subtracts one fraction from another.
Definition Fraction.h:162
constexpr bool operator!=(const Fraction &other) const
Inequality comparison operator.
Definition Fraction.h:254
Fraction operator/(const Fraction &other) const
Divides one fraction by another.
Definition Fraction.h:189
Fraction & operator/=(const Fraction &other)
Compound division assignment operator.
Definition Fraction.h:235
constexpr bool operator==(const Fraction &other) const
Equality comparison operator. (This depends on the fact that instances of Fraction are always reduced...
Definition Fraction.h:245
constexpr bool operator>=(const Fraction &other) const
Greater-than-or-equal-to comparison operator.
Definition Fraction.h:292
int32_t Edu
"Enigma Durational Units" value (1024 per quarter note)
Definition Fundamentals.h:61
object model for musx file (enigmaxml)
Definition BaseClasses.h:36