Skip to main content

music21_rs/
sieve.rs

1//! Xenakis sieves, ported from music21's `sieve` module.
2//!
3//! A sieve is a logical expression over *residual classes*. `3@0` selects every
4//! integer congruent to 0 modulo 3; `|`, `&` and `^` combine classes as union,
5//! intersection and symmetric difference; `-` complements one; and `{}` or `()`
6//! group. Applied to semitones, the resulting integer set is a scale — the
7//! major scale is `(-3@2 & 4) | (-3@1 & 4@1) | (3@2 & 4@2) | (-3 & 4@3)`.
8//!
9//! What is here is the sieve itself — parsing an expression, testing
10//! membership, the segment formats and the interval widths of one period —
11//! and the number helpers music21 keeps beside it: primes by
12//! [`eratosthenes`] and [`rabin_miller`], and the unit-interval spacings
13//! [`unit_norm_range`], [`unit_norm_equal`] and [`unit_norm_step`].
14//! [`Sieve::compressed`] is the compressed reading; pitch-range realization
15//! stays music21's.
16
17use std::fmt;
18
19use crate::defaults::{FloatType, IntegerType, UnsignedIntegerType};
20use crate::error::{Error, Result};
21
22/// A parsed Xenakis sieve.
23///
24/// ```
25/// use music21_rs::Sieve;
26///
27/// // Every third semitone: a cycle of minor thirds.
28/// let sieve = Sieve::parse("3@0")?;
29/// assert_eq!(sieve.period(), 3);
30/// assert_eq!(sieve.interval_widths()?, [3]);
31///
32/// // The major scale, as Xenakis would write it.
33/// let major = Sieve::parse("(-3@2 & 4) | (-3@1 & 4@1) | (3@2 & 4@2) | (-3 & 4@3)")?;
34/// assert_eq!(major.interval_widths()?, [2, 2, 1, 2, 2, 2, 1]);
35/// # Ok::<(), music21_rs::Error>(())
36/// ```
37#[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    /// Integers congruent to `shift` modulo `modulus`; none at all where
46    /// the modulus is nought.
47    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    /// A group the expression was written with, `{}` or `()`.
56    ///
57    /// Kept because music21 writes a sieve back out as it was given, with
58    /// only the residuals normalized, so `(5|2)&4&8` reads back as
59    /// `{5@0|2@0}&4@0&8@0` and a combined sieve wraps each side in braces
60    /// whether or not the precedence needs them.
61    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    /// The same tree with every residual's shift moved on by `n`.
80    ///
81    /// music21 passes an `n` down to each residual when it reads a segment,
82    /// which is the same thing: `3@2` read at `n = 10` selects the multiples
83    /// of three.
84    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    /// Whether the expression is more than runs of `&` joined by `|`: a
107    /// group, a `^`, or a complement of anything but a single class.
108    ///
109    /// music21 compresses such a sieve from its members over a range rather
110    /// than by intersecting its classes.
111    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    /// The runs of `&` an unbound expression joins with `|`, in the order
121    /// written, each as the classes it intersects.
122    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    /// The one class a run of `&` meets in. A class standing alone is kept
143    /// as written, complemented or not; a complemented one cannot be
144    /// intersected, as it cannot upstream.
145    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    /// Parses a sieve expression such as `"3@0|4@1"`.
185    ///
186    /// A bare modulus means a shift of zero, so `"5"` is `"5@0"`. Whitespace is
187    /// ignored, `{}` and `()` both group, and `&` binds tighter than `^`, which
188    /// binds tighter than `|` — matching music21, where `3@0|4@0&6@0` parses as
189    /// `3@0|{4@0&6@0}`.
190    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    /// Returns whether an integer is in the sieve.
207    pub fn contains(&self, z: IntegerType) -> bool {
208        self.root.contains(z)
209    }
210
211    /// Returns the period: the least common multiple of every modulus.
212    ///
213    /// The sieve's membership pattern repeats with this length.
214    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    /// Returns the members of the sieve in `low..=high`.
221    pub fn segment(&self, low: IntegerType, high: IntegerType) -> Vec<IntegerType> {
222        (low..=high).filter(|z| self.contains(*z)).collect()
223    }
224
225    /// Returns the widths between consecutive members of one period.
226    ///
227    /// This is music21's `PitchSieve.getIntervalSequence`, in semitones: the
228    /// sieve is evaluated over `0..=period` and the consecutive differences
229    /// taken. A sieve with fewer than two members in that window has no widths
230    /// and is an error, exactly as music21 raises for `3@1`.
231    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    /// The same sieve with every residual's shift moved on by `n`.
244    ///
245    /// This is the `n` music21 takes beside a range when it reads a segment.
246    pub fn shifted(&self, n: IntegerType) -> Self {
247        Self {
248            root: self.root.shifted(n),
249        }
250    }
251
252    /// The sieve over `low..=high` as ones and noughts, one per integer in
253    /// the range rather than one per member.
254    ///
255    /// music21's `segmentFormat='binary'`.
256    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    /// The widths between consecutive members over `low..=high`, one shorter
263    /// than the segment itself.
264    ///
265    /// music21's `segmentFormat='width'`. Unlike [`Sieve::interval_widths`]
266    /// this reads whatever range it is given rather than one period, so it
267    /// says nothing about where the pattern repeats.
268    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    /// Each member's place in `low..=high` as a fraction of the way across
274    /// it, so the range's own ends are nought and one.
275    ///
276    /// music21's `segmentFormat='unit'`. A range with no width answers nought
277    /// for every member, as music21 does rather than dividing by it.
278    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    /// The first `length` members at or above `z_minimum`, reading the sieve
294    /// shifted on by `n`.
295    ///
296    /// music21's `collect`, which walks upward a hundred integers at a time
297    /// until it has enough. The walk is bounded, so a sieve with too few
298    /// members to fill the length is an error rather than a loop that never
299    /// ends.
300    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    /// The members of both sieves, written the way music21 writes a combined
326    /// sieve: each side in braces around the operator.
327    ///
328    /// `a.intersection(&b)` is written `{a}&{b}`. Note that music21's
329    /// `a & b` writes the two the other way round, `{b}&{a}`.
330    pub fn intersection(&self, other: &Self) -> Self {
331        self.combined(other, Node::And)
332    }
333
334    /// The members of either sieve. See [`Sieve::intersection`] for the
335    /// bracketing and the order.
336    pub fn union(&self, other: &Self) -> Self {
337        self.combined(other, Node::Or)
338    }
339
340    /// The members of one sieve or the other but not both. See
341    /// [`Sieve::intersection`] for the bracketing and the order.
342    pub fn symmetric_difference(&self, other: &Self) -> Self {
343        self.combined(other, Node::Xor)
344    }
345
346    /// The compressed reading of the sieve: a union of residual classes with
347    /// the same members, which is music21's `cmp` state.
348    ///
349    /// A sieve written with nothing but `&` and `|` is compressed by
350    /// intersection: each run of `&` becomes the one class its residuals
351    /// meet in, whatever the range. Anything written with a group, a `^` or
352    /// a complemented group is compressed from its members in `low..=high`
353    /// instead, by finding for each member in turn the smallest modulus
354    /// whose class holds nothing the sieve does not, so that reading depends
355    /// on the range it was taken over.
356    ///
357    /// It is an error where there is no such reading: a complemented class
358    /// intersected with another, classes that never meet, or fewer than two
359    /// members in the range.
360    ///
361    /// ```
362    /// use music21_rs::Sieve;
363    ///
364    /// assert_eq!(Sieve::parse("2&4&8|5")?.compressed(0, 99)?.to_string(), "8@0|5@0");
365    /// assert_eq!(Sieve::parse("(5|2)&4&8")?.compressed(0, 99)?.to_string(), "8@0");
366    /// assert_eq!(
367    ///     Sieve::parse("3@0^4@0")?.compressed(0, 99)?.to_string(),
368    ///     "6@3|12@4|12@6|12@8"
369    /// );
370    /// # Ok::<(), music21_rs::Error>(())
371    /// ```
372    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    /// music21's `CompressionSegment` over the members in `low..=high`.
391    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
403/// music21's `CompressionSegment`: sorted, distinct `members` of `low..=high`
404/// written as residual classes, each run taken with the smallest modulus that
405/// stays inside them.
406fn 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    // music21 tries every modulus below the length of the range.
413    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    /// The sieve that is these integers, as music21 builds one from a list:
439    /// each run of them a residual class, found smallest modulus first, over
440    /// `range` or else from the least of them to the greatest.
441    ///
442    /// ```
443    /// use music21_rs::Sieve;
444    ///
445    /// assert_eq!(Sieve::from_segment(&[2, 4, 6, 8], None)?.to_string(), "2@0");
446    /// assert_eq!(
447    ///     Sieve::from_segment(&[2, 4, 6, 8], Some((0, 19)))?.to_string(),
448    ///     "12@8|14@6|16@4|18@2"
449    /// );
450    /// # Ok::<(), music21_rs::Error>(())
451    /// ```
452    ///
453    /// # Errors
454    ///
455    /// Fewer than two distinct integers, or one outside `range`.
456    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
507/// Writes the sieve as music21 writes it: the expression it was given, with
508/// each residual normalized to `modulus@shift` and the groups it was written
509/// with kept.
510///
511/// ```
512/// use music21_rs::Sieve;
513///
514/// assert_eq!(Sieve::parse("3@11")?.to_string(), "3@2");
515/// assert_eq!(Sieve::parse("(5|2)&4&8")?.to_string(), "{5@0|2@0}&4@0&8@0");
516/// # Ok::<(), music21_rs::Error>(())
517/// ```
518impl fmt::Display for Sieve {
519    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
520        write!(f, "{}", self.root)
521    }
522}
523
524/// The least common multiple, with nought where either side is nought.
525fn lcm(a: UnsignedIntegerType, b: UnsignedIntegerType) -> UnsignedIntegerType {
526    if a == 0 || b == 0 {
527        return 0;
528    }
529    num::integer::lcm(a, b)
530}
531
532/// The class two residual classes meet in, each given as modulus and shift:
533/// music21's `Residual.__and__`, after Xenakis.
534///
535/// Two classes whose moduli share a factor meet only when their shifts agree
536/// modulo it, and otherwise not at all. The empty class, of modulus nought,
537/// meets nothing.
538fn 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    // The g with g * c1 = 1 (mod c2), which exists because c1 and c2 share
551    // no factor.
552    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            // music21 writes a residual as `M@N`, `M,N` or `MsubN`.
594            '@' | ',' => {
595                chars.next();
596                tokens.push(Token::At);
597            }
598            // music21's other spellings: `and` and Xenakis's `*` intersect,
599            // `or` and `+` unite, `not` complements, and `sub` joins a
600            // modulus to its shift.
601            '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        // A bare modulus means a shift of zero, as music21's `5` is `5@0`.
738        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        // music21 keeps the shift of the empty class as written.
751        Ok(Node::Residual {
752            modulus,
753            shift: if modulus == 0 { shift } else { shift % modulus },
754        })
755    }
756}
757
758/// The primes in order from `first_candidate` up: music21's `eratosthenes`.
759///
760/// An incremental sieve: each prime found is filed under its next multiple,
761/// so a candidate that is nobody's multiple is prime and the primes need no
762/// upper bound. The iterator is endless.
763pub 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
789/// The witnesses that make Miller-Rabin exact for every number below
790/// 2<sup>64</sup>, so the answer is never a probability.
791const 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
806/// Whether a number is prime: music21's `rabinMiller`, answered for the
807/// number's magnitude, so a negative number is as prime as its opposite.
808///
809/// music21 tests random witnesses and answers "probably"; the witnesses
810/// here are the ones that decide every 64-bit number exactly.
811pub 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
851/// A set of integers as a run of ones and zeros over its range: music21's
852/// `discreteBinaryPad`, so `[3, 10, 12]` is a one, six noughts, a one, a
853/// nought and a one.
854///
855/// The range is the smallest to the largest member unless `fix_range` gives
856/// another, in which case its own smallest and largest bound the run. An
857/// empty series with no range is an error, since it has no range of its own.
858pub 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
873/// Numbers spaced across the unit interval in proportion to where each
874/// falls between the smallest and the largest: music21's `unitNormRange`,
875/// so `[0, 3, 4]` is `[0, 0.75, 1]`.
876///
877/// `fix_range` bounds the interval with a range other than the series' own.
878/// A series of one number answers nought, and so does every number of a
879/// series with no spread at all.
880pub 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
906/// The unit interval cut into `parts` points, nought and one included:
907/// music21's `unitNormEqual`, so three parts are `[0, 0.5, 1]`. One part or
908/// none is a single nought.
909pub 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
922/// The values a step of `step` reaches from `a` to `b` inclusive, either as
923/// they are or normalized onto the unit interval: music21's `unitNormStep`.
924/// A range of no width answers nothing; a step of no width cannot cross one
925/// and is an error.
926pub 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    /// Every answer here was read off music21's own docstrings.
959    #[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    /// music21 writes a sieve back out as it was given, with the residuals
1019    /// normalized and the groups kept. Every string here was read off
1020    /// music21 11.0.0b9.
1021    #[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        // By intersection, whatever the range.
1030        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        // From the members of a range.
1038        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        // No such reading.
1054        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    /// music21's `a & b` puts the right operand first and braces both sides.
1073    #[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        // A bare modulus is a shift of zero.
1125        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        // music21 parses 3@0|4@0&6@0 as 3@0|{4@0&6@0}.
1155        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        // music21 raises "interval segment has no values" for this: 3@1 has
1177        // only the member 1 in 0..=3.
1178        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    /// music21's own test: a modulus of nought is the empty class, and its
1192    /// complement every integer.
1193    #[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    /// music21 reads brackets as a group, and a residual written `M,N` or
1204    /// `MsubN` as `M@N`; the answers and the written forms are music21's.
1205    #[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    /// music21's `Sieve` given a list of integers, with and without a range.
1237    #[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}