use crate::{IntervalVector, PitchClassMask, SetClass, SetEquivalence, classify_set};
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum SetInclusion {
Equal,
ProperSubset,
ProperSuperset,
Overlap,
Disjoint,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SetRelationAnalysis {
pub source_class: SetClass,
pub target_class: SetClass,
pub inclusion: SetInclusion,
pub common_tones: PitchClassMask,
pub transpositions: Vec<u8>,
pub inversion_indices: Vec<u8>,
pub transposition_equivalent: bool,
pub transposition_inversion_equivalent: bool,
pub exact_complement: bool,
pub complement_equivalent: bool,
pub z_related: bool,
pub source_interval_vector: IntervalVector,
pub target_interval_vector: IntervalVector,
}
pub fn analyze_set_relations(
source: PitchClassMask,
target: PitchClassMask,
) -> SetRelationAnalysis {
let common_tones = source.intersection(target);
let inclusion = if source == target {
SetInclusion::Equal
} else if source.is_subset_of(target) {
SetInclusion::ProperSubset
} else if source.is_superset_of(target) {
SetInclusion::ProperSuperset
} else if source.is_disjoint_from(target) {
SetInclusion::Disjoint
} else {
SetInclusion::Overlap
};
let transpositions = (0..12)
.filter(|shift| source.rotate(i32::from(*shift)) == target)
.collect();
let inversion_indices = (0..12)
.filter(|index| source.invert_tni(*index) == target)
.collect();
let source_t = classify_set(source, SetEquivalence::Transposition);
let target_t = classify_set(target, SetEquivalence::Transposition);
let source_class = classify_set(source, SetEquivalence::TranspositionInversion);
let target_class = classify_set(target, SetEquivalence::TranspositionInversion);
let complement_class =
classify_set(source.complement(), SetEquivalence::TranspositionInversion);
let source_interval_vector = source.interval_vector();
let target_interval_vector = target.interval_vector();
SetRelationAnalysis {
source_class,
target_class: target_class.clone(),
inclusion,
common_tones,
transpositions,
inversion_indices,
transposition_equivalent: source_t.prime == target_t.prime,
transposition_inversion_equivalent: source_class_prime_eq(source, target),
exact_complement: source.complement() == target,
complement_equivalent: complement_class.prime == target_class.prime,
z_related: source.is_z_related_to(target),
source_interval_vector,
target_interval_vector,
}
}
fn source_class_prime_eq(source: PitchClassMask, target: PitchClassMask) -> bool {
classify_set(source, SetEquivalence::TranspositionInversion).prime
== classify_set(target, SetEquivalence::TranspositionInversion).prime
}