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