icu_collections/codepointinvlist/
builder.rs

1// This file is part of ICU4X. For terms of use, please see the file
2// called LICENSE at the top level of the ICU4X source tree
3// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
4
5use alloc::vec;
6use alloc::vec::Vec;
7use core::{char, cmp::Ordering, ops::RangeBounds};
8use potential_utf::PotentialCodePoint;
9
10use crate::codepointinvlist::{utils::deconstruct_range, CodePointInversionList};
11use zerovec::{ule::AsULE, ZeroVec};
12
13/// A builder for [`CodePointInversionList`].
14///
15/// Provides exposure to builder functions and conversion to [`CodePointInversionList`]
16#[derive(Default)]
17pub struct CodePointInversionListBuilder {
18    // A sorted list of even length, with values <= char::MAX + 1
19    intervals: Vec<u32>,
20}
21
22impl CodePointInversionListBuilder {
23    /// Returns empty [`CodePointInversionListBuilder`]
24    pub const fn new() -> Self {
25        Self { intervals: vec![] }
26    }
27
28    /// Returns a [`CodePointInversionList`] and consumes the [`CodePointInversionListBuilder`]
29    pub fn build(self) -> CodePointInversionList<'static> {
30        let inv_list: ZeroVec<PotentialCodePoint> = self
31            .intervals
32            .into_iter()
33            .map(PotentialCodePoint::from_u24)
34            .collect();
35        #[allow(clippy::unwrap_used)] // by invariant
36        CodePointInversionList::try_from_inversion_list(inv_list).unwrap()
37    }
38
39    /// Abstraction for adding/removing a range from start..end
40    ///
41    /// If add is true add, else remove
42    fn add_remove_middle(&mut self, start: u32, end: u32, add: bool) {
43        if start >= end || end > char::MAX as u32 + 1 {
44            return;
45        }
46        let start_res = self.intervals.binary_search(&start);
47        let end_res = self.intervals.binary_search(&end);
48        let mut start_ind = start_res.unwrap_or_else(|x| x);
49        let mut end_ind = end_res.unwrap_or_else(|x| x);
50        let start_pos_check = (start_ind % 2 == 0) == add;
51        let end_pos_check = (end_ind % 2 == 0) == add;
52        let start_eq_end = start_ind == end_ind;
53
54        #[allow(clippy::indexing_slicing)] // all indices are binary search results
55        if start_eq_end && start_pos_check && end_res.is_err() {
56            self.intervals.splice(start_ind..end_ind, [start, end]);
57        } else {
58            if start_pos_check {
59                self.intervals[start_ind] = start;
60                start_ind += 1;
61            }
62            if end_pos_check {
63                if end_res.is_ok() {
64                    end_ind += 1;
65                } else {
66                    end_ind -= 1;
67                    self.intervals[end_ind] = end;
68                }
69            }
70            if start_ind < end_ind {
71                self.intervals.drain(start_ind..end_ind);
72            }
73        }
74    }
75
76    /// Add the range to the [`CodePointInversionListBuilder`]
77    ///
78    /// Accomplishes this through binary search for the start and end indices and merges intervals
79    /// in between with inplace memory. Performs `O(1)` operation if adding to end of list, and `O(N)` otherwise,
80    /// where `N` is the number of endpoints.
81    fn add(&mut self, start: u32, end: u32) {
82        if start >= end {
83            return;
84        }
85        if self.intervals.is_empty() {
86            self.intervals.extend_from_slice(&[start, end]);
87            return;
88        }
89        self.add_remove_middle(start, end, true);
90    }
91
92    /// Add the character to the [`CodePointInversionListBuilder`]
93    ///
94    /// # Examples
95    ///
96    /// ```
97    /// use icu::collections::codepointinvlist::CodePointInversionListBuilder;
98    /// let mut builder = CodePointInversionListBuilder::new();
99    /// builder.add_char('a');
100    /// let check = builder.build();
101    /// assert_eq!(check.iter_chars().next(), Some('a'));
102    /// ```
103    pub fn add_char(&mut self, c: char) {
104        let to_add = c as u32;
105        self.add(to_add, to_add + 1);
106    }
107
108    /// Add the code point value to the [`CodePointInversionListBuilder`]
109    ///
110    /// Note: Even though [`u32`] and [`prim@char`] in Rust are non-negative 4-byte
111    /// values, there is an important difference. A [`u32`] can take values up to
112    /// a very large integer value, while a [`prim@char`] in Rust is defined to be in
113    /// the range from 0 to the maximum valid Unicode Scalar Value.
114    ///
115    /// # Examples
116    ///
117    /// ```
118    /// use icu::collections::codepointinvlist::CodePointInversionListBuilder;
119    /// let mut builder = CodePointInversionListBuilder::new();
120    /// builder.add32(0x41);
121    /// let check = builder.build();
122    /// assert!(check.contains32(0x41));
123    /// ```
124    pub fn add32(&mut self, c: u32) {
125        if c <= char::MAX as u32 {
126            // we already know 0 <= c  because c: u32
127            self.add(c, c + 1);
128        }
129    }
130
131    /// Add the range of characters to the [`CodePointInversionListBuilder`]
132    ///
133    /// # Examples
134    ///
135    /// ```
136    /// use icu::collections::codepointinvlist::CodePointInversionListBuilder;
137    /// let mut builder = CodePointInversionListBuilder::new();
138    /// builder.add_range('A'..='Z');
139    /// let check = builder.build();
140    /// assert_eq!(check.iter_chars().next(), Some('A'));
141    /// ```
142    pub fn add_range(&mut self, range: impl RangeBounds<char>) {
143        let (start, end) = deconstruct_range(range);
144        self.add(start, end);
145    }
146
147    /// Add the range of characters, represented as u32, to the [`CodePointInversionListBuilder`]
148    ///
149    /// # Examples
150    ///
151    /// ```
152    /// use icu::collections::codepointinvlist::CodePointInversionListBuilder;
153    /// let mut builder = CodePointInversionListBuilder::new();
154    /// builder.add_range32(0xd800..=0xdfff);
155    /// let check = builder.build();
156    /// assert!(check.contains32(0xd900));
157    /// ```
158    pub fn add_range32(&mut self, range: impl RangeBounds<u32>) {
159        let (start, end) = deconstruct_range(range);
160        // Sets that include char::MAX need to allow an end value of MAX + 1
161        if start <= end && end <= char::MAX as u32 + 1 {
162            self.add(start, end);
163        }
164    }
165
166    /// Add the [`CodePointInversionList`] reference to the [`CodePointInversionListBuilder`]
167    ///
168    /// # Examples
169    ///
170    /// ```
171    /// use icu::collections::codepointinvlist::{
172    ///     CodePointInversionList, CodePointInversionListBuilder,
173    /// };
174    /// let mut builder = CodePointInversionListBuilder::new();
175    /// let set = CodePointInversionList::try_from_u32_inversion_list_slice(&[
176    ///     0x41, 0x4C,
177    /// ])
178    /// .unwrap();
179    /// builder.add_set(&set);
180    /// let check = builder.build();
181    /// assert_eq!(check.iter_chars().next(), Some('A'));
182    /// ```
183    #[allow(unused_assignments)]
184    pub fn add_set(&mut self, set: &CodePointInversionList) {
185        #[allow(clippy::indexing_slicing)] // chunks
186        set.as_inversion_list()
187            .as_ule_slice()
188            .chunks(2)
189            .for_each(|pair| {
190                self.add(
191                    u32::from(PotentialCodePoint::from_unaligned(pair[0])),
192                    u32::from(PotentialCodePoint::from_unaligned(pair[1])),
193                )
194            });
195    }
196
197    /// Removes the range from the [`CodePointInversionListBuilder`]
198    ///
199    /// Performs binary search to find start and end affected intervals, then removes in an `O(N)` fashion
200    /// where `N` is the number of endpoints, with in-place memory.
201    fn remove(&mut self, start: u32, end: u32) {
202        if start >= end || self.intervals.is_empty() {
203            return;
204        }
205        if let Some(&last) = self.intervals.last() {
206            #[allow(clippy::indexing_slicing)]
207            // by invariant, if we have a last we have a (different) first
208            if start <= self.intervals[0] && end >= last {
209                self.intervals.clear();
210            } else {
211                self.add_remove_middle(start, end, false);
212            }
213        }
214    }
215
216    /// Remove the character from the [`CodePointInversionListBuilder`]
217    ///
218    /// # Examples
219    ///
220    /// ```
221    /// use icu::collections::codepointinvlist::CodePointInversionListBuilder;
222    /// let mut builder = CodePointInversionListBuilder::new();
223    /// builder.add_range('A'..='Z');
224    /// builder.remove_char('A');
225    /// let check = builder.build();
226    /// assert_eq!(check.iter_chars().next(), Some('B'));
227    pub fn remove_char(&mut self, c: char) {
228        self.remove32(c as u32)
229    }
230
231    /// See [`Self::remove_char`]
232    pub fn remove32(&mut self, c: u32) {
233        self.remove(c, c + 1);
234    }
235
236    /// Remove the range of characters from the [`CodePointInversionListBuilder`]
237    ///
238    /// # Examples
239    ///
240    /// ```
241    /// use icu::collections::codepointinvlist::CodePointInversionListBuilder;
242    /// let mut builder = CodePointInversionListBuilder::new();
243    /// builder.add_range('A'..='Z');
244    /// builder.remove_range('A'..='C');
245    /// let check = builder.build();
246    /// assert_eq!(check.iter_chars().next(), Some('D'));
247    pub fn remove_range(&mut self, range: impl RangeBounds<char>) {
248        let (start, end) = deconstruct_range(range);
249        self.remove(start, end);
250    }
251
252    /// See [`Self::remove_range`]
253    pub fn remove_range32(&mut self, range: impl RangeBounds<u32>) {
254        let (start, end) = deconstruct_range(range);
255        self.remove(start, end);
256    }
257
258    /// Remove the [`CodePointInversionList`] from the [`CodePointInversionListBuilder`]
259    ///
260    /// # Examples
261    ///
262    /// ```
263    /// use icu::collections::codepointinvlist::{CodePointInversionList, CodePointInversionListBuilder};
264    /// let mut builder = CodePointInversionListBuilder::new();
265    /// let set = CodePointInversionList::try_from_u32_inversion_list_slice(&[0x41, 0x46]).unwrap();
266    /// builder.add_range('A'..='Z');
267    /// builder.remove_set(&set); // removes 'A'..='E'
268    /// let check = builder.build();
269    /// assert_eq!(check.iter_chars().next(), Some('F'));
270    #[allow(clippy::indexing_slicing)] // chunks
271    pub fn remove_set(&mut self, set: &CodePointInversionList) {
272        set.as_inversion_list()
273            .as_ule_slice()
274            .chunks(2)
275            .for_each(|pair| {
276                self.remove(
277                    u32::from(PotentialCodePoint::from_unaligned(pair[0])),
278                    u32::from(PotentialCodePoint::from_unaligned(pair[1])),
279                )
280            });
281    }
282
283    /// Retain the specified character in the [`CodePointInversionListBuilder`] if it exists
284    ///
285    /// # Examples
286    ///
287    /// ```
288    /// use icu::collections::codepointinvlist::CodePointInversionListBuilder;
289    /// let mut builder = CodePointInversionListBuilder::new();
290    /// builder.add_range('A'..='Z');
291    /// builder.retain_char('A');
292    /// let set = builder.build();
293    /// let mut check = set.iter_chars();
294    /// assert_eq!(check.next(), Some('A'));
295    /// assert_eq!(check.next(), None);
296    /// ```
297    pub fn retain_char(&mut self, c: char) {
298        self.retain32(c as u32)
299    }
300
301    /// See [`Self::retain_char`]
302    pub fn retain32(&mut self, c: u32) {
303        self.remove(0, c);
304        self.remove(c + 1, (char::MAX as u32) + 1);
305    }
306
307    /// Retain the range of characters located within the [`CodePointInversionListBuilder`]
308    ///
309    /// # Examples
310    ///
311    /// ```
312    /// use icu::collections::codepointinvlist::CodePointInversionListBuilder;
313    /// let mut builder = CodePointInversionListBuilder::new();
314    /// builder.add_range('A'..='Z');
315    /// builder.retain_range('A'..='B');
316    /// let set = builder.build();
317    /// let mut check = set.iter_chars();
318    /// assert_eq!(check.next(), Some('A'));
319    /// assert_eq!(check.next(), Some('B'));
320    /// assert_eq!(check.next(), None);
321    /// ```
322    pub fn retain_range(&mut self, range: impl RangeBounds<char>) {
323        let (start, end) = deconstruct_range(range);
324        self.remove(0, start);
325        self.remove(end, (char::MAX as u32) + 1);
326    }
327
328    /// See [`Self::retain_range`]
329    pub fn retain_range32(&mut self, range: impl RangeBounds<u32>) {
330        let (start, end) = deconstruct_range(range);
331        self.remove(0, start);
332        self.remove(end, (char::MAX as u32) + 1);
333    }
334
335    /// Retain the elements in the specified set within the [`CodePointInversionListBuilder`]
336    ///
337    /// # Examples
338    ///
339    /// ```
340    /// use icu::collections::codepointinvlist::{
341    ///     CodePointInversionList, CodePointInversionListBuilder,
342    /// };
343    /// let mut builder = CodePointInversionListBuilder::new();
344    /// let set =
345    ///     CodePointInversionList::try_from_u32_inversion_list_slice(&[65, 70])
346    ///         .unwrap();
347    /// builder.add_range('A'..='Z');
348    /// builder.retain_set(&set); // retains 'A'..='E'
349    /// let check = builder.build();
350    /// assert!(check.contains('A'));
351    /// assert!(!check.contains('G'));
352    /// ```
353    #[allow(clippy::indexing_slicing)] // chunks
354    pub fn retain_set(&mut self, set: &CodePointInversionList) {
355        let mut prev = 0;
356        for pair in set.as_inversion_list().as_ule_slice().chunks(2) {
357            let range_start = u32::from(PotentialCodePoint::from_unaligned(pair[0]));
358            let range_limit = u32::from(PotentialCodePoint::from_unaligned(pair[1]));
359            self.remove(prev, range_start);
360            prev = range_limit;
361        }
362        self.remove(prev, (char::MAX as u32) + 1);
363    }
364
365    /// Computes the complement of the argument, adding any elements that do not yet exist in the builder,
366    /// and removing any elements that already exist in the builder. See public functions for examples.
367    ///
368    /// Performs in `O(B + S)`, where `B` is the number of endpoints in the Builder, and `S` is the number
369    /// of endpoints in the argument.
370    fn complement_list(&mut self, set_iter: impl IntoIterator<Item = u32>) {
371        let mut res: Vec<u32> = vec![]; // not the biggest fan of having to allocate new memory
372        let mut ai = self.intervals.iter();
373        let mut bi = set_iter.into_iter();
374        let mut a = ai.next();
375        let mut b = bi.next();
376        while let (Some(c), Some(d)) = (a, b) {
377            match c.cmp(&d) {
378                Ordering::Less => {
379                    res.push(*c);
380                    a = ai.next();
381                }
382                Ordering::Greater => {
383                    res.push(d);
384                    b = bi.next();
385                }
386                Ordering::Equal => {
387                    a = ai.next();
388                    b = bi.next();
389                }
390            }
391        }
392        if let Some(c) = a {
393            res.push(*c)
394        }
395        if let Some(d) = b {
396            res.push(d)
397        }
398        res.extend(ai);
399        res.extend(bi);
400        self.intervals = res;
401    }
402
403    /// Computes the complement of the builder, inverting the builder (any elements in the builder are removed,
404    /// while any elements not in the builder are added)
405    ///
406    /// # Examples
407    ///
408    /// ```
409    /// use icu::collections::codepointinvlist::{
410    ///     CodePointInversionList, CodePointInversionListBuilder,
411    /// };
412    /// let mut builder = CodePointInversionListBuilder::new();
413    /// let set = CodePointInversionList::try_from_u32_inversion_list_slice(&[
414    ///     0x0,
415    ///     0x41,
416    ///     0x46,
417    ///     (std::char::MAX as u32) + 1,
418    /// ])
419    /// .unwrap();
420    /// builder.add_set(&set);
421    /// builder.complement();
422    /// let check = builder.build();
423    /// assert_eq!(check.iter_chars().next(), Some('A'));
424    /// ```
425    pub fn complement(&mut self) {
426        if !self.intervals.is_empty() {
427            #[allow(clippy::indexing_slicing)] // by invariant
428            if self.intervals[0] == 0 {
429                self.intervals.drain(0..1);
430            } else {
431                self.intervals.insert(0, 0);
432            }
433            if self.intervals.last() == Some(&(char::MAX as u32 + 1)) {
434                self.intervals.pop();
435            } else {
436                self.intervals.push(char::MAX as u32 + 1);
437            }
438        } else {
439            self.intervals
440                .extend_from_slice(&[0, (char::MAX as u32 + 1)]);
441        }
442    }
443
444    /// Complements the character in the builder, adding it if not in the builder, and removing it otherwise.
445    ///
446    /// # Examples
447    ///
448    /// ```
449    /// use icu::collections::codepointinvlist::CodePointInversionListBuilder;
450    /// let mut builder = CodePointInversionListBuilder::new();
451    /// builder.add_range('A'..='D');
452    /// builder.complement_char('A');
453    /// builder.complement_char('E');
454    /// let check = builder.build();
455    /// assert!(check.contains('E'));
456    /// assert!(!check.contains('A'));
457    /// ```
458    pub fn complement_char(&mut self, c: char) {
459        self.complement32(c as u32);
460    }
461
462    /// See [`Self::complement_char`]
463    pub fn complement32(&mut self, c: u32) {
464        self.complement_list([c, c + 1]);
465    }
466
467    /// Complements the range in the builder, adding any elements in the range if not in the builder, and
468    /// removing them otherwise.
469    ///
470    /// # Examples
471    ///
472    /// ```
473    /// use icu::collections::codepointinvlist::CodePointInversionListBuilder;
474    /// let mut builder = CodePointInversionListBuilder::new();
475    /// builder.add_range('A'..='D');
476    /// builder.complement_range('C'..='F');
477    /// let check = builder.build();
478    /// assert!(check.contains('F'));
479    /// assert!(!check.contains('C'));
480    /// ```
481    pub fn complement_range(&mut self, range: impl RangeBounds<char>) {
482        let (start, end) = deconstruct_range(range);
483        self.complement_list([start, end]);
484    }
485
486    /// See [`Self::complement_range`]
487    pub fn complement_range32(&mut self, range: impl RangeBounds<u32>) {
488        let (start, end) = deconstruct_range(range);
489        self.complement_list([start, end]);
490    }
491
492    /// Complements the set in the builder, adding any elements in the set if not in the builder, and
493    /// removing them otherwise.
494    ///
495    /// # Examples
496    ///
497    /// ```
498    /// use icu::collections::codepointinvlist::{
499    ///     CodePointInversionList, CodePointInversionListBuilder,
500    /// };
501    /// let mut builder = CodePointInversionListBuilder::new();
502    /// let set = CodePointInversionList::try_from_u32_inversion_list_slice(&[
503    ///     0x41, 0x46, 0x4B, 0x5A,
504    /// ])
505    /// .unwrap();
506    /// builder.add_range('C'..='N'); // 67 - 78
507    /// builder.complement_set(&set);
508    /// let check = builder.build();
509    /// assert!(check.contains('Q')); // 81
510    /// assert!(!check.contains('N')); // 78
511    /// ```
512    pub fn complement_set(&mut self, set: &CodePointInversionList) {
513        let inv_list_iter_owned = set.as_inversion_list().iter().map(u32::from);
514        self.complement_list(inv_list_iter_owned);
515    }
516
517    /// Returns whether the build is empty.
518    ///
519    /// # Examples
520    ///
521    /// ```
522    /// use icu::collections::codepointinvlist::CodePointInversionListBuilder;
523    /// let mut builder = CodePointInversionListBuilder::new();
524    /// let check = builder.build();
525    /// assert!(check.is_empty());
526    /// ```
527    pub fn is_empty(&self) -> bool {
528        self.intervals.is_empty()
529    }
530}
531
532#[cfg(test)]
533mod tests {
534    use super::{CodePointInversionList, CodePointInversionListBuilder};
535    use core::char;
536
537    fn generate_tester(ex: &[u32]) -> CodePointInversionListBuilder {
538        let check = CodePointInversionList::try_from_u32_inversion_list_slice(ex).unwrap();
539        let mut builder = CodePointInversionListBuilder::new();
540        builder.add_set(&check);
541        builder
542    }
543
544    #[test]
545    fn test_new() {
546        let ex = CodePointInversionListBuilder::new();
547        assert!(ex.intervals.is_empty());
548    }
549
550    #[test]
551    fn test_build() {
552        let mut builder = CodePointInversionListBuilder::new();
553        builder.add(0x41, 0x42);
554        let check: CodePointInversionList = builder.build();
555        assert_eq!(check.iter_chars().next(), Some('A'));
556    }
557
558    #[test]
559    fn test_empty_build() {
560        let builder = CodePointInversionListBuilder::new();
561        let check: CodePointInversionList = builder.build();
562        assert!(check.is_empty());
563    }
564
565    #[test]
566    fn test_add_to_empty() {
567        let mut builder = CodePointInversionListBuilder::new();
568        builder.add(0x0, 0xA);
569        assert_eq!(builder.intervals, [0x0, 0xA]);
570    }
571
572    #[test]
573    fn test_add_invalid() {
574        let mut builder = CodePointInversionListBuilder::new();
575        builder.add(0x0, 0x0);
576        builder.add(0x5, 0x0);
577        assert!(builder.intervals.is_empty());
578    }
579
580    #[test]
581    fn test_add_to_start() {
582        let mut builder = generate_tester(&[0xA, 0x14, 0x28, 0x32]);
583        builder.add(0x0, 0x5);
584        let expected = [0x0, 0x5, 0xA, 0x14, 0x28, 0x32];
585        assert_eq!(builder.intervals, expected);
586    }
587
588    #[test]
589    fn test_add_to_start_overlap() {
590        let mut builder = generate_tester(&[0xA, 0x14, 0x28, 0x32]);
591        builder.add(0x0, 0xE);
592        let expected = [0x0, 0x14, 0x28, 0x32];
593        assert_eq!(builder.intervals, expected);
594    }
595
596    #[test]
597    fn test_add_to_end() {
598        let mut builder = generate_tester(&[0xA, 0x14, 0x28, 0x32]);
599        builder.add(0x3C, 0x46);
600        let expected = [0xA, 0x14, 0x28, 0x32, 60, 70];
601        assert_eq!(builder.intervals, expected);
602    }
603
604    #[test]
605    fn test_add_to_end_overlap() {
606        let mut builder = generate_tester(&[0xA, 0x14, 0x28, 0x32]);
607        builder.add(0x2B, 0x46);
608        let expected = [0xA, 0x14, 0x28, 0x46];
609        assert_eq!(builder.intervals, expected);
610    }
611
612    #[test]
613    fn test_add_to_middle_no_overlap() {
614        let mut builder = generate_tester(&[0xA, 0x14, 0x28, 0x32]);
615        builder.add(0x19, 0x1B);
616        let expected = [0xA, 0x14, 0x19, 0x1B, 0x28, 0x32];
617        assert_eq!(builder.intervals, expected);
618    }
619
620    #[test]
621    fn test_add_to_middle_inside() {
622        let mut builder = generate_tester(&[0xA, 0x14, 0x28, 0x32]);
623        builder.add(0xA, 0x14);
624        let expected = [0xA, 0x14, 0x28, 0x32];
625        assert_eq!(builder.intervals, expected);
626    }
627
628    #[test]
629    fn test_add_to_middle_left_overlap() {
630        let mut builder = generate_tester(&[0xA, 0x14, 0x28, 0x32]);
631        builder.add(0xF, 0x19);
632        let expected = [0xA, 0x19, 0x28, 0x32];
633        assert_eq!(builder.intervals, expected);
634    }
635
636    #[test]
637    fn test_add_to_middle_right_overlap() {
638        let mut builder = generate_tester(&[0xA, 0x14, 0x28, 0x32]);
639        builder.add(0x1E, 0x28);
640        let expected = [0xA, 0x14, 0x1E, 0x32];
641        assert_eq!(builder.intervals, expected);
642    }
643
644    #[test]
645    fn test_add_to_full_encompass() {
646        let mut builder = generate_tester(&[0xA, 0x14, 0x28, 0x32]);
647        builder.add(0x0, 0x3C);
648        let expected = [0x0, 0x3C];
649        assert_eq!(builder.intervals, expected);
650    }
651
652    #[test]
653    fn test_add_to_partial_encompass() {
654        let mut builder = generate_tester(&[0xA, 0x14, 0x28, 0x32]);
655        builder.add(0x0, 0x23);
656        let expected = [0x0, 0x23, 0x28, 0x32];
657        assert_eq!(builder.intervals, expected);
658    }
659
660    #[test]
661    fn test_add_aligned_front() {
662        let mut builder = generate_tester(&[0xA, 0x14, 0x28, 0x32]);
663        builder.add(5, 10);
664        let expected = [5, 0x14, 0x28, 0x32];
665        assert_eq!(builder.intervals, expected);
666    }
667
668    #[test]
669    fn test_add_aligned_back() {
670        let mut builder = generate_tester(&[0xA, 0x14, 0x28, 0x32]);
671        builder.add(0x32, 0x37);
672        let expected = [0xA, 0x14, 0x28, 0x37];
673        assert_eq!(builder.intervals, expected);
674    }
675
676    #[test]
677    fn test_add_aligned_start_middle() {
678        let mut builder = generate_tester(&[0xA, 0x14, 0x28, 0x32]);
679        builder.add(0x14, 0x19);
680        let expected = [0xA, 0x19, 0x28, 0x32];
681        assert_eq!(builder.intervals, expected);
682    }
683
684    #[test]
685    fn test_add_aligned_end_middle() {
686        let mut builder = generate_tester(&[0xA, 0x14, 0x28, 0x32]);
687        builder.add(0x23, 0x28);
688        let expected = [0xA, 0x14, 0x23, 0x32];
689        assert_eq!(builder.intervals, expected);
690    }
691
692    #[test]
693    fn test_add_aligned_in_between_end() {
694        let mut builder = generate_tester(&[0xA, 0x14, 0x1E, 0x28, 0x32, 0x3C]);
695        builder.add(0xF, 0x1E);
696        let expected = [0xA, 0x28, 0x32, 0x3C];
697        assert_eq!(builder.intervals, expected);
698    }
699
700    #[test]
701    fn test_add_aligned_in_between_start() {
702        let mut builder = generate_tester(&[0xA, 0x14, 0x1E, 0x28, 0x32, 0x3C]);
703        builder.add(20, 35);
704        let expected = [0xA, 0x28, 0x32, 0x3C];
705        assert_eq!(builder.intervals, expected);
706    }
707
708    #[test]
709    fn test_add_adjacent_ranges() {
710        let mut builder = generate_tester(&[0xA, 0x14, 0x28, 0x32]);
711        builder.add(0x13, 0x14);
712        builder.add(0x14, 0x15);
713        builder.add(0x15, 0x16);
714        let expected = [0xA, 0x16, 0x28, 0x32];
715        assert_eq!(builder.intervals, expected);
716    }
717
718    #[test]
719    fn test_add_codepointinversionlist() {
720        let mut builder = generate_tester(&[0xA, 0x14, 0x28, 0x32]);
721        let check = CodePointInversionList::try_from_u32_inversion_list_slice(&[
722            0x5, 0xA, 0x16, 0x21, 0x2C, 0x33,
723        ])
724        .unwrap();
725        builder.add_set(&check);
726        let expected = [0x5, 0x14, 0x16, 0x21, 0x28, 0x33];
727        assert_eq!(builder.intervals, expected);
728    }
729    #[test]
730    fn test_add_char() {
731        let mut builder = CodePointInversionListBuilder::new();
732        builder.add_char('a');
733        let expected = [0x61, 0x62];
734        assert_eq!(builder.intervals, expected);
735    }
736
737    #[test]
738    fn test_add_range() {
739        let mut builder = CodePointInversionListBuilder::new();
740        builder.add_range('A'..='Z');
741        let expected = [0x41, 0x5B];
742        assert_eq!(builder.intervals, expected);
743    }
744
745    #[test]
746    fn test_add_range32() {
747        let mut builder = CodePointInversionListBuilder::new();
748        builder.add_range32(0xd800..=0xdfff);
749        let expected = [0xd800, 0xe000];
750        assert_eq!(builder.intervals, expected);
751    }
752
753    #[test]
754    fn test_add_invalid_range() {
755        let mut builder = CodePointInversionListBuilder::new();
756        builder.add_range('Z'..='A');
757        assert!(builder.intervals.is_empty());
758    }
759
760    #[test]
761    fn test_remove_empty() {
762        let mut builder = CodePointInversionListBuilder::new();
763        builder.remove(0x0, 0xA);
764        assert!(builder.intervals.is_empty());
765    }
766
767    #[test]
768    fn test_remove_entire_builder() {
769        let mut builder = generate_tester(&[0xA, 0x14, 0x28, 0x32]);
770        builder.remove(0xA, 0x32);
771        assert!(builder.intervals.is_empty());
772    }
773
774    #[test]
775    fn test_remove_entire_range() {
776        let mut builder = generate_tester(&[0xA, 0x14, 0x28, 0x32]);
777        builder.remove(0xA, 0x14);
778        let expected = [0x28, 0x32];
779        assert_eq!(builder.intervals, expected);
780    }
781
782    #[test]
783    fn test_remove_partial_range_left() {
784        let mut builder = generate_tester(&[0xA, 0x14, 0x28, 0x32]);
785        builder.remove(0xA, 0x2B);
786        let expected = [0x2B, 0x32];
787        assert_eq!(builder.intervals, expected);
788    }
789
790    #[test]
791    fn test_remove_ne_range() {
792        let mut builder = generate_tester(&[0xA, 0x14, 0x28, 0x32]);
793        builder.remove(0x14, 0x28);
794        let expected = [0xA, 0x14, 0x28, 0x32];
795        assert_eq!(builder.intervals, expected);
796    }
797
798    #[test]
799    fn test_remove_partial_range_right() {
800        let mut builder = generate_tester(&[0xA, 0x14, 0x28, 0x32]);
801        builder.remove(0xF, 0x37);
802        let expected = [0xA, 0xF];
803        assert_eq!(builder.intervals, expected);
804    }
805
806    #[test]
807    fn test_remove_middle_range() {
808        let mut builder = generate_tester(&[0xA, 0x14, 0x28, 0x32]);
809        builder.remove(0xC, 0x12);
810        let expected = [0xA, 0xC, 0x12, 0x14, 0x28, 0x32];
811        assert_eq!(builder.intervals, expected);
812    }
813
814    #[test]
815    fn test_remove_ne_middle_range() {
816        let mut builder = generate_tester(&[0xA, 0x14, 0x28, 0x32]);
817        builder.remove(0x19, 0x1B);
818        let expected = [0xA, 0x14, 0x28, 0x32];
819        assert_eq!(builder.intervals, expected);
820    }
821
822    #[test]
823    fn test_remove_encompassed_range() {
824        let mut builder = generate_tester(&[0xA, 0x14, 0x28, 0x32, 70, 80]);
825        builder.remove(0x19, 0x37);
826        let expected = [0xA, 0x14, 0x46, 0x50];
827        assert_eq!(builder.intervals, expected);
828    }
829    #[test]
830    fn test_remove_adjacent_ranges() {
831        let mut builder = generate_tester(&[0xA, 0x14, 0x28, 0x32]);
832        builder.remove(0x27, 0x28);
833        builder.remove(0x28, 0x29);
834        builder.remove(0x29, 0x2A);
835        let expected = [0xA, 0x14, 0x2A, 0x32];
836        assert_eq!(builder.intervals, expected);
837    }
838
839    #[test]
840    fn test_remove_char() {
841        let mut builder = generate_tester(&[0x41, 0x46]);
842        builder.remove_char('A'); // 65
843        let expected = [0x42, 0x46];
844        assert_eq!(builder.intervals, expected);
845    }
846
847    #[test]
848    fn test_remove_range() {
849        let mut builder = generate_tester(&[0x41, 0x5A]);
850        builder.remove_range('A'..'L'); // 65 - 76
851        let expected = [0x4C, 0x5A];
852        assert_eq!(builder.intervals, expected);
853    }
854
855    #[test]
856    fn test_remove_set() {
857        let mut builder = generate_tester(&[0xA, 0x14, 0x28, 0x32, 70, 80]);
858        let remove =
859            CodePointInversionList::try_from_u32_inversion_list_slice(&[0xA, 0x14, 0x2D, 0x4B])
860                .unwrap();
861        builder.remove_set(&remove);
862        let expected = [0x28, 0x2D, 0x4B, 0x50];
863        assert_eq!(builder.intervals, expected);
864    }
865
866    #[test]
867    fn test_retain_char() {
868        let mut builder = generate_tester(&[0x41, 0x5A]);
869        builder.retain_char('A'); // 65
870        let expected = [0x41, 0x42];
871        assert_eq!(builder.intervals, expected);
872    }
873
874    #[test]
875    fn test_retain_range() {
876        let mut builder = generate_tester(&[0x41, 0x5A]);
877        builder.retain_range('C'..'F'); // 67 - 70
878        let expected = [0x43, 0x46];
879        assert_eq!(builder.intervals, expected);
880    }
881
882    #[test]
883    fn test_retain_range_empty() {
884        let mut builder = generate_tester(&[0x41, 0x46]);
885        builder.retain_range('F'..'Z');
886        assert!(builder.intervals.is_empty());
887    }
888
889    #[test]
890    fn test_retain_set() {
891        let mut builder = generate_tester(&[0xA, 0x14, 0x28, 0x32, 70, 80]);
892        let retain = CodePointInversionList::try_from_u32_inversion_list_slice(&[
893            0xE, 0x14, 0x19, 0x37, 0x4D, 0x51,
894        ])
895        .unwrap();
896        builder.retain_set(&retain);
897        let expected = [0xE, 0x14, 0x28, 0x32, 0x4D, 0x50];
898        assert_eq!(builder.intervals, expected);
899    }
900
901    #[test]
902    fn test_complement() {
903        let mut builder = generate_tester(&[0xA, 0x14, 0x28, 0x32]);
904        builder.complement();
905        let expected = [0x0, 0xA, 0x14, 0x28, 0x32, (char::MAX as u32) + 1];
906        assert_eq!(builder.intervals, expected);
907    }
908
909    #[test]
910    fn test_complement_empty() {
911        let mut builder = generate_tester(&[]);
912        builder.complement();
913        let expected = [0x0, (char::MAX as u32) + 1];
914        assert_eq!(builder.intervals, expected);
915
916        builder.complement();
917        let expected: [u32; 0] = [];
918        assert_eq!(builder.intervals, expected);
919    }
920
921    #[test]
922    fn test_complement_zero_max() {
923        let mut builder = generate_tester(&[0x0, 0xA, 0x5A, (char::MAX as u32) + 1]);
924        builder.complement();
925        let expected = [0xA, 0x5A];
926        assert_eq!(builder.intervals, expected);
927    }
928
929    #[test]
930    fn test_complement_interior() {
931        let mut builder = generate_tester(&[0xA, 0x14, 0x28, 0x32]);
932        builder.complement_list([0xE, 0x14]);
933        let expected = [0xA, 0xE, 0x28, 0x32];
934        assert_eq!(builder.intervals, expected);
935    }
936
937    #[test]
938    fn test_complement_exterior() {
939        let mut builder = generate_tester(&[0xA, 0x14, 0x28, 0x32]);
940        builder.complement_list([0x19, 0x23]);
941        let expected = [0xA, 0x14, 0x19, 0x23, 0x28, 0x32];
942        assert_eq!(builder.intervals, expected);
943    }
944
945    #[test]
946    fn test_complement_larger_list() {
947        let mut builder = generate_tester(&[0xA, 0x14, 0x28, 0x32]);
948        builder.complement_list([0x1E, 0x37, 0x3C, 0x46]);
949        let expected = [0xA, 0x14, 0x1E, 0x28, 0x32, 0x37, 0x3C, 0x46];
950        assert_eq!(builder.intervals, expected);
951    }
952
953    #[test]
954    fn test_complement_char() {
955        let mut builder = generate_tester(&[0x41, 0x4C]); // A - K
956        builder.complement_char('A');
957        builder.complement_char('L');
958        let expected = [0x42, 0x4D];
959        assert_eq!(builder.intervals, expected);
960    }
961
962    #[test]
963    fn test_complement_range() {
964        let mut builder = generate_tester(&[0x46, 0x4C]); // F - K
965        builder.complement_range('A'..='Z');
966        let expected = [0x41, 0x46, 0x4C, 0x5B];
967        assert_eq!(builder.intervals, expected);
968    }
969
970    #[test]
971    fn test_complement_set() {
972        let mut builder = generate_tester(&[0x43, 0x4E]);
973        let set =
974            CodePointInversionList::try_from_u32_inversion_list_slice(&[0x41, 0x46, 0x4B, 0x5A])
975                .unwrap();
976        builder.complement_set(&set);
977        let expected = [0x41, 0x43, 0x46, 0x4B, 0x4E, 0x5A];
978        assert_eq!(builder.intervals, expected);
979    }
980
981    #[test]
982    fn test_is_empty() {
983        let builder = CodePointInversionListBuilder::new();
984        assert!(builder.is_empty());
985    }
986}