1use std::collections::HashMap;
17
18use crate::defaults::FloatType;
19use crate::error::{Error, Result};
20use crate::meter::TimeSignature;
21use crate::search::difflib;
22use crate::stream::{Stream, StreamElement, StreamEvent, StreamKind};
23
24pub use crate::search::difflib::{Opcode, OpcodeTag};
25
26#[derive(Clone, Copy, Debug, PartialEq)]
29#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
30pub struct MeasureRelationship {
31 pub flagged_measure_part: usize,
33 pub flagged_measure_index: usize,
35 pub correct_measure_part: usize,
37 pub correct_measure_index: usize,
39 pub correction_probability: FloatType,
41}
42
43#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
46#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
47pub struct PriorsIntegrationScore {
48 pub total: usize,
50 pub horizontal: usize,
52 pub vertical: usize,
54 pub ignored: usize,
56}
57
58pub const PROBABILITY_ON_EQUALITY: FloatType = 0.9675;
60pub const PROBABILITY_ON_OMISSION: FloatType = 0.009;
62pub const PROBABILITY_ON_ADDITION: FloatType = 0.004;
65
66pub fn measure_hash(measure: &Stream) -> Result<String> {
89 let flat = !measure
90 .events()
91 .iter()
92 .any(|event| matches!(event.element(), StreamElement::Stream(_)));
93 let chordified;
94 let source = if flat {
95 measure
96 } else {
97 chordified = measure.chordify()?;
98 &chordified
99 };
100 let mut hash = String::new();
101 for event in source.events() {
102 let element = event.element();
103 if !is_general_note(element) {
104 continue;
105 }
106 let length = element.quarter_length();
107 let code = if length == 0.0 {
108 Some(hash_grace())
109 } else {
110 match element {
111 StreamElement::Note(_)
112 | StreamElement::Chord(_)
113 | StreamElement::ChordSymbol(_) => Some(hash_note(length)),
114 StreamElement::Rest(_) => Some(hash_rest(length)),
115 _ => None,
116 }
117 };
118 if let Some(code) = code {
119 hash.push(code);
120 }
121 }
122 Ok(hash)
123}
124
125fn is_general_note(element: &StreamElement) -> bool {
127 matches!(
128 element,
129 StreamElement::Note(_)
130 | StreamElement::Chord(_)
131 | StreamElement::Rest(_)
132 | StreamElement::Unpitched(_)
133 | StreamElement::PercussionChord(_)
134 | StreamElement::ChordSymbol(_)
135 )
136}
137
138pub fn hash_quarter_length(length: FloatType) -> u8 {
141 if length == 0.0 {
142 return 1;
143 }
144 let code = ((length * 256.0).log2() * 10.0) as i64;
145 code.clamp(1, 127) as u8
146}
147
148pub fn hash_note(length: FloatType) -> char {
151 let code = hash_quarter_length(length);
152 char::from(if code.is_multiple_of(2) {
153 code
154 } else {
155 code + 1
156 })
157}
158
159pub fn hash_rest(length: FloatType) -> char {
162 let code = hash_quarter_length(length);
163 char::from(if code.is_multiple_of(2) {
164 code + 1
165 } else {
166 code
167 })
168}
169
170pub fn hash_grace() -> char {
173 char::from(hash_quarter_length(0.015625))
174}
175
176pub fn measure_difference(own: &str, other: &str) -> FloatType {
180 difference(&chars(own), &chars(other))
181}
182
183fn chars(hash: &str) -> Vec<char> {
184 hash.chars().collect()
185}
186
187fn difference(own: &[char], other: &[char]) -> FloatType {
188 let ratio = difflib::ratio(own, other);
189 1.0 - if ratio == 1.0 { 0.0 } else { ratio }
190}
191
192pub fn opcodes(own: &str, other: &str) -> Vec<Opcode> {
195 difflib::opcodes(&chars(own), &chars(other))
196}
197
198pub fn probability_based_on_changes(own: &str, other: &str) -> FloatType {
204 changes(&chars(own), &chars(other))
205}
206
207fn changes(own: &[char], other: &[char]) -> FloatType {
208 changes_between(own, other, own)
209}
210
211pub fn probability_based_on_changes_between(
220 matching: &str,
221 source: &str,
222 destination: &str,
223) -> FloatType {
224 changes_between(&chars(matching), &chars(source), &chars(destination))
225}
226
227fn changes_between(matching: &[char], source: &[char], destination: &[char]) -> FloatType {
228 let mut probability = 0.0;
229 for (index, step) in difflib::opcodes(matching, source).iter().enumerate() {
230 let one = one_opcode(step, source, destination);
231 if index == 0 {
232 probability = one;
233 } else {
234 probability *= one;
235 }
236 }
237 probability
238}
239
240pub fn probability_for_one_opcode(step: &Opcode, source: &str, destination: &str) -> FloatType {
250 one_opcode(step, &chars(source), &chars(destination))
251}
252
253fn one_opcode(step: &Opcode, source: &[char], destination: &[char]) -> FloatType {
254 match step.tag {
255 OpcodeTag::Equal => PROBABILITY_ON_EQUALITY.powf((step.j2 - step.j1) as FloatType),
256 OpcodeTag::Replace => substitute(&source[step.j1..step.j2], &destination[step.i1..step.i2]),
257 OpcodeTag::Insert => PROBABILITY_ON_OMISSION.powf((step.j2 - step.j1) as FloatType),
258 OpcodeTag::Delete => PROBABILITY_ON_ADDITION.powf((step.i2 - step.i1) as FloatType),
259 }
260}
261
262pub fn probability_on_substitute(source: &str, destination: &str) -> FloatType {
267 substitute(&chars(source), &chars(destination))
268}
269
270fn substitute(source: &[char], destination: &[char]) -> FloatType {
271 let (mut source, mut destination) = (source, destination);
272 let mut probability = 1.0;
273 if source.len() > destination.len() {
274 let additions = source.len() - destination.len();
275 probability = PROBABILITY_ON_ADDITION.powf(additions as FloatType);
276 source = &source[..source.len() - additions];
277 } else if source.len() < destination.len() {
278 let omissions = destination.len() - source.len();
279 probability = PROBABILITY_ON_OMISSION.powf(omissions as FloatType);
280 destination = &destination[..destination.len() - omissions];
281 }
282 for (from, to) in source.iter().zip(destination) {
283 probability *= probability_from_one_char_sub(*from, *to);
284 }
285 probability
286}
287
288pub fn probability_from_one_char_sub(source: char, destination: char) -> FloatType {
294 let difference = source as i64 - destination as i64;
295 let distance = difference.abs() as FloatType;
296 if difference == 0 {
297 1.0
298 } else if distance % 10.0 == 0.0 {
299 0.0165_f64.powf(distance / 10.0)
300 } else if difference == 6 {
301 PROBABILITY_ON_ADDITION
302 } else if difference == -6 {
303 PROBABILITY_ON_OMISSION
304 } else if distance % 2.0 != 0.0 {
305 0.003
306 } else {
307 PROBABILITY_ON_OMISSION * PROBABILITY_ON_ADDITION
308 }
309}
310
311fn best(probabilities: &[FloatType]) -> (usize, FloatType) {
314 let most = probabilities
315 .iter()
316 .copied()
317 .fold(FloatType::NEG_INFINITY, FloatType::max);
318 let at = probabilities.iter().position(|&p| p == most).unwrap_or(0);
319 (at, most)
320}
321
322pub fn part_hashes(part: &Stream) -> Result<Vec<String>> {
329 ScoreCorrector::measure_events(part)
330 .into_iter()
331 .map(|index| match part.events()[index].element() {
332 StreamElement::Stream(measure) => measure_hash(measure),
333 _ => unreachable!("a measure's event holds the measure"),
334 })
335 .collect()
336}
337
338pub fn incorrect_measures(part: &Stream, run_fast: bool) -> Result<Vec<usize>> {
352 let measures = ScoreCorrector::measure_events(part);
353 let meter_of = |index: usize| -> Option<TimeSignature> {
354 let event = &part.events()[index];
355 let StreamElement::Stream(measure) = event.element() else {
356 return None;
357 };
358 measure
359 .events()
360 .iter()
361 .take_while(|inner| inner.offset() == 0.0)
362 .find_map(|inner| match inner.element() {
363 StreamElement::TimeSignature(meter) => Some(meter.clone()),
364 _ => None,
365 })
366 .or_else(|| part.time_signature_at(event.offset()))
367 };
368 let common_time = || TimeSignature::new(4, 4);
369 let mut meter = if run_fast {
370 match measures.first().and_then(|&index| meter_of(index)) {
371 Some(meter) => meter,
372 None => common_time()?,
373 }
374 } else {
375 common_time()?
376 };
377 let mut incorrect = Vec::new();
378 for (i, &index) in measures.iter().enumerate() {
379 if !run_fast {
380 meter = meter_of(index).ok_or_else(|| {
381 Error::Omr(format!("no time signature in force in measure {index}"))
382 })?;
383 }
384 if part.events()[index].element().quarter_length() != meter.bar_quarter_length() {
385 incorrect.push(i);
386 }
387 }
388 Ok(incorrect)
389}
390
391pub fn slice_search(
402 hashes: &[String],
403 flagged: &[bool],
404 distribution: &[FloatType],
405 part: usize,
406 measure: usize,
407) -> MeasureRelationship {
408 let mut probabilities = Vec::with_capacity(hashes.len());
409 for (k, hash) in hashes.iter().enumerate() {
410 let cannot = k == part || flagged.get(k).copied().unwrap_or(false);
411 if cannot {
412 probabilities.push(0.0);
413 continue;
414 }
415 let likely = probability_based_on_changes(&hashes[part], hash);
416 probabilities.push(likely * distribution.get(k).copied().unwrap_or(0.0));
417 }
418 let (at, most) = best(&probabilities);
419 MeasureRelationship {
420 flagged_measure_part: part,
421 flagged_measure_index: measure,
422 correct_measure_part: at,
423 correct_measure_index: measure,
424 correction_probability: most,
425 }
426}
427
428#[derive(Clone, Debug, PartialEq)]
431#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
432pub struct SinglePart {
433 part_number: usize,
434 hashed_notes: Vec<String>,
435 incorrect_measures: Vec<usize>,
436 probability_distribution: Option<Vec<FloatType>>,
437 index_array: Option<Vec<i64>>,
438}
439
440impl SinglePart {
441 pub fn from_part(part: &Stream, number: usize) -> Result<Self> {
449 Ok(Self {
450 part_number: number,
451 hashed_notes: part_hashes(part)?,
452 incorrect_measures: incorrect_measures(part, true)?,
453 probability_distribution: None,
454 index_array: None,
455 })
456 }
457
458 pub fn part_number(&self) -> usize {
460 self.part_number
461 }
462
463 pub fn hashed_notes(&self) -> &[String] {
467 &self.hashed_notes
468 }
469
470 pub fn incorrect_measures(&self) -> &[usize] {
473 &self.incorrect_measures
474 }
475
476 pub fn horizontal_probability_dist(&mut self) -> &[FloatType] {
483 if self.probability_distribution.is_none() {
484 let count = self.hashed_notes.len();
485 let hashes: Vec<Vec<char>> = self.hashed_notes.iter().map(|hash| chars(hash)).collect();
486 let mut all = vec![0.0; count * 2];
487 let mut indices = vec![0_i64; count * 2];
488 for i in 0..count {
489 for k in 0..count {
490 let at = count + k - i;
491 indices[at] = k as i64 - i as i64;
492 if i == k {
493 all[at] = count as FloatType;
494 } else if difference(&hashes[i], &hashes[k]) == 1.0 {
495 all[at] += 1.0;
496 }
497 }
498 }
499 if !indices.is_empty() {
500 indices.remove(0);
501 all.remove(0);
502 }
503 self.index_array = Some(indices);
504 self.probability_distribution =
505 Some(all.iter().map(|value| value / count as FloatType).collect());
506 }
507 self.probability_distribution.as_deref().unwrap_or_default()
508 }
509
510 pub fn index_array(&self) -> Option<&[i64]> {
514 self.index_array.as_deref()
515 }
516
517 pub fn probability_distribution(&mut self, source: usize, destination: usize) -> FloatType {
520 let count = self.hashed_notes.len();
521 let index = source + count - 1 - destination;
522 self.horizontal_probability_dist()[index]
523 }
524
525 pub fn run_horizontal_search(&mut self, i: usize) -> MeasureRelationship {
530 self.horizontal_probability_dist();
531 let incorrect = self.incorrect_measures[i];
532 let own = chars(&self.hashed_notes[incorrect]);
533 let mut probabilities = Vec::with_capacity(self.hashed_notes.len());
534 for k in 0..self.hashed_notes.len() {
535 if self.incorrect_measures.contains(&k) {
536 probabilities.push(0.0);
537 } else {
538 let likely = changes(&own, &chars(&self.hashed_notes[k]));
539 probabilities.push(likely * self.probability_distribution(k, incorrect));
540 }
541 }
542 let (at, most) = best(&probabilities);
543 MeasureRelationship {
544 flagged_measure_part: self.part_number,
545 flagged_measure_index: incorrect,
546 correct_measure_part: self.part_number,
547 correct_measure_index: at,
548 correction_probability: most,
549 }
550 }
551
552 pub fn run_horizontal_correction_model(&mut self) -> Vec<MeasureRelationship> {
555 (0..self.incorrect_measures.len())
556 .map(|i| self.run_horizontal_search(i))
557 .collect()
558 }
559}
560
561#[derive(Clone, Debug)]
580pub struct ScoreCorrector {
581 score: Stream,
582 single_parts: Vec<SinglePart>,
583 part_events: Vec<usize>,
585 distribution: Option<Vec<Vec<FloatType>>>,
586 slices: HashMap<usize, Vec<String>>,
589}
590
591impl ScoreCorrector {
592 pub fn new(score: Stream) -> Result<Self> {
599 let part_events: Vec<usize> = score
600 .events()
601 .iter()
602 .enumerate()
603 .filter_map(|(index, event)| match event.element() {
604 StreamElement::Stream(inner)
605 if matches!(inner.kind(), StreamKind::Part | StreamKind::PartStaff) =>
606 {
607 Some(index)
608 }
609 _ => None,
610 })
611 .collect();
612 let mut corrector = Self {
613 score,
614 single_parts: Vec::new(),
615 part_events,
616 distribution: None,
617 slices: HashMap::new(),
618 };
619 for part_number in 0..corrector.part_events.len() {
620 let single = SinglePart::from_part(corrector.part(part_number), part_number)?;
621 corrector.single_parts.push(single);
622 }
623 Ok(corrector)
624 }
625
626 pub fn score(&self) -> &Stream {
628 &self.score
629 }
630
631 pub fn into_score(self) -> Stream {
633 self.score
634 }
635
636 pub fn single_parts(&self) -> &[SinglePart] {
638 &self.single_parts
639 }
640
641 pub fn single_part_mut(&mut self, part: usize) -> &mut SinglePart {
643 &mut self.single_parts[part]
644 }
645
646 pub fn all_hashes(&self) -> Vec<&[String]> {
648 self.single_parts
649 .iter()
650 .map(SinglePart::hashed_notes)
651 .collect()
652 }
653
654 pub fn all_incorrect_measures(&self) -> Vec<&[usize]> {
656 self.single_parts
657 .iter()
658 .map(SinglePart::incorrect_measures)
659 .collect()
660 }
661
662 fn part(&self, part: usize) -> &Stream {
663 match self.score.events()[self.part_events[part]].element() {
664 StreamElement::Stream(inner) => inner,
665 _ => unreachable!("a part's event holds the part"),
666 }
667 }
668
669 fn measure_events(part: &Stream) -> Vec<usize> {
671 part.events()
672 .iter()
673 .enumerate()
674 .filter_map(|(index, event)| match event.element() {
675 StreamElement::Stream(inner) if inner.kind() == StreamKind::Measure => Some(index),
676 _ => None,
677 })
678 .collect()
679 }
680
681 fn hash_part(&self, part: usize) -> Result<Vec<String>> {
682 part_hashes(self.part(part))
683 }
684
685 pub fn find_incorrect_measures(&mut self, part: usize, run_fast: bool) -> Result<&[usize]> {
695 self.single_parts[part].incorrect_measures = incorrect_measures(self.part(part), run_fast)?;
696 Ok(&self.single_parts[part].incorrect_measures)
697 }
698
699 pub fn run_horizontal_correction_model(&mut self) -> Vec<Vec<MeasureRelationship>> {
702 self.single_parts
703 .iter_mut()
704 .map(SinglePart::run_horizontal_correction_model)
705 .collect()
706 }
707
708 pub fn vertical_probability_dist(&mut self) -> Result<&[Vec<FloatType>]> {
718 if self.distribution.is_none() {
719 let mut distribution = Vec::with_capacity(self.single_parts.len());
720 for part in 0..self.single_parts.len() {
721 distribution.push(self.vertical_probability_dist_of_part(part)?);
722 }
723 self.distribution = Some(distribution);
724 }
725 Ok(self.distribution.as_deref().unwrap_or_default())
726 }
727
728 pub fn vertical_probability_dist_of_part(&self, part: usize) -> Result<Vec<FloatType>> {
736 let measures = self
737 .single_parts
738 .get(part)
739 .ok_or_else(|| Error::Omr(format!("no part {part}")))?
740 .hashed_notes
741 .len();
742 if measures == 0 {
743 return Err(Error::Omr(format!("part {part} has no measures")));
744 }
745 let mut sums = vec![0.0; self.single_parts.len()];
746 for measure in 0..measures {
747 for (sum, alike) in sums
748 .iter_mut()
749 .zip(self.vertical_probability_dist_of_measure(part, measure)?)
750 {
751 *sum += alike;
752 }
753 }
754 Ok(sums.iter().map(|sum| sum / measures as FloatType).collect())
755 }
756
757 pub fn vertical_probability_dist_of_measure(
765 &self,
766 part: usize,
767 measure: usize,
768 ) -> Result<Vec<FloatType>> {
769 let hash = self
770 .single_parts
771 .get(part)
772 .and_then(|single| single.hashed_notes.get(measure))
773 .ok_or_else(|| Error::Omr(format!("no measure {measure} in part {part}")))?;
774 let mut alike = Vec::with_capacity(self.single_parts.len());
775 for (other, single) in self.single_parts.iter().enumerate() {
776 if other == part {
777 alike.push(1.0);
778 continue;
779 }
780 let their = single
781 .hashed_notes
782 .get(measure)
783 .ok_or_else(|| Error::Omr(format!("no measure {measure} in part {other}")))?;
784 alike.push(if measure_difference(hash, their) == 1.0 {
785 1.0
786 } else {
787 0.0
788 });
789 }
790 Ok(alike)
791 }
792
793 pub fn slice_hashes(&mut self, measure: usize) -> Result<&[String]> {
802 if !self.slices.contains_key(&measure) {
803 let mut hashes = Vec::with_capacity(self.single_parts.len());
804 for number in 0..self.single_parts.len() {
805 let stream = self.part(number);
806 let index = *Self::measure_events(stream)
807 .get(measure)
808 .ok_or_else(|| Error::Omr(format!("no measure {measure} in part {number}")))?;
809 let StreamElement::Stream(inner) = stream.events()[index].element() else {
810 unreachable!("a measure's event holds the measure");
811 };
812 hashes.push(measure_hash(inner)?);
813 }
814 self.slices.insert(measure, hashes);
815 }
816 Ok(&self.slices[&measure])
817 }
818
819 pub fn run_vertical_search(
830 &mut self,
831 measure: usize,
832 part: usize,
833 ) -> Result<MeasureRelationship> {
834 self.vertical_probability_dist()?;
835 self.slice_hashes(measure)?;
836 let flagged: Vec<bool> = self
837 .single_parts
838 .iter()
839 .map(|single| single.incorrect_measures.contains(&measure))
840 .collect();
841 let hashes = &self.slices[&measure];
842 let distribution = self.distribution.as_deref().unwrap_or_default();
843 Ok(slice_search(
844 hashes,
845 &flagged,
846 distribution.get(part).map_or(&[][..], Vec::as_slice),
847 part,
848 measure,
849 ))
850 }
851
852 pub fn run_vertical_correction_model(&mut self) -> Result<Vec<Vec<MeasureRelationship>>> {
859 self.vertical_probability_dist()?;
860 let mut all = Vec::with_capacity(self.single_parts.len());
861 for part in 0..self.single_parts.len() {
862 let flagged = self.single_parts[part].incorrect_measures.clone();
863 all.push(
864 flagged
865 .into_iter()
866 .map(|measure| self.run_vertical_search(measure, part))
867 .collect::<Result<Vec<_>>>()?,
868 );
869 }
870 Ok(all)
871 }
872
873 pub fn substitute_measure(
892 &mut self,
893 source_measure: usize,
894 source_part: usize,
895 destination_measure: usize,
896 destination_part: usize,
897 ) -> Result<()> {
898 let source = if (source_part, source_measure) == (destination_part, destination_measure) {
899 Stream::with_kind(StreamKind::Measure)
900 } else {
901 let stream = self.part(source_part);
902 let index = Self::measure_events(stream)[source_measure];
903 match stream.events()[index].element() {
904 StreamElement::Stream(inner) => (**inner).clone(),
905 _ => unreachable!("a measure's event holds the measure"),
906 }
907 };
908 let destination = self.part(destination_part);
909 let index = Self::measure_events(destination)[destination_measure];
910 let StreamElement::Stream(measure) = destination.events()[index].element() else {
911 unreachable!("a measure's event holds the measure");
912 };
913 let old_pitches: Vec<crate::pitch::Pitch> = measure
914 .events()
915 .iter()
916 .filter_map(|event| match event.element() {
917 StreamElement::Note(note) => Some(note.pitch().clone()),
918 _ => None,
919 })
920 .collect();
921 let mut elements: Vec<StreamEvent> = source.events().to_vec();
924 if let Some(barline) = source.left_barline() {
925 let at = elements
926 .iter()
927 .position(|event| event.offset() > 0.0 || event.element().class_sort_order() > -5)
928 .unwrap_or(elements.len());
929 elements.insert(
930 at,
931 StreamEvent::new(0.0, StreamElement::Barline(barline.clone())),
932 );
933 }
934 if let Some(barline) = source.right_barline() {
935 elements.push(StreamEvent::new(
936 0.0,
937 StreamElement::Barline(barline.clone()),
938 ));
939 }
940 let mut contents = Stream::with_kind(StreamKind::Measure);
941 let (mut left, mut pitch_index) = (None, 0);
942 for event in elements {
943 let mut element = event.element().clone();
944 if let StreamElement::Note(note) = &mut element
945 && let Some(old) = old_pitches.get(pitch_index)
946 {
947 let mut pitch = note.pitch().clone();
948 pitch.set_octave(old.octave());
949 pitch.take_name_of(old)?;
950 note.set_pitch(pitch);
951 pitch_index += 1;
952 }
953 let offset = contents.end_offset();
954 match element {
955 StreamElement::Barline(barline) if offset == 0.0 && left.is_none() => {
957 left = Some(barline);
958 }
959 element => contents.push(element),
960 }
961 }
962 let mut replaced = measure.with_events(contents.events().to_vec());
963 replaced.set_left_barline(left);
964 replaced.set_right_barline(None);
965 let path = [self.part_events[destination_part], index];
966 self.score.replace_nested(&path, replaced);
967 Ok(())
968 }
969
970 pub fn generate_corrected_score(
980 &mut self,
981 horizontal: &[Vec<MeasureRelationship>],
982 vertical: &[Vec<MeasureRelationship>],
983 ) -> Result<PriorsIntegrationScore> {
984 let mut score = PriorsIntegrationScore::default();
985 for part in 0..self.single_parts.len() {
986 let out_of_range = || Error::Omr(format!("no relationships given for part {part}"));
987 let across = horizontal.get(part).ok_or_else(out_of_range)?;
988 for h in across {
989 for v in vertical.get(part).ok_or_else(out_of_range)? {
990 if h.flagged_measure_part != v.flagged_measure_part
991 || h.flagged_measure_index != v.flagged_measure_index
992 {
993 continue;
994 }
995 score.total += 1;
996 let source = if h.correction_probability > v.correction_probability {
997 score.horizontal += 1;
998 h
999 } else {
1000 score.vertical += 1;
1001 v
1002 };
1003 self.substitute_measure(
1004 source.correct_measure_index,
1005 source.correct_measure_part,
1006 h.flagged_measure_index,
1007 h.flagged_measure_part,
1008 )?;
1009 }
1010 }
1011 self.single_parts[part].hashed_notes = self.hash_part(part)?;
1012 }
1013 Ok(score)
1014 }
1015
1016 pub fn run(&mut self) -> Result<PriorsIntegrationScore> {
1023 let horizontal = self.run_horizontal_correction_model();
1024 let vertical = self.run_vertical_correction_model()?;
1025 self.generate_corrected_score(&horizontal, &vertical)
1026 }
1027}
1028
1029#[cfg(test)]
1030mod tests {
1031 use super::*;
1032
1033 #[test]
1034 fn lengths_are_hashed_as_music21_hashes_them() {
1035 assert_eq!(hash_quarter_length(1.0), 80);
1036 assert_eq!(hash_quarter_length(0.015625), 20);
1037 assert_eq!(hash_quarter_length(0.0), 1);
1038 assert_eq!(hash_quarter_length(1000.0), 127);
1039 }
1040
1041 #[test]
1044 fn a_note_is_written_as_music21_writes_it() -> Result<()> {
1045 assert_eq!(hash_quarter_length(1.0), 80);
1046 assert_eq!(hash_quarter_length(2.0), 90);
1047 assert_eq!(hash_note(1.0), 'P');
1048 assert_eq!(hash_note(2.0), 'Z');
1049 assert_eq!(hash_note(1.5), 'V');
1050 assert_eq!(hash_rest(1.0), 'Q');
1051 assert_eq!(hash_grace(), char::from(hash_quarter_length(0.015625)));
1053 let mut measure = Stream::with_kind(StreamKind::Measure);
1054 for (name, length) in [("C4", 1.5), ("C4", 0.5)] {
1055 measure.push(
1056 crate::note::Note::from_name(name)?
1057 .with_duration(crate::duration::Duration::new(length)?),
1058 );
1059 }
1060 measure.push(crate::Rest::new(crate::duration::Duration::new(1.5)?));
1061 measure.push(
1062 crate::note::Note::from_name("B4")?.with_duration(crate::duration::Duration::new(0.5)?),
1063 );
1064 assert_eq!(measure_hash(&measure)?, "VFUF");
1065 assert_eq!(measure_difference("VFUF", "VGUF"), 0.25);
1066 assert_eq!(measure_difference("VFUF", "VFUF"), 1.0);
1067 Ok(())
1068 }
1069
1070 #[test]
1073 fn one_step_of_a_correction_is_as_likely_as_music21_has_it() {
1074 let step = |tag, i1, i2, j1, j2| Opcode {
1075 tag,
1076 i1,
1077 i2,
1078 j1,
1079 j2,
1080 };
1081 assert_eq!(
1082 probability_for_one_opcode(&step(OpcodeTag::Equal, 0, 4, 0, 4), "PFPFFF", "PFPFGF"),
1083 0.876_201_303_164_062_6
1084 );
1085 assert_eq!(
1086 probability_for_one_opcode(&step(OpcodeTag::Insert, 4, 4, 4, 5), "PFPFFF", "PFPFGF"),
1087 0.009
1088 );
1089 assert_eq!(
1090 probability_for_one_opcode(&step(OpcodeTag::Replace, 2, 4, 2, 4), "PPPPP", "PPVZP"),
1091 0.000_148_5
1092 );
1093 assert_eq!(
1094 probability_for_one_opcode(&step(OpcodeTag::Delete, 0, 5, 0, 0), "e", "GFPGF"),
1095 1.024e-12
1096 );
1097 assert_eq!(
1100 probability_based_on_changes("GFPGF", "e"),
1101 2.947_283_212_5e-14
1102 );
1103 }
1104
1105 #[test]
1106 fn a_rhythm_misread_is_as_likely_as_music21_has_it() {
1107 assert_eq!(probability_from_one_char_sub('P', 'P'), 1.0);
1109 assert_eq!(probability_from_one_char_sub('Z', 'P'), 0.0165);
1110 assert_eq!(
1111 probability_from_one_char_sub('V', 'P'),
1112 PROBABILITY_ON_ADDITION
1113 );
1114 assert_eq!(probability_from_one_char_sub('Q', 'P'), 0.003);
1115 assert_eq!(measure_difference("PPPP", "PPPP"), 1.0);
1116 assert_eq!(measure_difference("", ""), 1.0);
1117 assert_eq!(measure_difference("PP", "ZZ"), 1.0);
1118 }
1119}