1use std::fmt;
18
19use crate::defaults::{FloatType, IntegerType, UnsignedIntegerType};
20use crate::error::{Error, Result};
21
22#[derive(Clone, Debug, Eq, PartialEq)]
38#[must_use]
39pub struct Sieve {
40 root: Node,
41}
42
43#[derive(Clone, Debug, Eq, PartialEq)]
44enum Node {
45 Residual {
48 modulus: UnsignedIntegerType,
49 shift: UnsignedIntegerType,
50 },
51 Not(Box<Node>),
52 And(Box<Node>, Box<Node>),
53 Or(Box<Node>, Box<Node>),
54 Xor(Box<Node>, Box<Node>),
55 Group(Box<Node>),
62}
63
64impl Node {
65 fn contains(&self, z: IntegerType) -> bool {
66 match self {
67 Self::Residual { modulus: 0, .. } => false,
68 Self::Residual { modulus, shift } => {
69 z.rem_euclid(*modulus as IntegerType) == *shift as IntegerType
70 }
71 Self::Group(inner) => inner.contains(z),
72 Self::Not(inner) => !inner.contains(z),
73 Self::And(left, right) => left.contains(z) && right.contains(z),
74 Self::Or(left, right) => left.contains(z) || right.contains(z),
75 Self::Xor(left, right) => left.contains(z) != right.contains(z),
76 }
77 }
78
79 fn shifted(&self, n: IntegerType) -> Node {
85 match self {
86 Self::Residual { modulus: 0, .. } => self.clone(),
87 Self::Residual { modulus, shift } => Self::Residual {
88 modulus: *modulus,
89 shift: (*shift as IntegerType + n).rem_euclid(*modulus as IntegerType)
90 as UnsignedIntegerType,
91 },
92 Self::Group(inner) => Self::Group(Box::new(inner.shifted(n))),
93 Self::Not(inner) => Self::Not(Box::new(inner.shifted(n))),
94 Self::And(left, right) => {
95 Self::And(Box::new(left.shifted(n)), Box::new(right.shifted(n)))
96 }
97 Self::Or(left, right) => {
98 Self::Or(Box::new(left.shifted(n)), Box::new(right.shifted(n)))
99 }
100 Self::Xor(left, right) => {
101 Self::Xor(Box::new(left.shifted(n)), Box::new(right.shifted(n)))
102 }
103 }
104 }
105
106 fn is_bound(&self) -> bool {
112 match self {
113 Self::Residual { .. } => false,
114 Self::Group(_) | Self::Xor(..) => true,
115 Self::Not(inner) => !matches!(**inner, Self::Residual { .. }),
116 Self::And(left, right) | Self::Or(left, right) => left.is_bound() || right.is_bound(),
117 }
118 }
119
120 fn collect_or_groups<'a>(&'a self, out: &mut Vec<Vec<&'a Node>>) {
123 if let Self::Or(left, right) = self {
124 left.collect_or_groups(out);
125 right.collect_or_groups(out);
126 } else {
127 let mut group = Vec::new();
128 self.collect_and_leaves(&mut group);
129 out.push(group);
130 }
131 }
132
133 fn collect_and_leaves<'a>(&'a self, out: &mut Vec<&'a Node>) {
134 if let Self::And(left, right) = self {
135 left.collect_and_leaves(out);
136 right.collect_and_leaves(out);
137 } else {
138 out.push(self);
139 }
140 }
141
142 fn intersection_of(leaves: Vec<&Node>) -> Result<Node> {
146 if let [alone] = leaves[..] {
147 return Ok(alone.clone());
148 }
149 let mut met: Option<(i128, i128)> = None;
150 for leaf in leaves {
151 let Self::Residual { modulus, shift } = leaf else {
152 return Err(Error::Sieve(
153 "complemented residual classes cannot be intersected".to_string(),
154 ));
155 };
156 let next = (i128::from(*modulus), i128::from(*shift));
157 met = Some(match met {
158 None => next,
159 Some(so_far) => intersect_classes(so_far, next)?,
160 });
161 }
162 let (modulus, shift) = met.expect("a run of `&` holds at least one class");
163 let modulus = UnsignedIntegerType::try_from(modulus)
164 .map_err(|_| Error::Sieve(format!("the modulus {modulus} is too large")))?;
165 Ok(Self::Residual {
166 modulus,
167 shift: shift as UnsignedIntegerType,
168 })
169 }
170
171 fn collect_moduli(&self, out: &mut Vec<UnsignedIntegerType>) {
172 match self {
173 Self::Residual { modulus, .. } => out.push(*modulus),
174 Self::Group(inner) | Self::Not(inner) => inner.collect_moduli(out),
175 Self::And(left, right) | Self::Or(left, right) | Self::Xor(left, right) => {
176 left.collect_moduli(out);
177 right.collect_moduli(out);
178 }
179 }
180 }
181}
182
183impl Sieve {
184 pub fn parse(expression: &str) -> Result<Self> {
191 let tokens = tokenize(expression)?;
192 let mut parser = Parser {
193 tokens: &tokens,
194 position: 0,
195 };
196 let root = parser.parse_or()?;
197 if parser.position != tokens.len() {
198 return Err(Error::Sieve(format!(
199 "trailing input in sieve {expression:?} at token {}",
200 parser.position
201 )));
202 }
203 Ok(Self { root })
204 }
205
206 pub fn contains(&self, z: IntegerType) -> bool {
208 self.root.contains(z)
209 }
210
211 pub fn period(&self) -> UnsignedIntegerType {
215 let mut moduli = Vec::new();
216 self.root.collect_moduli(&mut moduli);
217 moduli.into_iter().fold(1, lcm)
218 }
219
220 pub fn segment(&self, low: IntegerType, high: IntegerType) -> Vec<IntegerType> {
222 (low..=high).filter(|z| self.contains(*z)).collect()
223 }
224
225 pub fn interval_widths(&self) -> Result<Vec<IntegerType>> {
232 let period = self.period();
233 let members = self.segment(0, period as IntegerType);
234 if members.len() < 2 {
235 return Err(Error::Sieve(format!(
236 "sieve has {} member(s) in its period of {period}, so it defines no intervals",
237 members.len()
238 )));
239 }
240 Ok(members.windows(2).map(|pair| pair[1] - pair[0]).collect())
241 }
242
243 pub fn shifted(&self, n: IntegerType) -> Self {
247 Self {
248 root: self.root.shifted(n),
249 }
250 }
251
252 pub fn segment_binary(&self, low: IntegerType, high: IntegerType) -> Vec<IntegerType> {
257 (low..=high)
258 .map(|z| IntegerType::from(self.contains(z)))
259 .collect()
260 }
261
262 pub fn segment_widths(&self, low: IntegerType, high: IntegerType) -> Vec<IntegerType> {
269 let members = self.segment(low, high);
270 members.windows(2).map(|pair| pair[1] - pair[0]).collect()
271 }
272
273 pub fn segment_unit(&self, low: IntegerType, high: IntegerType) -> Vec<FloatType> {
279 let members = self.segment(low, high);
280 if members.len() < 2 {
281 return vec![0.0; members.len().min(1)];
282 }
283 let span = FloatType::from(high - low);
284 if span == 0.0 {
285 return vec![0.0; members.len()];
286 }
287 members
288 .into_iter()
289 .map(|member| FloatType::from(member - low) / span)
290 .collect()
291 }
292
293 pub fn collect(
301 &self,
302 n: IntegerType,
303 z_minimum: IntegerType,
304 length: usize,
305 ) -> Result<Vec<IntegerType>> {
306 const STEP: IntegerType = 100;
307 const ROUNDS: usize = 10_000;
308
309 let shifted = self.shifted(n);
310 let mut found = Vec::with_capacity(length);
311 let mut low = z_minimum;
312 for _ in 0..ROUNDS {
313 found.extend(shifted.segment(low, low + STEP - 1));
314 if found.len() >= length {
315 found.truncate(length);
316 return Ok(found);
317 }
318 low += STEP;
319 }
320 Err(Error::Sieve(format!(
321 "desired length of {length} cannot be found in sieve {self}"
322 )))
323 }
324
325 pub fn intersection(&self, other: &Self) -> Self {
331 self.combined(other, Node::And)
332 }
333
334 pub fn union(&self, other: &Self) -> Self {
337 self.combined(other, Node::Or)
338 }
339
340 pub fn symmetric_difference(&self, other: &Self) -> Self {
343 self.combined(other, Node::Xor)
344 }
345
346 pub fn compressed(&self, low: IntegerType, high: IntegerType) -> Result<Self> {
373 let classes = if self.root.is_bound() {
374 self.classes_of_segment(low, high)?
375 } else {
376 let mut groups = Vec::new();
377 self.root.collect_or_groups(&mut groups);
378 groups
379 .into_iter()
380 .map(Node::intersection_of)
381 .collect::<Result<Vec<_>>>()?
382 };
383 let root = classes
384 .into_iter()
385 .reduce(|left, right| Node::Or(Box::new(left), Box::new(right)))
386 .ok_or_else(|| Error::Sieve(format!("sieve {self} has no residual classes")))?;
387 Ok(Self { root })
388 }
389
390 fn classes_of_segment(&self, low: IntegerType, high: IntegerType) -> Result<Vec<Node>> {
392 let members = self.segment(low, high);
393 if members.len() < 2 {
394 return Err(Error::Sieve(format!(
395 "sieve {self} has {} member(s) in {low}..={high}; compressing a segment takes more than one",
396 members.len()
397 )));
398 }
399 classes_of_members(&members, low, high)
400 }
401}
402
403fn classes_of_members(
407 members: &[IntegerType],
408 low: IntegerType,
409 high: IntegerType,
410) -> Result<Vec<Node>> {
411 let is_member = |z: IntegerType| members.binary_search(&z).is_ok();
412 let span = high - low + 1;
414
415 let mut remaining = members.to_vec();
416 let mut classes: Vec<(IntegerType, IntegerType)> = Vec::new();
417 while let Some(&n) = remaining.first() {
418 let modulus = (1..span)
419 .find(|m| {
420 let first = low + (n - low).rem_euclid(*m);
421 (first..=high).step_by(*m as usize).all(is_member)
422 })
423 .ok_or_else(|| Error::Sieve(format!("a mod was not found less than {span} for {n}")))?;
424 remaining.retain(|z| (z - n).rem_euclid(modulus) != 0);
425 classes.push((modulus, n.rem_euclid(modulus)));
426 }
427 classes.sort_unstable();
428 Ok(classes
429 .into_iter()
430 .map(|(modulus, shift)| Node::Residual {
431 modulus: modulus as UnsignedIntegerType,
432 shift: shift as UnsignedIntegerType,
433 })
434 .collect())
435}
436
437impl Sieve {
438 pub fn from_segment(
457 members: &[IntegerType],
458 range: Option<(IntegerType, IntegerType)>,
459 ) -> Result<Self> {
460 let mut members = members.to_vec();
461 members.sort_unstable();
462 members.dedup();
463 let (&least, &greatest) = match (members.first(), members.last()) {
464 (Some(least), Some(greatest)) if members.len() > 1 => (least, greatest),
465 _ => {
466 return Err(Error::Sieve(
467 "segment must have more than one element".to_string(),
468 ));
469 }
470 };
471 let (low, high) = range.unwrap_or((least, greatest));
472 if least < low || greatest > high {
473 return Err(Error::Sieve(
474 "z range must be a superset of desired segment".to_string(),
475 ));
476 }
477 let root = classes_of_members(&members, low, high)?
478 .into_iter()
479 .reduce(|left, right| Node::Or(Box::new(left), Box::new(right)))
480 .ok_or_else(|| Error::Sieve("segment has no residual classes".to_string()))?;
481 Ok(Self { root })
482 }
483
484 fn combined(&self, other: &Self, join: fn(Box<Node>, Box<Node>) -> Node) -> Self {
485 Self {
486 root: join(
487 Box::new(Node::Group(Box::new(self.root.clone()))),
488 Box::new(Node::Group(Box::new(other.root.clone()))),
489 ),
490 }
491 }
492}
493
494impl fmt::Display for Node {
495 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
496 match self {
497 Self::Residual { modulus, shift } => write!(f, "{modulus}@{shift}"),
498 Self::Group(inner) => write!(f, "{{{inner}}}"),
499 Self::Not(inner) => write!(f, "-{inner}"),
500 Self::And(left, right) => write!(f, "{left}&{right}"),
501 Self::Or(left, right) => write!(f, "{left}|{right}"),
502 Self::Xor(left, right) => write!(f, "{left}^{right}"),
503 }
504 }
505}
506
507impl fmt::Display for Sieve {
519 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
520 write!(f, "{}", self.root)
521 }
522}
523
524fn lcm(a: UnsignedIntegerType, b: UnsignedIntegerType) -> UnsignedIntegerType {
526 if a == 0 || b == 0 {
527 return 0;
528 }
529 num::integer::lcm(a, b)
530}
531
532fn intersect_classes((m1, n1): (i128, i128), (m2, n2): (i128, i128)) -> Result<(i128, i128)> {
539 if m1 == 0 || m2 == 0 {
540 return Ok((0, 0));
541 }
542 let d = num::integer::gcd(m1, m2);
543 if (n1 - n2) % d != 0 {
544 return Err(Error::Sieve(format!(
545 "the classes {m1}@{n1} and {m2}@{n2} never meet"
546 )));
547 }
548 let (c1, c2) = (m1 / d, m2 / d);
549 let modulus = c1 * c2 * d;
550 let g = num::Integer::extended_gcd(&c1, &c2).x.rem_euclid(c2);
553 Ok((modulus, (n1 + g * (n2 - n1) * c1).rem_euclid(modulus)))
554}
555
556#[derive(Clone, Copy, Debug, Eq, PartialEq)]
557enum Token {
558 Number(UnsignedIntegerType),
559 At,
560 Not,
561 And,
562 Or,
563 Xor,
564 Open,
565 Close,
566}
567
568fn tokenize(expression: &str) -> Result<Vec<Token>> {
569 let mut tokens = Vec::new();
570 let mut chars = expression.chars().peekable();
571
572 while let Some(&character) = chars.peek() {
573 match character {
574 ' ' | '\t' | '\n' | '\r' => {
575 chars.next();
576 }
577 '0'..='9' => {
578 let mut value: UnsignedIntegerType = 0;
579 while let Some(&digit) = chars.peek() {
580 let Some(digit) = digit.to_digit(10) else {
581 break;
582 };
583 value = value
584 .checked_mul(10)
585 .and_then(|value| value.checked_add(digit))
586 .ok_or_else(|| {
587 Error::Sieve(format!("number overflows in sieve {expression:?}"))
588 })?;
589 chars.next();
590 }
591 tokens.push(Token::Number(value));
592 }
593 '@' | ',' => {
595 chars.next();
596 tokens.push(Token::At);
597 }
598 'a'..='z' => {
602 let mut word = String::new();
603 while let Some(&letter) = chars.peek() {
604 if !letter.is_ascii_lowercase() {
605 break;
606 }
607 word.push(letter);
608 chars.next();
609 }
610 tokens.push(match word.as_str() {
611 "sub" => Token::At,
612 "and" => Token::And,
613 "or" => Token::Or,
614 "not" => Token::Not,
615 _ => {
616 return Err(Error::Sieve(format!(
617 "unexpected word {word:?} in sieve {expression:?}"
618 )));
619 }
620 });
621 }
622 '*' => {
623 chars.next();
624 tokens.push(Token::And);
625 }
626 '+' => {
627 chars.next();
628 tokens.push(Token::Or);
629 }
630 '-' => {
631 chars.next();
632 tokens.push(Token::Not);
633 }
634 '&' => {
635 chars.next();
636 tokens.push(Token::And);
637 }
638 '|' => {
639 chars.next();
640 tokens.push(Token::Or);
641 }
642 '^' => {
643 chars.next();
644 tokens.push(Token::Xor);
645 }
646 '{' | '(' | '[' => {
647 chars.next();
648 tokens.push(Token::Open);
649 }
650 '}' | ')' | ']' => {
651 chars.next();
652 tokens.push(Token::Close);
653 }
654 other => {
655 return Err(Error::Sieve(format!(
656 "unexpected character {other:?} in sieve {expression:?}"
657 )));
658 }
659 }
660 }
661
662 if tokens.is_empty() {
663 return Err(Error::Sieve("sieve expression is empty".to_string()));
664 }
665 Ok(tokens)
666}
667
668struct Parser<'a> {
669 tokens: &'a [Token],
670 position: usize,
671}
672
673impl Parser<'_> {
674 fn peek(&self) -> Option<Token> {
675 self.tokens.get(self.position).copied()
676 }
677
678 fn eat(&mut self, token: Token) -> bool {
679 if self.peek() == Some(token) {
680 self.position += 1;
681 return true;
682 }
683 false
684 }
685
686 fn parse_or(&mut self) -> Result<Node> {
687 let mut left = self.parse_xor()?;
688 while self.eat(Token::Or) {
689 let right = self.parse_xor()?;
690 left = Node::Or(Box::new(left), Box::new(right));
691 }
692 Ok(left)
693 }
694
695 fn parse_xor(&mut self) -> Result<Node> {
696 let mut left = self.parse_and()?;
697 while self.eat(Token::Xor) {
698 let right = self.parse_and()?;
699 left = Node::Xor(Box::new(left), Box::new(right));
700 }
701 Ok(left)
702 }
703
704 fn parse_and(&mut self) -> Result<Node> {
705 let mut left = self.parse_unary()?;
706 while self.eat(Token::And) {
707 let right = self.parse_unary()?;
708 left = Node::And(Box::new(left), Box::new(right));
709 }
710 Ok(left)
711 }
712
713 fn parse_unary(&mut self) -> Result<Node> {
714 if self.eat(Token::Not) {
715 return Ok(Node::Not(Box::new(self.parse_unary()?)));
716 }
717 self.parse_primary()
718 }
719
720 fn parse_primary(&mut self) -> Result<Node> {
721 if self.eat(Token::Open) {
722 let inner = self.parse_or()?;
723 if !self.eat(Token::Close) {
724 return Err(Error::Sieve("unclosed group in sieve".to_string()));
725 }
726 return Ok(Node::Group(Box::new(inner)));
727 }
728
729 let Some(Token::Number(modulus)) = self.peek() else {
730 return Err(Error::Sieve(format!(
731 "expected a modulus in sieve at token {}",
732 self.position
733 )));
734 };
735 self.position += 1;
736
737 let shift = if self.eat(Token::At) {
739 let Some(Token::Number(shift)) = self.peek() else {
740 return Err(Error::Sieve(
741 "expected a shift after `@` in sieve".to_string(),
742 ));
743 };
744 self.position += 1;
745 shift
746 } else {
747 0
748 };
749
750 Ok(Node::Residual {
752 modulus,
753 shift: if modulus == 0 { shift } else { shift % modulus },
754 })
755 }
756}
757
758pub fn eratosthenes(first_candidate: u64) -> impl Iterator<Item = u64> {
764 let mut composites: std::collections::HashMap<u64, u64> = std::collections::HashMap::new();
765 let mut candidate: u64 = 2;
766 std::iter::from_fn(move || {
767 loop {
768 let found = candidate;
769 candidate += 1;
770 match composites.remove(&found) {
771 Some(prime) => {
772 let mut next = found + prime;
773 while composites.contains_key(&next) {
774 next += prime;
775 }
776 composites.insert(next, prime);
777 }
778 None => {
779 composites.insert(found * found, found);
780 if found >= first_candidate {
781 return Some(found);
782 }
783 }
784 }
785 }
786 })
787}
788
789const MILLER_RABIN_WITNESSES: [u64; 12] = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37];
792
793fn power_mod(mut base: u64, mut exponent: u64, modulus: u64) -> u64 {
794 let mut result: u64 = 1;
795 base %= modulus;
796 while exponent > 0 {
797 if exponent & 1 == 1 {
798 result = ((u128::from(result) * u128::from(base)) % u128::from(modulus)) as u64;
799 }
800 base = ((u128::from(base) * u128::from(base)) % u128::from(modulus)) as u64;
801 exponent >>= 1;
802 }
803 result
804}
805
806pub fn rabin_miller(n: i64) -> bool {
812 let n = n.unsigned_abs();
813 if n < 2 {
814 return false;
815 }
816 if n < 4 {
817 return true;
818 }
819 if !matches!(n % 6, 1 | 5) {
820 return false;
821 }
822 for witness in MILLER_RABIN_WITNESSES {
823 if n == witness {
824 return true;
825 }
826 if n.is_multiple_of(witness) {
827 return false;
828 }
829 }
830 let (mut odd, mut rounds) = (n - 1, 0);
831 while odd.is_multiple_of(2) {
832 odd /= 2;
833 rounds += 1;
834 }
835 'witnesses: for witness in MILLER_RABIN_WITNESSES {
836 let mut x = power_mod(witness, odd, n);
837 if x == 1 || x == n - 1 {
838 continue;
839 }
840 for _ in 1..rounds {
841 x = ((u128::from(x) * u128::from(x)) % u128::from(n)) as u64;
842 if x == n - 1 {
843 continue 'witnesses;
844 }
845 }
846 return false;
847 }
848 true
849}
850
851pub fn discrete_binary_pad(
859 series: &[IntegerType],
860 fix_range: Option<&[IntegerType]>,
861) -> Result<Vec<u8>> {
862 let bounds = fix_range.unwrap_or(series);
863 let (Some(lowest), Some(highest)) = (bounds.iter().min(), bounds.iter().max()) else {
864 return Err(Error::Sieve(
865 "a binary pad needs a range: give a series or a fixRange".to_string(),
866 ));
867 };
868 Ok((*lowest..=*highest)
869 .map(|value| u8::from(series.contains(&value)))
870 .collect())
871}
872
873pub fn unit_norm_range(series: &[FloatType], fix_range: Option<&[FloatType]>) -> Vec<FloatType> {
881 let bounds = fix_range.unwrap_or(series);
882 let lowest = bounds
883 .iter()
884 .copied()
885 .fold(FloatType::INFINITY, FloatType::min);
886 let highest = bounds
887 .iter()
888 .copied()
889 .fold(FloatType::NEG_INFINITY, FloatType::max);
890 let span = highest - lowest;
891 if series.len() <= 1 {
892 return vec![0.0];
893 }
894 series
895 .iter()
896 .map(|value| {
897 if span == 0.0 {
898 0.0
899 } else {
900 (value - lowest) / span
901 }
902 })
903 .collect()
904}
905
906pub fn unit_norm_equal(parts: usize) -> Vec<FloatType> {
910 match parts {
911 0 | 1 => vec![0.0],
912 2 => vec![0.0, 1.0],
913 _ => {
914 let step = 1.0 / (parts - 1) as FloatType;
915 let mut unit: Vec<FloatType> = (0..parts - 1).map(|y| y as FloatType * step).collect();
916 unit.push(1.0);
917 unit
918 }
919 }
920}
921
922pub fn unit_norm_step(
927 step: FloatType,
928 a: FloatType,
929 b: FloatType,
930 normalized: bool,
931) -> Result<Vec<FloatType>> {
932 if a == b {
933 return Ok(Vec::new());
934 }
935 if step.is_nan() || step <= 0.0 {
936 return Err(Error::Sieve(format!(
937 "a step of {step} never crosses the range"
938 )));
939 }
940 let (lowest, highest) = if a < b { (a, b) } else { (b, a) };
941 let mut values = Vec::new();
942 let mut x = lowest;
943 while x <= highest {
944 values.push(x);
945 x += step;
946 }
947 Ok(if normalized {
948 unit_norm_equal(values.len())
949 } else {
950 values
951 })
952}
953
954#[cfg(test)]
955mod tests {
956 use super::*;
957
958 #[test]
960 fn the_number_helpers_answer_what_music21_answers() {
961 assert_eq!(
962 eratosthenes(2).take(5).collect::<Vec<_>>(),
963 [2, 3, 5, 7, 11]
964 );
965 assert_eq!(eratosthenes(95).take(2).collect::<Vec<_>>(), [97, 101]);
966
967 assert!(!rabin_miller(234));
968 assert!(rabin_miller(5));
969 assert!(!rabin_miller(4));
970 assert!(!rabin_miller(97 * 2));
971 assert!(rabin_miller(6_i64.pow(4) + 1));
972 assert!(!rabin_miller(123_986_234_193));
973 assert!(rabin_miller(-7));
974 assert!(!rabin_miller(1));
975 assert!(rabin_miller(1_000_000_007));
976 assert!(!rabin_miller(1_000_000_007 * 3));
977
978 assert_eq!(
979 discrete_binary_pad(&[3, 10, 12], None).unwrap(),
980 [1, 0, 0, 0, 0, 0, 0, 1, 0, 1]
981 );
982 assert_eq!(discrete_binary_pad(&[3, 4, 5], None).unwrap(), [1, 1, 1]);
983 assert_eq!(
984 discrete_binary_pad(&[4], Some(&[2, 5])).unwrap(),
985 [0, 0, 1, 0]
986 );
987 assert!(discrete_binary_pad(&[], None).is_err());
988
989 assert_eq!(unit_norm_range(&[0.0, 3.0, 4.0], None), [0.0, 0.75, 1.0]);
990 let thirds = unit_norm_range(&[1.0, 3.0, 4.0], None);
991 assert!((thirds[1] - 2.0 / 3.0).abs() < 1e-12);
992 assert_eq!(unit_norm_range(&[5.0], None), [0.0]);
993 assert_eq!(unit_norm_range(&[2.0, 2.0], None), [0.0, 0.0]);
994 assert_eq!(unit_norm_range(&[1.0, 2.0], Some(&[0.0, 4.0])), [0.25, 0.5]);
995
996 assert_eq!(unit_norm_equal(3), [0.0, 0.5, 1.0]);
997 assert_eq!(unit_norm_equal(1), [0.0]);
998 assert_eq!(unit_norm_equal(2), [0.0, 1.0]);
999
1000 assert_eq!(
1001 unit_norm_step(0.5, 0.0, 1.0, true).unwrap(),
1002 [0.0, 0.5, 1.0]
1003 );
1004 assert_eq!(
1005 unit_norm_step(0.5, -1.0, 1.0, true).unwrap(),
1006 [0.0, 0.25, 0.5, 0.75, 1.0]
1007 );
1008 assert_eq!(
1009 unit_norm_step(0.5, -1.0, 1.0, false).unwrap(),
1010 [-1.0, -0.5, 0.0, 0.5, 1.0]
1011 );
1012 assert_eq!(unit_norm_step(0.25, 0.0, 20.0, true).unwrap().len(), 81);
1013 assert_eq!(unit_norm_step(0.25, 0.0, 20.0, false).unwrap().len(), 81);
1014 assert!(unit_norm_step(0.5, 1.0, 1.0, true).unwrap().is_empty());
1015 assert!(unit_norm_step(0.0, 0.0, 1.0, true).is_err());
1016 }
1017
1018 #[test]
1022 fn compression_answers_what_music21_answers() {
1023 let compressed = |expression: &str, low, high| {
1024 Sieve::parse(expression)
1025 .unwrap()
1026 .compressed(low, high)
1027 .map(|sieve| sieve.to_string())
1028 };
1029 assert_eq!(compressed("3@11", 0, 99).unwrap(), "3@2");
1031 assert_eq!(compressed("2&4&8|5", 0, 99).unwrap(), "8@0|5@0");
1032 assert_eq!(compressed("3@2&5@1", 0, 99).unwrap(), "15@11");
1033 assert_eq!(compressed("4@1&6@3", 0, 99).unwrap(), "12@9");
1034 assert_eq!(compressed("5@2&4@1&3@0|7", 0, 99).unwrap(), "60@57|7@0");
1035 assert_eq!(compressed("3@1|3@1&2", 0, 99).unwrap(), "3@1|6@4");
1036 assert_eq!(compressed("-3@0|5", 0, 99).unwrap(), "-3@0|5@0");
1037 assert_eq!(compressed("(5|2)&4&8", 0, 99).unwrap(), "8@0");
1039 assert_eq!(compressed("(5|2)&4&8", 20, 49).unwrap(), "8@0");
1040 assert_eq!(compressed("3@0^4@0", 0, 99).unwrap(), "6@3|12@4|12@6|12@8");
1041 assert_eq!(compressed("-(3|4)", 0, 99).unwrap(), "6@1|6@5|12@2|12@10");
1042 assert_eq!(compressed("-{3}", 0, 99).unwrap(), "3@1|3@2");
1043 assert_eq!(compressed("7@3|{-3@0&5}", 0, 99).unwrap(), "7@3|15@5|15@10");
1044 assert_eq!(compressed("{-4@1}&{3@0}", 0, 99).unwrap(), "6@0|12@3");
1045 assert_eq!(compressed("{7}", 0, 7).unwrap(), "7@0");
1046
1047 let sieve = Sieve::parse("(5|2)&4&8").unwrap();
1048 let compressed_sieve = sieve.compressed(0, 99).unwrap();
1049 assert_eq!(compressed_sieve.period(), 8);
1050 assert_eq!(compressed_sieve.segment_widths(0, 99), [8; 12]);
1051 assert_eq!(compressed_sieve.segment(0, 99), sieve.segment(0, 99));
1052
1053 assert!(compressed("-3@0&5", 0, 99).is_err());
1055 assert!(compressed("4@1&6@2", 0, 99).is_err());
1056 assert!(compressed("{7}", 5, 8).is_err());
1057 }
1058
1059 #[test]
1060 fn a_sieve_is_written_the_way_music21_writes_one() {
1061 for (written, expected) in [
1062 ("3@11", "3@2"),
1063 ("2&4&8|5", "2@0&4@0&8@0|5@0"),
1064 ("(5|2)&4&8", "{5@0|2@0}&4@0&8@0"),
1065 ("3@2|7@1", "3@2|7@1"),
1066 ] {
1067 let sieve = Sieve::parse(written).expect("the expression parses");
1068 assert_eq!(sieve.to_string(), expected, "writing {written}");
1069 }
1070 }
1071
1072 #[test]
1074 fn combining_two_sieves_braces_each_side() {
1075 let a = Sieve::parse("3@11").expect("a parses");
1076 let b = Sieve::parse("2&4&8|5").expect("b parses");
1077 assert_eq!(b.intersection(&a).to_string(), "{2@0&4@0&8@0|5@0}&{3@2}");
1078 assert_eq!(b.union(&a).to_string(), "{2@0&4@0&8@0|5@0}|{3@2}");
1079 assert_eq!(
1080 b.symmetric_difference(&a).to_string(),
1081 "{2@0&4@0&8@0|5@0}^{3@2}"
1082 );
1083 }
1084
1085 #[test]
1086 fn collecting_reads_the_sieve_shifted_on_from_a_starting_point() {
1087 let sieve = Sieve::parse("3@11").expect("the expression parses");
1088 assert_eq!(
1089 sieve.collect(10, 100, 10).expect("ten members are found"),
1090 [102, 105, 108, 111, 114, 117, 120, 123, 126, 129]
1091 );
1092 }
1093
1094 #[test]
1095 fn the_segment_formats_answer_what_music21_answers() {
1096 let sieve = Sieve::parse("3@2|7@1").expect("the expression parses");
1097 assert_eq!(
1098 sieve.segment_binary(0, 99)[..12],
1099 [0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1]
1100 );
1101 assert_eq!(
1102 sieve.segment_widths(0, 99)[..12],
1103 [1, 3, 3, 3, 3, 1, 2, 3, 2, 1, 3, 3]
1104 );
1105 let unit = sieve.segment_unit(0, 99);
1106 assert_eq!(unit.len(), 43);
1107 assert_eq!(unit[0], 1.0 / 99.0);
1108 assert_eq!(*unit.last().expect("the segment is not empty"), 1.0);
1109 }
1110
1111 fn widths(expression: &str) -> Vec<IntegerType> {
1112 Sieve::parse(expression)
1113 .expect("sieve parses")
1114 .interval_widths()
1115 .expect("sieve has intervals")
1116 }
1117
1118 #[test]
1119 fn a_single_residual_class_cycles_at_its_modulus() {
1120 assert_eq!(widths("3@0"), [3]);
1121 assert_eq!(widths("4@0"), [4]);
1122 assert_eq!(widths("2@0"), [2]);
1123 assert_eq!(widths("12@0"), [12]);
1124 assert_eq!(widths("5"), [5]);
1126 }
1127
1128 #[test]
1129 fn the_major_scale_is_a_sieve() {
1130 assert_eq!(
1131 widths("(-3@2 & 4) | (-3@1 & 4@1) | (3@2 & 4@2) | (-3 & 4@3)"),
1132 [2, 2, 1, 2, 2, 2, 1]
1133 );
1134 }
1135
1136 #[test]
1137 fn union_intersection_and_symmetric_difference_match_music21() {
1138 assert_eq!(widths("3@0|7@0"), [3, 3, 1, 2, 3, 2, 1, 3, 3]);
1139 assert_eq!(widths("{3@0|4@0}"), [3, 1, 2, 2, 1, 3]);
1140 assert_eq!(widths("3@0&4@0"), [12]);
1141 assert_eq!(widths("3@0^4@0"), [1, 2, 2, 1]);
1142 assert_eq!(widths("5@2|7@3"), [1, 4, 3, 2, 5, 5, 2, 3, 4, 1]);
1143 }
1144
1145 #[test]
1146 fn negation_applies_to_residuals_and_to_groups() {
1147 assert_eq!(widths("-3@0"), [1]);
1148 assert_eq!(widths("-5@2"), [1, 2, 1, 1]);
1149 assert_eq!(widths("-{3@0|4@0}"), [1, 3, 2, 3, 1]);
1150 }
1151
1152 #[test]
1153 fn and_binds_tighter_than_or() {
1154 assert_eq!(widths("3@0|4@0&6@0"), widths("3@0|{4@0&6@0}"));
1156 assert_eq!(widths("3@0|4@0&6@0"), [3, 3, 3, 3]);
1157 assert_ne!(widths("3@0|4@0&6@0"), widths("{3@0|4@0}&6@0"));
1158 assert_eq!(widths("{3@0|4@0}&6@0"), [6, 6]);
1159 }
1160
1161 #[test]
1162 fn parentheses_and_braces_group_alike() {
1163 assert_eq!(widths("(3@0|4@0)"), widths("{3@0|4@0}"));
1164 }
1165
1166 #[test]
1167 fn period_is_the_lcm_of_the_moduli() {
1168 assert_eq!(Sieve::parse("3@0").unwrap().period(), 3);
1169 assert_eq!(Sieve::parse("3@0|7@0").unwrap().period(), 21);
1170 assert_eq!(Sieve::parse("5@2|7@3").unwrap().period(), 35);
1171 assert_eq!(Sieve::parse("3@0|4@0").unwrap().period(), 12);
1172 }
1173
1174 #[test]
1175 fn a_sieve_too_sparse_for_intervals_errors() {
1176 assert!(Sieve::parse("3@1").unwrap().interval_widths().is_err());
1179 }
1180
1181 #[test]
1182 fn malformed_expressions_error_instead_of_panicking() {
1183 for bad in [
1184 "", " ", "@", "3@", "|3@0", "3@0|", "(3@0", "3@0)", "3@0 & ", "x", "3@@0",
1185 ] {
1186 let parsed = Sieve::parse(bad);
1187 assert!(parsed.is_err(), "{bad:?} should be rejected");
1188 }
1189 }
1190
1191 #[test]
1194 fn a_modulus_of_nought_is_the_empty_class() {
1195 let all: Vec<IntegerType> = (0..6).collect();
1196 assert!(Sieve::parse("0@0").unwrap().segment(0, 5).is_empty());
1197 assert_eq!(Sieve::parse("-0@0").unwrap().segment(0, 5), all);
1198 assert_eq!(Sieve::parse("-0@0|3@1").unwrap().segment(0, 5), all);
1199 assert_eq!(Sieve::parse("0@0|3@1").unwrap().segment(0, 5), [1, 4]);
1200 assert_eq!(Sieve::parse("0@0|3@1").unwrap().period(), 0);
1201 }
1202
1203 #[test]
1206 fn every_spelling_music21_reads_is_read() {
1207 let bracketed = Sieve::parse("[(8@0 | 8@1 | 8@7) & (5@1 | 5@3)] | [8@3 & 5@0]").unwrap();
1208 assert_eq!(bracketed.segment(0, 39), [1, 8, 16, 23, 31, 33, 35]);
1209 assert_eq!(bracketed.to_string(), "{{8@0|8@1|8@7}&{5@1|5@3}}|{8@3&5@0}");
1210
1211 let sub = Sieve::parse("-5 | 4 & 4sub3 & 6").unwrap();
1212 assert_eq!(
1213 sub.segment(0, 29),
1214 [
1215 1, 2, 3, 4, 6, 7, 8, 9, 11, 12, 13, 14, 16, 17, 18, 19, 21, 22, 23, 24, 26, 27, 28,
1216 29
1217 ]
1218 );
1219 assert_eq!(sub.to_string(), "-5@0|4@0&4@3&6@0");
1220
1221 let comma = Sieve::parse("3,1 | 4").unwrap();
1222 assert_eq!(comma.segment(0, 19), [0, 1, 4, 7, 8, 10, 12, 13, 16, 19]);
1223 assert_eq!(comma.to_string(), "3@1|4@0");
1224
1225 assert!(Sieve::parse("4su3").is_err());
1226
1227 let words = Sieve::parse("2 or 4 and 4 & 6 or 4 & 4").unwrap();
1228 assert_eq!(words.to_string(), "2@0|4@0&4@0&6@0|4@0&4@0");
1229 assert_eq!(words.segment(0, 9), [0, 2, 4, 6, 8]);
1230 let xenakis = Sieve::parse("3 * 1 + not 4").unwrap();
1231 assert_eq!(xenakis.to_string(), "3@0&1@0|-4@0");
1232 assert_eq!(xenakis.segment(0, 9), [0, 1, 2, 3, 5, 6, 7, 9]);
1233 assert!(Sieve::parse("3 xor 4").is_err());
1234 }
1235
1236 #[test]
1238 fn a_segment_is_written_as_music21_writes_it() {
1239 let written = |members: &[IntegerType], range| {
1240 Sieve::from_segment(members, range).map(|sieve| sieve.to_string())
1241 };
1242 assert_eq!(written(&[2, 4, 6, 8], None).unwrap(), "2@0");
1243 assert_eq!(written(&[1, 6, 11, 16, 17], None).unwrap(), "5@1|11@6");
1244 assert_eq!(written(&[0, 3, 4, 9], None).unwrap(), "5@4|6@3|9@0");
1245 assert_eq!(
1246 written(&[2, 4, 6, 8], Some((0, 19))).unwrap(),
1247 "12@8|14@6|16@4|18@2"
1248 );
1249 assert_eq!(
1250 Sieve::from_segment(&[1, 6, 11, 16, 17], None)
1251 .unwrap()
1252 .segment(0, 29),
1253 [1, 6, 11, 16, 17, 21, 26, 28]
1254 );
1255 assert!(written(&[5], None).is_err());
1256 assert!(written(&[2, 40], Some((0, 19))).is_err());
1257 }
1258
1259 #[test]
1260 fn membership_wraps_for_negative_integers() {
1261 let sieve = Sieve::parse("3@1").unwrap();
1262 assert!(sieve.contains(1));
1263 assert!(sieve.contains(4));
1264 assert!(sieve.contains(-2));
1265 assert!(!sieve.contains(0));
1266 }
1267}