diff --git a/src/comb.rs b/src/comb.rs index e2cc7fd..56faa7e 100644 --- a/src/comb.rs +++ b/src/comb.rs @@ -360,9 +360,14 @@ impl VecPerm { if seek_0 < seek_non0 { // If `slice` has a `0` and it's before a non-`0` element, swap 'em. vec.swap(seek_0, seek_non0); - perm = VecPerm::transposition(vec.len(), seek_0, seek_non0) - .o(&perm) - .unwrap(); // Multiply by the transposition. + let p = VecPerm::transposition(vec.len(), seek_0, seek_non0) + .o(&perm); + // Multiply by the transposition. + if let Ok(p1) = p { + perm = p1; + } else { + return (slice.to_vec(), Self::id(slice.len())); + } } else { // Early return, nothing to do. return (vec, perm); @@ -372,23 +377,29 @@ impl VecPerm { (inner_slice, inner_perm) = Self::sift_zeros(&slice[(seek_0 + 1)..=(seek_non0 - 1)]); }); - vec = Self::nest_replacing(&vec, &inner_slice, seek_0 + 1).unwrap(); - perm.pi = Self::nest_replacing( - &perm.pi, - &inner_perm - .pi - .into_iter() - .map(|x| x + seek_0 + 1) - .collect::>(), - seek_0 + 1, - ) - .unwrap(); - (vec, perm) + if let Ok(vec) = Self::nest_replacing(&vec, &inner_slice, seek_0 + 1) { + let p = Self::nest_replacing( + &perm.pi, + &inner_perm + .pi + .into_iter() + .map(|x| x + seek_0 + 1) + .collect::>(), + seek_0 + 1, + ); + if let Ok(perm) = p { + (vec, VecPerm { pi: perm }) + } else { + return (slice.to_vec(), Self::id(slice.len())); + } + } else { + return (slice.to_vec(), Self::id(slice.len())); + } } } } } -/* + #[cfg(test)] mod tests { use super::*; @@ -398,13 +409,13 @@ mod tests { fn test_necklace_generation_with_zeros_in_content() { let content = [1usize, 1, 0, 0, 1]; let attempt: BTreeSet> = - BTreeSet::from_iter(necklaces_fixed_content(&content).into_iter()).await; + BTreeSet::from_iter(necklaces_fixed_content(&content).into_iter()); let correct_scales = vec![vec![0, 1, 4], vec![0, 4, 1]]; let correct_result: BTreeSet> = BTreeSet::from_iter(correct_scales.into_iter()); assert_eq!(attempt, correct_result); let content = [5usize, 0, 0, 0, 2]; let attempt: BTreeSet> = - BTreeSet::from_iter(necklaces_fixed_content(&content).into_iter()).await; + BTreeSet::from_iter(necklaces_fixed_content(&content).into_iter()); let correct_result: BTreeSet> = BTreeSet::from_iter( vec![ vec![0, 0, 0, 4, 0, 0, 4], @@ -1034,4 +1045,3 @@ mod tests { assert_eq!(attempt, correct_set); } } - */ diff --git a/src/equal.rs b/src/equal.rs index c8470ab..2232a44 100644 --- a/src/equal.rs +++ b/src/equal.rs @@ -119,7 +119,7 @@ pub fn odd_limit_l1_error(odd: u64, edo: f64) -> f64 { odd_limit(odd) .into_iter() .filter(|&r| r * r < RawJiRatio::OCTAVE) - .map(|r| Monzo::try_from_ratio(r).unwrap()) + .map(|r| Monzo::try_from_ratio(r).unwrap_or(Monzo::UNISON)) .map(|monzo| relative_error(monzo, edo).abs()) // get the magnitudes of the relative errors of primes .sum() } @@ -129,7 +129,7 @@ pub fn odd_limit_l2_error(odd: u64, edo: f64) -> f64 { odd_limit(odd) .into_iter() .filter(|&r| r * r < RawJiRatio::OCTAVE) - .map(|r| Monzo::try_from_ratio(r).unwrap()) + .map(|r| Monzo::try_from_ratio(r).unwrap_or(Monzo::UNISON)) .map(|monzo| relative_error(monzo, edo).pow(2)) // square the relative error of each prime .sum::() .sqrt() diff --git a/src/ji.rs b/src/ji.rs index 2b951f9..99c8c88 100644 --- a/src/ji.rs +++ b/src/ji.rs @@ -372,21 +372,6 @@ pub fn well_formed_necklace_in_ji_scale( Err(ScaleError::NonCoprimeGenError) } } -/// Uses tempering to 2901533edo (a highly consistent edo) as a heuristic to check the CS property. -pub fn is_cs_ji_scale_fast(scale: &[RawJiRatio]) -> bool { - let val_2901533 = crate::equal::gpval(2901533.0); - (1..=scale.len() / 2) - .map(|i| { - spectrum(scale, i) - .into_inner() - .keys() - .map(|r| val_2901533.evaluate(Monzo::try_from_ratio(*r).unwrap())) - .collect::>() - }) - .reduce(|acc, next| acc.intersection(&next).copied().collect()) - .expect("should be nonempty since 1..=scale.len() / 2 is nonempty for a nonempty `scale`") - .is_empty() -} #[cfg(test)] mod tests { diff --git a/src/lib.rs b/src/lib.rs index 9174349..e679a5f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -156,7 +156,7 @@ fn numbers_to_string(word: &[usize]) -> String { let arity = word.iter().collect::>().len(); for i in word { if *i <= arity { - result.push(STEP_LETTERS[min(arity, 12)].chars().nth(*i).unwrap()); + result.push(STEP_LETTERS[min(arity, 12)].chars().nth(*i).unwrap_or('?')); } } result @@ -299,9 +299,12 @@ fn get_unimodular_basis( // this branch handles multiplicity > 1 scales let structure = guide_frame_to_result(structure); let vec_for_gs_element = structure.gs[0].clone(); - let vec_for_offset = structure.polyoffset.last().unwrap(); - if det3(step_sig, &vec_for_gs_element, vec_for_offset).abs() == 1 { - return Some((vec![vec_for_gs_element, vec_for_offset.clone()], structure)); + if let Some(vec_for_offset) = structure.polyoffset.last() { + if det3(step_sig, &vec_for_gs_element, vec_for_offset).abs() == 1 { + return Some((vec![vec_for_gs_element, vec_for_offset.clone()], structure)); + } + } else { + return None; } } } diff --git a/src/words.rs b/src/words.rs index 2e4da98..fcdaa41 100644 --- a/src/words.rs +++ b/src/words.rs @@ -186,12 +186,20 @@ pub fn countvector_to_slice(v: CountVector) -> Vec { if v.is_empty() { vec![] } else { - let max = *v.into_inner().last_key_value().unwrap().0; - let mut result = vec![0; max + 1]; - for key in v.into_inner().keys() { - result[*key] = *v.get(key).unwrap(); + if let Some(max_key_value) = v.into_inner().last_key_value() { + let max = max_key_value.0; + let mut result = vec![0; max + 1]; + for key in v.into_inner().keys() { + if let Some(value) = v.get(key) { + result[*key] = *value; + } else { + return vec![]; + } + } + result + } else { + vec![] } - result } } @@ -325,7 +333,7 @@ where // Only need to check half of the step size classes. let floor_half: usize = scale.len() / 2; let distinct_letters = scale.iter().cloned().collect::>(); - (1..=floor_half) + let mb_max = (1..=floor_half) .flat_map(|subword_length| { distinct_letters.iter().map(move |letter| { let counts = CountVector::distinct_spectrum(scale, subword_length) @@ -338,8 +346,12 @@ where - *counts.first().expect("`counts` should be nonempty") // this will always be >= 0 for a nonempty `BTreeSet` }) }) - .max() - .unwrap() as usize + .max(); + if let Some(max) = mb_max { + max as usize + } else { + usize::MAX + } } }