Skip to main content

music21_rs/
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_rs::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_rs::{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_rs::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_rs::{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    pub fn aggregate_tuplet_multiplier(&self) -> FractionType {
1158        self.tuplets()
1159            .iter()
1160            .map(Tuplet::multiplier)
1161            .fold(FractionType::from(1), |total, ratio| total * ratio)
1162    }
1163
1164    /// The total of the written values, before any tuplet shortens them:
1165    /// music21's `quarterLengthNoTuplets`.
1166    pub fn quarter_length_no_tuplets(&self) -> FloatType {
1167        // Folded from a positive nought: Rust sums floats from `-0.0`.
1168        self.written_values()
1169            .iter()
1170            .fold(0.0, |total, value| total + value.quarter_length)
1171    }
1172
1173    /// Says what tuplets this length is written inside, keeping the written
1174    /// values and changing the sounding length to match: music21's `tuplets`
1175    /// setter.
1176    pub fn set_tuplets(&mut self, tuplets: Vec<Tuplet>) {
1177        let written = self.quarter_length_no_tuplets();
1178        self.tuplets = Some(tuplets);
1179        if self.unlinked {
1180            return;
1181        }
1182        self.quarter_length = written * float_from_fraction(self.aggregate_tuplet_multiplier());
1183    }
1184
1185    /// Writes this length inside one more tuplet, shortening it by that
1186    /// tuplet's ratio: music21's `appendTuplet`.
1187    pub fn append_tuplet(&mut self, tuplet: Tuplet) {
1188        let mut tuplets = self.tuplets();
1189        tuplets.push(tuplet);
1190        self.set_tuplets(tuplets);
1191    }
1192
1193    /// The length of the written values alone, with the tuplets a caller set
1194    /// divided back out.
1195    ///
1196    /// Only the set ones: an inferred tuplet is read *off* this length, so
1197    /// dividing by it here would be circular. Dividing in binary floating
1198    /// point rarely lands back on an exact note value, so the result is
1199    /// snapped onto one when it is within a hair of it.
1200    fn written_quarter_length(&self) -> FloatType {
1201        let Some(tuplets) = &self.tuplets else {
1202            return self.quarter_length;
1203        };
1204        if tuplets.is_empty() {
1205            return self.quarter_length;
1206        }
1207        let multiplier = float_from_fraction(self.aggregate_tuplet_multiplier());
1208        if multiplier == 0.0 {
1209            return self.quarter_length;
1210        }
1211        let written = self.quarter_length / multiplier;
1212        let tolerance = written.abs() * TUPLET_TOLERANCE;
1213        DurationType::ALL
1214            .into_iter()
1215            .flat_map(|duration_type| {
1216                (0..=MAX_DOTS).map(move |dots| duration_type.quarter_length_with_dots(dots))
1217            })
1218            .find(|candidate| (candidate - written).abs() <= tolerance)
1219            .unwrap_or(written)
1220    }
1221
1222    /// The written note values this length is made of, tied together:
1223    /// music21's `components`.
1224    ///
1225    /// This is the first half of [`quarter_conversion`], read over the
1226    /// written length. A length whose tuplets a caller has set is read as
1227    /// the tie those tuplets leave, without looking for a tuplet of its own.
1228    ///
1229    /// Where the written values have been said these are they, less any
1230    /// with no type; [`Duration::written_values`] keeps those too.
1231    pub fn components(&self) -> Vec<(DurationType, u32)> {
1232        match &self.written {
1233            Some(written) => written
1234                .iter()
1235                .filter_map(|value| Some((value.duration_type?, value.dots)))
1236                .collect(),
1237            None => convert(self.written_quarter_length(), self.tuplets.is_none()).0,
1238        }
1239    }
1240
1241    /// The written values with the length of each: music21's `components`
1242    /// as the `DurationTuple`s they are there.
1243    ///
1244    /// A length no tie of note values reaches is one value with no type,
1245    /// as it is upstream, and nought is no values at all.
1246    pub fn written_values(&self) -> Vec<DurationTuple> {
1247        if let Some(written) = &self.written {
1248            return written.clone();
1249        }
1250        let components = self.components();
1251        if components.is_empty() && self.quarter_length != 0.0 {
1252            return vec![DurationTuple::from_quarter_length(
1253                self.written_quarter_length(),
1254            )];
1255        }
1256        components
1257            .into_iter()
1258            .map(|(duration_type, dots)| DurationTuple::new(duration_type, dots))
1259            .collect()
1260    }
1261
1262    /// Makes these the written values, and the length what they come to
1263    /// inside the tuplets and dot groups the duration already has.
1264    fn set_written_values(&mut self, written: Vec<DurationTuple>) {
1265        // Read the tuplets off the length while it still says them.
1266        self.tuplets = Some(self.tuplets());
1267        self.written = Some(written);
1268        self.relength();
1269    }
1270
1271    /// music21's `_updateQuarterLength`: the length the written values come
1272    /// to. An unlinked duration keeps the length it was given.
1273    fn relength(&mut self) {
1274        if self.unlinked {
1275            return;
1276        }
1277        let groups: FloatType = self
1278            .dot_groups
1279            .iter()
1280            .flatten()
1281            .filter(|dots| **dots != 0)
1282            .map(|dots| 2.0 - FloatType::powi(0.5, *dots as i32))
1283            .product();
1284        self.quarter_length = self.quarter_length_no_tuplets()
1285            * float_from_fraction(self.aggregate_tuplet_multiplier())
1286            * groups;
1287    }
1288
1289    /// One written value for the whole length, losing how it had been
1290    /// written: music21's `consolidate`.
1291    ///
1292    /// The value is the one the written total comes to, before any tuplet,
1293    /// and has no type where no single note value does. A duration already
1294    /// written as one value is left alone.
1295    ///
1296    /// ```
1297    /// use music21_rs::{Duration, DurationType};
1298    ///
1299    /// let mut duration = Duration::quarter();
1300    /// duration.add_duration_tuple(DurationType::Half, 0);
1301    /// duration.add_duration_tuple(DurationType::Quarter, 0);
1302    /// assert_eq!(duration.components().len(), 3);
1303    /// duration.consolidate();
1304    /// assert_eq!(duration.components(), [(DurationType::Whole, 0)]);
1305    /// ```
1306    pub fn consolidate(&mut self) {
1307        if self.written_values().len() == 1 {
1308            return;
1309        }
1310        let total = self.quarter_length_no_tuplets();
1311        self.set_written_values(vec![DurationTuple::from_quarter_length(total)]);
1312    }
1313
1314    /// Cuts the written value sounding at a position in two, leaving the
1315    /// whole length as it was: music21's `sliceComponentAtPosition`.
1316    ///
1317    /// The position is counted in the written values. One that falls on the
1318    /// join between two values, or at either end, cuts nothing and is an
1319    /// error.
1320    pub fn slice_component_at_position(&mut self, position: FloatType) -> Result<()> {
1321        let written = self.written_values();
1322        let mut start = 0.0;
1323        for (index, value) in written.iter().enumerate() {
1324            let end = start + value.quarter_length;
1325            if position > start && position < end {
1326                let mut sliced = written[..index].to_vec();
1327                sliced.push(DurationTuple::from_quarter_length(position - start));
1328                sliced.push(DurationTuple::from_quarter_length(end - position));
1329                sliced.extend_from_slice(&written[index + 1..]);
1330                self.set_written_values(sliced);
1331                return Ok(());
1332            }
1333            start = end;
1334        }
1335        Err(Error::Duration(
1336            "no slice is possible at this quarter position".to_string(),
1337        ))
1338    }
1339
1340    /// Writes the duration with dots above its dots: music21's `dotGroups`
1341    /// setter. Each group lengthens the value as dots do, so a half under
1342    /// `[1, 1]` lasts four and a half quarters. The plain dots come off the
1343    /// written values, since the groups now say how the note is dotted.
1344    pub fn set_dot_groups(&mut self, groups: Vec<u32>) {
1345        let undotted = self
1346            .written_values()
1347            .into_iter()
1348            .map(|value| match value.duration_type {
1349                Some(duration_type) => DurationTuple::new(duration_type, 0),
1350                None => value,
1351            })
1352            .collect();
1353        self.dot_groups = Some(groups);
1354        self.set_written_values(undotted);
1355    }
1356
1357    /// The same length written as a tie of singly dotted values, for a
1358    /// reader with no dot groups: music21's `splitDotGroups`.
1359    ///
1360    /// Each group after the first ties on every value so far at the next
1361    /// note value down, so a half under `[1, 1]` is a dotted half tied to a
1362    /// dotted quarter. It is an error for a duration that is not one
1363    /// written value.
1364    pub fn split_dot_groups(&self) -> Result<Duration> {
1365        let (duration_type, _) = self.type_and_dots().ok_or_else(|| {
1366            Error::Duration("only a single written value has dot groups to split".to_string())
1367        })?;
1368        let groups = self.dot_groups();
1369        let mut values = vec![DurationTuple::new(
1370            duration_type,
1371            groups.first().copied().unwrap_or(0),
1372        )];
1373        for _ in 1..groups.len() {
1374            let smaller: Vec<DurationTuple> = values
1375                .iter()
1376                .map(|value| {
1377                    let kind = value.duration_type.unwrap_or(duration_type);
1378                    DurationTuple::new(kind.next_smaller().unwrap_or(kind), value.dots)
1379                })
1380                .collect();
1381            values.extend(smaller);
1382        }
1383        let mut split = self.clone();
1384        split.dot_groups = None;
1385        split.set_written_values(values);
1386        Ok(split)
1387    }
1388
1389    /// Whether the written values and the sounding length move together:
1390    /// music21's `linked`, true unless a caller has said otherwise.
1391    pub fn linked(&self) -> bool {
1392        !self.unlinked
1393    }
1394
1395    /// Links or unlinks the written values and the length: music21's
1396    /// `linked` setter.
1397    ///
1398    /// Unlinking keeps the values the duration is written as today, and
1399    /// [`Duration::set_quarter_length`] then changes how long it sounds
1400    /// without touching them. Linking again keeps the length and reads the
1401    /// values off it afresh.
1402    pub fn set_linked(&mut self, linked: bool) {
1403        if !linked && !self.unlinked {
1404            self.tuplets = Some(self.tuplets());
1405            self.written = Some(self.written_values());
1406        } else if linked && self.unlinked {
1407            self.written = None;
1408            self.tuplets = None;
1409            self.dot_groups = None;
1410        }
1411        self.unlinked = !linked;
1412    }
1413
1414    /// The same written values sounding for no time, unlinked from their
1415    /// length: the duration of a grace note, music21's `getGraceDuration`.
1416    ///
1417    /// A duration with no written values is written as an eighth, since a
1418    /// grace note has to be written as something. The tuplets are dropped,
1419    /// as they are upstream.
1420    ///
1421    /// ```
1422    /// use music21_rs::{Duration, DurationType};
1423    ///
1424    /// let grace = Duration::new(1.25)?.grace_duration();
1425    /// assert_eq!(grace.quarter_length(), 0.0);
1426    /// assert!(!grace.linked());
1427    /// assert_eq!(
1428    ///     grace.components(),
1429    ///     [(DurationType::Quarter, 0), (DurationType::Sixteenth, 0)]
1430    /// );
1431    /// # Ok::<(), music21_rs::Error>(())
1432    /// ```
1433    pub fn grace_duration(&self) -> Duration {
1434        let mut written: Vec<DurationTuple> = self
1435            .written_values()
1436            .into_iter()
1437            .map(DurationTuple::silenced)
1438            .collect();
1439        if written.is_empty() {
1440            written.push(DurationTuple::new(DurationType::Eighth, 0).silenced());
1441        }
1442        Duration {
1443            quarter_length: 0.0,
1444            tuplets: Some(Vec::new()),
1445            written: Some(written),
1446            dot_groups: None,
1447            unlinked: true,
1448            grace: Some(Grace::new()),
1449            said: true,
1450        }
1451    }
1452
1453    /// Whether the way this length is written was worked out from the
1454    /// length rather than said: music21's `expressionIsInferred`.
1455    ///
1456    /// A duration made from a length is inferred, and one made from a note
1457    /// value is not. Making notation may write an inferred length another
1458    /// way, cutting it to finish a tuplet or joining it with its neighbours;
1459    /// one that was said is left as it was written.
1460    ///
1461    /// ```
1462    /// use music21_rs::{Duration, DurationType};
1463    ///
1464    /// assert!(Duration::new(0.5)?.expression_is_inferred());
1465    /// assert!(!Duration::from_type(DurationType::Eighth).expression_is_inferred());
1466    /// # Ok::<(), music21_rs::Error>(())
1467    /// ```
1468    pub fn expression_is_inferred(&self) -> bool {
1469        !self.said
1470    }
1471
1472    /// Says whether the way this length is written may be worked out again
1473    /// from the length.
1474    pub fn set_expression_is_inferred(&mut self, inferred: bool) {
1475        self.said = !inferred;
1476    }
1477
1478    /// [`Duration::grace_duration`], as an appoggiatura where `appoggiatura`
1479    /// says so: music21's `getGraceDuration(appoggiatura=True)`, which is
1480    /// unslashed and takes its time from the note it leans on.
1481    pub(crate) fn grace_duration_as(&self, appoggiatura: bool) -> Duration {
1482        let mut grace = self.grace_duration();
1483        if appoggiatura {
1484            grace.grace = Some(Grace::appoggiatura());
1485        }
1486        grace
1487    }
1488
1489    /// Whether the length needs more than one written value, tied: music21's
1490    /// `isComplex`.
1491    pub fn is_complex(&self) -> bool {
1492        self.components().len() > 1
1493    }
1494
1495    /// The dots on the written value, as the one dot group the crate keeps:
1496    /// music21's `dotGroups`, which can also write one length twice over
1497    /// where a mensural notation asks for it.
1498    pub fn dot_groups(&self) -> Vec<u32> {
1499        match &self.dot_groups {
1500            Some(groups) => groups.clone(),
1501            None => vec![self.dots()],
1502        }
1503    }
1504
1505    /// Takes the length away: music21's `clear`, which empties the written
1506    /// values so the duration sounds for no time. The tuplets it was told
1507    /// stay.
1508    pub fn clear(&mut self) {
1509        self.tuplets = Some(self.tuplets());
1510        self.written = Some(Vec::new());
1511        self.dot_groups = None;
1512        self.quarter_length = 0.0;
1513    }
1514
1515    /// Ties one more written value onto the end: music21's
1516    /// `addDurationTuple`, which lengthens the duration by that value inside
1517    /// whatever tuplets it is written in.
1518    ///
1519    /// The values are kept as they were added from then on, so a quarter
1520    /// with a half and a quarter tied on is three values and not a whole
1521    /// until it is [consolidated](Duration::consolidate).
1522    pub fn add_duration_tuple(&mut self, duration_type: DurationType, dots: u32) {
1523        let mut written = self.written_values();
1524        written.push(DurationTuple::new(duration_type, dots));
1525        self.set_written_values(written);
1526    }
1527
1528    /// Which written value is sounding at a position within the duration:
1529    /// music21's `componentIndexAtQtrPosition`. The positions are counted
1530    /// in the written values, before any tuplet scales them, and the start
1531    /// and the very end answer the first and the last value.
1532    pub fn component_index_at_qtr_position(&self, position: FloatType) -> Result<usize> {
1533        let components = self.components();
1534        if components.is_empty() {
1535            return Err(Error::Duration(
1536                "Need components to run getComponentIndexAtQtrPosition".to_string(),
1537            ));
1538        }
1539        let total = self.quarter_length_no_tuplets();
1540        if position.is_nan() || position < 0.0 {
1541            return Err(Error::Value(
1542                "position is before the start of the duration".to_string(),
1543            ));
1544        }
1545        if position > total {
1546            return Err(Error::Value(
1547                "position is after the end of the duration".to_string(),
1548            ));
1549        }
1550        if position == total {
1551            return Ok(components.len() - 1);
1552        }
1553        let mut reached = 0.0;
1554        for (index, (duration_type, dots)) in components.iter().enumerate() {
1555            reached += duration_type.quarter_length_with_dots(*dots);
1556            if reached > position {
1557                return Ok(index);
1558            }
1559        }
1560        Ok(components.len() - 1)
1561    }
1562
1563    /// Where a written value starts within the duration, counted in the
1564    /// written values: music21's `componentStartTime`. An index past the
1565    /// values is an error.
1566    pub fn component_start_time(&self, index: usize) -> Result<FloatType> {
1567        let components = self.components();
1568        if index >= components.len() {
1569            return Err(Error::Duration(format!(
1570                "invalid component index value {index} submitted; value must be an integer between 0 and {}",
1571                components.len().saturating_sub(1)
1572            )));
1573        }
1574        Ok(components[..index]
1575            .iter()
1576            .map(|(duration_type, dots)| duration_type.quarter_length_with_dots(*dots))
1577            .sum())
1578    }
1579
1580    /// Returns music21's `fullName` for a single written note value, such as
1581    /// `"Dotted Quarter"`, `"Double Dotted Half"`, `"Imperfect Longa"` or
1582    /// `"Quarter Triplet (2/3 QL)"`.
1583    ///
1584    /// A length that has to be written as a tie names each of its values and
1585    /// joins them, `"Quarter tied to 16th (1 1/4 total QL)"`. A length no
1586    /// note value reaches is `"Inexpressible"` and a zero one is
1587    /// `"Zero Duration (0 total QL)"`, both as music21 names them.
1588    pub fn full_name(&self) -> String {
1589        let components = self.components();
1590        if components.is_empty() {
1591            return if self.quarter_length == 0.0 {
1592                // music21 writes a stray second space here, from joining a
1593                // name that already ends in one; its own docstring for this
1594                // shows a single space, which is what the whitespace its
1595                // test runner normalises away comes to.
1596                "Zero Duration (0 total QL)".to_string()
1597            } else {
1598                "Inexpressible".to_string()
1599            };
1600        }
1601        // A tuplet is only asked for once the length has failed to be a
1602        // plain or dotted note value, which is music21's order: a quarter is
1603        // a quarter, even though it is also a dotted quarter in a triplet.
1604        let tuplet = if components.len() == 1 && self.type_and_dots().is_none() {
1605            self.tuplets().first().copied()
1606        } else {
1607            None
1608        };
1609        let names: Vec<String> = components
1610            .iter()
1611            .map(|(duration_type, dots)| {
1612                let mensural = matches!(duration_type, DurationType::Longa | DurationType::Maxima);
1613                let mut name = format!("{}{}", dot_prefix(*dots, mensural), duration_type.title());
1614                if let Some(tuplet) = &tuplet {
1615                    name.push(' ');
1616                    name.push_str(&tuplet.full_name());
1617                }
1618                // music21 shows the length itself once the name alone stops
1619                // saying what it is: past two dots, or inside a tuplet.
1620                if tuplet.is_some() || *dots >= 3 {
1621                    name.push_str(&format!(" ({} QL)", mixed_numeral(self.quarter_length)));
1622                }
1623                name
1624            })
1625            .collect();
1626        let mut name = names.join(" tied to ");
1627        if components.len() != 1 {
1628            name.push_str(&format!(
1629                " ({} total QL)",
1630                mixed_numeral(self.quarter_length)
1631            ));
1632        }
1633        name
1634    }
1635
1636    /// Returns the note-value type whose undotted length this duration is.
1637    ///
1638    /// Returns `None` for a length that is not a plain note value, such as a
1639    /// dotted or tuplet duration.
1640    pub fn duration_type(&self) -> Option<DurationType> {
1641        self.type_and_dots().map(|(duration_type, _)| duration_type)
1642    }
1643
1644    /// Returns the duration in quarter lengths.
1645    pub fn quarter_length(&self) -> FloatType {
1646        self.quarter_length
1647    }
1648
1649    /// Updates the duration in quarter lengths.
1650    ///
1651    /// The written values are read off the new length, unless the duration
1652    /// is [unlinked](Duration::set_linked) and keeps the ones it has.
1653    pub fn set_quarter_length(&mut self, quarter_length: FloatType) -> Result<()> {
1654        let mut replacement = Self::new(quarter_length)?;
1655        if self.unlinked {
1656            self.quarter_length = replacement.quarter_length;
1657        } else {
1658            // A length given again as it already is says nothing new.
1659            replacement.said = self.said && self.quarter_length == replacement.quarter_length;
1660            *self = replacement;
1661        }
1662        Ok(())
1663    }
1664}
1665
1666const MAX_DOTS: u32 = 4;
1667
1668/// The note value and dot count whose length is exactly this one, if any.
1669fn exact_type_and_dots(quarter_length: FloatType) -> Option<(DurationType, u32)> {
1670    DurationType::ALL.into_iter().find_map(|duration_type| {
1671        (0..=MAX_DOTS)
1672            .find(|dots| duration_type.quarter_length_with_dots(*dots) == quarter_length)
1673            .map(|dots| (duration_type, dots))
1674    })
1675}
1676
1677/// A ratio as a float, which a tuplet's is whenever it meets a quarter
1678/// length. Zero for the ratio that has no value, which no tuplet has.
1679fn float_from_fraction(ratio: FractionType) -> FloatType {
1680    ratio.to_f64().unwrap_or(0.0)
1681}
1682
1683/// Returns the note-value type closest to a quarter length, and whether it is
1684/// exact, as music21's `quarterLengthToClosestType` does: a length between two
1685/// types reports the longer one, so a triplet quarter is an inexact eighth.
1686pub fn quarter_length_to_closest_type(quarter_length: FloatType) -> Result<(DurationType, bool)> {
1687    let too_small = || {
1688        Error::Duration(format!(
1689            "cannot return types smaller than 2048th; quarter length was {quarter_length}"
1690        ))
1691    };
1692    if quarter_length.is_nan() || quarter_length <= 0.0 {
1693        return Err(too_small());
1694    }
1695    let note_length = 4.0 / quarter_length;
1696    if let Some(exact) = DurationType::ALL
1697        .into_iter()
1698        .find(|duration_type| duration_type.type_number() == Some(note_length))
1699    {
1700        return Ok((exact, true));
1701    }
1702    let upper_bound = 8.0 / quarter_length;
1703    if let Some(closest) = DurationType::ALL.into_iter().find(|duration_type| {
1704        duration_type
1705            .type_number()
1706            .is_some_and(|number| note_length < number && number < upper_bound)
1707    }) {
1708        return Ok((closest, false));
1709    }
1710    if quarter_length > 128.0 {
1711        return Ok((DurationType::DuplexMaxima, false));
1712    }
1713    Err(too_small())
1714}
1715
1716/// The tuplets a length can be written as, shortest note value first:
1717/// music21's `quarterLengthToTuplet`, stopping after `max_to_return` of
1718/// them.
1719///
1720/// The search walks the note values from shortest to longest and, for
1721/// each, tries every tuplet numerator and every count of them that fits.
1722/// Order is what decides the answer: two thirds of a quarter matches a
1723/// quarter in a triplet before it matches anything longer, which is why
1724/// music21 calls it a quarter triplet and not two eighth triplets. Dotted
1725/// tuplets are found; nested ones and four in the time of three are not,
1726/// the latter being a dotted note.
1727pub fn quarter_length_to_tuplet(quarter_length: FloatType, max_to_return: usize) -> Vec<Tuplet> {
1728    let mut found = Vec::new();
1729    if quarter_length.is_nan() || quarter_length <= 0.0 || max_to_return == 0 {
1730        return found;
1731    }
1732    let mut values = DurationType::ALL;
1733    values.sort_by(|left, right| {
1734        left.quarter_length()
1735            .partial_cmp(&right.quarter_length())
1736            .unwrap_or(std::cmp::Ordering::Equal)
1737    });
1738    let tolerance = quarter_length * TUPLET_TOLERANCE;
1739    for duration_type in values {
1740        for actual in TUPLET_NUMERATORS {
1741            for normal in 1..actual {
1742                for dots in TUPLET_DOTS {
1743                    let candidate = duration_type.quarter_length_with_dots(dots)
1744                        * FloatType::from(normal)
1745                        / FloatType::from(actual);
1746                    if (candidate - quarter_length).abs() <= tolerance {
1747                        found.push(Tuplet::new(actual, normal, duration_type, dots));
1748                        break;
1749                    }
1750                }
1751            }
1752            if found.len() >= max_to_return {
1753                return found;
1754            }
1755        }
1756    }
1757    found
1758}
1759
1760/// The one enormous tuplet music21 falls back on for a length no tie of
1761/// written values reaches: its `quarterLengthToNonPowerOf2Tuplet`.
1762///
1763/// Any length can be written as a single note inside a strange enough
1764/// tuplet — 53/25 of a quarter is a whole note in a tuplet of a hundred
1765/// in the time of fifty-three — and music21 tries that before it calls a
1766/// length inexpressible. The answer is the tuplet together with the note
1767/// value and dots written inside it, or nothing for a length that defeats
1768/// even that.
1769pub fn quarter_length_to_non_power_of_2_tuplet(
1770    quarter_length: FloatType,
1771) -> Option<(Tuplet, DurationType, u32)> {
1772    if quarter_length.is_nan() || quarter_length <= 0.0 {
1773        return None;
1774    }
1775    let (original_actual, original_normal) =
1776        limited_fraction(1.0 / quarter_length, DENOMINATOR_LIMIT)?;
1777    let (mut actual, mut normal) = (original_actual, original_normal);
1778    // Between one and two, which is where a tuplet ratio belongs.
1779    while actual < normal {
1780        actual *= 2;
1781        reduce(&mut actual, &mut normal);
1782    }
1783    while actual > normal * 2 {
1784        normal *= 2;
1785        reduce(&mut actual, &mut normal);
1786    }
1787    let (written, _) = quarter_length_to_closest_type(quarter_length / normal as FloatType).ok()?;
1788    // What is written inside the tuplet, which is the ratio the normalising
1789    // undid.
1790    let inside = (actual as FloatType / normal as FloatType)
1791        / (original_actual as FloatType / original_normal as FloatType);
1792    let (kind, dots) = exact_type_and_dots(inside)?;
1793    Some((
1794        Tuplet::new(
1795            u32::try_from(actual).ok()?,
1796            u32::try_from(normal).ok()?,
1797            written,
1798            0,
1799        ),
1800        kind,
1801        dots,
1802    ))
1803}
1804
1805/// The written note values a length is made of and the tuplet, if any, they
1806/// are written inside: music21's `quarterConversion`.
1807///
1808/// One value for a plain or dotted note, one for a tuplet (the written
1809/// value, which the tuplet's ratio then scales), and several for a length
1810/// that can only be written as a tie — a quarter tied to a sixteenth for
1811/// five sixteenths. Empty for a length that runs off the end of the note
1812/// values, which is music21's `inexpressible`, and for a length of nought.
1813///
1814/// The tie is found greedily, largest value first, as music21 finds it:
1815/// take the largest note that fits, and look for a single dotted value
1816/// covering what is left before taking another bite. A length no tie of
1817/// eight values reaches is written as one note inside the tuplet
1818/// [`quarter_length_to_non_power_of_2_tuplet`] finds.
1819pub fn quarter_conversion(quarter_length: FloatType) -> (Vec<(DurationType, u32)>, Option<Tuplet>) {
1820    convert(quarter_length, true)
1821}
1822
1823/// [`quarter_conversion`], with the search for an ordinary tuplet turned off
1824/// where a caller has already said which tuplets the length is written in.
1825fn convert(
1826    written: FloatType,
1827    look_for_tuplet: bool,
1828) -> (Vec<(DurationType, u32)>, Option<Tuplet>) {
1829    // Zero is written as nothing at all, which is what music21's empty
1830    // `components` says; `type_and_dots` would call it a `zero` note.
1831    if written == 0.0 {
1832        return (Vec::new(), None);
1833    }
1834    if let Some(value) = exact_type_and_dots(written) {
1835        return (vec![value], None);
1836    }
1837    // Shorter than the shortest note value, or longer than a tie of the
1838    // longest can reach: music21 calls both *inexpressible*, and asks this
1839    // before it looks for a tuplet.
1840    let Ok((largest, _)) = quarter_length_to_closest_type(written) else {
1841        return (Vec::new(), None);
1842    };
1843    if largest.next_larger().is_none() {
1844        return (Vec::new(), None);
1845    }
1846    if look_for_tuplet && let Some(tuplet) = quarter_length_to_tuplet(written, 1).into_iter().next()
1847    {
1848        return (vec![(tuplet.duration_type(), tuplet.dots())], Some(tuplet));
1849    }
1850    let mut components = vec![(largest, 0)];
1851    let mut remainder = written - largest.quarter_length();
1852    for _ in 0..MAX_TIED_COMPONENTS {
1853        if let Some(rest) = exact_type_and_dots(remainder) {
1854            components.push(rest);
1855            return (components, None);
1856        }
1857        let Ok((next, _)) = quarter_length_to_closest_type(remainder) else {
1858            break;
1859        };
1860        remainder -= next.quarter_length();
1861        components.push((next, 0));
1862    }
1863    match quarter_length_to_non_power_of_2_tuplet(written) {
1864        Some((tuplet, kind, dots)) => (vec![(kind, dots)], Some(tuplet)),
1865        None => (Vec::new(), None),
1866    }
1867}
1868
1869/// The largest denominator [`mixed_numeral`] will write. music21 allows any
1870/// up to 65535, but it prints the quarter length of a written note, and a
1871/// note nobody can write does not want a fraction with a four-digit
1872/// denominator in its name.
1873const MAX_MIXED_NUMERAL_DENOMINATOR: u32 = 1024;
1874
1875/// How close a fraction has to be to be written as one, relative to the
1876/// value.
1877const MIXED_NUMERAL_TOLERANCE: FloatType = 1e-6;
1878
1879/// Writes a quarter length the way music21's `mixedNumeral` does: `"2"`,
1880/// `"1/2"`, `"1 3/4"`.
1881fn mixed_numeral(value: FloatType) -> String {
1882    let whole = value.trunc() as IntegerType;
1883    let remainder = value - value.trunc();
1884    if remainder == 0.0 {
1885        return whole.to_string();
1886    }
1887    // Ascending denominators, so the simplest fraction that fits wins: a
1888    // third has to be reachable as well as a quarter, since a tuplet length
1889    // is not a power of two. The tolerance stands in for the snapping
1890    // music21 does when it reads a quarter length, which is what makes its
1891    // own `0.333333` a triplet third rather than a decimal.
1892    let tolerance = MIXED_NUMERAL_TOLERANCE * value.abs().max(1.0);
1893    let fractional = (1..=MAX_MIXED_NUMERAL_DENOMINATOR)
1894        .find_map(|denominator| {
1895            let numerator = (remainder * FloatType::from(denominator)).round();
1896            ((remainder - numerator / FloatType::from(denominator)).abs() <= tolerance)
1897                .then(|| format!("{}/{denominator}", numerator as IntegerType))
1898        })
1899        .unwrap_or_else(|| remainder.to_string());
1900    if whole == 0 {
1901        fractional
1902    } else {
1903        format!("{whole} {fractional}")
1904    }
1905}
1906
1907impl Default for Duration {
1908    fn default() -> Self {
1909        Self::quarter()
1910    }
1911}
1912
1913impl PartialEq for Duration {
1914    fn eq(&self, other: &Self) -> bool {
1915        self.quarter_length == other.quarter_length
1916    }
1917}
1918
1919impl Eq for Duration {}
1920
1921/// Hashes what equality compares, the quarter length.
1922impl std::hash::Hash for Duration {
1923    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1924        ordered_float::OrderedFloat(self.quarter_length).hash(state);
1925    }
1926}
1927
1928impl TryFrom<FloatType> for Duration {
1929    type Error = Error;
1930
1931    fn try_from(value: FloatType) -> Result<Self> {
1932        Self::new(value)
1933    }
1934}
1935
1936impl TryFrom<IntegerType> for Duration {
1937    type Error = Error;
1938
1939    fn try_from(value: IntegerType) -> Result<Self> {
1940        Self::new(value as FloatType)
1941    }
1942}
1943
1944#[cfg(test)]
1945mod tests {
1946
1947    #[test]
1948    fn a_type_is_found_from_its_undotted_length_alone() {
1949        assert_eq!(
1950            DurationType::from_quarter_length(2.0),
1951            Some(DurationType::Half)
1952        );
1953        assert_eq!(
1954            DurationType::from_quarter_length(0.125),
1955            Some(DurationType::ThirtySecond)
1956        );
1957        assert_eq!(DurationType::from_quarter_length(3.0), None);
1958        assert_eq!(super::mixed_numeral(2.0 / 3.0), "2/3");
1959        assert_eq!(super::mixed_numeral(1.0 / 3.0 + 1.0), "1 1/3");
1960    }
1961
1962    #[test]
1963    fn a_duration_is_cleared_and_lengthened_a_value_at_a_time() {
1964        let mut duration = Duration::new(1.5).unwrap();
1965        assert_eq!(duration.dot_groups(), [1]);
1966        duration.add_duration_tuple(DurationType::Eighth, 0);
1967        assert_eq!(duration.quarter_length(), 2.0);
1968        duration.clear();
1969        assert_eq!(duration.quarter_length(), 0.0);
1970        assert_eq!(duration.dot_groups(), [0]);
1971        // Inside a triplet the value added is scaled as the rest is.
1972        let mut triplet = Duration::new(2.0 / 3.0).unwrap();
1973        triplet.add_duration_tuple(DurationType::Quarter, 0);
1974        assert!((triplet.quarter_length() - 4.0 / 3.0).abs() < 1e-9);
1975    }
1976
1977    /// Every expectation here was read off music21.
1978    #[test]
1979    fn written_values_are_cut_consolidated_split_and_unlinked_as_music21_does() {
1980        let typed = |duration: &Duration| -> Vec<(Option<DurationType>, u32, FloatType)> {
1981            duration
1982                .written_values()
1983                .iter()
1984                .map(|value| (value.duration_type(), value.dots(), value.quarter_length()))
1985                .collect()
1986        };
1987
1988        // A cut leaves the length alone, and a piece may have no type.
1989        let mut tied = Duration::new(1.25).unwrap();
1990        tied.slice_component_at_position(0.5).unwrap();
1991        assert_eq!(
1992            tied.components(),
1993            [
1994                (DurationType::Eighth, 0),
1995                (DurationType::Eighth, 0),
1996                (DurationType::Sixteenth, 0)
1997            ]
1998        );
1999        assert_eq!(tied.quarter_length(), 1.25);
2000        let mut dotted = Duration::new(3.0).unwrap();
2001        dotted.slice_component_at_position(1.25).unwrap();
2002        assert_eq!(
2003            typed(&dotted),
2004            [(None, 0, 1.25), (Some(DurationType::Quarter), 2, 1.75)]
2005        );
2006        assert!(
2007            Duration::new(1.25)
2008                .unwrap()
2009                .slice_component_at_position(1.0)
2010                .is_err()
2011        );
2012
2013        // Consolidating keeps the length and may lose the type.
2014        let mut long = Duration::quarter();
2015        long.add_duration_tuple(DurationType::Half, 0);
2016        long.add_duration_tuple(DurationType::Half, 0);
2017        assert_eq!(long.components().len(), 3);
2018        assert_eq!(long.duration_type(), None);
2019        long.consolidate();
2020        assert_eq!(typed(&long), [(None, 0, 5.0)]);
2021        assert_eq!(long.quarter_length(), 5.0);
2022        let mut triplets = Duration::new(1.0 / 3.0).unwrap();
2023        triplets.add_duration_tuple(DurationType::Eighth, 0);
2024        triplets.consolidate();
2025        assert_eq!(triplets.components(), [(DurationType::Quarter, 0)]);
2026        assert!((triplets.quarter_length() - 2.0 / 3.0).abs() < 1e-9);
2027
2028        // Dot groups multiply, and split into a tie of dotted values.
2029        let mut half = Duration::half();
2030        half.set_dot_groups(vec![1, 1]);
2031        assert_eq!(half.quarter_length(), 4.5);
2032        assert_eq!(half.components(), [(DurationType::Half, 0)]);
2033        let split = half.split_dot_groups().unwrap();
2034        assert_eq!(
2035            split.components(),
2036            [(DurationType::Half, 1), (DurationType::Quarter, 1)]
2037        );
2038        assert_eq!(split.quarter_length(), 4.5);
2039        half.set_dot_groups(vec![1, 1, 1]);
2040        assert_eq!(half.quarter_length(), 6.75);
2041        assert_eq!(
2042            half.split_dot_groups().unwrap().components(),
2043            [
2044                (DurationType::Half, 1),
2045                (DurationType::Quarter, 1),
2046                (DurationType::Quarter, 1),
2047                (DurationType::Eighth, 1)
2048            ]
2049        );
2050        assert_eq!(
2051            Duration::new(1.5)
2052                .unwrap()
2053                .split_dot_groups()
2054                .unwrap()
2055                .components(),
2056            [(DurationType::Quarter, 1)]
2057        );
2058
2059        // Unlinked, the length and the written value go their own ways;
2060        // linked again, the value is read off the length.
2061        let mut unlinked = Duration::quarter();
2062        assert!(unlinked.linked());
2063        unlinked.set_linked(false);
2064        unlinked.set_quarter_length(3.0).unwrap();
2065        assert_eq!(unlinked.duration_type(), Some(DurationType::Quarter));
2066        assert_eq!(unlinked.quarter_length(), 3.0);
2067        unlinked.set_linked(true);
2068        assert_eq!(unlinked.type_and_dots(), Some((DurationType::Half, 1)));
2069        assert_eq!(unlinked.quarter_length(), 3.0);
2070
2071        // A grace note keeps what it is written as and sounds nothing.
2072        let grace = Duration::new(1.25).unwrap().grace_duration();
2073        assert_eq!(
2074            typed(&grace),
2075            [
2076                (Some(DurationType::Quarter), 0, 0.0),
2077                (Some(DurationType::Sixteenth), 0, 0.0)
2078            ]
2079        );
2080        assert!(!grace.linked());
2081        assert_eq!(grace.quarter_length(), 0.0);
2082        let bare = Duration::new(0.0).unwrap().grace_duration();
2083        assert_eq!(bare.duration_type(), Some(DurationType::Eighth));
2084        let appoggiatura = Duration::new(1.0 / 3.0).unwrap().grace_duration();
2085        assert_eq!(appoggiatura.components(), [(DurationType::Eighth, 0)]);
2086        assert!(appoggiatura.tuplets().is_empty());
2087    }
2088
2089    /// music21's own examples, over a tie the crate reads off the length.
2090    #[test]
2091    fn a_position_within_a_tie_names_the_value_sounding_there() {
2092        let tied = Duration::new(2.5).unwrap();
2093        assert_eq!(tied.components().len(), 2);
2094        assert_eq!(tied.component_index_at_qtr_position(0.0).unwrap(), 0);
2095        assert_eq!(tied.component_index_at_qtr_position(1.5).unwrap(), 0);
2096        assert_eq!(tied.component_index_at_qtr_position(2.0).unwrap(), 1);
2097        assert_eq!(tied.component_index_at_qtr_position(2.5).unwrap(), 1);
2098        assert!(tied.component_index_at_qtr_position(3.0).is_err());
2099        assert!(tied.component_index_at_qtr_position(-1.0).is_err());
2100        assert!(
2101            Duration::new(0.0)
2102                .unwrap()
2103                .component_index_at_qtr_position(0.0)
2104                .is_err()
2105        );
2106        assert_eq!(tied.component_start_time(0).unwrap(), 0.0);
2107        assert_eq!(tied.component_start_time(1).unwrap(), 2.0);
2108        assert!(tied.component_start_time(2).is_err());
2109    }
2110
2111    #[test]
2112    fn a_tuplet_reads_and_writes_both_of_its_sides() {
2113        let mut tuplet = Tuplet::new(3, 2, DurationType::Eighth, 0);
2114        assert_eq!(tuplet.duration_actual(), Some((DurationType::Eighth, 0)));
2115        assert_eq!(tuplet.duration_normal(), Some((DurationType::Eighth, 0)));
2116        assert_eq!(tuplet.tuplet_actual(), (3, Some((DurationType::Eighth, 0))));
2117        assert_eq!(tuplet.tuplet_normal(), (2, Some((DurationType::Eighth, 0))));
2118        tuplet.set_duration_type(DurationType::Quarter, 1);
2119        assert_eq!(tuplet.duration_actual(), Some((DurationType::Quarter, 1)));
2120        assert_eq!(tuplet.normal_duration_type(), DurationType::Quarter);
2121        assert_eq!(tuplet.normal_dots(), 1);
2122        tuplet.set_ratio(5, 4);
2123        assert_eq!((tuplet.actual(), tuplet.normal()), (5, 4));
2124        assert_eq!(tuplet.multiplier(), FractionType::new(4, 5));
2125    }
2126
2127    /// Every answer here is music21's, for a tuplet saying no written value
2128    /// and for ones whose two sides are counted in different values.
2129    #[test]
2130    fn a_tuplet_may_leave_its_written_values_unsaid() {
2131        let mut triplet = Tuplet::ratio(3, 2);
2132        assert_eq!(triplet.duration_actual(), None);
2133        assert_eq!(triplet.total_tuplet_length(), 1.0);
2134        assert_eq!(triplet.multiplier(), FractionType::new(2, 3));
2135        triplet.set_duration_type(DurationType::Half, 1);
2136        assert_eq!(triplet.total_tuplet_length(), 6.0);
2137
2138        let mut three_in_one = Tuplet::ratio(3, 1);
2139        three_in_one.set_duration_actual(Some((DurationType::Quarter, 0)));
2140        three_in_one.set_duration_normal(Some((DurationType::Half, 0)));
2141        assert_eq!(three_in_one.multiplier(), FractionType::new(2, 3));
2142        assert_eq!(three_in_one.total_tuplet_length(), 2.0);
2143
2144        let mut six = Tuplet::ratio(6, 2);
2145        six.set_duration_type(DurationType::Eighth, 0);
2146        let halved = six.augmented(0.5).unwrap();
2147        assert_eq!(halved.duration_actual(), Some((DurationType::Sixteenth, 0)));
2148        assert_eq!(halved.multiplier(), FractionType::new(1, 3));
2149        assert!(six.augmented(-1.0).is_err());
2150
2151        let mut five = Tuplet::ratio(5, 2);
2152        five.set_duration_type(DurationType::Half, 0);
2153        five.set_duration_normal(Some((DurationType::Whole, 0)));
2154        assert_eq!(five.total_tuplet_length(), 8.0);
2155    }
2156
2157    /// music21's own examples for `quarterLengthToTuplet`,
2158    /// `quarterLengthToNonPowerOf2Tuplet` and `quarterConversion`.
2159    #[test]
2160    fn a_length_is_read_as_the_tuplets_and_ties_music21_reads() {
2161        use super::{
2162            Duration, DurationType, quarter_conversion, quarter_length_to_non_power_of_2_tuplet,
2163            quarter_length_to_tuplet,
2164        };
2165
2166        let names = |tuplets: Vec<Tuplet>| -> Vec<String> {
2167            tuplets
2168                .iter()
2169                .map(|tuplet| {
2170                    format!(
2171                        "{}/{}/{}",
2172                        tuplet.actual(),
2173                        tuplet.normal(),
2174                        tuplet.duration_type().music21_name()
2175                    )
2176                })
2177                .collect()
2178        };
2179        assert_eq!(
2180            names(quarter_length_to_tuplet(0.333_333_33, 4)),
2181            ["3/2/eighth", "3/1/quarter"]
2182        );
2183        assert_eq!(
2184            names(quarter_length_to_tuplet(0.20, 4)),
2185            ["5/4/16th", "5/2/eighth", "5/1/quarter"]
2186        );
2187        assert_eq!(
2188            names(quarter_length_to_tuplet(0.333_333_3, 1)),
2189            ["3/2/eighth"]
2190        );
2191        // A plain quarter is also two dotted quarters in the time of three,
2192        // which is why `Duration::tuplet` asks for the exact value first.
2193        let plain = quarter_length_to_tuplet(1.0, 1);
2194        assert_eq!(names(plain.clone()), ["3/2/quarter"]);
2195        assert_eq!(plain[0].dots(), 1);
2196        assert!(quarter_length_to_tuplet(0.0, 4).is_empty());
2197
2198        let (tuplet, kind, dots) = quarter_length_to_non_power_of_2_tuplet(7.0).unwrap();
2199        assert_eq!(names(vec![tuplet]), ["8/7/quarter"]);
2200        assert_eq!((kind, dots), (DurationType::Breve, 0));
2201        let (tuplet, kind, _) = quarter_length_to_non_power_of_2_tuplet(7.0 / 3.0).unwrap();
2202        assert_eq!(names(vec![tuplet]), ["12/7/16th"]);
2203        assert_eq!(kind, DurationType::Whole);
2204        assert!(quarter_length_to_non_power_of_2_tuplet(0.0).is_none());
2205
2206        let (components, tuplet) = quarter_conversion(2.5);
2207        assert_eq!(
2208            components,
2209            [(DurationType::Half, 0), (DurationType::Eighth, 0)]
2210        );
2211        assert!(tuplet.is_none());
2212        let (components, tuplet) = quarter_conversion(2.0 / 3.0);
2213        assert_eq!(components, [(DurationType::Quarter, 0)]);
2214        assert_eq!(names(tuplet.into_iter().collect()), ["3/2/quarter"]);
2215        let (components, tuplet) = quarter_conversion(3.75);
2216        assert_eq!(components, [(DurationType::Half, 3)]);
2217        assert!(tuplet.is_none());
2218        assert_eq!(quarter_conversion(99.0), (Vec::new(), None));
2219        assert_eq!(quarter_conversion(0.0), (Vec::new(), None));
2220
2221        assert!(Duration::new(2.5).unwrap().is_complex());
2222        assert!(!Duration::new(3.0).unwrap().is_complex());
2223        assert!(!Duration::new(2.0 / 3.0).unwrap().is_complex());
2224    }
2225
2226    #[test]
2227    fn neighbouring_types_match_music21() {
2228        assert_eq!(
2229            DurationType::Quarter.next_larger(),
2230            Some(DurationType::Half)
2231        );
2232        assert_eq!(
2233            DurationType::Quarter.next_smaller(),
2234            Some(DurationType::Eighth)
2235        );
2236        assert_eq!(DurationType::Whole.next_larger(), Some(DurationType::Breve));
2237        assert_eq!(DurationType::Breve.next_larger(), Some(DurationType::Longa));
2238        assert_eq!(
2239            DurationType::Sixteenth.next_smaller(),
2240            Some(DurationType::ThirtySecond)
2241        );
2242        assert_eq!(DurationType::DuplexMaxima.next_larger(), None);
2243        assert_eq!(DurationType::ALL[15].next_smaller(), None);
2244        assert_eq!(DurationType::Zero.next_larger(), None);
2245        assert_eq!(DurationType::Zero.next_smaller(), None);
2246    }
2247
2248    #[test]
2249    fn ordinal_and_scaling_match_music21() {
2250        assert_eq!(DurationType::DuplexMaxima.ordinal(), Some(0));
2251        assert_eq!(DurationType::Quarter.ordinal(), Some(6));
2252        assert_eq!(DurationType::Sixteenth.ordinal(), Some(8));
2253        assert_eq!(DurationType::Zero.ordinal(), None);
2254        assert_eq!(Duration::new(1.5).unwrap().ordinal(), Some(6));
2255        assert_eq!(Duration::new(2.5).unwrap().ordinal(), None);
2256        assert_eq!(Duration::new(0.0).unwrap().ordinal(), None);
2257        assert_eq!(
2258            Duration::new(1.0)
2259                .unwrap()
2260                .augment_or_diminish(2.0)
2261                .unwrap()
2262                .quarter_length(),
2263            2.0
2264        );
2265        assert_eq!(
2266            Duration::new(1.5)
2267                .unwrap()
2268                .augment_or_diminish(0.5)
2269                .unwrap()
2270                .quarter_length(),
2271            0.75
2272        );
2273        assert!(
2274            Duration::new(1.0)
2275                .unwrap()
2276                .augment_or_diminish(0.0)
2277                .is_err()
2278        );
2279        assert!(
2280            Duration::new(1.0)
2281                .unwrap()
2282                .augment_or_diminish(-1.0)
2283                .is_err()
2284        );
2285        // A 16th sounding 4/17 of a quarter, as music21's reader leaves one.
2286        let mut unlinked = Duration::new(0.25).unwrap();
2287        unlinked.set_linked(false);
2288        unlinked.set_quarter_length(4.0 / 17.0).unwrap();
2289        let scaled = unlinked.augment_or_diminish(1.5).unwrap();
2290        assert!(!scaled.linked());
2291        assert_eq!(scaled.quarter_length(), 4.0 / 17.0 * 1.5);
2292        assert_ne!(scaled.quarter_length(), 6.0 / 17.0);
2293        assert_eq!(scaled.type_and_dots(), Some((DurationType::Sixteenth, 0)));
2294    }
2295    use super::*;
2296
2297    /// music21's `duration.typeToDuration`, verbatim.
2298    const MUSIC21_TYPE_TO_DURATION: [(&str, FloatType); 17] = [
2299        ("duplex-maxima", 64.0),
2300        ("maxima", 32.0),
2301        ("longa", 16.0),
2302        ("breve", 8.0),
2303        ("whole", 4.0),
2304        ("half", 2.0),
2305        ("quarter", 1.0),
2306        ("eighth", 0.5),
2307        ("16th", 0.25),
2308        ("32nd", 0.125),
2309        ("64th", 0.0625),
2310        ("128th", 0.03125),
2311        ("256th", 0.015625),
2312        ("512th", 0.0078125),
2313        ("1024th", 0.00390625),
2314        ("2048th", 0.001953125),
2315        ("zero", 0.0),
2316    ];
2317
2318    #[test]
2319    fn dots_types_and_full_names_match_music21() {
2320        let cases = [
2321            (
2322                4.0,
2323                Some((DurationType::Whole, 0)),
2324                "Whole",
2325                (DurationType::Whole, true),
2326            ),
2327            (
2328                2.0,
2329                Some((DurationType::Half, 0)),
2330                "Half",
2331                (DurationType::Half, true),
2332            ),
2333            (
2334                1.0,
2335                Some((DurationType::Quarter, 0)),
2336                "Quarter",
2337                (DurationType::Quarter, true),
2338            ),
2339            (
2340                0.5,
2341                Some((DurationType::Eighth, 0)),
2342                "Eighth",
2343                (DurationType::Eighth, true),
2344            ),
2345            (
2346                0.25,
2347                Some((DurationType::Sixteenth, 0)),
2348                "16th",
2349                (DurationType::Sixteenth, true),
2350            ),
2351            (
2352                3.0,
2353                Some((DurationType::Half, 1)),
2354                "Dotted Half",
2355                (DurationType::Half, false),
2356            ),
2357            (
2358                1.5,
2359                Some((DurationType::Quarter, 1)),
2360                "Dotted Quarter",
2361                (DurationType::Quarter, false),
2362            ),
2363            (
2364                0.75,
2365                Some((DurationType::Eighth, 1)),
2366                "Dotted Eighth",
2367                (DurationType::Eighth, false),
2368            ),
2369            (
2370                6.0,
2371                Some((DurationType::Whole, 1)),
2372                "Dotted Whole",
2373                (DurationType::Whole, false),
2374            ),
2375            (
2376                1.75,
2377                Some((DurationType::Quarter, 2)),
2378                "Double Dotted Quarter",
2379                (DurationType::Quarter, false),
2380            ),
2381            (
2382                0.875,
2383                Some((DurationType::Eighth, 2)),
2384                "Double Dotted Eighth",
2385                (DurationType::Eighth, false),
2386            ),
2387            (
2388                7.0,
2389                Some((DurationType::Whole, 2)),
2390                "Double Dotted Whole",
2391                (DurationType::Whole, false),
2392            ),
2393            (
2394                0.375,
2395                Some((DurationType::Sixteenth, 1)),
2396                "Dotted 16th",
2397                (DurationType::Sixteenth, false),
2398            ),
2399            (
2400                3.5,
2401                Some((DurationType::Half, 2)),
2402                "Double Dotted Half",
2403                (DurationType::Half, false),
2404            ),
2405            (
2406                3.75,
2407                Some((DurationType::Half, 3)),
2408                "Triple Dotted Half (3 3/4 QL)",
2409                (DurationType::Half, false),
2410            ),
2411            (
2412                1.875,
2413                Some((DurationType::Quarter, 3)),
2414                "Triple Dotted Quarter (1 7/8 QL)",
2415                (DurationType::Quarter, false),
2416            ),
2417            (
2418                8.0,
2419                Some((DurationType::Breve, 0)),
2420                "Breve",
2421                (DurationType::Breve, true),
2422            ),
2423            (
2424                16.0,
2425                Some((DurationType::Longa, 0)),
2426                "Imperfect Longa",
2427                (DurationType::Longa, true),
2428            ),
2429            (
2430                24.0,
2431                Some((DurationType::Longa, 1)),
2432                "Perfect Longa",
2433                (DurationType::Longa, false),
2434            ),
2435        ];
2436        for (quarter_length, type_and_dots, full_name, closest) in cases {
2437            let duration = Duration::new(quarter_length).unwrap();
2438            assert_eq!(duration.type_and_dots(), type_and_dots, "{quarter_length}");
2439            assert_eq!(duration.full_name(), full_name, "{quarter_length}");
2440            assert_eq!(
2441                quarter_length_to_closest_type(quarter_length).unwrap(),
2442                closest,
2443                "{quarter_length}"
2444            );
2445        }
2446
2447        let inexact = [
2448            (2.0 / 3.0, DurationType::Eighth),
2449            (1.0 / 3.0, DurationType::Sixteenth),
2450            (4.0 / 3.0, DurationType::Quarter),
2451            (0.2, DurationType::ThirtySecond),
2452            (0.4, DurationType::Sixteenth),
2453            (1.25, DurationType::Quarter),
2454            (5.0, DurationType::Whole),
2455            (2.5, DurationType::Half),
2456        ];
2457        // None of these is a single written note value. The ones that are a
2458        // tuplet are named as one; the ones that are a tie of two values are
2459        // not named at all, since the crate keeps a length and not the
2460        // components music21 spells them out from.
2461        let tuplet_names = [
2462            (2.0 / 3.0, "Quarter Triplet (2/3 QL)"),
2463            (1.0 / 3.0, "Eighth Triplet (1/3 QL)"),
2464            (4.0 / 3.0, "Half Triplet (1 1/3 QL)"),
2465            (0.2, "16th Quintuplet (1/5 QL)"),
2466            (0.4, "Eighth Quintuplet (2/5 QL)"),
2467        ];
2468        // and the ones music21 has to write as two values tied together
2469        let tied = [
2470            (1.25, "Quarter tied to 16th (1 1/4 total QL)"),
2471            (5.0, "Whole tied to Quarter (5 total QL)"),
2472            (2.5, "Half tied to Eighth (2 1/2 total QL)"),
2473        ];
2474        for (quarter_length, closest) in inexact {
2475            let duration = Duration::new(quarter_length).unwrap();
2476            assert_eq!(duration.type_and_dots(), None, "{quarter_length}");
2477            assert_eq!(duration.dots(), 0, "{quarter_length}");
2478            let tuplet_name = tuplet_names
2479                .iter()
2480                .find(|(length, _)| *length == quarter_length);
2481            let tied_name = tied.iter().find(|(length, _)| *length == quarter_length);
2482            let expected = tuplet_name.or(tied_name).map(|(_, name)| *name);
2483            assert_eq!(
2484                duration.full_name(),
2485                expected.expect("every inexact length here is named"),
2486                "{quarter_length}"
2487            );
2488            assert_eq!(
2489                duration.tuplet().is_some(),
2490                tuplet_name.is_some(),
2491                "{quarter_length}"
2492            );
2493            assert_eq!(
2494                duration.components().len(),
2495                if tuplet_name.is_some() { 1 } else { 2 },
2496                "{quarter_length}"
2497            );
2498            assert_eq!(
2499                quarter_length_to_closest_type(quarter_length).unwrap(),
2500                (closest, false),
2501                "{quarter_length}"
2502            );
2503        }
2504
2505        // the two lengths music21 refuses to write at all
2506        for quarter_length in [0.001, 100.0] {
2507            let duration = Duration::new(quarter_length).unwrap();
2508            assert!(duration.components().is_empty(), "{quarter_length}");
2509            assert_eq!(duration.full_name(), "Inexpressible", "{quarter_length}");
2510        }
2511        let zero = Duration::new(0.0).unwrap();
2512        assert!(zero.components().is_empty());
2513        assert_eq!(zero.full_name(), "Zero Duration (0 total QL)");
2514
2515        // music21 snaps a quarter length before it looks, so a truncated
2516        // third is still a triplet there and here.
2517        let truncated = Duration::new(0.333_333).unwrap();
2518        assert_eq!(truncated.full_name(), "Eighth Triplet (1/3 QL)");
2519
2520        let triplet = Duration::new(2.0 / 3.0).unwrap().tuplet().unwrap();
2521        assert_eq!((triplet.actual(), triplet.normal()), (3, 2));
2522        assert_eq!(triplet.duration_type(), DurationType::Quarter);
2523        assert_eq!(triplet.dots(), 0);
2524        assert_eq!(triplet.full_name(), "Triplet");
2525        assert_eq!(triplet.multiplier(), FractionType::new(2i32, 3i32));
2526
2527        // the ratios music21 has no word for say the ratio instead
2528        let odd = Tuplet::new(17, 14, DurationType::Quarter, 0);
2529        assert_eq!(odd.full_name(), "Tuplet of 17/14ths");
2530        assert_eq!(odd.total_tuplet_length(), 14.0);
2531        // music21 leaves `st` without its plural
2532        for (actual, normal, name) in [
2533            (40, 21, "Tuplet of 40/21st"),
2534            (3, 1, "Tuplet of 3/1st"),
2535            (12, 11, "Tuplet of 12/11ths"),
2536            (5, 3, "Tuplet of 5/3rds"),
2537            (23, 22, "Tuplet of 23/22nds"),
2538        ] {
2539            let tuplet = Tuplet::new(actual, normal, DurationType::Quarter, 0);
2540            assert_eq!(tuplet.full_name(), name);
2541        }
2542        // Three eighths in the time of one quarter is the same ratio written
2543        // the other way, and lasts exactly as long.
2544        let across =
2545            Tuplet::new(3, 1, DurationType::Eighth, 0).with_normal(DurationType::Quarter, 0);
2546        assert_eq!(across.total_tuplet_length(), 1.0);
2547        // A quarter over three eighths, as music21 reads it: the multiplier
2548        // is what each eighth is scaled by, not the ratio of the counts.
2549        assert_eq!(across.multiplier(), FractionType::new(2i32, 3i32));
2550        assert_eq!(Duration::new(3.75).unwrap().dots(), 3);
2551        assert_eq!(
2552            quarter_length_to_closest_type(200.0).unwrap(),
2553            (DurationType::DuplexMaxima, false)
2554        );
2555        assert!(quarter_length_to_closest_type(0.0).is_err());
2556        assert!(quarter_length_to_closest_type(0.0001).is_err());
2557    }
2558
2559    #[test]
2560    fn duration_types_match_music21s_table() {
2561        assert_eq!(DurationType::ALL.len(), MUSIC21_TYPE_TO_DURATION.len());
2562        for (duration_type, (name, quarter_length)) in
2563            DurationType::ALL.into_iter().zip(MUSIC21_TYPE_TO_DURATION)
2564        {
2565            assert_eq!(duration_type.music21_name(), name);
2566            assert_eq!(duration_type.quarter_length(), quarter_length, "{name}");
2567            assert_eq!(DurationType::from_music21_name(name), Some(duration_type));
2568        }
2569    }
2570
2571    #[test]
2572    fn duration_types_round_trip_through_their_names() {
2573        for duration_type in DurationType::ALL {
2574            let name = duration_type.music21_name();
2575            assert_eq!(name.parse::<DurationType>().unwrap(), duration_type);
2576            assert_eq!(duration_type.to_string(), name);
2577        }
2578        assert!("not-a-duration".parse::<DurationType>().is_err());
2579    }
2580
2581    #[test]
2582    fn each_type_is_half_the_one_before_it() {
2583        // `zero` is the exception and is excluded.
2584        let ordered = &DurationType::ALL[..DurationType::ALL.len() - 1];
2585        for pair in ordered.windows(2) {
2586            assert_eq!(
2587                pair[1].quarter_length() * 2.0,
2588                pair[0].quarter_length(),
2589                "{} should be half of {}",
2590                pair[1],
2591                pair[0]
2592            );
2593        }
2594    }
2595
2596    #[test]
2597    fn dots_add_half_of_what_came_before() {
2598        assert_eq!(DurationType::Half.quarter_length_with_dots(0), 2.0);
2599        assert_eq!(DurationType::Half.quarter_length_with_dots(1), 3.0);
2600        assert_eq!(DurationType::Half.quarter_length_with_dots(2), 3.5);
2601        assert_eq!(DurationType::Half.quarter_length_with_dots(3), 3.75);
2602        assert_eq!(DurationType::Quarter.quarter_length_with_dots(1), 1.5);
2603    }
2604
2605    #[test]
2606    fn durations_convert_to_and_from_note_values() {
2607        assert_eq!(
2608            Duration::from_type(DurationType::Whole).quarter_length(),
2609            4.0
2610        );
2611        assert_eq!(
2612            Duration::from_type(DurationType::Whole).duration_type(),
2613            Some(DurationType::Whole)
2614        );
2615        assert_eq!(
2616            Duration::from_type_with_dots(DurationType::Half, 1).quarter_length(),
2617            3.0
2618        );
2619        // A dotted value keeps the type it is dotted from, as music21 reads
2620        // it: a dotted half is a half with one dot, not a type of its own.
2621        assert_eq!(
2622            Duration::from_type_with_dots(DurationType::Half, 1).duration_type(),
2623            Some(DurationType::Half)
2624        );
2625        assert_eq!(
2626            Duration::from_type_with_dots(DurationType::Half, 1).dots(),
2627            1
2628        );
2629        assert_eq!(
2630            Duration::new(3.75).unwrap().duration_type(),
2631            Some(DurationType::Half)
2632        );
2633        assert_eq!(Duration::new(3.75).unwrap().dots(), 3);
2634        // A triplet eighth is no note value at all.
2635        assert_eq!(Duration::new(1.0 / 3.0).unwrap().duration_type(), None);
2636    }
2637
2638    #[test]
2639    fn the_named_helpers_agree_with_their_types() {
2640        assert_eq!(
2641            Duration::quarter(),
2642            Duration::from_type(DurationType::Quarter)
2643        );
2644        assert_eq!(Duration::half(), Duration::from_type(DurationType::Half));
2645        assert_eq!(Duration::whole(), Duration::from_type(DurationType::Whole));
2646        assert_eq!(
2647            Duration::eighth(),
2648            Duration::from_type(DurationType::Eighth)
2649        );
2650    }
2651
2652    #[test]
2653    fn duration_tracks_quarter_lengths() {
2654        assert_eq!(Duration::quarter().quarter_length(), 1.0);
2655        assert_eq!(Duration::half().quarter_length(), 2.0);
2656        assert_eq!(Duration::whole().quarter_length(), 4.0);
2657        assert_eq!(Duration::eighth().quarter_length(), 0.5);
2658    }
2659
2660    #[test]
2661    fn duration_rejects_invalid_values() {
2662        assert!(Duration::new(-1.0).is_err());
2663        assert!(Duration::new(FloatType::INFINITY).is_err());
2664    }
2665
2666    #[test]
2667    fn a_plain_note_value_is_not_read_as_a_tuplet() {
2668        // music21 tries the exact written value before any ratio, so a
2669        // quarter is a quarter and not two thirds of a dotted quarter.
2670        assert_eq!(Duration::quarter().tuplet(), None);
2671        assert_eq!(Duration::new(3.0).unwrap().tuplet(), None);
2672        assert_eq!(
2673            Duration::new(1.0 / 3.0).unwrap().tuplet(),
2674            Some(Tuplet::new(3, 2, DurationType::Eighth, 0))
2675        );
2676    }
2677
2678    #[test]
2679    fn appending_a_tuplet_shortens_the_length_by_its_ratio() {
2680        // music21's own `appendTuplet` docstring, exactly.
2681        let mut duration = Duration::new(1.0).unwrap();
2682        duration.append_tuplet(Tuplet::new(3, 2, DurationType::Quarter, 0));
2683        assert!((duration.quarter_length() - 2.0 / 3.0).abs() < 1e-12);
2684        assert_eq!(duration.quarter_length_no_tuplets(), 1.0);
2685
2686        duration.append_tuplet(Tuplet::new(5, 4, DurationType::Quarter, 0));
2687        assert!((duration.quarter_length() - 8.0 / 15.0).abs() < 1e-12);
2688        assert_eq!(
2689            duration.aggregate_tuplet_multiplier(),
2690            FractionType::new(8, 15)
2691        );
2692        // The written value is untouched by either tuplet.
2693        assert_eq!(
2694            duration.components(),
2695            vec![(DurationType::Quarter, 0)],
2696            "the written value stays a quarter inside both tuplets"
2697        );
2698    }
2699
2700    #[test]
2701    fn saying_a_length_is_in_no_tuplet_is_not_the_same_as_saying_nothing() {
2702        let inferred = Duration::new(1.0 / 3.0).unwrap();
2703        assert_eq!(inferred.tuplets().len(), 1);
2704
2705        let mut told = Duration::new(1.0 / 3.0).unwrap();
2706        told.set_tuplets(Vec::new());
2707        assert!(told.tuplets().is_empty());
2708        // Losing the triplet leaves the written eighth sounding in full.
2709        assert_eq!(told.quarter_length(), 0.5);
2710    }
2711
2712    #[test]
2713    fn setting_a_length_forgets_the_tuplets_it_was_told() {
2714        let mut duration = Duration::new(1.0).unwrap();
2715        duration.append_tuplet(Tuplet::new(3, 2, DurationType::Quarter, 0));
2716        duration.set_quarter_length(2.0).unwrap();
2717        assert!(duration.tuplets().is_empty());
2718        assert_eq!(duration.quarter_length(), 2.0);
2719    }
2720
2721    #[test]
2722    fn duration_supports_conversions_and_updates() {
2723        let mut duration = Duration::try_from(3 as IntegerType).unwrap();
2724        assert_eq!(duration.quarter_length(), 3.0);
2725
2726        duration.set_quarter_length(1.5).unwrap();
2727        assert_eq!(duration, Duration::try_from(1.5).unwrap());
2728        assert!(duration.set_quarter_length(FloatType::NAN).is_err());
2729    }
2730}