sim_lib_pitch_set/
relation.rs1use crate::{IntervalVector, PitchClassMask, SetClass, SetEquivalence, classify_set};
4
5#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
7pub enum SetInclusion {
8 Equal,
10 ProperSubset,
12 ProperSuperset,
14 Overlap,
16 Disjoint,
18}
19
20#[derive(Clone, Debug, PartialEq, Eq)]
26pub struct SetRelationAnalysis {
27 pub source_class: SetClass,
29 pub target_class: SetClass,
31 pub inclusion: SetInclusion,
33 pub common_tones: PitchClassMask,
35 pub transpositions: Vec<u8>,
37 pub inversion_indices: Vec<u8>,
39 pub transposition_equivalent: bool,
41 pub transposition_inversion_equivalent: bool,
43 pub exact_complement: bool,
45 pub complement_equivalent: bool,
47 pub z_related: bool,
49 pub source_interval_vector: IntervalVector,
51 pub target_interval_vector: IntervalVector,
53}
54
55pub fn analyze_set_relations(
61 source: PitchClassMask,
62 target: PitchClassMask,
63) -> SetRelationAnalysis {
64 let common_tones = source.intersection(target);
65 let inclusion = if source == target {
66 SetInclusion::Equal
67 } else if source.is_subset_of(target) {
68 SetInclusion::ProperSubset
69 } else if source.is_superset_of(target) {
70 SetInclusion::ProperSuperset
71 } else if source.is_disjoint_from(target) {
72 SetInclusion::Disjoint
73 } else {
74 SetInclusion::Overlap
75 };
76
77 let transpositions = (0..12)
78 .filter(|shift| source.rotate(i32::from(*shift)) == target)
79 .collect();
80 let inversion_indices = (0..12)
81 .filter(|index| source.invert_tni(*index) == target)
82 .collect();
83
84 let source_t = classify_set(source, SetEquivalence::Transposition);
85 let target_t = classify_set(target, SetEquivalence::Transposition);
86 let source_class = classify_set(source, SetEquivalence::TranspositionInversion);
87 let target_class = classify_set(target, SetEquivalence::TranspositionInversion);
88 let complement_class =
89 classify_set(source.complement(), SetEquivalence::TranspositionInversion);
90 let source_interval_vector = source.interval_vector();
91 let target_interval_vector = target.interval_vector();
92
93 SetRelationAnalysis {
94 source_class,
95 target_class: target_class.clone(),
96 inclusion,
97 common_tones,
98 transpositions,
99 inversion_indices,
100 transposition_equivalent: source_t.prime == target_t.prime,
101 transposition_inversion_equivalent: source_class_prime_eq(source, target),
102 exact_complement: source.complement() == target,
103 complement_equivalent: complement_class.prime == target_class.prime,
104 z_related: source.is_z_related_to(target),
105 source_interval_vector,
106 target_interval_vector,
107 }
108}
109
110fn source_class_prime_eq(source: PitchClassMask, target: PitchClassMask) -> bool {
111 classify_set(source, SetEquivalence::TranspositionInversion).prime
112 == classify_set(target, SetEquivalence::TranspositionInversion).prime
113}