Skip to main content

music21/
duration.rs

1use crate::{
2    defaults::{FloatType, FractionType, IntegerType},
3    error::{Error, Result},
4};
5
6use fraction::ToPrimitive;
7use std::fmt::{Display, Formatter};
8use std::str::FromStr;
9
10/// A note-value name, as music21's `duration.typeToDuration` defines them.
11///
12/// Each type is a power-of-two multiple of a quarter note, from the
13/// `duplex-maxima` (sixteen whole notes) down to the `2048th`, plus the `zero`
14/// length music21 uses for grace notes.
15#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
16#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
17#[must_use]
18pub enum DurationType {
19    /// Duplex maxima, sixteen whole notes.
20    DuplexMaxima,
21    /// Maxima, eight whole notes.
22    Maxima,
23    /// Longa, four whole notes.
24    Longa,
25    /// Breve, or double whole note.
26    Breve,
27    /// Whole note.
28    Whole,
29    /// Half note.
30    Half,
31    /// Quarter note.
32    Quarter,
33    /// Eighth note.
34    Eighth,
35    /// Sixteenth note.
36    Sixteenth,
37    /// Thirty-second note.
38    ThirtySecond,
39    /// Sixty-fourth note.
40    SixtyFourth,
41    /// Hundred-twenty-eighth note.
42    HundredTwentyEighth,
43    /// Two-hundred-fifty-sixth note.
44    TwoHundredFiftySixth,
45    /// Five-hundred-twelfth note.
46    FiveHundredTwelfth,
47    /// Ten-twenty-fourth note.
48    TenTwentyFourth,
49    /// Twenty-forty-eighth note.
50    TwentyFortyEighth,
51    /// A grace-note duration of no length.
52    Zero,
53}
54
55impl DurationType {
56    /// Every duration type, longest first, matching music21's ordering.
57    pub const ALL: [DurationType; 17] = [
58        Self::DuplexMaxima,
59        Self::Maxima,
60        Self::Longa,
61        Self::Breve,
62        Self::Whole,
63        Self::Half,
64        Self::Quarter,
65        Self::Eighth,
66        Self::Sixteenth,
67        Self::ThirtySecond,
68        Self::SixtyFourth,
69        Self::HundredTwentyEighth,
70        Self::TwoHundredFiftySixth,
71        Self::FiveHundredTwelfth,
72        Self::TenTwentyFourth,
73        Self::TwentyFortyEighth,
74        Self::Zero,
75    ];
76
77    /// Returns the music21 type name, such as `"whole"` or `"16th"`.
78    pub fn music21_name(self) -> &'static str {
79        match self {
80            Self::DuplexMaxima => "duplex-maxima",
81            Self::Maxima => "maxima",
82            Self::Longa => "longa",
83            Self::Breve => "breve",
84            Self::Whole => "whole",
85            Self::Half => "half",
86            Self::Quarter => "quarter",
87            Self::Eighth => "eighth",
88            Self::Sixteenth => "16th",
89            Self::ThirtySecond => "32nd",
90            Self::SixtyFourth => "64th",
91            Self::HundredTwentyEighth => "128th",
92            Self::TwoHundredFiftySixth => "256th",
93            Self::FiveHundredTwelfth => "512th",
94            Self::TenTwentyFourth => "1024th",
95            Self::TwentyFortyEighth => "2048th",
96            Self::Zero => "zero",
97        }
98    }
99
100    /// music21's `ordinal`: the position in the list of note values from the
101    /// duplex maxima (`0`) down to the 2048th (`15`). `None` for `Zero`.
102    pub fn ordinal(self) -> Option<usize> {
103        (self != Self::Zero).then(|| Self::ALL.iter().position(|kind| *kind == self))?
104    }
105
106    /// The next longer note value: music21's `nextLargerType`, so a quarter
107    /// gives a half. `None` above the duplex maxima and for `Zero`.
108    pub fn next_larger(self) -> Option<DurationType> {
109        let ordinal = self.ordinal()?;
110        Self::ALL.get(ordinal.checked_sub(1)?).copied()
111    }
112
113    /// The next shorter note value: music21's `nextSmallerType`, so a quarter
114    /// gives an eighth. `None` below the 2048th and for `Zero`.
115    pub fn next_smaller(self) -> Option<DurationType> {
116        let ordinal = self.ordinal()?;
117        Self::ALL
118            .get(ordinal + 1)
119            .copied()
120            .filter(|kind| *kind != Self::Zero)
121    }
122
123    /// Returns music21's type number: how many of this note value make a
124    /// whole note, so a quarter is `4` and a breve `0.5`. `None` for `Zero`.
125    pub fn type_number(self) -> Option<FloatType> {
126        (self != Self::Zero).then(|| 4.0 / self.quarter_length())
127    }
128
129    fn title(self) -> String {
130        let name = self.music21_name();
131        if name.starts_with(|c: char| c.is_ascii_digit()) {
132            return name.to_string();
133        }
134        name.split('-')
135            .map(|word| {
136                let mut chars = word.chars();
137                chars
138                    .next()
139                    .map(|first| first.to_ascii_uppercase().to_string() + chars.as_str())
140                    .unwrap_or_default()
141            })
142            .collect::<Vec<_>>()
143            .join("-")
144    }
145
146    /// Returns the length of one undotted note of this type, in quarter lengths.
147    pub fn quarter_length(self) -> FloatType {
148        match self {
149            Self::DuplexMaxima => 64.0,
150            Self::Maxima => 32.0,
151            Self::Longa => 16.0,
152            Self::Breve => 8.0,
153            Self::Whole => 4.0,
154            Self::Half => 2.0,
155            Self::Quarter => 1.0,
156            Self::Eighth => 0.5,
157            Self::Sixteenth => 0.25,
158            Self::ThirtySecond => 0.125,
159            Self::SixtyFourth => 0.0625,
160            Self::HundredTwentyEighth => 0.03125,
161            Self::TwoHundredFiftySixth => 0.015625,
162            Self::FiveHundredTwelfth => 0.0078125,
163            Self::TenTwentyFourth => 0.00390625,
164            Self::TwentyFortyEighth => 0.001953125,
165            Self::Zero => 0.0,
166        }
167    }
168
169    /// Parses a music21 type name.
170    pub fn from_music21_name(name: &str) -> Option<Self> {
171        match name {
172            "duplex-maxima" => Some(Self::DuplexMaxima),
173            "maxima" => Some(Self::Maxima),
174            "longa" => Some(Self::Longa),
175            "breve" => Some(Self::Breve),
176            "whole" => Some(Self::Whole),
177            "half" => Some(Self::Half),
178            "quarter" => Some(Self::Quarter),
179            "eighth" => Some(Self::Eighth),
180            "16th" => Some(Self::Sixteenth),
181            "32nd" => Some(Self::ThirtySecond),
182            "64th" => Some(Self::SixtyFourth),
183            "128th" => Some(Self::HundredTwentyEighth),
184            "256th" => Some(Self::TwoHundredFiftySixth),
185            "512th" => Some(Self::FiveHundredTwelfth),
186            "1024th" => Some(Self::TenTwentyFourth),
187            "2048th" => Some(Self::TwentyFortyEighth),
188            "zero" => Some(Self::Zero),
189            _ => None,
190        }
191    }
192
193    /// Returns the type whose undotted length is exactly `quarter_length`.
194    pub fn from_quarter_length(quarter_length: FloatType) -> Option<Self> {
195        Self::ALL
196            .into_iter()
197            .find(|candidate| candidate.quarter_length() == quarter_length)
198    }
199
200    /// Returns the length of this type carrying `dots` augmentation dots.
201    ///
202    /// Each dot adds half of what came before, so a dotted half is `3.0` and a
203    /// double-dotted half is `3.5`.
204    pub fn quarter_length_with_dots(self, dots: u32) -> FloatType {
205        self.quarter_length() * (2.0 - (0.5 as FloatType).powi(dots as i32))
206    }
207}
208
209impl Display for DurationType {
210    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
211        f.write_str(self.music21_name())
212    }
213}
214
215impl FromStr for DurationType {
216    type Err = Error;
217
218    fn from_str(value: &str) -> Result<Self> {
219        Self::from_music21_name(value)
220            .ok_or_else(|| Error::Duration(format!("unknown duration type {value:?}")))
221    }
222}
223
224#[derive(Clone, Debug)]
225#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
226/// Rhythmic duration measured in quarter lengths.
227///
228/// A quarter note has a quarter length of `1.0`; an eighth note is `0.5`;
229/// a whole note is `4.0`.
230#[must_use]
231pub struct Duration {
232    quarter_length: FloatType,
233    /// The tuplets this length is written inside, when a caller has said so.
234    ///
235    /// `None` is the ordinary case: nobody has said, so the tuplet is read
236    /// off the length by [`Duration::tuplet`]. `Some` is what a caller set,
237    /// an empty list included — saying "written inside no tuplet at all" is
238    /// different from saying nothing, and music21 keeps the difference too.
239    #[cfg_attr(feature = "serde", serde(default))]
240    tuplets: Option<Vec<Tuplet>>,
241    /// The written values, once something has said what they are.
242    ///
243    /// `None` is the ordinary case: the values are read off the length by
244    /// [`Duration::components`]. `Some` is a list a caller built a value at a
245    /// time, cut, consolidated or unlinked from the length, and it is then
246    /// what the length is worked out from rather than the other way round.
247    #[cfg_attr(feature = "serde", serde(default))]
248    written: Option<Vec<DurationTuple>>,
249    /// Dots written above dots, once set: music21's `dotGroups`.
250    #[cfg_attr(feature = "serde", serde(default))]
251    dot_groups: Option<Vec<u32>>,
252    /// Whether the written values and the sounding length have been told
253    /// apart, so that neither is worked out from the other.
254    #[cfg_attr(feature = "serde", serde(default))]
255    unlinked: bool,
256    /// How a grace note is written, where this is the duration of one.
257    #[cfg_attr(feature = "serde", serde(default))]
258    grace: Option<Grace>,
259    /// Whether the way this length is written was said, by a note value,
260    /// rather than left to be worked out from the length.
261    #[cfg_attr(feature = "serde", serde(default))]
262    said: bool,
263}
264
265/// How a grace note is written and played: what music21's `GraceDuration`
266/// carries beside its written values.
267#[derive(Clone, Copy, Debug, PartialEq)]
268#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
269pub struct Grace {
270    slash: bool,
271    steal_time_previous: Option<FloatType>,
272    steal_time_following: Option<FloatType>,
273    make_time: bool,
274    /// Whether the grace note said no note value: music21 calls it an
275    /// eighth but keeps no written value for it, so it is not the same
276    /// length as an eighth grace note that says so.
277    #[cfg_attr(feature = "serde", serde(default))]
278    value_unsaid: bool,
279}
280
281impl Default for Grace {
282    /// An acciaccatura: slashed, taking no time, as music21's
283    /// `GraceDuration` starts out.
284    fn default() -> Self {
285        Self {
286            slash: true,
287            steal_time_previous: None,
288            steal_time_following: None,
289            make_time: false,
290            value_unsaid: false,
291        }
292    }
293}
294
295impl Grace {
296    /// Whether the grace note said no note value, which music21 keeps none
297    /// for while calling it an eighth.
298    pub(crate) fn value_unsaid(&self) -> bool {
299        self.value_unsaid
300    }
301
302    #[cfg(feature = "musicxml")]
303    pub(crate) fn set_value_unsaid(&mut self, unsaid: bool) {
304        self.value_unsaid = unsaid;
305    }
306
307    /// An acciaccatura, slashed and taking no time.
308    pub fn new() -> Self {
309        Self::default()
310    }
311
312    /// An appoggiatura: unslashed, taking its time from the note it leans
313    /// on, as music21's `AppoggiaturaDuration` starts out.
314    pub fn appoggiatura() -> Self {
315        Self {
316            slash: false,
317            make_time: true,
318            ..Self::default()
319        }
320    }
321
322    /// Whether the stem is drawn with a slash through it: music21's `slash`.
323    pub fn slash(&self) -> bool {
324        self.slash
325    }
326
327    /// Says whether the stem is slashed.
328    pub fn set_slash(&mut self, slash: bool) {
329        self.slash = slash;
330    }
331
332    /// The share of the note before's time this one takes: music21's
333    /// `stealTimePrevious`.
334    pub fn steal_time_previous(&self) -> Option<FloatType> {
335        self.steal_time_previous
336    }
337
338    /// Says what share of the note before's time it takes.
339    pub fn set_steal_time_previous(&mut self, share: Option<FloatType>) {
340        self.steal_time_previous = share;
341    }
342
343    /// The share of the note after's time this one takes: music21's
344    /// `stealTimeFollowing`.
345    pub fn steal_time_following(&self) -> Option<FloatType> {
346        self.steal_time_following
347    }
348
349    /// Says what share of the note after's time it takes.
350    pub fn set_steal_time_following(&mut self, share: Option<FloatType>) {
351        self.steal_time_following = share;
352    }
353
354    /// Whether the grace note takes time of its own in performance:
355    /// music21's `makeTime`.
356    pub fn make_time(&self) -> bool {
357        self.make_time
358    }
359
360    /// Says whether it takes time of its own.
361    pub fn set_make_time(&mut self, make_time: bool) {
362        self.make_time = make_time;
363    }
364}
365
366/// One written value of a duration and how long it lasts: music21's
367/// `DurationTuple`.
368///
369/// The length is kept beside the value because the two can disagree. A grace
370/// note is written as an eighth and lasts no time, and a piece cut out of a
371/// longer value may have a length no single note value reaches, which
372/// music21 calls `inexpressible` and which has no type here.
373#[derive(Clone, Copy, Debug, PartialEq)]
374#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
375#[must_use]
376pub struct DurationTuple {
377    duration_type: Option<DurationType>,
378    dots: u32,
379    quarter_length: FloatType,
380}
381
382impl DurationTuple {
383    /// A note value with its dots, lasting as long as that is written.
384    pub fn new(duration_type: DurationType, dots: u32) -> Self {
385        Self {
386            duration_type: Some(duration_type),
387            dots,
388            quarter_length: duration_type.quarter_length_with_dots(dots),
389        }
390    }
391
392    /// The single written value a length comes to: music21's
393    /// `durationTupleFromQuarterLength`. Nought is the `zero` value, and a
394    /// length no dotted note value has is one with no type.
395    pub fn from_quarter_length(quarter_length: FloatType) -> Self {
396        if quarter_length == 0.0 {
397            return Self::new(DurationType::Zero, 0);
398        }
399        match exact_type_and_dots(quarter_length) {
400            Some((duration_type, dots)) => Self::new(duration_type, dots),
401            None => Self {
402                duration_type: None,
403                dots: 0,
404                quarter_length,
405            },
406        }
407    }
408
409    /// The same written value lasting no time, which is how a grace note
410    /// keeps what it is written as.
411    pub fn silenced(self) -> Self {
412        Self {
413            quarter_length: 0.0,
414            ..self
415        }
416    }
417
418    /// The note value, or `None` for music21's `inexpressible`.
419    pub fn duration_type(&self) -> Option<DurationType> {
420        self.duration_type
421    }
422
423    /// The augmentation dots on the value.
424    pub fn dots(&self) -> u32 {
425        self.dots
426    }
427
428    /// How long the value lasts, in quarter lengths.
429    pub fn quarter_length(&self) -> FloatType {
430        self.quarter_length
431    }
432}
433
434/// The numerators music21 searches when reading a length as a tuplet: its
435/// `defaultTupletNumerators`. Four in the time of three is deliberately
436/// absent, since that length is a dotted note.
437const TUPLET_NUMERATORS: [u32; 5] = [3, 5, 7, 11, 13];
438
439/// The dot counts music21 allows inside a tuplet: its
440/// `POSSIBLE_DOTS_IN_TUPLETS`.
441const TUPLET_DOTS: [u32; 2] = [0, 1];
442
443/// How many written values music21 will tie together before it gives up, its
444/// `range(8)`.
445const MAX_TIED_COMPONENTS: usize = 8;
446
447/// music21's `defaults.limitOffsetDenominator`: the largest denominator it
448/// will read a length as a fraction with.
449const DENOMINATOR_LIMIT: i128 = 65535;
450
451/// Reduces a fraction to its lowest terms.
452fn reduce(numerator: &mut i128, denominator: &mut i128) {
453    let divisor = num::integer::gcd(*numerator, *denominator);
454    if divisor > 1 {
455        *numerator /= divisor;
456        *denominator /= divisor;
457    }
458}
459
460/// The fraction closest to a value with a denominator no larger than the
461/// limit, as Python's `Fraction.limit_denominator` finds it.
462pub(crate) fn limited_fraction(value: FloatType, max_denominator: i128) -> Option<(i128, i128)> {
463    if !value.is_finite() || value <= 0.0 {
464        return None;
465    }
466    // The exact fraction the float stands for, as `Fraction.from_float` reads
467    // it: a float is a binary fraction, so doubling reaches a whole number.
468    let mut exact = value;
469    let mut denominator: i128 = 1;
470    for _ in 0..64 {
471        if exact.fract() == 0.0 {
472            break;
473        }
474        exact *= 2.0;
475        denominator = denominator.checked_mul(2)?;
476    }
477    let mut numerator = exact as i128;
478    if exact.fract() != 0.0 {
479        return None;
480    }
481    if denominator <= max_denominator {
482        reduce(&mut numerator, &mut denominator);
483        return Some((numerator, denominator));
484    }
485    // Python's own walk up the Stern-Brocot tree.
486    let (mut p0, mut q0, mut p1, mut q1) = (0i128, 1i128, 1i128, 0i128);
487    let (mut n, mut d) = (numerator, denominator);
488    loop {
489        let a = n / d;
490        let q2 = q0 + a * q1;
491        if q2 > max_denominator {
492            break;
493        }
494        (p0, q0, p1, q1) = (p1, q1, p0 + a * p1, q2);
495        let next = n - a * d;
496        n = d;
497        d = next;
498        if d == 0 {
499            break;
500        }
501    }
502    if q1 == 0 {
503        return None;
504    }
505    let k = (max_denominator - q0) / q1;
506    let (bound_numerator, bound_denominator) = (p0 + k * p1, q0 + k * q1);
507    let value_as =
508        |numerator: i128, denominator: i128| numerator as FloatType / denominator as FloatType;
509    let one = (value_as(bound_numerator, bound_denominator) - value).abs();
510    let two = (value_as(p1, q1) - value).abs();
511    let (mut numerator, mut denominator) = if one <= two {
512        (bound_numerator, bound_denominator)
513    } else {
514        (p1, q1)
515    };
516    reduce(&mut numerator, &mut denominator);
517    Some((numerator, denominator))
518}
519
520/// How close a length has to be to a tuplet's to be read as one, relative to
521/// the length itself.
522///
523/// music21 snaps a quarter length onto a limited-denominator fraction before
524/// it looks, which is why `0.333333` is a triplet eighth there; this is the
525/// same latitude for a crate that keeps the length as a float.
526const TUPLET_TOLERANCE: FloatType = 1e-5;
527
528/// A length written as a tuplet: `actual` notes in the time of `normal`,
529/// each side optionally counted in a written value.
530///
531/// This is the naming half of music21's `Tuplet`. A [`Duration`] here is a
532/// quarter length, so a tuplet is something a length is *read as* rather
533/// than something a duration carries — what it is for is being able to say
534/// that two thirds of a quarter is a quarter triplet.
535///
536/// Either written value may be unsaid, as music21's may: a bare triplet is
537/// three in the time of two of nothing in particular, and wherever a length
538/// is needed an eighth is assumed, which is music21's basic triplet.
539#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
540#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
541#[must_use]
542pub struct Tuplet {
543    actual: u32,
544    normal: u32,
545    /// The written value the `actual` notes are written as, with its dots.
546    duration_actual: Option<(DurationType, u32)>,
547    /// The written value the `normal` count is counted in, which is not
548    /// always the one the `actual` count is written as: three quarters in the
549    /// time of one half is the same ratio as three in the time of two
550    /// quarters, and music21 keeps both spellings.
551    duration_normal: Option<(DurationType, u32)>,
552    /// How the tuplet is bracketed on the page, which is no part of its
553    /// ratio: two tuplets differing only in this are equal.
554    #[cfg_attr(feature = "serde", serde(default))]
555    notation: crate::display::Drawn<TupletNotation>,
556}
557
558/// Where a tuplet's bracket falls over its notes: music21's `Tuplet.type`.
559#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
560#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
561pub enum TupletType {
562    /// The bracket starts on this note.
563    Start,
564    /// The bracket stops on this note.
565    Stop,
566    /// The bracket starts and stops on this one note.
567    StartStop,
568}
569
570impl TupletType {
571    /// music21's name for it.
572    pub fn as_str(self) -> &'static str {
573        match self {
574            Self::Start => "start",
575            Self::Stop => "stop",
576            Self::StartStop => "startStop",
577        }
578    }
579
580    /// Reads music21's name for it.
581    pub fn from_name(name: &str) -> Result<Self> {
582        match name {
583            "start" => Ok(Self::Start),
584            "stop" => Ok(Self::Stop),
585            "startStop" => Ok(Self::StartStop),
586            other => Err(Error::Duration(format!(
587                "Type must be 'start', 'stop', 'startStop', or None, not {other}"
588            ))),
589        }
590    }
591}
592
593/// How a tuplet's bracket is drawn: music21's `bracket`, `True`, `False` or
594/// `'slur'`.
595#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
596#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
597pub enum TupletBracket {
598    /// A square bracket.
599    #[default]
600    Bracket,
601    /// No bracket at all.
602    None,
603    /// A curved line, as a slur is drawn.
604    Slur,
605}
606
607/// Which of a tuplet's counts is shown and how: music21's
608/// `tupletActualShow` and `tupletNormalShow`.
609#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
610#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
611pub enum TupletShow {
612    /// The number of notes.
613    Number,
614    /// The note value.
615    Type,
616    /// Both.
617    Both,
618}
619
620impl TupletShow {
621    /// music21's name for it.
622    pub fn as_str(self) -> &'static str {
623        match self {
624            Self::Number => "number",
625            Self::Type => "type",
626            Self::Both => "both",
627        }
628    }
629
630    /// Reads music21's name for it.
631    pub fn from_name(name: &str) -> Result<Self> {
632        match name {
633            "number" => Ok(Self::Number),
634            "type" => Ok(Self::Type),
635            "both" => Ok(Self::Both),
636            other => Err(Error::Duration(format!(
637                "a tuplet shows a number, a type or both, not {other}"
638            ))),
639        }
640    }
641}
642
643/// How a tuplet is written on the page, as music21 starts one: no bracket
644/// placed yet, drawn as a bracket above the notes, with the number of notes
645/// shown.
646#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
647#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
648pub struct TupletNotation {
649    tuplet_type: Option<TupletType>,
650    bracket: TupletBracket,
651    placement: Option<crate::notation::Placement>,
652    actual_show: Option<TupletShow>,
653    normal_show: Option<TupletShow>,
654}
655
656impl Default for TupletNotation {
657    fn default() -> Self {
658        Self {
659            tuplet_type: None,
660            bracket: TupletBracket::Bracket,
661            placement: Some(crate::notation::Placement::Above),
662            actual_show: Some(TupletShow::Number),
663            normal_show: None,
664        }
665    }
666}
667
668/// The value music21 counts a side in when nothing says: an eighth.
669const ASSUMED_VALUE: (DurationType, u32) = (DurationType::Eighth, 0);
670
671impl Tuplet {
672    /// A tuplet of `actual` notes of a written value in the time of
673    /// `normal` of them.
674    pub fn new(actual: u32, normal: u32, duration_type: DurationType, dots: u32) -> Self {
675        Self {
676            actual,
677            normal,
678            duration_actual: Some((duration_type, dots)),
679            duration_normal: Some((duration_type, dots)),
680            notation: crate::display::Drawn(TupletNotation::default()),
681        }
682    }
683
684    /// `actual` notes in the time of `normal`, with neither side counted in
685    /// a written value: music21's `Tuplet(actual, normal)`.
686    ///
687    /// ```
688    /// use music21::Tuplet;
689    ///
690    /// let triplet = Tuplet::ratio(3, 2);
691    /// assert_eq!(triplet.duration_actual(), None);
692    /// assert_eq!(triplet.total_tuplet_length(), 1.0);
693    /// ```
694    pub fn ratio(actual: u32, normal: u32) -> Self {
695        Self {
696            actual,
697            normal,
698            duration_actual: None,
699            duration_normal: None,
700            notation: crate::display::Drawn(TupletNotation::default()),
701        }
702    }
703
704    /// Where the tuplet's bracket falls over its notes, where something has
705    /// said: music21's `type`.
706    pub fn tuplet_type(&self) -> Option<TupletType> {
707        self.notation.0.tuplet_type
708    }
709
710    /// Says where the bracket falls.
711    pub fn set_tuplet_type(&mut self, tuplet_type: Option<TupletType>) {
712        self.notation.0.tuplet_type = tuplet_type;
713    }
714
715    /// How the bracket is drawn: music21's `bracket`.
716    pub fn bracket(&self) -> TupletBracket {
717        self.notation.0.bracket
718    }
719
720    /// Says how the bracket is drawn.
721    pub fn set_bracket(&mut self, bracket: TupletBracket) {
722        self.notation.0.bracket = bracket;
723    }
724
725    /// Which side of the notes the bracket is drawn: music21's `placement`,
726    /// above unless something says otherwise.
727    pub fn placement(&self) -> Option<crate::notation::Placement> {
728        self.notation.0.placement
729    }
730
731    /// Says which side of the notes the bracket is drawn.
732    pub fn set_placement(&mut self, placement: Option<crate::notation::Placement>) {
733        self.notation.0.placement = placement;
734    }
735
736    /// How the number of notes played is shown: music21's
737    /// `tupletActualShow`.
738    pub fn actual_show(&self) -> Option<TupletShow> {
739        self.notation.0.actual_show
740    }
741
742    /// Says how the number of notes played is shown.
743    pub fn set_actual_show(&mut self, show: Option<TupletShow>) {
744        self.notation.0.actual_show = show;
745    }
746
747    /// How the number of notes they stand for is shown: music21's
748    /// `tupletNormalShow`.
749    pub fn normal_show(&self) -> Option<TupletShow> {
750        self.notation.0.normal_show
751    }
752
753    /// Says how the number of notes they stand for is shown.
754    pub fn set_normal_show(&mut self, show: Option<TupletShow>) {
755        self.notation.0.normal_show = show;
756    }
757
758    /// The same tuplet counting the `normal` side in a different written
759    /// value: music21's `durationNormal` apart from its `durationActual`.
760    pub fn with_normal(mut self, duration_type: DurationType, dots: u32) -> Self {
761        self.duration_normal = Some((duration_type, dots));
762        self
763    }
764
765    /// The written value the `actual` notes are written as, with its dots,
766    /// where one is said: music21's `durationActual`.
767    pub fn duration_actual(&self) -> Option<(DurationType, u32)> {
768        self.duration_actual
769    }
770
771    /// The written value the `normal` count is counted in, with its dots,
772    /// where one is said: music21's `durationNormal`.
773    pub fn duration_normal(&self) -> Option<(DurationType, u32)> {
774        self.duration_normal
775    }
776
777    /// Says, or unsays, the value the `actual` notes are written as.
778    pub fn set_duration_actual(&mut self, value: Option<(DurationType, u32)>) {
779        self.duration_actual = value;
780    }
781
782    /// Says, or unsays, the value the `normal` count is counted in.
783    pub fn set_duration_normal(&mut self, value: Option<(DurationType, u32)>) {
784        self.duration_normal = value;
785    }
786
787    /// The `actual` count beside the value it is written in: music21's
788    /// `tupletActual`.
789    pub fn tuplet_actual(&self) -> (u32, Option<(DurationType, u32)>) {
790        (self.actual, self.duration_actual)
791    }
792
793    /// The `normal` count beside the value it is counted in: music21's
794    /// `tupletNormal`.
795    pub fn tuplet_normal(&self) -> (u32, Option<(DurationType, u32)>) {
796        (self.normal, self.duration_normal)
797    }
798
799    /// Writes both sides of the tuplet in one value: music21's
800    /// `setDurationType`.
801    pub fn set_duration_type(&mut self, duration_type: DurationType, dots: u32) {
802        self.duration_actual = Some((duration_type, dots));
803        self.duration_normal = Some((duration_type, dots));
804    }
805
806    /// Changes how many notes are played in the time of how many: music21's
807    /// `setRatio`.
808    pub fn set_ratio(&mut self, actual: u32, normal: u32) {
809        self.actual = actual;
810        self.normal = normal;
811    }
812
813    /// The same tuplet with both its written values scaled by `amount`:
814    /// music21's `augmentOrDiminish`. The ratio stays as it is.
815    ///
816    /// ```
817    /// use music21::{DurationType, Tuplet};
818    ///
819    /// let mut sextuplet = Tuplet::ratio(6, 2);
820    /// sextuplet.set_duration_type(DurationType::Eighth, 0);
821    /// let halved = sextuplet.augmented(0.5)?;
822    /// assert_eq!(halved.duration_actual(), Some((DurationType::Sixteenth, 0)));
823    /// # Ok::<(), music21::Error>(())
824    /// ```
825    ///
826    /// # Errors
827    ///
828    /// An `amount` that is not greater than nought, or a scaled value no
829    /// dotted note value writes.
830    pub fn augmented(&self, amount: FloatType) -> Result<Self> {
831        if amount.is_nan() || amount <= 0.0 {
832            return Err(Error::Value(
833                "amountToScale must be greater than zero".to_string(),
834            ));
835        }
836        let scaled = |value: Option<(DurationType, u32)>| -> Result<Option<(DurationType, u32)>> {
837            value
838                .map(|(duration_type, dots)| {
839                    let length = duration_type.quarter_length_with_dots(dots) * amount;
840                    exact_type_and_dots(length).ok_or_else(|| {
841                        Error::Duration(format!("no written value lasts {length} quarter lengths"))
842                    })
843                })
844                .transpose()
845        };
846        Ok(Self {
847            duration_actual: scaled(self.duration_actual)?,
848            duration_normal: scaled(self.duration_normal)?,
849            ..*self
850        })
851    }
852
853    /// How long the whole tuplet lasts: music21's `totalTupletLength`, the
854    /// `normal` count of the value it is counted in.
855    pub fn total_tuplet_length(&self) -> FloatType {
856        let (duration_type, dots) = self.duration_normal.unwrap_or(ASSUMED_VALUE);
857        FloatType::from(self.normal) * duration_type.quarter_length_with_dots(dots)
858    }
859
860    /// How many notes are played: music21's `numberNotesActual`.
861    pub fn actual(&self) -> u32 {
862        self.actual
863    }
864
865    /// How many notes they are played in the time of: `numberNotesNormal`.
866    pub fn normal(&self) -> u32 {
867        self.normal
868    }
869
870    /// The note value each one is written as, an eighth where none is said.
871    pub fn duration_type(&self) -> DurationType {
872        self.duration_actual.unwrap_or(ASSUMED_VALUE).0
873    }
874
875    /// How many augmentation dots that written value carries.
876    pub fn dots(&self) -> u32 {
877        self.duration_actual.unwrap_or(ASSUMED_VALUE).1
878    }
879
880    /// The written value the `normal` count is counted in, an eighth where
881    /// none is said.
882    pub fn normal_duration_type(&self) -> DurationType {
883        self.duration_normal.unwrap_or(ASSUMED_VALUE).0
884    }
885
886    /// The dots on that value.
887    pub fn normal_dots(&self) -> u32 {
888        self.duration_normal.unwrap_or(ASSUMED_VALUE).1
889    }
890
891    /// What the written length is multiplied by inside the tuplet:
892    /// music21's `tupletMultiplier`, how long the `normal` side lasts over
893    /// how long the `actual` notes would last unaltered.
894    ///
895    /// ```
896    /// use music21::{DurationType, FractionType, Tuplet};
897    ///
898    /// assert_eq!(Tuplet::ratio(3, 2).multiplier(), FractionType::new(2, 3));
899    /// // Three quarters in the time of one half.
900    /// let written =
901    ///     Tuplet::new(3, 1, DurationType::Quarter, 0).with_normal(DurationType::Half, 0);
902    /// assert_eq!(written.multiplier(), FractionType::new(2, 3));
903    /// ```
904    pub fn multiplier(&self) -> FractionType {
905        let (actual_type, actual_dots) = self.duration_actual.unwrap_or(ASSUMED_VALUE);
906        let unaltered =
907            FloatType::from(self.actual) * actual_type.quarter_length_with_dots(actual_dots);
908        let (numerator, denominator) =
909            limited_fraction(self.total_tuplet_length() / unaltered, 65535).unwrap_or((0, 1));
910        FractionType::new(
911            IntegerType::try_from(numerator).unwrap_or(IntegerType::MAX),
912            IntegerType::try_from(denominator).unwrap_or(IntegerType::MAX),
913        )
914    }
915
916    /// music21's `Tuplet.fullName`: the familiar name for the ratios that
917    /// have one, and `Tuplet of 17/14ths` for the ones that do not.
918    pub fn full_name(&self) -> String {
919        match (self.actual, self.normal) {
920            (3, 2) => "Triplet".to_string(),
921            (5, 4 | 2) => "Quintuplet".to_string(),
922            (6, 4) => "Sextuplet".to_string(),
923            (7, 4) => "Septuplet".to_string(),
924            (actual, normal) => format!(
925                "Tuplet of {actual}/{normal}{}",
926                plural_ordinal_abbreviation(normal)
927            ),
928        }
929    }
930}
931
932/// The `st`, `nds`, `rds` or `ths` that follows a number: music21's
933/// `ordinalAbbreviation` asked for a plural, which the tuplet ratios without
934/// a name use. It leaves `st` as it is, so a ratio over 21 is `21st`.
935fn plural_ordinal_abbreviation(value: u32) -> &'static str {
936    if matches!(value % 100, 11..=13) {
937        return "ths";
938    }
939    match value % 10 {
940        1 => "st",
941        2 => "nds",
942        3 => "rds",
943        _ => "ths",
944    }
945}
946
947/// The word music21 puts in front of a note value for its dots. The mensural
948/// values say it differently: an undotted longa is *imperfect* and a dotted
949/// one *perfect*.
950fn dot_prefix(dots: u32, mensural: bool) -> &'static str {
951    match dots {
952        0 if mensural => "Imperfect ",
953        1 if mensural => "Perfect ",
954        0 => "",
955        1 => "Dotted ",
956        2 => "Double Dotted ",
957        3 => "Triple Dotted ",
958        _ => "Quadruple Dotted ",
959    }
960}
961
962impl Duration {
963    /// Creates a duration from a quarter-length value.
964    pub fn new(quarter_length: FloatType) -> Result<Self> {
965        if !quarter_length.is_finite() || quarter_length < 0.0 {
966            return Err(Error::Duration(format!(
967                "duration quarter length must be finite and non-negative, got {quarter_length}"
968            )));
969        }
970
971        Ok(Self::of_length(quarter_length))
972    }
973
974    /// A length with nothing said about how it is written.
975    fn of_length(quarter_length: FloatType) -> Self {
976        Self {
977            quarter_length,
978            tuplets: None,
979            written: None,
980            dot_groups: None,
981            unlinked: false,
982            grace: None,
983            said: false,
984        }
985    }
986
987    /// How this grace note is written, or nothing where this is not the
988    /// duration of a grace note: music21's `isGrace` and what its
989    /// `GraceDuration` says.
990    pub fn grace(&self) -> Option<&Grace> {
991        self.grace.as_ref()
992    }
993
994    /// Whether this is the duration of a grace note: music21's `isGrace`.
995    pub fn is_grace(&self) -> bool {
996        self.grace.is_some()
997    }
998
999    /// Says how this grace note is written. A duration is made a grace
1000    /// note's by [`Duration::grace_duration`], which gives it no length.
1001    pub fn set_grace(&mut self, grace: Option<Grace>) {
1002        self.grace = grace;
1003    }
1004
1005    /// Returns a quarter-note duration.
1006    pub fn quarter() -> Self {
1007        Self::from_type(DurationType::Quarter)
1008    }
1009
1010    /// Returns a half-note duration.
1011    pub fn half() -> Self {
1012        Self::from_type(DurationType::Half)
1013    }
1014
1015    /// Returns a whole-note duration.
1016    pub fn whole() -> Self {
1017        Self::from_type(DurationType::Whole)
1018    }
1019
1020    /// Returns an eighth-note duration.
1021    pub fn eighth() -> Self {
1022        Self::from_type(DurationType::Eighth)
1023    }
1024
1025    /// Creates a duration from a note-value type.
1026    pub fn from_type(duration_type: DurationType) -> Self {
1027        Self::from_type_with_dots(duration_type, 0)
1028    }
1029
1030    /// Creates a duration from a note-value type carrying augmentation dots.
1031    pub fn from_type_with_dots(duration_type: DurationType, dots: u32) -> Self {
1032        Self {
1033            said: true,
1034            ..Self::of_length(duration_type.quarter_length_with_dots(dots))
1035        }
1036    }
1037
1038    /// Returns the note-value type and dot count that together make exactly
1039    /// this length, such as a half note with one dot for `3.0`. `None` for a
1040    /// length no single dotted note value has, such as a tuplet or a tie.
1041    ///
1042    /// Where the written values have been said, it is the one value there
1043    /// is, whatever the length: an unlinked duration written as a half is a
1044    /// half however long it sounds.
1045    pub fn type_and_dots(&self) -> Option<(DurationType, u32)> {
1046        match self.written.as_deref() {
1047            Some([one]) => one
1048                .duration_type
1049                .map(|duration_type| (duration_type, one.dots)),
1050            Some(_) => None,
1051            None => exact_type_and_dots(self.quarter_length),
1052        }
1053    }
1054
1055    /// Returns the augmentation dots on this duration, or `0` when it is not
1056    /// a single dotted note value.
1057    pub fn dots(&self) -> u32 {
1058        self.type_and_dots().map_or(0, |(_, dots)| dots)
1059    }
1060
1061    /// Writes every value of the duration with so many dots, each keeping
1062    /// its note value and the whole its tuplets: music21's `dots` setter.
1063    /// The length becomes what the values then come to, and a duration
1064    /// still worked out from its length is then taken as written so.
1065    ///
1066    /// # Errors
1067    ///
1068    /// A value written as no note value at all, which cannot take dots.
1069    pub fn set_dots(&mut self, dots: u32) -> Result<()> {
1070        let values = self.written_values();
1071        if values.is_empty() {
1072            return Ok(());
1073        }
1074        let dotted = values
1075            .iter()
1076            .map(|value| {
1077                value
1078                    .duration_type()
1079                    .map(|duration_type| DurationTuple::new(duration_type, dots))
1080                    .ok_or_else(|| Error::Duration("Unknown type: inexpressible".to_string()))
1081            })
1082            .collect::<Result<Vec<_>>>()?;
1083        self.set_written_values(dotted);
1084        if self.linked() {
1085            self.set_expression_is_inferred(false);
1086        }
1087        Ok(())
1088    }
1089
1090    /// music21's `ordinal` for the duration's type, or `None` where music21
1091    /// says `complex` or the duration is zero: a duration whose quarter length
1092    /// is not a single dotted note value.
1093    pub fn ordinal(&self) -> Option<usize> {
1094        self.type_and_dots()?.0.ordinal()
1095    }
1096
1097    /// Returns the duration scaled by a positive factor: music21's
1098    /// `augmentOrDiminish`, so a quarter by two is a half.
1099    ///
1100    /// An unlinked duration stays unlinked and keeps its written values; only
1101    /// its length is scaled, and it is not snapped to a fraction.
1102    pub fn augment_or_diminish(&self, factor: FloatType) -> Result<Duration> {
1103        if factor.is_nan() || factor <= 0.0 {
1104            return Err(Error::Duration(
1105                "amountToScale must be greater than zero".to_string(),
1106            ));
1107        }
1108        if self.unlinked {
1109            let mut scaled = self.clone();
1110            scaled.tuplets = Some(Vec::new());
1111            scaled.quarter_length = self.quarter_length * factor;
1112            return Ok(scaled);
1113        }
1114        Duration::new(crate::makenotation::op_frac(self.quarter_length * factor))
1115    }
1116
1117    /// The tuplet this length is written as, if any: the first of
1118    /// [`quarter_length_to_tuplet`].
1119    ///
1120    /// A length that is already a plain written value is that value rather
1121    /// than a tuplet of some other one, so a quarter answers `None`: music21
1122    /// tries the exact match before it tries any ratio, and a plain quarter
1123    /// is a quarter even though it is also two thirds of a dotted quarter in
1124    /// a triplet.
1125    pub fn tuplet(&self) -> Option<Tuplet> {
1126        if self.type_and_dots().is_some() {
1127            return None;
1128        }
1129        quarter_length_to_tuplet(self.quarter_length, 1)
1130            .into_iter()
1131            .next()
1132    }
1133
1134    /// The tuplets this length is written inside: the ones a caller set, or
1135    /// the one read off the length when nobody has.
1136    ///
1137    /// This is music21's `tuplets`, which is likewise inferred until it is
1138    /// assigned. Setting it to nothing is not the same as never setting it:
1139    /// two thirds of a quarter reads as a quarter triplet on its own, and
1140    /// stays two thirds of a quarter written as no tuplet once told so.
1141    pub fn tuplets(&self) -> Vec<Tuplet> {
1142        match &self.tuplets {
1143            Some(tuplets) => tuplets.clone(),
1144            None => convert(self.quarter_length, true).1.into_iter().collect(),
1145        }
1146    }
1147
1148    /// The tuplets a caller has said, and nothing where they are only read
1149    /// off the length.
1150    pub(crate) fn said_tuplets(&self) -> Option<&[Tuplet]> {
1151        self.tuplets.as_deref()
1152    }
1153
1154    /// What the written values are multiplied by to give the sounding
1155    /// length: music21's `aggregateTupletMultiplier`, every tuplet's ratio
1156    /// multiplied together, so a triplet inside a quintuplet is `8/15`.
1157    ///
1158    /// A product too fine for an `i32` fraction is multiplied in floats, as
1159    /// music21 multiplies, and limited to the finest fraction that fits.
1160    pub fn aggregate_tuplet_multiplier(&self) -> FractionType {
1161        let ratios: Vec<FractionType> = self.tuplets().iter().map(Tuplet::multiplier).collect();
1162        let exact = ratios
1163            .iter()
1164            .try_fold(FractionType::from(1), |total, ratio| {
1165                fraction::CheckedMul::checked_mul(&total, ratio)
1166            });
1167        if let Some(exact) = exact {
1168            return exact;
1169        }
1170        let product: FloatType = ratios
1171            .iter()
1172            .map(|ratio| ratio.to_f64().unwrap_or(0.0))
1173            .product();
1174        // The finest denominator whose numerator still fits.
1175        let finest = (FloatType::from(IntegerType::MAX) / product).clamp(1.0, 65535.0) as i128;
1176        let (numerator, denominator) = limited_fraction(product, finest).unwrap_or((0, 1));
1177        FractionType::new(
1178            IntegerType::try_from(numerator).unwrap_or(IntegerType::MAX),
1179            IntegerType::try_from(denominator).unwrap_or(IntegerType::MAX),
1180        )
1181    }
1182
1183    /// The total of the written values, before any tuplet shortens them:
1184    /// music21's `quarterLengthNoTuplets`.
1185    pub fn quarter_length_no_tuplets(&self) -> FloatType {
1186        // Folded from a positive nought: Rust sums floats from `-0.0`.
1187        self.written_values()
1188            .iter()
1189            .fold(0.0, |total, value| total + value.quarter_length)
1190    }
1191
1192    /// Says what tuplets this length is written inside, keeping the written
1193    /// values and changing the sounding length to match: music21's `tuplets`
1194    /// setter.
1195    pub fn set_tuplets(&mut self, tuplets: Vec<Tuplet>) {
1196        let written = self.quarter_length_no_tuplets();
1197        self.tuplets = Some(tuplets);
1198        if self.unlinked {
1199            return;
1200        }
1201        self.quarter_length = written * float_from_fraction(self.aggregate_tuplet_multiplier());
1202    }
1203
1204    /// Writes this length inside one more tuplet, shortening it by that
1205    /// tuplet's ratio: music21's `appendTuplet`.
1206    pub fn append_tuplet(&mut self, tuplet: Tuplet) {
1207        let mut tuplets = self.tuplets();
1208        tuplets.push(tuplet);
1209        self.set_tuplets(tuplets);
1210    }
1211
1212    /// The length of the written values alone, with the tuplets a caller set
1213    /// divided back out.
1214    ///
1215    /// Only the set ones: an inferred tuplet is read *off* this length, so
1216    /// dividing by it here would be circular. Dividing in binary floating
1217    /// point rarely lands back on an exact note value, so the result is
1218    /// snapped onto one when it is within a hair of it.
1219    fn written_quarter_length(&self) -> FloatType {
1220        let Some(tuplets) = &self.tuplets else {
1221            return self.quarter_length;
1222        };
1223        if tuplets.is_empty() {
1224            return self.quarter_length;
1225        }
1226        let multiplier = float_from_fraction(self.aggregate_tuplet_multiplier());
1227        if multiplier == 0.0 {
1228            return self.quarter_length;
1229        }
1230        let written = self.quarter_length / multiplier;
1231        let tolerance = written.abs() * TUPLET_TOLERANCE;
1232        DurationType::ALL
1233            .into_iter()
1234            .flat_map(|duration_type| {
1235                (0..=MAX_DOTS).map(move |dots| duration_type.quarter_length_with_dots(dots))
1236            })
1237            .find(|candidate| (candidate - written).abs() <= tolerance)
1238            .unwrap_or(written)
1239    }
1240
1241    /// The written note values this length is made of, tied together:
1242    /// music21's `components`.
1243    ///
1244    /// This is the first half of [`quarter_conversion`], read over the
1245    /// written length. A length whose tuplets a caller has set is read as
1246    /// the tie those tuplets leave, without looking for a tuplet of its own.
1247    ///
1248    /// Where the written values have been said these are they, less any
1249    /// with no type; [`Duration::written_values`] keeps those too.
1250    pub fn components(&self) -> Vec<(DurationType, u32)> {
1251        match &self.written {
1252            Some(written) => written
1253                .iter()
1254                .filter_map(|value| Some((value.duration_type?, value.dots)))
1255                .collect(),
1256            None => convert(self.written_quarter_length(), self.tuplets.is_none()).0,
1257        }
1258    }
1259
1260    /// The written values with the length of each: music21's `components`
1261    /// as the `DurationTuple`s they are there.
1262    ///
1263    /// A length no tie of note values reaches is one value with no type,
1264    /// as it is upstream, and nought is no values at all.
1265    pub fn written_values(&self) -> Vec<DurationTuple> {
1266        if let Some(written) = &self.written {
1267            return written.clone();
1268        }
1269        let components = self.components();
1270        if components.is_empty() && self.quarter_length != 0.0 {
1271            return vec![DurationTuple::from_quarter_length(
1272                self.written_quarter_length(),
1273            )];
1274        }
1275        components
1276            .into_iter()
1277            .map(|(duration_type, dots)| DurationTuple::new(duration_type, dots))
1278            .collect()
1279    }
1280
1281    /// Makes these the written values, and the length what they come to
1282    /// inside the tuplets and dot groups the duration already has.
1283    fn set_written_values(&mut self, written: Vec<DurationTuple>) {
1284        // Read the tuplets off the length while it still says them.
1285        self.tuplets = Some(self.tuplets());
1286        self.written = Some(written);
1287        self.relength();
1288    }
1289
1290    /// music21's `_updateQuarterLength`: the length the written values come
1291    /// to. An unlinked duration keeps the length it was given.
1292    fn relength(&mut self) {
1293        if self.unlinked {
1294            return;
1295        }
1296        let groups: FloatType = self
1297            .dot_groups
1298            .iter()
1299            .flatten()
1300            .filter(|dots| **dots != 0)
1301            .map(|dots| 2.0 - FloatType::powi(0.5, *dots as i32))
1302            .product();
1303        self.quarter_length = self.quarter_length_no_tuplets()
1304            * float_from_fraction(self.aggregate_tuplet_multiplier())
1305            * groups;
1306    }
1307
1308    /// One written value for the whole length, losing how it had been
1309    /// written: music21's `consolidate`.
1310    ///
1311    /// The value is the one the written total comes to, before any tuplet,
1312    /// and has no type where no single note value does. A duration already
1313    /// written as one value is left alone.
1314    ///
1315    /// ```
1316    /// use music21::{Duration, DurationType};
1317    ///
1318    /// let mut duration = Duration::quarter();
1319    /// duration.add_duration_tuple(DurationType::Half, 0);
1320    /// duration.add_duration_tuple(DurationType::Quarter, 0);
1321    /// assert_eq!(duration.components().len(), 3);
1322    /// duration.consolidate();
1323    /// assert_eq!(duration.components(), [(DurationType::Whole, 0)]);
1324    /// ```
1325    pub fn consolidate(&mut self) {
1326        if self.written_values().len() == 1 {
1327            return;
1328        }
1329        let total = self.quarter_length_no_tuplets();
1330        self.set_written_values(vec![DurationTuple::from_quarter_length(total)]);
1331    }
1332
1333    /// Cuts the written value sounding at a position in two, leaving the
1334    /// whole length as it was: music21's `sliceComponentAtPosition`.
1335    ///
1336    /// The position is counted in the written values. One that falls on the
1337    /// join between two values, or at either end, cuts nothing and is an
1338    /// error.
1339    pub fn slice_component_at_position(&mut self, position: FloatType) -> Result<()> {
1340        let written = self.written_values();
1341        let mut start = 0.0;
1342        for (index, value) in written.iter().enumerate() {
1343            let end = start + value.quarter_length;
1344            if position > start && position < end {
1345                let mut sliced = written[..index].to_vec();
1346                sliced.push(DurationTuple::from_quarter_length(position - start));
1347                sliced.push(DurationTuple::from_quarter_length(end - position));
1348                sliced.extend_from_slice(&written[index + 1..]);
1349                self.set_written_values(sliced);
1350                return Ok(());
1351            }
1352            start = end;
1353        }
1354        Err(Error::Duration(
1355            "no slice is possible at this quarter position".to_string(),
1356        ))
1357    }
1358
1359    /// Writes the duration with dots above its dots: music21's `dotGroups`
1360    /// setter. Each group lengthens the value as dots do, so a half under
1361    /// `[1, 1]` lasts four and a half quarters. The plain dots come off the
1362    /// written values, since the groups now say how the note is dotted.
1363    pub fn set_dot_groups(&mut self, groups: Vec<u32>) {
1364        let undotted = self
1365            .written_values()
1366            .into_iter()
1367            .map(|value| match value.duration_type {
1368                Some(duration_type) => DurationTuple::new(duration_type, 0),
1369                None => value,
1370            })
1371            .collect();
1372        self.dot_groups = Some(groups);
1373        self.set_written_values(undotted);
1374    }
1375
1376    /// The same length written as a tie of singly dotted values, for a
1377    /// reader with no dot groups: music21's `splitDotGroups`.
1378    ///
1379    /// Each group after the first ties on every value so far at the next
1380    /// note value down, so a half under `[1, 1]` is a dotted half tied to a
1381    /// dotted quarter. It is an error for a duration that is not one
1382    /// written value.
1383    pub fn split_dot_groups(&self) -> Result<Duration> {
1384        let (duration_type, _) = self.type_and_dots().ok_or_else(|| {
1385            Error::Duration("only a single written value has dot groups to split".to_string())
1386        })?;
1387        let groups = self.dot_groups();
1388        let mut values = vec![DurationTuple::new(
1389            duration_type,
1390            groups.first().copied().unwrap_or(0),
1391        )];
1392        for _ in 1..groups.len() {
1393            let smaller: Vec<DurationTuple> = values
1394                .iter()
1395                .map(|value| {
1396                    let kind = value.duration_type.unwrap_or(duration_type);
1397                    DurationTuple::new(kind.next_smaller().unwrap_or(kind), value.dots)
1398                })
1399                .collect();
1400            values.extend(smaller);
1401        }
1402        let mut split = self.clone();
1403        split.dot_groups = None;
1404        split.set_written_values(values);
1405        Ok(split)
1406    }
1407
1408    /// Whether the written values and the sounding length move together:
1409    /// music21's `linked`, true unless a caller has said otherwise.
1410    pub fn linked(&self) -> bool {
1411        !self.unlinked
1412    }
1413
1414    /// Links or unlinks the written values and the length: music21's
1415    /// `linked` setter.
1416    ///
1417    /// Unlinking keeps the values the duration is written as today, and
1418    /// [`Duration::set_quarter_length`] then changes how long it sounds
1419    /// without touching them. Linking again keeps the length and reads the
1420    /// values off it afresh.
1421    pub fn set_linked(&mut self, linked: bool) {
1422        if !linked && !self.unlinked {
1423            self.tuplets = Some(self.tuplets());
1424            self.written = Some(self.written_values());
1425        } else if linked && self.unlinked {
1426            self.written = None;
1427            self.tuplets = None;
1428            self.dot_groups = None;
1429        }
1430        self.unlinked = !linked;
1431    }
1432
1433    /// The same written values sounding for no time, unlinked from their
1434    /// length: the duration of a grace note, music21's `getGraceDuration`.
1435    ///
1436    /// A duration with no written values is written as an eighth, since a
1437    /// grace note has to be written as something. The tuplets are dropped,
1438    /// as they are upstream.
1439    ///
1440    /// ```
1441    /// use music21::{Duration, DurationType};
1442    ///
1443    /// let grace = Duration::new(1.25)?.grace_duration();
1444    /// assert_eq!(grace.quarter_length(), 0.0);
1445    /// assert!(!grace.linked());
1446    /// assert_eq!(
1447    ///     grace.components(),
1448    ///     [(DurationType::Quarter, 0), (DurationType::Sixteenth, 0)]
1449    /// );
1450    /// # Ok::<(), music21::Error>(())
1451    /// ```
1452    pub fn grace_duration(&self) -> Duration {
1453        let mut written: Vec<DurationTuple> = self
1454            .written_values()
1455            .into_iter()
1456            .map(DurationTuple::silenced)
1457            .collect();
1458        if written.is_empty() {
1459            written.push(DurationTuple::new(DurationType::Eighth, 0).silenced());
1460        }
1461        Duration {
1462            quarter_length: 0.0,
1463            tuplets: Some(Vec::new()),
1464            written: Some(written),
1465            dot_groups: None,
1466            unlinked: true,
1467            grace: Some(Grace::new()),
1468            said: true,
1469        }
1470    }
1471
1472    /// Whether the way this length is written was worked out from the
1473    /// length rather than said: music21's `expressionIsInferred`.
1474    ///
1475    /// A duration made from a length is inferred, and one made from a note
1476    /// value is not. Making notation may write an inferred length another
1477    /// way, cutting it to finish a tuplet or joining it with its neighbours;
1478    /// one that was said is left as it was written.
1479    ///
1480    /// ```
1481    /// use music21::{Duration, DurationType};
1482    ///
1483    /// assert!(Duration::new(0.5)?.expression_is_inferred());
1484    /// assert!(!Duration::from_type(DurationType::Eighth).expression_is_inferred());
1485    /// # Ok::<(), music21::Error>(())
1486    /// ```
1487    pub fn expression_is_inferred(&self) -> bool {
1488        !self.said
1489    }
1490
1491    /// Says whether the way this length is written may be worked out again
1492    /// from the length.
1493    pub fn set_expression_is_inferred(&mut self, inferred: bool) {
1494        self.said = !inferred;
1495    }
1496
1497    /// [`Duration::grace_duration`], as an appoggiatura where `appoggiatura`
1498    /// says so: music21's `getGraceDuration(appoggiatura=True)`, which is
1499    /// unslashed and takes its time from the note it leans on.
1500    pub(crate) fn grace_duration_as(&self, appoggiatura: bool) -> Duration {
1501        let mut grace = self.grace_duration();
1502        if appoggiatura {
1503            grace.grace = Some(Grace::appoggiatura());
1504        }
1505        grace
1506    }
1507
1508    /// Whether the length needs more than one written value, tied: music21's
1509    /// `isComplex`.
1510    pub fn is_complex(&self) -> bool {
1511        self.components().len() > 1
1512    }
1513
1514    /// The dots on the written value, as the one dot group the crate keeps:
1515    /// music21's `dotGroups`, which can also write one length twice over
1516    /// where a mensural notation asks for it.
1517    pub fn dot_groups(&self) -> Vec<u32> {
1518        match &self.dot_groups {
1519            Some(groups) => groups.clone(),
1520            None => vec![self.dots()],
1521        }
1522    }
1523
1524    /// Takes the length away: music21's `clear`, which empties the written
1525    /// values so the duration sounds for no time. The tuplets it was told
1526    /// stay.
1527    pub fn clear(&mut self) {
1528        self.tuplets = Some(self.tuplets());
1529        self.written = Some(Vec::new());
1530        self.dot_groups = None;
1531        self.quarter_length = 0.0;
1532    }
1533
1534    /// Ties one more written value onto the end: music21's
1535    /// `addDurationTuple`, which lengthens the duration by that value inside
1536    /// whatever tuplets it is written in.
1537    ///
1538    /// The values are kept as they were added from then on, so a quarter
1539    /// with a half and a quarter tied on is three values and not a whole
1540    /// until it is [consolidated](Duration::consolidate).
1541    pub fn add_duration_tuple(&mut self, duration_type: DurationType, dots: u32) {
1542        let mut written = self.written_values();
1543        written.push(DurationTuple::new(duration_type, dots));
1544        self.set_written_values(written);
1545    }
1546
1547    /// Which written value is sounding at a position within the duration:
1548    /// music21's `componentIndexAtQtrPosition`. The positions are counted
1549    /// in the written values, before any tuplet scales them, and the start
1550    /// and the very end answer the first and the last value.
1551    pub fn component_index_at_qtr_position(&self, position: FloatType) -> Result<usize> {
1552        let components = self.components();
1553        if components.is_empty() {
1554            return Err(Error::Duration(
1555                "Need components to run getComponentIndexAtQtrPosition".to_string(),
1556            ));
1557        }
1558        let total = self.quarter_length_no_tuplets();
1559        if position.is_nan() || position < 0.0 {
1560            return Err(Error::Value(
1561                "position is before the start of the duration".to_string(),
1562            ));
1563        }
1564        if position > total {
1565            return Err(Error::Value(
1566                "position is after the end of the duration".to_string(),
1567            ));
1568        }
1569        if position == total {
1570            return Ok(components.len() - 1);
1571        }
1572        let mut reached = 0.0;
1573        for (index, (duration_type, dots)) in components.iter().enumerate() {
1574            reached += duration_type.quarter_length_with_dots(*dots);
1575            if reached > position {
1576                return Ok(index);
1577            }
1578        }
1579        Ok(components.len() - 1)
1580    }
1581
1582    /// Where a written value starts within the duration, counted in the
1583    /// written values: music21's `componentStartTime`. An index past the
1584    /// values is an error.
1585    pub fn component_start_time(&self, index: usize) -> Result<FloatType> {
1586        let components = self.components();
1587        if index >= components.len() {
1588            return Err(Error::Duration(format!(
1589                "invalid component index value {index} submitted; value must be an integer between 0 and {}",
1590                components.len().saturating_sub(1)
1591            )));
1592        }
1593        Ok(components[..index]
1594            .iter()
1595            .map(|(duration_type, dots)| duration_type.quarter_length_with_dots(*dots))
1596            .sum())
1597    }
1598
1599    /// Returns music21's `fullName` for a single written note value, such as
1600    /// `"Dotted Quarter"`, `"Double Dotted Half"`, `"Imperfect Longa"` or
1601    /// `"Quarter Triplet (2/3 QL)"`.
1602    ///
1603    /// A length that has to be written as a tie names each of its values and
1604    /// joins them, `"Quarter tied to 16th (1 1/4 total QL)"`. A length no
1605    /// note value reaches is `"Inexpressible"` and a zero one is
1606    /// `"Zero Duration (0 total QL)"`, both as music21 names them.
1607    pub fn full_name(&self) -> String {
1608        let components = self.components();
1609        if components.is_empty() {
1610            return if self.quarter_length == 0.0 {
1611                // music21 writes a stray second space here, from joining a
1612                // name that already ends in one; its own docstring for this
1613                // shows a single space, which is what the whitespace its
1614                // test runner normalises away comes to.
1615                "Zero Duration (0 total QL)".to_string()
1616            } else {
1617                "Inexpressible".to_string()
1618            };
1619        }
1620        // A tuplet is only asked for once the length has failed to be a
1621        // plain or dotted note value, which is music21's order: a quarter is
1622        // a quarter, even though it is also a dotted quarter in a triplet.
1623        let tuplet = if components.len() == 1 && self.type_and_dots().is_none() {
1624            self.tuplets().first().copied()
1625        } else {
1626            None
1627        };
1628        let names: Vec<String> = components
1629            .iter()
1630            .map(|(duration_type, dots)| {
1631                let mensural = matches!(duration_type, DurationType::Longa | DurationType::Maxima);
1632                let mut name = format!("{}{}", dot_prefix(*dots, mensural), duration_type.title());
1633                if let Some(tuplet) = &tuplet {
1634                    name.push(' ');
1635                    name.push_str(&tuplet.full_name());
1636                }
1637                // music21 shows the length itself once the name alone stops
1638                // saying what it is: past two dots, or inside a tuplet.
1639                if tuplet.is_some() || *dots >= 3 {
1640                    name.push_str(&format!(" ({} QL)", mixed_numeral(self.quarter_length)));
1641                }
1642                name
1643            })
1644            .collect();
1645        let mut name = names.join(" tied to ");
1646        if components.len() != 1 {
1647            name.push_str(&format!(
1648                " ({} total QL)",
1649                mixed_numeral(self.quarter_length)
1650            ));
1651        }
1652        name
1653    }
1654
1655    /// Returns the note-value type whose undotted length this duration is.
1656    ///
1657    /// Returns `None` for a length that is not a plain note value, such as a
1658    /// dotted or tuplet duration.
1659    pub fn duration_type(&self) -> Option<DurationType> {
1660        self.type_and_dots().map(|(duration_type, _)| duration_type)
1661    }
1662
1663    /// Returns the duration in quarter lengths.
1664    pub fn quarter_length(&self) -> FloatType {
1665        self.quarter_length
1666    }
1667
1668    /// Updates the duration in quarter lengths.
1669    ///
1670    /// The written values are read off the new length, unless the duration
1671    /// is [unlinked](Duration::set_linked) and keeps the ones it has.
1672    pub fn set_quarter_length(&mut self, quarter_length: FloatType) -> Result<()> {
1673        let mut replacement = Self::new(quarter_length)?;
1674        if self.unlinked {
1675            self.quarter_length = replacement.quarter_length;
1676        } else {
1677            // A length given again as it already is says nothing new.
1678            replacement.said = self.said && self.quarter_length == replacement.quarter_length;
1679            *self = replacement;
1680        }
1681        Ok(())
1682    }
1683}
1684
1685const MAX_DOTS: u32 = 4;
1686
1687/// The note value and dot count whose length is exactly this one, if any.
1688fn exact_type_and_dots(quarter_length: FloatType) -> Option<(DurationType, u32)> {
1689    DurationType::ALL.into_iter().find_map(|duration_type| {
1690        (0..=MAX_DOTS)
1691            .find(|dots| duration_type.quarter_length_with_dots(*dots) == quarter_length)
1692            .map(|dots| (duration_type, dots))
1693    })
1694}
1695
1696/// A ratio as a float, which a tuplet's is whenever it meets a quarter
1697/// length. Zero for the ratio that has no value, which no tuplet has.
1698fn float_from_fraction(ratio: FractionType) -> FloatType {
1699    ratio.to_f64().unwrap_or(0.0)
1700}
1701
1702/// Returns the note-value type closest to a quarter length, and whether it is
1703/// exact, as music21's `quarterLengthToClosestType` does: a length between two
1704/// types reports the longer one, so a triplet quarter is an inexact eighth.
1705pub fn quarter_length_to_closest_type(quarter_length: FloatType) -> Result<(DurationType, bool)> {
1706    let too_small = || {
1707        Error::Duration(format!(
1708            "cannot return types smaller than 2048th; quarter length was {quarter_length}"
1709        ))
1710    };
1711    if quarter_length.is_nan() || quarter_length <= 0.0 {
1712        return Err(too_small());
1713    }
1714    let note_length = 4.0 / quarter_length;
1715    if let Some(exact) = DurationType::ALL
1716        .into_iter()
1717        .find(|duration_type| duration_type.type_number() == Some(note_length))
1718    {
1719        return Ok((exact, true));
1720    }
1721    let upper_bound = 8.0 / quarter_length;
1722    if let Some(closest) = DurationType::ALL.into_iter().find(|duration_type| {
1723        duration_type
1724            .type_number()
1725            .is_some_and(|number| note_length < number && number < upper_bound)
1726    }) {
1727        return Ok((closest, false));
1728    }
1729    if quarter_length > 128.0 {
1730        return Ok((DurationType::DuplexMaxima, false));
1731    }
1732    Err(too_small())
1733}
1734
1735/// The tuplets a length can be written as, shortest note value first:
1736/// music21's `quarterLengthToTuplet`, stopping after `max_to_return` of
1737/// them.
1738///
1739/// The search walks the note values from shortest to longest and, for
1740/// each, tries every tuplet numerator and every count of them that fits.
1741/// Order is what decides the answer: two thirds of a quarter matches a
1742/// quarter in a triplet before it matches anything longer, which is why
1743/// music21 calls it a quarter triplet and not two eighth triplets. Dotted
1744/// tuplets are found; nested ones and four in the time of three are not,
1745/// the latter being a dotted note.
1746pub fn quarter_length_to_tuplet(quarter_length: FloatType, max_to_return: usize) -> Vec<Tuplet> {
1747    let mut found = Vec::new();
1748    if quarter_length.is_nan() || quarter_length <= 0.0 || max_to_return == 0 {
1749        return found;
1750    }
1751    let mut values = DurationType::ALL;
1752    values.sort_by(|left, right| {
1753        left.quarter_length()
1754            .partial_cmp(&right.quarter_length())
1755            .unwrap_or(std::cmp::Ordering::Equal)
1756    });
1757    let tolerance = quarter_length * TUPLET_TOLERANCE;
1758    for duration_type in values {
1759        for actual in TUPLET_NUMERATORS {
1760            for normal in 1..actual {
1761                for dots in TUPLET_DOTS {
1762                    let candidate = duration_type.quarter_length_with_dots(dots)
1763                        * FloatType::from(normal)
1764                        / FloatType::from(actual);
1765                    if (candidate - quarter_length).abs() <= tolerance {
1766                        found.push(Tuplet::new(actual, normal, duration_type, dots));
1767                        break;
1768                    }
1769                }
1770            }
1771            if found.len() >= max_to_return {
1772                return found;
1773            }
1774        }
1775    }
1776    found
1777}
1778
1779/// The one enormous tuplet music21 falls back on for a length no tie of
1780/// written values reaches: its `quarterLengthToNonPowerOf2Tuplet`.
1781///
1782/// Any length can be written as a single note inside a strange enough
1783/// tuplet — 53/25 of a quarter is a whole note in a tuplet of a hundred
1784/// in the time of fifty-three — and music21 tries that before it calls a
1785/// length inexpressible. The answer is the tuplet together with the note
1786/// value and dots written inside it, or nothing for a length that defeats
1787/// even that.
1788pub fn quarter_length_to_non_power_of_2_tuplet(
1789    quarter_length: FloatType,
1790) -> Option<(Tuplet, DurationType, u32)> {
1791    if quarter_length.is_nan() || quarter_length <= 0.0 {
1792        return None;
1793    }
1794    let (original_actual, original_normal) =
1795        limited_fraction(1.0 / quarter_length, DENOMINATOR_LIMIT)?;
1796    let (mut actual, mut normal) = (original_actual, original_normal);
1797    // Between one and two, which is where a tuplet ratio belongs.
1798    while actual < normal {
1799        actual *= 2;
1800        reduce(&mut actual, &mut normal);
1801    }
1802    while actual > normal * 2 {
1803        normal *= 2;
1804        reduce(&mut actual, &mut normal);
1805    }
1806    let (written, _) = quarter_length_to_closest_type(quarter_length / normal as FloatType).ok()?;
1807    // What is written inside the tuplet, which is the ratio the normalising
1808    // undid.
1809    let inside = (actual as FloatType / normal as FloatType)
1810        / (original_actual as FloatType / original_normal as FloatType);
1811    let (kind, dots) = exact_type_and_dots(inside)?;
1812    Some((
1813        Tuplet::new(
1814            u32::try_from(actual).ok()?,
1815            u32::try_from(normal).ok()?,
1816            written,
1817            0,
1818        ),
1819        kind,
1820        dots,
1821    ))
1822}
1823
1824/// The written note values a length is made of and the tuplet, if any, they
1825/// are written inside: music21's `quarterConversion`.
1826///
1827/// One value for a plain or dotted note, one for a tuplet (the written
1828/// value, which the tuplet's ratio then scales), and several for a length
1829/// that can only be written as a tie — a quarter tied to a sixteenth for
1830/// five sixteenths. Empty for a length that runs off the end of the note
1831/// values, which is music21's `inexpressible`, and for a length of nought.
1832///
1833/// The tie is found greedily, largest value first, as music21 finds it:
1834/// take the largest note that fits, and look for a single dotted value
1835/// covering what is left before taking another bite. A length no tie of
1836/// eight values reaches is written as one note inside the tuplet
1837/// [`quarter_length_to_non_power_of_2_tuplet`] finds.
1838pub fn quarter_conversion(quarter_length: FloatType) -> (Vec<(DurationType, u32)>, Option<Tuplet>) {
1839    convert(quarter_length, true)
1840}
1841
1842/// [`quarter_conversion`], with the search for an ordinary tuplet turned off
1843/// where a caller has already said which tuplets the length is written in.
1844fn convert(
1845    written: FloatType,
1846    look_for_tuplet: bool,
1847) -> (Vec<(DurationType, u32)>, Option<Tuplet>) {
1848    // Zero is written as nothing at all, which is what music21's empty
1849    // `components` says; `type_and_dots` would call it a `zero` note.
1850    if written == 0.0 {
1851        return (Vec::new(), None);
1852    }
1853    if let Some(value) = exact_type_and_dots(written) {
1854        return (vec![value], None);
1855    }
1856    // Shorter than the shortest note value, or longer than a tie of the
1857    // longest can reach: music21 calls both *inexpressible*, and asks this
1858    // before it looks for a tuplet.
1859    let Ok((largest, _)) = quarter_length_to_closest_type(written) else {
1860        return (Vec::new(), None);
1861    };
1862    if largest.next_larger().is_none() {
1863        return (Vec::new(), None);
1864    }
1865    if look_for_tuplet && let Some(tuplet) = quarter_length_to_tuplet(written, 1).into_iter().next()
1866    {
1867        return (vec![(tuplet.duration_type(), tuplet.dots())], Some(tuplet));
1868    }
1869    let mut components = vec![(largest, 0)];
1870    let mut remainder = written - largest.quarter_length();
1871    for _ in 0..MAX_TIED_COMPONENTS {
1872        if let Some(rest) = exact_type_and_dots(remainder) {
1873            components.push(rest);
1874            return (components, None);
1875        }
1876        let Ok((next, _)) = quarter_length_to_closest_type(remainder) else {
1877            break;
1878        };
1879        remainder -= next.quarter_length();
1880        components.push((next, 0));
1881    }
1882    match quarter_length_to_non_power_of_2_tuplet(written) {
1883        Some((tuplet, kind, dots)) => (vec![(kind, dots)], Some(tuplet)),
1884        None => (Vec::new(), None),
1885    }
1886}
1887
1888/// The largest denominator [`mixed_numeral`] will write. music21 allows any
1889/// up to 65535, but it prints the quarter length of a written note, and a
1890/// note nobody can write does not want a fraction with a four-digit
1891/// denominator in its name.
1892const MAX_MIXED_NUMERAL_DENOMINATOR: u32 = 1024;
1893
1894/// How close a fraction has to be to be written as one, relative to the
1895/// value.
1896const MIXED_NUMERAL_TOLERANCE: FloatType = 1e-6;
1897
1898/// Writes a quarter length the way music21's `mixedNumeral` does: `"2"`,
1899/// `"1/2"`, `"1 3/4"`.
1900fn mixed_numeral(value: FloatType) -> String {
1901    let whole = value.trunc() as IntegerType;
1902    let remainder = value - value.trunc();
1903    if remainder == 0.0 {
1904        return whole.to_string();
1905    }
1906    // Ascending denominators, so the simplest fraction that fits wins: a
1907    // third has to be reachable as well as a quarter, since a tuplet length
1908    // is not a power of two. The tolerance stands in for the snapping
1909    // music21 does when it reads a quarter length, which is what makes its
1910    // own `0.333333` a triplet third rather than a decimal.
1911    let tolerance = MIXED_NUMERAL_TOLERANCE * value.abs().max(1.0);
1912    let fractional = (1..=MAX_MIXED_NUMERAL_DENOMINATOR)
1913        .find_map(|denominator| {
1914            let numerator = (remainder * FloatType::from(denominator)).round();
1915            ((remainder - numerator / FloatType::from(denominator)).abs() <= tolerance)
1916                .then(|| format!("{}/{denominator}", numerator as IntegerType))
1917        })
1918        .unwrap_or_else(|| remainder.to_string());
1919    if whole == 0 {
1920        fractional
1921    } else {
1922        format!("{whole} {fractional}")
1923    }
1924}
1925
1926impl Default for Duration {
1927    fn default() -> Self {
1928        Self::quarter()
1929    }
1930}
1931
1932impl PartialEq for Duration {
1933    fn eq(&self, other: &Self) -> bool {
1934        self.quarter_length == other.quarter_length
1935    }
1936}
1937
1938impl Eq for Duration {}
1939
1940/// Hashes what equality compares, the quarter length.
1941impl std::hash::Hash for Duration {
1942    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1943        ordered_float::OrderedFloat(self.quarter_length).hash(state);
1944    }
1945}
1946
1947impl TryFrom<FloatType> for Duration {
1948    type Error = Error;
1949
1950    fn try_from(value: FloatType) -> Result<Self> {
1951        Self::new(value)
1952    }
1953}
1954
1955impl TryFrom<IntegerType> for Duration {
1956    type Error = Error;
1957
1958    fn try_from(value: IntegerType) -> Result<Self> {
1959        Self::new(value as FloatType)
1960    }
1961}
1962
1963#[cfg(test)]
1964mod tests {
1965
1966    /// Tuplets whose exact product no `i32` fraction holds multiply as
1967    /// music21 multiplies them, in floats.
1968    #[test]
1969    fn tuplets_too_fine_to_multiply_exactly_are_multiplied_as_floats() {
1970        use super::{Duration, DurationType, Tuplet};
1971        use fraction::ToPrimitive;
1972
1973        let mut duration = Duration::new(0.25).unwrap();
1974        for normal in [2_147_483_647, 2] {
1975            duration.append_tuplet(Tuplet::new(3, normal, DurationType::Sixteenth, 0));
1976        }
1977        let multiplier = duration.aggregate_tuplet_multiplier().to_f64().unwrap();
1978        let expected = 2_147_483_647.0 / 3.0 * 2.0 / 3.0;
1979        assert!(
1980            (multiplier - expected).abs() / expected < 1e-6,
1981            "{multiplier}"
1982        );
1983    }
1984
1985    #[test]
1986    fn a_type_is_found_from_its_undotted_length_alone() {
1987        assert_eq!(
1988            DurationType::from_quarter_length(2.0),
1989            Some(DurationType::Half)
1990        );
1991        assert_eq!(
1992            DurationType::from_quarter_length(0.125),
1993            Some(DurationType::ThirtySecond)
1994        );
1995        assert_eq!(DurationType::from_quarter_length(3.0), None);
1996        assert_eq!(super::mixed_numeral(2.0 / 3.0), "2/3");
1997        assert_eq!(super::mixed_numeral(1.0 / 3.0 + 1.0), "1 1/3");
1998    }
1999
2000    #[test]
2001    fn a_duration_is_cleared_and_lengthened_a_value_at_a_time() {
2002        let mut duration = Duration::new(1.5).unwrap();
2003        assert_eq!(duration.dot_groups(), [1]);
2004        duration.add_duration_tuple(DurationType::Eighth, 0);
2005        assert_eq!(duration.quarter_length(), 2.0);
2006        duration.clear();
2007        assert_eq!(duration.quarter_length(), 0.0);
2008        assert_eq!(duration.dot_groups(), [0]);
2009        // Inside a triplet the value added is scaled as the rest is.
2010        let mut triplet = Duration::new(2.0 / 3.0).unwrap();
2011        triplet.add_duration_tuple(DurationType::Quarter, 0);
2012        assert!((triplet.quarter_length() - 4.0 / 3.0).abs() < 1e-9);
2013    }
2014
2015    /// Every expectation here was read off music21.
2016    #[test]
2017    fn written_values_are_cut_consolidated_split_and_unlinked_as_music21_does() {
2018        let typed = |duration: &Duration| -> Vec<(Option<DurationType>, u32, FloatType)> {
2019            duration
2020                .written_values()
2021                .iter()
2022                .map(|value| (value.duration_type(), value.dots(), value.quarter_length()))
2023                .collect()
2024        };
2025
2026        // A cut leaves the length alone, and a piece may have no type.
2027        let mut tied = Duration::new(1.25).unwrap();
2028        tied.slice_component_at_position(0.5).unwrap();
2029        assert_eq!(
2030            tied.components(),
2031            [
2032                (DurationType::Eighth, 0),
2033                (DurationType::Eighth, 0),
2034                (DurationType::Sixteenth, 0)
2035            ]
2036        );
2037        assert_eq!(tied.quarter_length(), 1.25);
2038        let mut dotted = Duration::new(3.0).unwrap();
2039        dotted.slice_component_at_position(1.25).unwrap();
2040        assert_eq!(
2041            typed(&dotted),
2042            [(None, 0, 1.25), (Some(DurationType::Quarter), 2, 1.75)]
2043        );
2044        assert!(
2045            Duration::new(1.25)
2046                .unwrap()
2047                .slice_component_at_position(1.0)
2048                .is_err()
2049        );
2050
2051        // Consolidating keeps the length and may lose the type.
2052        let mut long = Duration::quarter();
2053        long.add_duration_tuple(DurationType::Half, 0);
2054        long.add_duration_tuple(DurationType::Half, 0);
2055        assert_eq!(long.components().len(), 3);
2056        assert_eq!(long.duration_type(), None);
2057        long.consolidate();
2058        assert_eq!(typed(&long), [(None, 0, 5.0)]);
2059        assert_eq!(long.quarter_length(), 5.0);
2060        let mut triplets = Duration::new(1.0 / 3.0).unwrap();
2061        triplets.add_duration_tuple(DurationType::Eighth, 0);
2062        triplets.consolidate();
2063        assert_eq!(triplets.components(), [(DurationType::Quarter, 0)]);
2064        assert!((triplets.quarter_length() - 2.0 / 3.0).abs() < 1e-9);
2065
2066        // Dot groups multiply, and split into a tie of dotted values.
2067        let mut half = Duration::half();
2068        half.set_dot_groups(vec![1, 1]);
2069        assert_eq!(half.quarter_length(), 4.5);
2070        assert_eq!(half.components(), [(DurationType::Half, 0)]);
2071        let split = half.split_dot_groups().unwrap();
2072        assert_eq!(
2073            split.components(),
2074            [(DurationType::Half, 1), (DurationType::Quarter, 1)]
2075        );
2076        assert_eq!(split.quarter_length(), 4.5);
2077        half.set_dot_groups(vec![1, 1, 1]);
2078        assert_eq!(half.quarter_length(), 6.75);
2079        assert_eq!(
2080            half.split_dot_groups().unwrap().components(),
2081            [
2082                (DurationType::Half, 1),
2083                (DurationType::Quarter, 1),
2084                (DurationType::Quarter, 1),
2085                (DurationType::Eighth, 1)
2086            ]
2087        );
2088        assert_eq!(
2089            Duration::new(1.5)
2090                .unwrap()
2091                .split_dot_groups()
2092                .unwrap()
2093                .components(),
2094            [(DurationType::Quarter, 1)]
2095        );
2096
2097        // Unlinked, the length and the written value go their own ways;
2098        // linked again, the value is read off the length.
2099        let mut unlinked = Duration::quarter();
2100        assert!(unlinked.linked());
2101        unlinked.set_linked(false);
2102        unlinked.set_quarter_length(3.0).unwrap();
2103        assert_eq!(unlinked.duration_type(), Some(DurationType::Quarter));
2104        assert_eq!(unlinked.quarter_length(), 3.0);
2105        unlinked.set_linked(true);
2106        assert_eq!(unlinked.type_and_dots(), Some((DurationType::Half, 1)));
2107        assert_eq!(unlinked.quarter_length(), 3.0);
2108
2109        // A grace note keeps what it is written as and sounds nothing.
2110        let grace = Duration::new(1.25).unwrap().grace_duration();
2111        assert_eq!(
2112            typed(&grace),
2113            [
2114                (Some(DurationType::Quarter), 0, 0.0),
2115                (Some(DurationType::Sixteenth), 0, 0.0)
2116            ]
2117        );
2118        assert!(!grace.linked());
2119        assert_eq!(grace.quarter_length(), 0.0);
2120        let bare = Duration::new(0.0).unwrap().grace_duration();
2121        assert_eq!(bare.duration_type(), Some(DurationType::Eighth));
2122        let appoggiatura = Duration::new(1.0 / 3.0).unwrap().grace_duration();
2123        assert_eq!(appoggiatura.components(), [(DurationType::Eighth, 0)]);
2124        assert!(appoggiatura.tuplets().is_empty());
2125    }
2126
2127    /// music21's own examples, over a tie the crate reads off the length.
2128    #[test]
2129    fn a_position_within_a_tie_names_the_value_sounding_there() {
2130        let tied = Duration::new(2.5).unwrap();
2131        assert_eq!(tied.components().len(), 2);
2132        assert_eq!(tied.component_index_at_qtr_position(0.0).unwrap(), 0);
2133        assert_eq!(tied.component_index_at_qtr_position(1.5).unwrap(), 0);
2134        assert_eq!(tied.component_index_at_qtr_position(2.0).unwrap(), 1);
2135        assert_eq!(tied.component_index_at_qtr_position(2.5).unwrap(), 1);
2136        assert!(tied.component_index_at_qtr_position(3.0).is_err());
2137        assert!(tied.component_index_at_qtr_position(-1.0).is_err());
2138        assert!(
2139            Duration::new(0.0)
2140                .unwrap()
2141                .component_index_at_qtr_position(0.0)
2142                .is_err()
2143        );
2144        assert_eq!(tied.component_start_time(0).unwrap(), 0.0);
2145        assert_eq!(tied.component_start_time(1).unwrap(), 2.0);
2146        assert!(tied.component_start_time(2).is_err());
2147    }
2148
2149    #[test]
2150    fn a_tuplet_reads_and_writes_both_of_its_sides() {
2151        let mut tuplet = Tuplet::new(3, 2, DurationType::Eighth, 0);
2152        assert_eq!(tuplet.duration_actual(), Some((DurationType::Eighth, 0)));
2153        assert_eq!(tuplet.duration_normal(), Some((DurationType::Eighth, 0)));
2154        assert_eq!(tuplet.tuplet_actual(), (3, Some((DurationType::Eighth, 0))));
2155        assert_eq!(tuplet.tuplet_normal(), (2, Some((DurationType::Eighth, 0))));
2156        tuplet.set_duration_type(DurationType::Quarter, 1);
2157        assert_eq!(tuplet.duration_actual(), Some((DurationType::Quarter, 1)));
2158        assert_eq!(tuplet.normal_duration_type(), DurationType::Quarter);
2159        assert_eq!(tuplet.normal_dots(), 1);
2160        tuplet.set_ratio(5, 4);
2161        assert_eq!((tuplet.actual(), tuplet.normal()), (5, 4));
2162        assert_eq!(tuplet.multiplier(), FractionType::new(4, 5));
2163    }
2164
2165    /// Every answer here is music21's, for a tuplet saying no written value
2166    /// and for ones whose two sides are counted in different values.
2167    #[test]
2168    fn a_tuplet_may_leave_its_written_values_unsaid() {
2169        let mut triplet = Tuplet::ratio(3, 2);
2170        assert_eq!(triplet.duration_actual(), None);
2171        assert_eq!(triplet.total_tuplet_length(), 1.0);
2172        assert_eq!(triplet.multiplier(), FractionType::new(2, 3));
2173        triplet.set_duration_type(DurationType::Half, 1);
2174        assert_eq!(triplet.total_tuplet_length(), 6.0);
2175
2176        let mut three_in_one = Tuplet::ratio(3, 1);
2177        three_in_one.set_duration_actual(Some((DurationType::Quarter, 0)));
2178        three_in_one.set_duration_normal(Some((DurationType::Half, 0)));
2179        assert_eq!(three_in_one.multiplier(), FractionType::new(2, 3));
2180        assert_eq!(three_in_one.total_tuplet_length(), 2.0);
2181
2182        let mut six = Tuplet::ratio(6, 2);
2183        six.set_duration_type(DurationType::Eighth, 0);
2184        let halved = six.augmented(0.5).unwrap();
2185        assert_eq!(halved.duration_actual(), Some((DurationType::Sixteenth, 0)));
2186        assert_eq!(halved.multiplier(), FractionType::new(1, 3));
2187        assert!(six.augmented(-1.0).is_err());
2188
2189        let mut five = Tuplet::ratio(5, 2);
2190        five.set_duration_type(DurationType::Half, 0);
2191        five.set_duration_normal(Some((DurationType::Whole, 0)));
2192        assert_eq!(five.total_tuplet_length(), 8.0);
2193    }
2194
2195    /// music21's own examples for `quarterLengthToTuplet`,
2196    /// `quarterLengthToNonPowerOf2Tuplet` and `quarterConversion`.
2197    #[test]
2198    fn a_length_is_read_as_the_tuplets_and_ties_music21_reads() {
2199        use super::{
2200            Duration, DurationType, quarter_conversion, quarter_length_to_non_power_of_2_tuplet,
2201            quarter_length_to_tuplet,
2202        };
2203
2204        let names = |tuplets: Vec<Tuplet>| -> Vec<String> {
2205            tuplets
2206                .iter()
2207                .map(|tuplet| {
2208                    format!(
2209                        "{}/{}/{}",
2210                        tuplet.actual(),
2211                        tuplet.normal(),
2212                        tuplet.duration_type().music21_name()
2213                    )
2214                })
2215                .collect()
2216        };
2217        assert_eq!(
2218            names(quarter_length_to_tuplet(0.333_333_33, 4)),
2219            ["3/2/eighth", "3/1/quarter"]
2220        );
2221        assert_eq!(
2222            names(quarter_length_to_tuplet(0.20, 4)),
2223            ["5/4/16th", "5/2/eighth", "5/1/quarter"]
2224        );
2225        assert_eq!(
2226            names(quarter_length_to_tuplet(0.333_333_3, 1)),
2227            ["3/2/eighth"]
2228        );
2229        // A plain quarter is also two dotted quarters in the time of three,
2230        // which is why `Duration::tuplet` asks for the exact value first.
2231        let plain = quarter_length_to_tuplet(1.0, 1);
2232        assert_eq!(names(plain.clone()), ["3/2/quarter"]);
2233        assert_eq!(plain[0].dots(), 1);
2234        assert!(quarter_length_to_tuplet(0.0, 4).is_empty());
2235
2236        let (tuplet, kind, dots) = quarter_length_to_non_power_of_2_tuplet(7.0).unwrap();
2237        assert_eq!(names(vec![tuplet]), ["8/7/quarter"]);
2238        assert_eq!((kind, dots), (DurationType::Breve, 0));
2239        let (tuplet, kind, _) = quarter_length_to_non_power_of_2_tuplet(7.0 / 3.0).unwrap();
2240        assert_eq!(names(vec![tuplet]), ["12/7/16th"]);
2241        assert_eq!(kind, DurationType::Whole);
2242        assert!(quarter_length_to_non_power_of_2_tuplet(0.0).is_none());
2243
2244        let (components, tuplet) = quarter_conversion(2.5);
2245        assert_eq!(
2246            components,
2247            [(DurationType::Half, 0), (DurationType::Eighth, 0)]
2248        );
2249        assert!(tuplet.is_none());
2250        let (components, tuplet) = quarter_conversion(2.0 / 3.0);
2251        assert_eq!(components, [(DurationType::Quarter, 0)]);
2252        assert_eq!(names(tuplet.into_iter().collect()), ["3/2/quarter"]);
2253        let (components, tuplet) = quarter_conversion(3.75);
2254        assert_eq!(components, [(DurationType::Half, 3)]);
2255        assert!(tuplet.is_none());
2256        assert_eq!(quarter_conversion(99.0), (Vec::new(), None));
2257        assert_eq!(quarter_conversion(0.0), (Vec::new(), None));
2258
2259        assert!(Duration::new(2.5).unwrap().is_complex());
2260        assert!(!Duration::new(3.0).unwrap().is_complex());
2261        assert!(!Duration::new(2.0 / 3.0).unwrap().is_complex());
2262    }
2263
2264    #[test]
2265    fn neighbouring_types_match_music21() {
2266        assert_eq!(
2267            DurationType::Quarter.next_larger(),
2268            Some(DurationType::Half)
2269        );
2270        assert_eq!(
2271            DurationType::Quarter.next_smaller(),
2272            Some(DurationType::Eighth)
2273        );
2274        assert_eq!(DurationType::Whole.next_larger(), Some(DurationType::Breve));
2275        assert_eq!(DurationType::Breve.next_larger(), Some(DurationType::Longa));
2276        assert_eq!(
2277            DurationType::Sixteenth.next_smaller(),
2278            Some(DurationType::ThirtySecond)
2279        );
2280        assert_eq!(DurationType::DuplexMaxima.next_larger(), None);
2281        assert_eq!(DurationType::ALL[15].next_smaller(), None);
2282        assert_eq!(DurationType::Zero.next_larger(), None);
2283        assert_eq!(DurationType::Zero.next_smaller(), None);
2284    }
2285
2286    #[test]
2287    fn ordinal_and_scaling_match_music21() {
2288        assert_eq!(DurationType::DuplexMaxima.ordinal(), Some(0));
2289        assert_eq!(DurationType::Quarter.ordinal(), Some(6));
2290        assert_eq!(DurationType::Sixteenth.ordinal(), Some(8));
2291        assert_eq!(DurationType::Zero.ordinal(), None);
2292        assert_eq!(Duration::new(1.5).unwrap().ordinal(), Some(6));
2293        assert_eq!(Duration::new(2.5).unwrap().ordinal(), None);
2294        assert_eq!(Duration::new(0.0).unwrap().ordinal(), None);
2295        assert_eq!(
2296            Duration::new(1.0)
2297                .unwrap()
2298                .augment_or_diminish(2.0)
2299                .unwrap()
2300                .quarter_length(),
2301            2.0
2302        );
2303        assert_eq!(
2304            Duration::new(1.5)
2305                .unwrap()
2306                .augment_or_diminish(0.5)
2307                .unwrap()
2308                .quarter_length(),
2309            0.75
2310        );
2311        assert!(
2312            Duration::new(1.0)
2313                .unwrap()
2314                .augment_or_diminish(0.0)
2315                .is_err()
2316        );
2317        assert!(
2318            Duration::new(1.0)
2319                .unwrap()
2320                .augment_or_diminish(-1.0)
2321                .is_err()
2322        );
2323        // A 16th sounding 4/17 of a quarter, as music21's reader leaves one.
2324        let mut unlinked = Duration::new(0.25).unwrap();
2325        unlinked.set_linked(false);
2326        unlinked.set_quarter_length(4.0 / 17.0).unwrap();
2327        let scaled = unlinked.augment_or_diminish(1.5).unwrap();
2328        assert!(!scaled.linked());
2329        assert_eq!(scaled.quarter_length(), 4.0 / 17.0 * 1.5);
2330        assert_ne!(scaled.quarter_length(), 6.0 / 17.0);
2331        assert_eq!(scaled.type_and_dots(), Some((DurationType::Sixteenth, 0)));
2332    }
2333    use super::*;
2334
2335    /// music21's `duration.typeToDuration`, verbatim.
2336    const MUSIC21_TYPE_TO_DURATION: [(&str, FloatType); 17] = [
2337        ("duplex-maxima", 64.0),
2338        ("maxima", 32.0),
2339        ("longa", 16.0),
2340        ("breve", 8.0),
2341        ("whole", 4.0),
2342        ("half", 2.0),
2343        ("quarter", 1.0),
2344        ("eighth", 0.5),
2345        ("16th", 0.25),
2346        ("32nd", 0.125),
2347        ("64th", 0.0625),
2348        ("128th", 0.03125),
2349        ("256th", 0.015625),
2350        ("512th", 0.0078125),
2351        ("1024th", 0.00390625),
2352        ("2048th", 0.001953125),
2353        ("zero", 0.0),
2354    ];
2355
2356    #[test]
2357    fn dots_types_and_full_names_match_music21() {
2358        let cases = [
2359            (
2360                4.0,
2361                Some((DurationType::Whole, 0)),
2362                "Whole",
2363                (DurationType::Whole, true),
2364            ),
2365            (
2366                2.0,
2367                Some((DurationType::Half, 0)),
2368                "Half",
2369                (DurationType::Half, true),
2370            ),
2371            (
2372                1.0,
2373                Some((DurationType::Quarter, 0)),
2374                "Quarter",
2375                (DurationType::Quarter, true),
2376            ),
2377            (
2378                0.5,
2379                Some((DurationType::Eighth, 0)),
2380                "Eighth",
2381                (DurationType::Eighth, true),
2382            ),
2383            (
2384                0.25,
2385                Some((DurationType::Sixteenth, 0)),
2386                "16th",
2387                (DurationType::Sixteenth, true),
2388            ),
2389            (
2390                3.0,
2391                Some((DurationType::Half, 1)),
2392                "Dotted Half",
2393                (DurationType::Half, false),
2394            ),
2395            (
2396                1.5,
2397                Some((DurationType::Quarter, 1)),
2398                "Dotted Quarter",
2399                (DurationType::Quarter, false),
2400            ),
2401            (
2402                0.75,
2403                Some((DurationType::Eighth, 1)),
2404                "Dotted Eighth",
2405                (DurationType::Eighth, false),
2406            ),
2407            (
2408                6.0,
2409                Some((DurationType::Whole, 1)),
2410                "Dotted Whole",
2411                (DurationType::Whole, false),
2412            ),
2413            (
2414                1.75,
2415                Some((DurationType::Quarter, 2)),
2416                "Double Dotted Quarter",
2417                (DurationType::Quarter, false),
2418            ),
2419            (
2420                0.875,
2421                Some((DurationType::Eighth, 2)),
2422                "Double Dotted Eighth",
2423                (DurationType::Eighth, false),
2424            ),
2425            (
2426                7.0,
2427                Some((DurationType::Whole, 2)),
2428                "Double Dotted Whole",
2429                (DurationType::Whole, false),
2430            ),
2431            (
2432                0.375,
2433                Some((DurationType::Sixteenth, 1)),
2434                "Dotted 16th",
2435                (DurationType::Sixteenth, false),
2436            ),
2437            (
2438                3.5,
2439                Some((DurationType::Half, 2)),
2440                "Double Dotted Half",
2441                (DurationType::Half, false),
2442            ),
2443            (
2444                3.75,
2445                Some((DurationType::Half, 3)),
2446                "Triple Dotted Half (3 3/4 QL)",
2447                (DurationType::Half, false),
2448            ),
2449            (
2450                1.875,
2451                Some((DurationType::Quarter, 3)),
2452                "Triple Dotted Quarter (1 7/8 QL)",
2453                (DurationType::Quarter, false),
2454            ),
2455            (
2456                8.0,
2457                Some((DurationType::Breve, 0)),
2458                "Breve",
2459                (DurationType::Breve, true),
2460            ),
2461            (
2462                16.0,
2463                Some((DurationType::Longa, 0)),
2464                "Imperfect Longa",
2465                (DurationType::Longa, true),
2466            ),
2467            (
2468                24.0,
2469                Some((DurationType::Longa, 1)),
2470                "Perfect Longa",
2471                (DurationType::Longa, false),
2472            ),
2473        ];
2474        for (quarter_length, type_and_dots, full_name, closest) in cases {
2475            let duration = Duration::new(quarter_length).unwrap();
2476            assert_eq!(duration.type_and_dots(), type_and_dots, "{quarter_length}");
2477            assert_eq!(duration.full_name(), full_name, "{quarter_length}");
2478            assert_eq!(
2479                quarter_length_to_closest_type(quarter_length).unwrap(),
2480                closest,
2481                "{quarter_length}"
2482            );
2483        }
2484
2485        let inexact = [
2486            (2.0 / 3.0, DurationType::Eighth),
2487            (1.0 / 3.0, DurationType::Sixteenth),
2488            (4.0 / 3.0, DurationType::Quarter),
2489            (0.2, DurationType::ThirtySecond),
2490            (0.4, DurationType::Sixteenth),
2491            (1.25, DurationType::Quarter),
2492            (5.0, DurationType::Whole),
2493            (2.5, DurationType::Half),
2494        ];
2495        // None of these is a single written note value. The ones that are a
2496        // tuplet are named as one; the ones that are a tie of two values are
2497        // not named at all, since the crate keeps a length and not the
2498        // components music21 spells them out from.
2499        let tuplet_names = [
2500            (2.0 / 3.0, "Quarter Triplet (2/3 QL)"),
2501            (1.0 / 3.0, "Eighth Triplet (1/3 QL)"),
2502            (4.0 / 3.0, "Half Triplet (1 1/3 QL)"),
2503            (0.2, "16th Quintuplet (1/5 QL)"),
2504            (0.4, "Eighth Quintuplet (2/5 QL)"),
2505        ];
2506        // and the ones music21 has to write as two values tied together
2507        let tied = [
2508            (1.25, "Quarter tied to 16th (1 1/4 total QL)"),
2509            (5.0, "Whole tied to Quarter (5 total QL)"),
2510            (2.5, "Half tied to Eighth (2 1/2 total QL)"),
2511        ];
2512        for (quarter_length, closest) in inexact {
2513            let duration = Duration::new(quarter_length).unwrap();
2514            assert_eq!(duration.type_and_dots(), None, "{quarter_length}");
2515            assert_eq!(duration.dots(), 0, "{quarter_length}");
2516            let tuplet_name = tuplet_names
2517                .iter()
2518                .find(|(length, _)| *length == quarter_length);
2519            let tied_name = tied.iter().find(|(length, _)| *length == quarter_length);
2520            let expected = tuplet_name.or(tied_name).map(|(_, name)| *name);
2521            assert_eq!(
2522                duration.full_name(),
2523                expected.expect("every inexact length here is named"),
2524                "{quarter_length}"
2525            );
2526            assert_eq!(
2527                duration.tuplet().is_some(),
2528                tuplet_name.is_some(),
2529                "{quarter_length}"
2530            );
2531            assert_eq!(
2532                duration.components().len(),
2533                if tuplet_name.is_some() { 1 } else { 2 },
2534                "{quarter_length}"
2535            );
2536            assert_eq!(
2537                quarter_length_to_closest_type(quarter_length).unwrap(),
2538                (closest, false),
2539                "{quarter_length}"
2540            );
2541        }
2542
2543        // the two lengths music21 refuses to write at all
2544        for quarter_length in [0.001, 100.0] {
2545            let duration = Duration::new(quarter_length).unwrap();
2546            assert!(duration.components().is_empty(), "{quarter_length}");
2547            assert_eq!(duration.full_name(), "Inexpressible", "{quarter_length}");
2548        }
2549        let zero = Duration::new(0.0).unwrap();
2550        assert!(zero.components().is_empty());
2551        assert_eq!(zero.full_name(), "Zero Duration (0 total QL)");
2552
2553        // music21 snaps a quarter length before it looks, so a truncated
2554        // third is still a triplet there and here.
2555        let truncated = Duration::new(0.333_333).unwrap();
2556        assert_eq!(truncated.full_name(), "Eighth Triplet (1/3 QL)");
2557
2558        let triplet = Duration::new(2.0 / 3.0).unwrap().tuplet().unwrap();
2559        assert_eq!((triplet.actual(), triplet.normal()), (3, 2));
2560        assert_eq!(triplet.duration_type(), DurationType::Quarter);
2561        assert_eq!(triplet.dots(), 0);
2562        assert_eq!(triplet.full_name(), "Triplet");
2563        assert_eq!(triplet.multiplier(), FractionType::new(2i32, 3i32));
2564
2565        // the ratios music21 has no word for say the ratio instead
2566        let odd = Tuplet::new(17, 14, DurationType::Quarter, 0);
2567        assert_eq!(odd.full_name(), "Tuplet of 17/14ths");
2568        assert_eq!(odd.total_tuplet_length(), 14.0);
2569        // music21 leaves `st` without its plural
2570        for (actual, normal, name) in [
2571            (40, 21, "Tuplet of 40/21st"),
2572            (3, 1, "Tuplet of 3/1st"),
2573            (12, 11, "Tuplet of 12/11ths"),
2574            (5, 3, "Tuplet of 5/3rds"),
2575            (23, 22, "Tuplet of 23/22nds"),
2576        ] {
2577            let tuplet = Tuplet::new(actual, normal, DurationType::Quarter, 0);
2578            assert_eq!(tuplet.full_name(), name);
2579        }
2580        // Three eighths in the time of one quarter is the same ratio written
2581        // the other way, and lasts exactly as long.
2582        let across =
2583            Tuplet::new(3, 1, DurationType::Eighth, 0).with_normal(DurationType::Quarter, 0);
2584        assert_eq!(across.total_tuplet_length(), 1.0);
2585        // A quarter over three eighths, as music21 reads it: the multiplier
2586        // is what each eighth is scaled by, not the ratio of the counts.
2587        assert_eq!(across.multiplier(), FractionType::new(2i32, 3i32));
2588        assert_eq!(Duration::new(3.75).unwrap().dots(), 3);
2589        assert_eq!(
2590            quarter_length_to_closest_type(200.0).unwrap(),
2591            (DurationType::DuplexMaxima, false)
2592        );
2593        assert!(quarter_length_to_closest_type(0.0).is_err());
2594        assert!(quarter_length_to_closest_type(0.0001).is_err());
2595    }
2596
2597    #[test]
2598    fn duration_types_match_music21s_table() {
2599        assert_eq!(DurationType::ALL.len(), MUSIC21_TYPE_TO_DURATION.len());
2600        for (duration_type, (name, quarter_length)) in
2601            DurationType::ALL.into_iter().zip(MUSIC21_TYPE_TO_DURATION)
2602        {
2603            assert_eq!(duration_type.music21_name(), name);
2604            assert_eq!(duration_type.quarter_length(), quarter_length, "{name}");
2605            assert_eq!(DurationType::from_music21_name(name), Some(duration_type));
2606        }
2607    }
2608
2609    #[test]
2610    fn duration_types_round_trip_through_their_names() {
2611        for duration_type in DurationType::ALL {
2612            let name = duration_type.music21_name();
2613            assert_eq!(name.parse::<DurationType>().unwrap(), duration_type);
2614            assert_eq!(duration_type.to_string(), name);
2615        }
2616        assert!("not-a-duration".parse::<DurationType>().is_err());
2617    }
2618
2619    #[test]
2620    fn each_type_is_half_the_one_before_it() {
2621        // `zero` is the exception and is excluded.
2622        let ordered = &DurationType::ALL[..DurationType::ALL.len() - 1];
2623        for pair in ordered.windows(2) {
2624            assert_eq!(
2625                pair[1].quarter_length() * 2.0,
2626                pair[0].quarter_length(),
2627                "{} should be half of {}",
2628                pair[1],
2629                pair[0]
2630            );
2631        }
2632    }
2633
2634    #[test]
2635    fn dots_add_half_of_what_came_before() {
2636        assert_eq!(DurationType::Half.quarter_length_with_dots(0), 2.0);
2637        assert_eq!(DurationType::Half.quarter_length_with_dots(1), 3.0);
2638        assert_eq!(DurationType::Half.quarter_length_with_dots(2), 3.5);
2639        assert_eq!(DurationType::Half.quarter_length_with_dots(3), 3.75);
2640        assert_eq!(DurationType::Quarter.quarter_length_with_dots(1), 1.5);
2641    }
2642
2643    #[test]
2644    fn durations_convert_to_and_from_note_values() {
2645        assert_eq!(
2646            Duration::from_type(DurationType::Whole).quarter_length(),
2647            4.0
2648        );
2649        assert_eq!(
2650            Duration::from_type(DurationType::Whole).duration_type(),
2651            Some(DurationType::Whole)
2652        );
2653        assert_eq!(
2654            Duration::from_type_with_dots(DurationType::Half, 1).quarter_length(),
2655            3.0
2656        );
2657        // A dotted value keeps the type it is dotted from, as music21 reads
2658        // it: a dotted half is a half with one dot, not a type of its own.
2659        assert_eq!(
2660            Duration::from_type_with_dots(DurationType::Half, 1).duration_type(),
2661            Some(DurationType::Half)
2662        );
2663        assert_eq!(
2664            Duration::from_type_with_dots(DurationType::Half, 1).dots(),
2665            1
2666        );
2667        assert_eq!(
2668            Duration::new(3.75).unwrap().duration_type(),
2669            Some(DurationType::Half)
2670        );
2671        assert_eq!(Duration::new(3.75).unwrap().dots(), 3);
2672        // A triplet eighth is no note value at all.
2673        assert_eq!(Duration::new(1.0 / 3.0).unwrap().duration_type(), None);
2674    }
2675
2676    #[test]
2677    fn the_named_helpers_agree_with_their_types() {
2678        assert_eq!(
2679            Duration::quarter(),
2680            Duration::from_type(DurationType::Quarter)
2681        );
2682        assert_eq!(Duration::half(), Duration::from_type(DurationType::Half));
2683        assert_eq!(Duration::whole(), Duration::from_type(DurationType::Whole));
2684        assert_eq!(
2685            Duration::eighth(),
2686            Duration::from_type(DurationType::Eighth)
2687        );
2688    }
2689
2690    #[test]
2691    fn duration_tracks_quarter_lengths() {
2692        assert_eq!(Duration::quarter().quarter_length(), 1.0);
2693        assert_eq!(Duration::half().quarter_length(), 2.0);
2694        assert_eq!(Duration::whole().quarter_length(), 4.0);
2695        assert_eq!(Duration::eighth().quarter_length(), 0.5);
2696    }
2697
2698    #[test]
2699    fn duration_rejects_invalid_values() {
2700        assert!(Duration::new(-1.0).is_err());
2701        assert!(Duration::new(FloatType::INFINITY).is_err());
2702    }
2703
2704    #[test]
2705    fn a_plain_note_value_is_not_read_as_a_tuplet() {
2706        // music21 tries the exact written value before any ratio, so a
2707        // quarter is a quarter and not two thirds of a dotted quarter.
2708        assert_eq!(Duration::quarter().tuplet(), None);
2709        assert_eq!(Duration::new(3.0).unwrap().tuplet(), None);
2710        assert_eq!(
2711            Duration::new(1.0 / 3.0).unwrap().tuplet(),
2712            Some(Tuplet::new(3, 2, DurationType::Eighth, 0))
2713        );
2714    }
2715
2716    #[test]
2717    fn appending_a_tuplet_shortens_the_length_by_its_ratio() {
2718        // music21's own `appendTuplet` docstring, exactly.
2719        let mut duration = Duration::new(1.0).unwrap();
2720        duration.append_tuplet(Tuplet::new(3, 2, DurationType::Quarter, 0));
2721        assert!((duration.quarter_length() - 2.0 / 3.0).abs() < 1e-12);
2722        assert_eq!(duration.quarter_length_no_tuplets(), 1.0);
2723
2724        duration.append_tuplet(Tuplet::new(5, 4, DurationType::Quarter, 0));
2725        assert!((duration.quarter_length() - 8.0 / 15.0).abs() < 1e-12);
2726        assert_eq!(
2727            duration.aggregate_tuplet_multiplier(),
2728            FractionType::new(8, 15)
2729        );
2730        // The written value is untouched by either tuplet.
2731        assert_eq!(
2732            duration.components(),
2733            vec![(DurationType::Quarter, 0)],
2734            "the written value stays a quarter inside both tuplets"
2735        );
2736    }
2737
2738    #[test]
2739    fn saying_a_length_is_in_no_tuplet_is_not_the_same_as_saying_nothing() {
2740        let inferred = Duration::new(1.0 / 3.0).unwrap();
2741        assert_eq!(inferred.tuplets().len(), 1);
2742
2743        let mut told = Duration::new(1.0 / 3.0).unwrap();
2744        told.set_tuplets(Vec::new());
2745        assert!(told.tuplets().is_empty());
2746        // Losing the triplet leaves the written eighth sounding in full.
2747        assert_eq!(told.quarter_length(), 0.5);
2748    }
2749
2750    #[test]
2751    fn setting_a_length_forgets_the_tuplets_it_was_told() {
2752        let mut duration = Duration::new(1.0).unwrap();
2753        duration.append_tuplet(Tuplet::new(3, 2, DurationType::Quarter, 0));
2754        duration.set_quarter_length(2.0).unwrap();
2755        assert!(duration.tuplets().is_empty());
2756        assert_eq!(duration.quarter_length(), 2.0);
2757    }
2758
2759    #[test]
2760    fn duration_supports_conversions_and_updates() {
2761        let mut duration = Duration::try_from(3 as IntegerType).unwrap();
2762        assert_eq!(duration.quarter_length(), 3.0);
2763
2764        duration.set_quarter_length(1.5).unwrap();
2765        assert_eq!(duration, Duration::try_from(1.5).unwrap());
2766        assert!(duration.set_quarter_length(FloatType::NAN).is_err());
2767    }
2768}