Skip to main content

music21_rs/meter/
sequence.rs

1//! How a bar divides: music21's `MeterTerminal` and `MeterSequence`.
2//!
3//! A terminal is a span of a bar written as a ratio, with a weight. A
4//! sequence is a terminal that is made of other terminals, so a bar of `4/4`
5//! partitioned in two is `{1/2+1/2}`, and either half may be partitioned
6//! again. music21 hangs four of these off every time signature — the beats,
7//! the beams, the accents and what is displayed — and this is the part of it
8//! that is arithmetic rather than notation.
9//!
10//! What a sequence may be partitioned into is not free: music21 keeps a list
11//! of the ways each meter is conventionally divided, in priority order, and
12//! partitioning by a count takes the first of those with that many parts.
13//! That is why `5/8` in two is `{2/8+3/8}` and not `{2.5/8+2.5/8}`.
14
15use crate::defaults::{FloatType, UnsignedIntegerType};
16use crate::error::{Error, Result};
17
18/// How close two quarter lengths must be to count as the same boundary.
19const OFFSET_TOLERANCE: FloatType = 1e-9;
20
21/// The denominators music21 will write a meter in.
22const VALID_DENOMINATORS: [UnsignedIntegerType; 8] = [1, 2, 4, 8, 16, 32, 64, 128];
23
24/// How an offset is matched against the boundaries of a level: music21's
25/// `align` argument to `offsetToDepth`.
26#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
27#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
28pub enum OffsetAlign {
29    /// Move the offset back to the start of the finest part holding it, then
30    /// count the levels beginning there. music21's default.
31    #[default]
32    Quantize,
33    /// Count only the levels whose part begins exactly at the offset.
34    Start,
35    /// Count the levels whose part ends exactly at the offset.
36    End,
37}
38
39/// One span of a bar: a ratio, and how strongly it is felt.
40#[derive(Clone, Debug, PartialEq)]
41#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
42pub struct MeterTerminal {
43    numerator: UnsignedIntegerType,
44    denominator: UnsignedIntegerType,
45    weight: FloatType,
46    /// What this span is itself divided into, if anything. A terminal with
47    /// parts is music21's `MeterSequence`; one without is a leaf.
48    parts: Vec<MeterTerminal>,
49}
50
51impl MeterTerminal {
52    /// A span of `numerator/denominator`, undivided, weighing one.
53    pub fn new(numerator: UnsignedIntegerType, denominator: UnsignedIntegerType) -> Result<Self> {
54        if numerator == 0 {
55            return Err(Error::Meter(
56                "a meter terminal numerator must be non-zero".to_string(),
57            ));
58        }
59        if denominator == 0 {
60            return Err(Error::Meter(
61                "a meter terminal denominator must be non-zero".to_string(),
62            ));
63        }
64        Ok(Self {
65            numerator,
66            denominator,
67            weight: 1.0,
68            parts: Vec::new(),
69        })
70    }
71
72    /// The same, read from `"3/8"`.
73    pub fn from_ratio_string(ratio: &str) -> Result<Self> {
74        let (numerator, denominator) = split_ratio(ratio)?;
75        Self::new(numerator, denominator)
76    }
77
78    /// The numerator this span is written with.
79    #[must_use]
80    pub fn numerator(&self) -> UnsignedIntegerType {
81        self.numerator
82    }
83
84    /// The denominator this span is written with.
85    #[must_use]
86    pub fn denominator(&self) -> UnsignedIntegerType {
87        self.denominator
88    }
89
90    /// How long this span is in quarter notes.
91    #[must_use]
92    pub fn quarter_length(&self) -> FloatType {
93        FloatType::from(self.numerator) * (4.0 / FloatType::from(self.denominator))
94    }
95
96    /// How strongly this span is felt: music21's `weight`.
97    #[must_use]
98    pub fn weight(&self) -> FloatType {
99        self.weight
100    }
101
102    /// Sets how strongly this span is felt.
103    pub fn set_weight(&mut self, weight: FloatType) {
104        self.weight = weight;
105    }
106
107    /// What this span is divided into. Empty where it is a leaf.
108    #[must_use]
109    pub fn parts(&self) -> &[MeterTerminal] {
110        &self.parts
111    }
112
113    /// The same, to be changed.
114    pub fn parts_mut(&mut self) -> &mut Vec<MeterTerminal> {
115        &mut self.parts
116    }
117
118    /// How many parts this span has at its top level, music21's `len`.
119    #[must_use]
120    pub fn len(&self) -> usize {
121        self.parts.len()
122    }
123
124    /// Whether this span is divided at all.
125    #[must_use]
126    pub fn is_empty(&self) -> bool {
127        self.parts.is_empty()
128    }
129
130    /// How many levels of division this span has: music21's `depth`. A leaf
131    /// is nought.
132    #[must_use]
133    pub fn depth(&self) -> usize {
134        self.parts
135            .iter()
136            .map(|part| 1 + part.depth())
137            .max()
138            .unwrap_or(0)
139    }
140
141    /// Every leaf of this span, in order: music21's `flatten`.
142    #[must_use]
143    pub fn flattened(&self) -> Vec<MeterTerminal> {
144        if self.parts.is_empty() {
145            return vec![self.clone()];
146        }
147        self.parts
148            .iter()
149            .flat_map(MeterTerminal::flattened)
150            .collect()
151    }
152
153    /// Divides this span into the parts named, as `["2/8", "3/8"]`.
154    ///
155    /// The parts must come to what this span already is; a partition that
156    /// lengthened or shortened the bar would not be one.
157    pub fn partition_by_parts(&mut self, parts: &[&str]) -> Result<()> {
158        let mut built = Vec::with_capacity(parts.len());
159        for part in parts {
160            built.push(MeterTerminal::from_ratio_string(part)?);
161        }
162        let total: FloatType = built.iter().map(MeterTerminal::quarter_length).sum();
163        if (total - self.quarter_length()).abs() > OFFSET_TOLERANCE {
164            return Err(Error::Meter(format!(
165                "cannot set partition by {parts:?}: it comes to {total} where the meter is {}",
166                self.quarter_length()
167            )));
168        }
169        self.parts = built;
170        Ok(())
171    }
172
173    /// Divides this span into `count` parts, the way music21 divides it.
174    ///
175    /// The first of [`Self::division_options`] with that many parts wins. A
176    /// count nothing divides into leaves the first option standing, which is
177    /// music21's `loadDefault`.
178    pub fn partition_by_count(&mut self, count: usize, load_default: bool) -> Result<()> {
179        let options = self.division_options();
180        let chosen = options.iter().find(|option| option.len() == count);
181        let chosen = match chosen {
182            Some(option) => option.clone(),
183            None => {
184                if !load_default || options.is_empty() {
185                    return Err(Error::Meter(format!(
186                        "cannot set partition by {count} ({}/{})",
187                        self.numerator, self.denominator
188                    )));
189                }
190                options[0].clone()
191            }
192        };
193        let borrowed: Vec<&str> = chosen.iter().map(String::as_str).collect();
194        self.partition_by_parts(&borrowed)
195    }
196
197    /// Divides this span by a list of numerators, as `[3, 1]` for `{3/4+1/4}`.
198    ///
199    /// music21 reads the list against this span's own denominator where the
200    /// numerators come to its numerator, and against a finer one where they
201    /// come to a multiple of it — `[1, 1, 1, 1]` of a `2/4` bar is four
202    /// eighths, not four quarters.
203    pub fn partition_by_list(&mut self, numerators: &[UnsignedIntegerType]) -> Result<()> {
204        if numerators.is_empty() {
205            return Err(Error::Meter(
206                "cannot partition a meter into nothing".to_string(),
207            ));
208        }
209        let total: UnsignedIntegerType = numerators.iter().sum();
210        for multiple in 1..=8 {
211            if total != self.numerator * multiple {
212                continue;
213            }
214            let denominator = self.denominator * multiple;
215            let parts: Vec<String> = numerators
216                .iter()
217                .map(|numerator| format!("{numerator}/{denominator}"))
218                .collect();
219            let borrowed: Vec<&str> = parts.iter().map(String::as_str).collect();
220            return self.partition_by_parts(&borrowed);
221        }
222        Err(Error::Meter(format!(
223            "cannot set partition by {numerators:?} ({}/{})",
224            self.numerator, self.denominator
225        )))
226    }
227
228    /// Divides this span into `count` parts and leaves those parts divided
229    /// as they were: music21's `MeterTerminal.subdivide`.
230    pub fn subdivide(&mut self, count: usize) -> Result<()> {
231        self.partition_by_count(count, false)
232    }
233
234    /// Divides every part of this span again, the way music21 divides it
235    /// when nobody has said how: music21's `subdividePartitionsEqual`.
236    ///
237    /// `divisions` says how many parts each part becomes; `None` asks for
238    /// the division each part conventionally takes — two for a part written
239    /// in a binary numerator, three for one written in three, and threes for
240    /// the compound numerators. A part nothing divides into is an error,
241    /// which is what music21 raises and what a meter written in notes
242    /// shorter than a 128th is forgiven.
243    pub fn subdivide_partitions_equal(&mut self, divisions: Option<usize>) -> Result<()> {
244        for part in &mut self.parts {
245            let count = match divisions {
246                Some(count) => count,
247                None => match part.numerator {
248                    1 | 2 | 4 | 8 | 16 | 32 | 64 => 2,
249                    3 => 3,
250                    6 | 9 | 12 | 15 | 18 | 21 | 24 | 27 => (part.numerator / 3) as usize,
251                    other => other as usize,
252                },
253            };
254            part.partition_by_count(count, false)?;
255        }
256        Ok(())
257    }
258
259    /// The ways music21 conventionally divides this meter, in the order it
260    /// prefers them: music21's `getPartitionOptions`.
261    #[must_use]
262    pub fn division_options(&self) -> Vec<Vec<String>> {
263        let mut options = division_options_algorithmic(self.numerator, self.denominator);
264        options.extend(division_options_preset(self.numerator, self.denominator));
265        let mut seen = Vec::new();
266        options.retain(|option| {
267            if option.is_empty() || seen.contains(option) {
268                return false;
269            }
270            seen.push(option.clone());
271            true
272        });
273        options
274    }
275
276    /// The terminals at one level of this sequence: music21's `getLevelList`.
277    ///
278    /// A part that is not divided is taken as it stands. A part that is gets
279    /// recursed into while there are levels left to descend, and at the level
280    /// asked for is either kept whole or, when `flat`, flattened into a
281    /// single terminal carrying that part's own weight.
282    #[must_use]
283    pub fn level_list(&self, level: usize, flat: bool) -> Vec<MeterTerminal> {
284        let mut out = Vec::new();
285        for part in &self.parts {
286            if part.parts.is_empty() {
287                out.push(part.clone());
288            } else if level > 0 {
289                out.extend(part.level_list(level - 1, flat));
290            } else if flat {
291                let mut flattened = part.clone();
292                flattened.parts.clear();
293                out.push(flattened);
294            } else {
295                out.push(part.clone());
296            }
297        }
298        out
299    }
300
301    /// One level of this sequence as a sequence of its own: music21's
302    /// `getLevel`.
303    pub fn level(&self, level: usize, flat: bool) -> Result<Self> {
304        let mut out = Self::new(self.numerator, self.denominator)?;
305        out.weight = self.weight;
306        out.parts = self.level_list(level, flat);
307        Ok(out)
308    }
309
310    /// Where each terminal of a level starts and ends, in quarter lengths
311    /// from the start of this span: music21's `getLevelSpan`.
312    #[must_use]
313    pub fn level_span(&self, level: usize) -> Vec<(FloatType, FloatType)> {
314        let mut spans = Vec::new();
315        let mut position = 0.0;
316        for part in self.level_list(level, true) {
317            let end = position + part.quarter_length();
318            spans.push((position, end));
319            position = end;
320        }
321        spans
322    }
323
324    /// How many levels of this sequence start at an offset: music21's
325    /// `offsetToDepth`.
326    ///
327    /// A level counts when one of its parts begins where the offset does.
328    /// Under [`OffsetAlign::Quantize`] the offset is first moved back to the
329    /// start of the finest part holding it, so an offset inside a part still
330    /// counts the levels that part begins.
331    pub fn offset_to_depth(&self, offset: FloatType, align: OffsetAlign) -> Result<usize> {
332        let length = self.quarter_length();
333        if offset.is_nan() || offset < 0.0 || offset >= length {
334            return Err(Error::Meter(format!(
335                "cannot access from qLenPos {offset} where total duration is {length}"
336            )));
337        }
338        let depth = self.depth();
339        if depth == 0 {
340            return Ok(0);
341        }
342        let finest = self.level(depth - 1, true)?;
343        let index = finest.offset_to_index(offset)?;
344        let spans = self.level_span(depth - 1);
345        let position = match align {
346            OffsetAlign::Quantize => spans[index].0,
347            OffsetAlign::Start | OffsetAlign::End => offset,
348        };
349        let mut score = 0;
350        for level in 0..depth {
351            for (start, end) in self.level_span(level) {
352                let boundary = match align {
353                    OffsetAlign::Start | OffsetAlign::Quantize => start,
354                    OffsetAlign::End => end,
355                };
356                if (boundary - position).abs() < OFFSET_TOLERANCE {
357                    score += 1;
358                }
359            }
360        }
361        Ok(score)
362    }
363
364    /// Weighs the terminals of a level, looping the weights given over them:
365    /// the write half of music21's `setAccentWeight`.
366    ///
367    /// A level with no terminals, or no weights to give it, is an error
368    /// rather than a silent nothing.
369    pub fn set_weights_at_level(&mut self, level: usize, weights: &[FloatType]) -> Result<()> {
370        if weights.is_empty() {
371            return Err(Error::Meter(
372                "a weight has to be given to weigh a level with".to_string(),
373            ));
374        }
375        let mut index = 0;
376        Self::weigh(&mut self.parts, level, weights, &mut index);
377        if index == 0 {
378            return Err(Error::Meter(format!(
379                "this meter has no level {level} to weigh"
380            )));
381        }
382        Ok(())
383    }
384
385    /// Walks the terminals of a level in order, weighing each in turn.
386    fn weigh(parts: &mut [MeterTerminal], level: usize, weights: &[FloatType], index: &mut usize) {
387        for part in parts.iter_mut() {
388            if part.parts.is_empty() || level == 0 {
389                part.weight = weights[*index % weights.len()];
390                *index += 1;
391            } else {
392                Self::weigh(&mut part.parts, level - 1, weights, index);
393            }
394        }
395    }
396
397    /// Whether every part of a level is the same ratio: music21's
398    /// `isUniformPartition`.
399    #[must_use]
400    pub fn is_uniform_partition(&self, depth: usize) -> bool {
401        let mut numerator = None;
402        let mut denominator = None;
403        for part in self.level_list(depth, false) {
404            if *numerator.get_or_insert(part.numerator) != part.numerator
405                || *denominator.get_or_insert(part.denominator) != part.denominator
406            {
407                return false;
408            }
409        }
410        true
411    }
412
413    /// The parts written out without the braces around them: music21's
414    /// `partitionDisplay`, so a bar of `2/4+6/8` reads as it was written.
415    #[must_use]
416    pub fn partition_display(&self) -> String {
417        self.parts
418            .iter()
419            .map(MeterTerminal::to_string)
420            .collect::<Vec<String>>()
421            .join("+")
422    }
423
424    /// This span divided into `count` parts, as a new sequence: music21's
425    /// `subdivideByCount`. The weight of this span goes with it.
426    pub fn subdivide_by_count(&self, count: usize) -> Result<Self> {
427        let mut out = Self::new(self.numerator, self.denominator)?;
428        out.weight = self.weight;
429        out.partition_by_count(count, true)?;
430        Ok(out)
431    }
432
433    /// This span divided by a list of numerators, as a new sequence:
434    /// music21's `subdivideByList`.
435    pub fn subdivide_by_list(&self, numerators: &[UnsignedIntegerType]) -> Result<Self> {
436        let mut out = Self::new(self.numerator, self.denominator)?;
437        out.weight = self.weight;
438        out.partition_by_list(numerators)?;
439        Ok(out)
440    }
441
442    /// Which part an offset in quarter notes falls in: music21's
443    /// `offsetToIndex`. An offset outside the span is an error.
444    pub fn offset_to_index(&self, offset: FloatType) -> Result<usize> {
445        let length = self.quarter_length();
446        if offset.is_nan() || offset < 0.0 || offset >= length {
447            return Err(Error::Meter(format!(
448                "cannot access from qLenPos {offset} where total duration is {length}"
449            )));
450        }
451        let mut start = 0.0;
452        for (index, part) in self.parts.iter().enumerate() {
453            let end = start + part.quarter_length();
454            if offset >= start - OFFSET_TOLERANCE && offset < end - OFFSET_TOLERANCE {
455                return Ok(index);
456            }
457            start = end;
458        }
459        Ok(self.parts.len().saturating_sub(1))
460    }
461
462    /// Where the part an offset falls in begins and ends: music21's
463    /// `offsetToSpan`. With `permit_meter_modulus` an offset past the end of
464    /// the span is read within it.
465    pub fn offset_to_span(
466        &self,
467        offset: FloatType,
468        permit_meter_modulus: bool,
469    ) -> Result<(FloatType, FloatType)> {
470        let length = self.quarter_length();
471        let offset = if permit_meter_modulus && offset >= length {
472            offset.rem_euclid(length)
473        } else {
474            offset
475        };
476        let index = self.offset_to_index(offset)?;
477        let mut start = 0.0;
478        for (position, part) in self.parts.iter().enumerate() {
479            let end = start + part.quarter_length();
480            if position == index {
481                return Ok((start, end));
482            }
483            start = end;
484        }
485        Ok((0.0, length))
486    }
487
488    /// The weight of the part an offset falls in: music21's `offsetToWeight`.
489    pub fn offset_to_weight(&self, offset: FloatType) -> Result<FloatType> {
490        let index = self.offset_to_index(offset)?;
491        Ok(self
492            .parts
493            .get(index)
494            .map_or(self.weight, MeterTerminal::weight))
495    }
496
497    /// How this span is written: `3/4` undivided, `{1/4+1/4+1/4}` divided.
498    #[must_use]
499    pub fn partition_string(&self) -> String {
500        if self.parts.is_empty() {
501            return format!("{}/{}", self.numerator, self.denominator);
502        }
503        let inner: Vec<String> = self
504            .parts
505            .iter()
506            .map(MeterTerminal::partition_string)
507            .collect();
508        format!("{{{}}}", inner.join("+"))
509    }
510}
511
512impl std::fmt::Display for MeterTerminal {
513    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
514        formatter.write_str(&self.partition_string())
515    }
516}
517
518/// `"3/8"` as the two numbers it is written with.
519fn split_ratio(ratio: &str) -> Result<(UnsignedIntegerType, UnsignedIntegerType)> {
520    let trimmed = ratio.trim();
521    let (numerator, denominator) = trimmed.split_once('/').ok_or_else(|| {
522        Error::Meter(format!(
523            "a meter terminal is written numerator/denominator, not {ratio:?}"
524        ))
525    })?;
526    let read = |part: &str, label: &str| {
527        part.trim()
528            .parse::<UnsignedIntegerType>()
529            .map_err(|_| Error::Meter(format!("cannot read a {label} from {part:?} in {ratio:?}")))
530    };
531    Ok((
532        read(numerator, "numerator")?,
533        read(denominator, "denominator")?,
534    ))
535}
536
537/// The same ratio in shorter notes, while the denominator allows: music21's
538/// `divisionOptionsFractionsUpward`.
539fn fractions_upward(
540    numerator: UnsignedIntegerType,
541    denominator: UnsignedIntegerType,
542) -> Vec<String> {
543    let largest = VALID_DENOMINATORS[VALID_DENOMINATORS.len() - 1];
544    let mut out = Vec::new();
545    if denominator >= largest {
546        return out;
547    }
548    let (mut numerator, mut denominator) = (numerator * 2, denominator * 2);
549    while denominator <= largest {
550        out.push(format!("{numerator}/{denominator}"));
551        numerator *= 2;
552        denominator *= 2;
553    }
554    out
555}
556
557/// The same ratio in longer notes, while it stays whole: music21's
558/// `divisionOptionsFractionsDownward`.
559fn fractions_downward(
560    numerator: UnsignedIntegerType,
561    denominator: UnsignedIntegerType,
562) -> Vec<String> {
563    let smallest = VALID_DENOMINATORS[0];
564    let mut out = Vec::new();
565    if denominator <= smallest || !numerator.is_multiple_of(2) {
566        return out;
567    }
568    let (mut numerator, mut denominator) = (numerator / 2, denominator / 2);
569    loop {
570        out.push(format!("{numerator}/{denominator}"));
571        if !numerator.is_multiple_of(2) || denominator <= smallest {
572            break;
573        }
574        numerator /= 2;
575        denominator /= 2;
576    }
577    out
578}
579
580/// One unit at a time, then twice as many half as long: music21's
581/// `divisionOptionsAdditiveMultiplesUpward`, which stops at sixteen parts or
582/// at the numerator where that is larger.
583fn additive_multiples_upward(
584    numerator: UnsignedIntegerType,
585    denominator: UnsignedIntegerType,
586) -> Vec<Vec<String>> {
587    let mut out = Vec::new();
588    if numerator <= 1 {
589        return out;
590    }
591    let largest = VALID_DENOMINATORS[VALID_DENOMINATORS.len() - 1];
592    let limit = if numerator > 16 { numerator } else { 16 };
593    let (mut denominator, mut count) = (denominator, numerator);
594    while denominator <= largest && count <= limit {
595        out.push(vec![format!("1/{denominator}"); count as usize]);
596        denominator *= 2;
597        count *= 2;
598    }
599    out
600}
601
602/// The bar halved, and halved again while it stays even: music21's
603/// `divisionOptionsAdditiveMultiplesEvenDivision`. A `4/4` bar reads
604/// `1/2+1/2`, written in the note value a half of it actually is.
605fn additive_multiples_even_division(
606    numerator: UnsignedIntegerType,
607    denominator: UnsignedIntegerType,
608) -> Vec<Vec<String>> {
609    let mut out = Vec::new();
610    if !numerator.is_multiple_of(2) || denominator < 2 {
611        return out;
612    }
613    let (mut count, mut denominator) = (numerator / 2, denominator / 2);
614    while denominator >= 1 && count > 1 {
615        out.push(vec![format!("1/{denominator}"); count as usize]);
616        if !count.is_multiple_of(2) || denominator == 1 {
617            break;
618        }
619        denominator /= 2;
620        count /= 2;
621    }
622    out
623}
624
625/// Even groupings in the bar's own note value: music21's
626/// `divisionOptionsAdditiveMultiples`, which reads `6/4` as `3/4+3/4`.
627fn additive_multiples(
628    numerator: UnsignedIntegerType,
629    denominator: UnsignedIntegerType,
630) -> Vec<Vec<String>> {
631    let mut out = Vec::new();
632    if numerator <= 3 || !numerator.is_multiple_of(2) {
633        return out;
634    }
635    let mut divisor = 2;
636    while numerator.is_multiple_of(divisor) {
637        let count = numerator / divisor;
638        if count <= 1 {
639            break;
640        }
641        out.push(vec![format!("{count}/{denominator}"); divisor as usize]);
642        divisor *= 2;
643    }
644    out
645}
646
647/// A single unit split into smaller ones: music21's
648/// `divisionOptionsAdditiveMultiplesDownward`, which only ever applies where
649/// the numerator is one.
650fn additive_multiples_downward(
651    numerator: UnsignedIntegerType,
652    denominator: UnsignedIntegerType,
653) -> Vec<Vec<String>> {
654    let largest = VALID_DENOMINATORS[VALID_DENOMINATORS.len() - 1];
655    let mut out = Vec::new();
656    if denominator >= largest || numerator != 1 {
657        return out;
658    }
659    let (mut count, mut denominator) = (2usize, denominator * 2);
660    while denominator <= largest {
661        out.push(vec![format!("{numerator}/{denominator}"); count]);
662        denominator *= 2;
663        count *= 2;
664    }
665    out
666}
667
668/// The ways music21 divides a meter, in the order it prefers them:
669/// music21's `divisionOptionsAlgo`.
670///
671/// The order is the behaviour, not decoration:
672/// [`MeterTerminal::partition_by_count`] takes the first option of the right
673/// length, so these pieces are composed in the same sequence music21
674/// composes them in.
675fn division_options_algorithmic(
676    numerator: UnsignedIntegerType,
677    denominator: UnsignedIntegerType,
678) -> Vec<Vec<String>> {
679    let mut options: Vec<Vec<String>> = Vec::new();
680
681    // Compound meters divide into threes first: 6/8 is two dotted beats.
682    if numerator > 3 && numerator.is_multiple_of(3) {
683        options.push(vec![format!("3/{denominator}"); (numerator / 3) as usize]);
684    }
685    // The odd meters music21 keeps a grouping for.
686    let groupings: &[&[UnsignedIntegerType]] = match numerator {
687        5 => &[&[2, 3], &[3, 2]],
688        7 => &[&[2, 2, 3], &[3, 2, 2], &[2, 3, 2]],
689        10 => &[&[2, 2, 3, 3]],
690        _ => &[],
691    };
692    for grouping in groupings {
693        options.push(
694            grouping
695                .iter()
696                .map(|count| format!("{count}/{denominator}"))
697                .collect(),
698        );
699    }
700    options.extend(additive_multiples_upward(numerator, denominator));
701    options.extend(additive_multiples_even_division(numerator, denominator));
702    options.push(vec![format!("{numerator}/{denominator}")]);
703    options.extend(additive_multiples(numerator, denominator));
704    options.extend(additive_multiples_downward(numerator, denominator));
705    for written in fractions_downward(numerator, denominator) {
706        options.push(vec![written]);
707    }
708    for written in fractions_upward(numerator, denominator) {
709        options.push(vec![written]);
710    }
711    options
712}
713
714/// The divisions music21 keeps by hand because no rule produces them: only
715/// the two extra readings of a five.
716fn division_options_preset(
717    numerator: UnsignedIntegerType,
718    denominator: UnsignedIntegerType,
719) -> Vec<Vec<String>> {
720    if numerator != 5 {
721        return Vec::new();
722    }
723    vec![
724        vec![
725            format!("2/{denominator}"),
726            format!("2/{denominator}"),
727            format!("1/{denominator}"),
728        ],
729        vec![
730            format!("2/{denominator}"),
731            format!("1/{denominator}"),
732            format!("2/{denominator}"),
733        ],
734    ]
735}
736
737#[cfg(test)]
738mod tests {
739    use super::MeterTerminal;
740    use super::OffsetAlign;
741
742    /// music21's own `divisionOptionsAlgo(4, 4)`, in its order.
743    ///
744    /// The order is what decides a partition: `partition_by_count` takes the
745    /// first option of the length asked for, so `4/4` in two is `1/2+1/2`
746    /// and not `2/4+2/4`, which comes later in the same list.
747    #[test]
748    fn levels_are_read_as_music21_reads_them() {
749        // Read off music21 11.0.0b9: TimeSignature("4/4").beatSequence.
750        let mut bar = MeterTerminal::new(4, 4).unwrap();
751        bar.partition_by_parts(&["4/4"]).unwrap();
752        bar.partition_by_count(4, true).unwrap();
753        bar.subdivide_partitions_equal(None).unwrap();
754        assert_eq!(bar.to_string(), "{{1/8+1/8}+{1/8+1/8}+{1/8+1/8}+{1/8+1/8}}");
755
756        // getLevelList(0, True) is four quarters; (1, True) is eight eighths.
757        let first: Vec<String> = bar
758            .level_list(0, true)
759            .iter()
760            .map(MeterTerminal::to_string)
761            .collect();
762        assert_eq!(first, ["1/4", "1/4", "1/4", "1/4"]);
763        let second: Vec<String> = bar
764            .level_list(1, true)
765            .iter()
766            .map(MeterTerminal::to_string)
767            .collect();
768        assert_eq!(second, ["1/8"; 8]);
769
770        // music21 reads this bar as two levels deep.
771        assert_eq!(bar.depth(), 2);
772        assert_eq!(bar.level_span(0).len(), 4);
773        assert_eq!(bar.level_span(0)[1], (1.0, 2.0));
774
775        // Every part of either level is the same ratio.
776        assert!(bar.is_uniform_partition(0));
777        assert!(bar.is_uniform_partition(1));
778        // music21 reads the depth at an offset as 2, 1, 2 across the first beat.
779        assert_eq!(bar.offset_to_depth(0.0, OffsetAlign::Quantize).unwrap(), 2);
780        assert_eq!(bar.offset_to_depth(0.5, OffsetAlign::Quantize).unwrap(), 1);
781        assert_eq!(bar.offset_to_depth(1.0, OffsetAlign::Quantize).unwrap(), 2);
782    }
783
784    #[test]
785    fn a_bar_written_in_unequal_parts_is_not_uniform() {
786        let mut bar = MeterTerminal::new(5, 8).unwrap();
787        bar.partition_by_parts(&["2/8", "3/8"]).unwrap();
788        assert!(!bar.is_uniform_partition(0));
789        assert_eq!(bar.partition_display(), "2/8+3/8");
790        assert_eq!(bar.depth(), 1);
791    }
792
793    #[test]
794    fn subdividing_leaves_the_span_alone_and_returns_a_new_one() {
795        let mut beat = MeterTerminal::new(1, 4).unwrap();
796        beat.set_weight(0.5);
797        let divided = beat.subdivide_by_count(2).unwrap();
798        assert_eq!(divided.to_string(), "{1/8+1/8}");
799        // music21's subdivide does not happen in place, and carries the
800        // weight of the span it divided.
801        assert!(beat.is_empty());
802        assert!((divided.weight() - 0.5).abs() < 1e-9);
803
804        let listed = MeterTerminal::new(5, 8)
805            .unwrap()
806            .subdivide_by_list(&[2, 3])
807            .unwrap();
808        assert_eq!(listed.to_string(), "{2/8+3/8}");
809    }
810
811    #[test]
812    fn the_options_come_in_the_order_music21_offers_them() {
813        use super::division_options_algorithmic;
814
815        let offered: Vec<Vec<String>> = division_options_algorithmic(4, 4)
816            .into_iter()
817            .take(6)
818            .collect();
819        assert_eq!(
820            offered,
821            vec![
822                vec!["1/4"; 4],
823                vec!["1/8"; 8],
824                vec!["1/16"; 16],
825                vec!["1/2"; 2],
826                vec!["4/4"],
827                vec!["2/4"; 2],
828            ]
829        );
830
831        // A compound meter is offered its threes first, and a five the two
832        // groupings music21 keeps for it.
833        assert_eq!(division_options_algorithmic(6, 8)[0], vec!["3/8"; 2]);
834        assert_eq!(
835            division_options_algorithmic(5, 8)[0],
836            vec!["2/8".to_string(), "3/8".to_string()]
837        );
838    }
839
840    /// music21's own examples, which is what this has to reproduce.
841    #[test]
842    fn a_bar_divides_the_way_music21_divides_it() {
843        let mut four_four = MeterTerminal::from_ratio_string("4/4").unwrap();
844        four_four.partition_by_count(2, true).unwrap();
845        assert_eq!(four_four.partition_string(), "{1/2+1/2}");
846        four_four.partition_by_count(4, true).unwrap();
847        assert_eq!(four_four.partition_string(), "{1/4+1/4+1/4+1/4}");
848
849        // Irregular meters take the grouping music21 keeps for them.
850        let mut five_eight = MeterTerminal::from_ratio_string("5/8").unwrap();
851        five_eight.partition_by_count(2, true).unwrap();
852        assert_eq!(five_eight.partition_string(), "{2/8+3/8}");
853        five_eight.partition_by_count(3, true).unwrap();
854        assert_eq!(five_eight.partition_string(), "{2/8+2/8+1/8}");
855
856        // A count nothing divides into falls back to the first option.
857        let mut also_five = MeterTerminal::from_ratio_string("5/8").unwrap();
858        also_five.partition_by_count(11, true).unwrap();
859        assert_eq!(also_five.partition_string(), "{2/8+3/8}");
860        assert!(
861            MeterTerminal::from_ratio_string("5/8")
862                .unwrap()
863                .partition_by_count(11, false)
864                .is_err()
865        );
866
867        // Compound meters divide into threes first.
868        let mut six_eight = MeterTerminal::from_ratio_string("6/8").unwrap();
869        six_eight.partition_by_count(2, true).unwrap();
870        assert_eq!(six_eight.partition_string(), "{3/8+3/8}");
871    }
872
873    #[test]
874    fn a_partition_must_come_to_what_the_bar_is() {
875        let mut bar = MeterTerminal::from_ratio_string("4/4").unwrap();
876        bar.partition_by_parts(&["3/4", "1/8", "1/8"]).unwrap();
877        assert_eq!(bar.partition_string(), "{3/4+1/8+1/8}");
878        assert!(bar.partition_by_parts(&["3/4", "1/8", "5/8"]).is_err());
879
880        // A list of numerators is read against a finer note value where it
881        // comes to a multiple of the meter's own.
882        let mut halves = MeterTerminal::from_ratio_string("2/4").unwrap();
883        halves.partition_by_list(&[1, 1]).unwrap();
884        assert_eq!(halves.partition_string(), "{1/4+1/4}");
885        halves.partition_by_list(&[1, 1, 1, 1]).unwrap();
886        assert_eq!(halves.partition_string(), "{1/8+1/8+1/8+1/8}");
887        assert!(halves.partition_by_list(&[1, 1, 1]).is_err());
888    }
889
890    #[test]
891    fn an_offset_finds_the_part_it_falls_in() {
892        let mut bar = MeterTerminal::from_ratio_string("4/4").unwrap();
893        bar.partition_by_count(4, true).unwrap();
894        assert_eq!(bar.offset_to_index(0.0).unwrap(), 0);
895        assert_eq!(bar.offset_to_index(1.5).unwrap(), 1);
896        assert_eq!(bar.offset_to_index(3.99).unwrap(), 3);
897        assert_eq!(bar.offset_to_span(1.5, false).unwrap(), (1.0, 2.0));
898        // Past the bar is an error, or the same offset read within it.
899        assert!(bar.offset_to_index(4.0).is_err());
900        assert!(bar.offset_to_index(-0.5).is_err());
901        assert_eq!(bar.offset_to_span(5.5, true).unwrap(), (1.0, 2.0));
902    }
903
904    #[test]
905    fn a_span_says_how_deep_and_how_flat_it_is() {
906        let mut bar = MeterTerminal::from_ratio_string("4/4").unwrap();
907        assert_eq!(bar.depth(), 0);
908        bar.partition_by_count(2, true).unwrap();
909        assert_eq!(bar.depth(), 1);
910        bar.parts_mut()[0].partition_by_count(2, true).unwrap();
911        assert_eq!(bar.depth(), 2);
912        assert_eq!(bar.partition_string(), "{{1/4+1/4}+1/2}");
913        assert_eq!(bar.flattened().len(), 3);
914        assert!((bar.flattened()[0].quarter_length() - 1.0).abs() < 1e-9);
915    }
916}