1use crate::defaults::{FloatType, UnsignedIntegerType};
16use crate::error::{Error, Result};
17
18const OFFSET_TOLERANCE: FloatType = 1e-9;
20
21const VALID_DENOMINATORS: [UnsignedIntegerType; 8] = [1, 2, 4, 8, 16, 32, 64, 128];
23
24#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
27#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
28pub enum OffsetAlign {
29 #[default]
32 Quantize,
33 Start,
35 End,
37}
38
39#[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 parts: Vec<MeterTerminal>,
49}
50
51impl MeterTerminal {
52 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 pub fn from_ratio_string(ratio: &str) -> Result<Self> {
74 let (numerator, denominator) = split_ratio(ratio)?;
75 Self::new(numerator, denominator)
76 }
77
78 #[must_use]
80 pub fn numerator(&self) -> UnsignedIntegerType {
81 self.numerator
82 }
83
84 #[must_use]
86 pub fn denominator(&self) -> UnsignedIntegerType {
87 self.denominator
88 }
89
90 #[must_use]
92 pub fn quarter_length(&self) -> FloatType {
93 FloatType::from(self.numerator) * (4.0 / FloatType::from(self.denominator))
94 }
95
96 #[must_use]
98 pub fn weight(&self) -> FloatType {
99 self.weight
100 }
101
102 pub fn set_weight(&mut self, weight: FloatType) {
104 self.weight = weight;
105 }
106
107 #[must_use]
109 pub fn parts(&self) -> &[MeterTerminal] {
110 &self.parts
111 }
112
113 pub fn parts_mut(&mut self) -> &mut Vec<MeterTerminal> {
115 &mut self.parts
116 }
117
118 #[must_use]
120 pub fn len(&self) -> usize {
121 self.parts.len()
122 }
123
124 #[must_use]
126 pub fn is_empty(&self) -> bool {
127 self.parts.is_empty()
128 }
129
130 #[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 #[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 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 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 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 pub fn subdivide(&mut self, count: usize) -> Result<()> {
231 self.partition_by_count(count, false)
232 }
233
234 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 #[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 #[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 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 #[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 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 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 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 #[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 #[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 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 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 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 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 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 #[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
518fn 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
537fn 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
557fn 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
580fn 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
602fn 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
625fn 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
647fn 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
668fn division_options_algorithmic(
676 numerator: UnsignedIntegerType,
677 denominator: UnsignedIntegerType,
678) -> Vec<Vec<String>> {
679 let mut options: Vec<Vec<String>> = Vec::new();
680
681 if numerator > 3 && numerator.is_multiple_of(3) {
683 options.push(vec![format!("3/{denominator}"); (numerator / 3) as usize]);
684 }
685 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
714fn 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 #[test]
748 fn levels_are_read_as_music21_reads_them() {
749 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 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 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 assert!(bar.is_uniform_partition(0));
777 assert!(bar.is_uniform_partition(1));
778 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 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 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 #[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 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 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 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 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 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}