tea_map/
valid_iter.rs

1use std::fmt::Debug;
2
3use tea_core::prelude::*;
4
5#[derive(Clone, Debug)]
6pub enum Keep {
7    First,
8    Last,
9}
10
11pub trait MapValidBasic<T: IsNone>: TrustedLen<Item = T> + Sized {
12    /// Computes the absolute value of each element in the iterator, ignoring None values.
13    ///
14    /// This method is similar to `abs()`, but it can handle None values.
15    ///
16    /// # Examples
17    ///
18    /// ```
19    /// use tea_core::prelude::*;
20    /// use tea_map::MapValidBasic;
21    ///
22    /// let v = vec![Some(-1), None, Some(2), Some(-3)];
23    /// let result: Vec<_> = v.titer().vabs().collect();
24    /// assert_eq!(result, vec![Some(1), None, Some(2), Some(3)]);
25    /// ```
26    ///
27    /// See also: [`abs()`](crate::MapBasic::abs)
28    #[inline]
29    fn vabs(self) -> impl TrustedLen<Item = T>
30    where
31        T::Inner: Number,
32    {
33        self.map(|v| v.vabs())
34    }
35
36    /// Forward fill values where the mask is true, ignoring None values.
37    ///
38    /// # Arguments
39    ///
40    /// * `mask_func` - A function that returns true for values that should be filled.
41    /// * `value` - An optional value to fill if head values are still None after forward fill.
42    ///
43    /// # Examples
44    ///
45    /// ```
46    /// use tea_core::prelude::*;
47    /// use tea_map::MapValidBasic;
48    ///
49    /// let v = vec![Some(1), None, Some(2), None, Some(3)];
50    /// let result: Vec<_> = v.titer().ffill_mask(|x| x.is_none(), Some(Some(0))).collect();
51    /// assert_eq!(result, vec![Some(1), Some(1), Some(2), Some(2), Some(3)]);
52    /// ```
53    fn ffill_mask<F: Fn(&T) -> bool>(
54        self,
55        mask_func: F,
56        value: Option<T>,
57    ) -> impl TrustedLen<Item = T> {
58        let mut last_valid: Option<T> = None;
59        let f = move |v: T| {
60            if mask_func(&v) {
61                if let Some(lv) = last_valid.as_ref() {
62                    lv.clone()
63                } else if let Some(value) = &value {
64                    value.clone()
65                } else {
66                    T::none()
67                }
68            } else {
69                // v is valid, update last_valid
70                last_valid = Some(v.clone());
71                v
72            }
73        };
74        self.map(f)
75    }
76
77    /// Forward fill None values.
78    ///
79    /// This method is similar to `ffill()`, but it can handle None values.
80    ///
81    /// # Arguments
82    ///
83    /// * `value` - An optional value to fill if head values are still None after forward fill.
84    ///
85    /// # Examples
86    ///
87    /// ```
88    /// use tea_core::prelude::*;
89    /// use tea_map::MapValidBasic;
90    ///
91    /// let v = vec![Some(1), None, Some(2), None, Some(3)];
92    /// let result: Vec<_> = v.titer().ffill(Some(Some(0))).collect();
93    /// assert_eq!(result, vec![Some(1), Some(1), Some(2), Some(2), Some(3)]);
94    /// ```
95    #[inline]
96    fn ffill(self, value: Option<T>) -> impl TrustedLen<Item = T> {
97        self.ffill_mask(T::is_none, value)
98    }
99
100    /// Backward fill values where the mask is true, ignoring None values.
101    ///
102    /// # Arguments
103    ///
104    /// * `mask_func` - A function that returns true for values that should be filled.
105    /// * `value` - An optional value to fill if tail values are still None after backward fill.
106    ///
107    /// # Examples
108    ///
109    /// ```
110    /// use tea_core::prelude::*;
111    /// use tea_map::MapValidBasic;
112    ///
113    /// let v = vec![Some(1), None, Some(2), None, Some(3)];
114    /// let result: Vec<_> = v.titer().bfill_mask(|x| x.is_none(), Some(Some(0))).collect();
115    /// assert_eq!(result, vec![Some(1), Some(2), Some(2), Some(3), Some(3)]);
116    /// ```
117    fn bfill_mask<F: Fn(&T) -> bool>(
118        self,
119        mask_func: F,
120        value: Option<T>,
121    ) -> impl TrustedLen<Item = T>
122    where
123        Self: DoubleEndedIterator<Item = T>,
124    {
125        let mut last_valid: Option<T> = None;
126        let f = move |v: T| {
127            if mask_func(&v) {
128                if let Some(lv) = last_valid.as_ref() {
129                    lv.clone()
130                } else if let Some(value) = &value {
131                    value.clone()
132                } else {
133                    T::none()
134                }
135            } else {
136                // v is valid, update last_valid
137                last_valid = Some(v.clone());
138                v
139            }
140        };
141        // if we use `self.rev().map(f).rev()` here, we will get a wrong result
142        // so collect the result to a vec and then return rev iterator
143        self.rev().map(f).collect_trusted_to_vec().into_iter().rev()
144    }
145
146    /// Backward fill None values.
147    ///
148    /// This method is similar to `bfill()`, but it can handle None values.
149    ///
150    /// # Arguments
151    ///
152    /// * `value` - An optional value to fill if tail values are still None after backward fill.
153    ///
154    /// # Examples
155    ///
156    /// ```
157    /// use tea_core::prelude::*;
158    /// use tea_map::MapValidBasic;
159    ///
160    /// let v = vec![Some(1), None, Some(2), None, Some(3)];
161    /// let result: Vec<_> = v.titer().bfill(Some(Some(0))).collect();
162    /// assert_eq!(result, vec![Some(1), Some(2), Some(2), Some(3), Some(3)]);
163    /// ```
164    #[inline]
165    fn bfill(self, value: Option<T>) -> impl TrustedLen<Item = T>
166    where
167        Self: DoubleEndedIterator<Item = T>,
168    {
169        self.bfill_mask(T::is_none, value)
170    }
171
172    /// Clip (limit) the values in an iterator, ignoring None values.
173    ///
174    /// This method is similar to `clip()`, but it can handle None values.
175    ///
176    /// # Arguments
177    ///
178    /// * `lower` - The lower bound.
179    /// * `upper` - The upper bound.
180    ///
181    /// # Examples
182    ///
183    /// ```
184    /// use tea_core::prelude::*;
185    /// use tea_map::MapValidBasic;
186    ///
187    /// let v = vec![Some(1), None, Some(3), Some(5), Some(7)];
188    /// let result: Vec<_> = v.titer().vclip(Some(2), Some(6)).collect();
189    /// assert_eq!(result, vec![Some(2), None, Some(3), Some(5), Some(6)]);
190    /// ```
191    #[inline]
192    fn vclip<'a>(self, lower: T, upper: T) -> Box<dyn TrustedLen<Item = T> + 'a>
193    where
194        T::Inner: PartialOrd,
195        T: 'a,
196        Self: 'a,
197    {
198        let lower_flag = lower.not_none();
199        let upper_flag = upper.not_none();
200        match (lower_flag, upper_flag) {
201            (true, true) => {
202                let (lower_inner, upper_inner) = (lower.clone().unwrap(), upper.clone().unwrap());
203                Box::new(self.map(move |v| {
204                    if v.not_none() {
205                        let v_inner = v.clone().unwrap();
206                        if v_inner < lower_inner {
207                            lower.clone()
208                        } else if v_inner > upper_inner {
209                            upper.clone()
210                        } else {
211                            v
212                        }
213                    } else {
214                        v
215                    }
216                }))
217            },
218            (true, false) => {
219                let lower_inner = lower.clone().unwrap();
220                Box::new(self.map(move |v: T| {
221                    if v.not_none() && (v.clone().unwrap() < lower_inner) {
222                        lower.clone()
223                    } else {
224                        v
225                    }
226                }))
227            },
228            (false, true) => {
229                let upper_inner = upper.clone().unwrap();
230                Box::new(self.map(move |v: T| {
231                    if v.not_none() && (v.clone().unwrap() > upper_inner) {
232                        upper.clone()
233                    } else {
234                        v
235                    }
236                }))
237            },
238            (false, false) => Box::new(self),
239        }
240    }
241
242    /// Fill values where the mask is true, ignoring None values.
243    ///
244    /// # Arguments
245    ///
246    /// * `mask_func` - A function that returns true for values that should be filled.
247    /// * `value` - The value to fill with.
248    ///
249    /// # Examples
250    ///
251    /// ```
252    /// use tea_core::prelude::*;
253    /// use tea_map::MapValidBasic;
254    ///
255    /// let v = vec![Some(1), None, Some(3), Some(4), Some(5)];
256    /// let result: Vec<_> = v.titer().fill_mask(|x| x.map_or(false, |v| v % 2 == 0), Some(0)).collect();
257    /// assert_eq!(result, vec![Some(1), None, Some(3), Some(0), Some(5)]);
258    /// ```
259    #[inline]
260    fn fill_mask<F: Fn(&T) -> bool>(self, mask_func: F, value: T) -> impl TrustedLen<Item = T> {
261        self.map(move |v| if mask_func(&v) { value.clone() } else { v })
262    }
263
264    /// Fill None values with a specified value.
265    ///
266    /// This method is similar to `fill()`, but it can handle None values.
267    ///
268    /// # Arguments
269    ///
270    /// * `value` - The value to fill None values with.
271    ///
272    /// # Examples
273    ///
274    /// ```
275    /// use tea_core::prelude::*;
276    /// use tea_map::MapValidBasic;
277    ///
278    /// let v = vec![Some(1), None, Some(3), None, Some(5)];
279    /// let result: Vec<_> = v.titer().fill(Some(0)).collect();
280    /// assert_eq!(result, vec![Some(1), Some(0), Some(3), Some(0), Some(5)]);
281    /// ```
282    #[inline]
283    fn fill(self, value: T) -> impl TrustedLen<Item = T> {
284        self.fill_mask(T::is_none, value)
285    }
286
287    /// Shift the elements in the iterator, ignoring None values.
288    ///
289    /// This method is similar to [`shift()`](crate::MapBasic::shift), but it can handle None values.
290    ///
291    /// # Arguments
292    ///
293    /// * `n` - The number of positions to shift. Positive values shift right, negative values shift left.
294    /// * `value` - An optional value to fill the vacated positions.
295    ///
296    /// # Examples
297    ///
298    /// ```
299    /// use tea_core::prelude::*;
300    /// use tea_map::MapValidBasic;
301    ///
302    /// let v = vec![Some(1), None, Some(3), Some(4), Some(5)];
303    /// let result: Vec<_> = v.titer().vshift(2, Some(Some(0))).collect();
304    /// assert_eq!(result, vec![Some(0), Some(0), Some(1), None, Some(3)]);
305    /// ```
306    ///
307    /// See also: [`shift()`](crate::MapBasic::shift)
308    fn vshift<'a>(self, n: i32, value: Option<T>) -> Box<dyn TrustedLen<Item = T> + 'a>
309    where
310        T: Clone + 'a,
311        Self: 'a,
312    {
313        let len = self.len();
314        let n_abs = n.unsigned_abs() as usize;
315        let value = value.unwrap_or_else(|| T::none());
316        if len <= n_abs {
317            return Box::new(std::iter::repeat_n(value, len));
318        }
319        match n {
320            n if n > 0 => Box::new(
321                std::iter::repeat_n(value, n_abs)
322                    .chain(self.take(len - n_abs))
323                    .to_trust(len),
324            ),
325            n if n < 0 => Box::new(
326                self.skip(n_abs)
327                    .chain(std::iter::repeat_n(value, n_abs))
328                    .to_trust(len),
329            ),
330            _ => Box::new(self),
331        }
332    }
333
334    /// Drop None values from the iterator.
335    ///
336    /// # Examples
337    ///
338    /// ```
339    /// use tea_core::prelude::*;
340    /// use tea_map::MapValidBasic;
341    ///
342    /// let v = vec![Some(1), None, Some(3), None, Some(5)];
343    /// let result: Vec<_> = v.titer().drop_none().collect();
344    /// assert_eq!(result, vec![Some(1), Some(3), Some(5)]);
345    /// ```
346    #[inline]
347    fn drop_none(self) -> impl Iterator<Item = T> {
348        self.filter(T::not_none)
349    }
350    /// Categorize values into bins.
351    ///
352    /// This function categorizes the values in the iterator into bins defined by the `bins` parameter.
353    /// It assigns labels to each bin as specified by the `labels` parameter.
354    ///
355    /// # Arguments
356    ///
357    /// * `bins` - A slice of bin edges.
358    /// * `labels` - A slice of labels for each bin.
359    /// * `right` - If true, intervals are closed on the right. If false, intervals are closed on the left.
360    /// * `add_bounds` - If true, adds -∞ and +∞ as the first and last bin edges respectively.
361    ///
362    /// # Returns
363    ///
364    /// Returns a `TResult` containing a boxed `TrustedLen` iterator of `TResult<T2>` items.
365    ///
366    /// # Errors
367    ///
368    /// Returns an error if:
369    /// - The number of labels doesn't match the number of bins (accounting for `add_bounds`).
370    /// - A value falls outside the bin ranges.
371    ///
372    /// # Examples
373    ///
374    /// ```
375    /// use tea_core::prelude::*;
376    /// use tea_map::MapValidBasic;
377    ///
378    /// let v = vec![1, 3, 5, 7, 9];
379    /// let bins = vec![4, 8];
380    /// let labels = vec!["low", "medium", "high"];
381    /// let result: Vec<_> = v.titer().vcut(&bins, &labels, true, true).unwrap().collect::<Result<Vec<_>, _>>().unwrap();
382    /// assert_eq!(result, vec!["low", "low", "medium", "medium", "high"]);
383    /// ```
384    fn vcut<'a, V2, V3, T2>(
385        self,
386        bins: &'a V2,
387        labels: &'a V3,
388        right: bool,
389        add_bounds: bool,
390    ) -> TResult<Box<dyn TrustedLen<Item = TResult<T2>> + 'a>>
391    where
392        Self: 'a,
393        T::Inner: Number + Debug,
394        (T::Inner, T::Inner): itertools::traits::HomogeneousTuple<Item = T::Inner>,
395        T2: IsNone + 'a,
396        V2: Vec1View<T>,
397        V3: Vec1View<T2>,
398    {
399        use itertools::Itertools;
400        let bins: Vec<T::Inner> = if add_bounds {
401            if labels.len() != bins.len() + 1 {
402                tbail!(
403                    func = cut,
404                    "Number of labels must be one more than the number of bin edges, label: {}, bins: {}",
405                    labels.len(),
406                    bins.len()
407                )
408            }
409            vec![T::Inner::min_()]
410                .into_iter()
411                .chain(bins.titer().map(IsNone::unwrap))
412                .chain(vec![T::Inner::max_()])
413                .collect()
414        } else {
415            if labels.len() + 1 != bins.len() {
416                tbail!(
417                    func = cut,
418                    "Number of labels must be one fewer than the number of bin edges, label: {}, bins: {}",
419                    labels.len(),
420                    bins.len()
421                )
422            }
423            bins.titer().map(IsNone::unwrap).collect_trusted_vec1()
424        };
425        if right {
426            Ok(Box::new(self.map(move |value| {
427                if value.is_none() {
428                    Ok(T2::none())
429                } else {
430                    let value = value.unwrap();
431                    let mut out = None;
432                    for (bound, label) in bins
433                        .titer()
434                        .tuple_windows::<(T::Inner, T::Inner)>()
435                        .zip(labels.titer())
436                    {
437                        if (bound.0 < value) && (value <= bound.1) {
438                            out = Some(label.clone());
439                            break;
440                        }
441                    }
442                    out.ok_or_else(|| terr!(func = cut, "value: {:?} not in bins", value))
443                }
444            })))
445        } else {
446            Ok(Box::new(self.map(move |value| {
447                if value.is_none() {
448                    Ok(T2::none())
449                } else {
450                    let value = value.unwrap();
451                    let mut out = None;
452                    for (bound, label) in bins
453                        .titer()
454                        .tuple_windows::<(T::Inner, T::Inner)>()
455                        .zip(labels.titer())
456                    {
457                        if (bound.0 <= value) && (value < bound.1) {
458                            out = Some(label.clone());
459                            break;
460                        }
461                    }
462                    out.ok_or_else(|| terr!(func = cut, "value: {:?} not in bins", value))
463                }
464            })))
465        }
466    }
467
468    /// Returns indices of unique elements in a sorted iterator, keeping either the first or last occurrence.
469    ///
470    /// # Arguments
471    ///
472    /// * `keep` - Specifies whether to keep the first or last occurrence of each unique element.
473    ///
474    /// # Returns
475    ///
476    /// A boxed iterator yielding indices of unique elements.
477    ///
478    /// # Examples
479    ///
480    /// ```
481    /// use tea_core::prelude::*;
482    /// use tea_map::{MapValidBasic, Keep};
483    ///
484    /// let v = vec![Some(1), Some(1), Some(2), Some(2), Some(3)];
485    /// let result: Vec<_> = v.titer().vsorted_unique_idx(Keep::First).collect();
486    /// assert_eq!(result, vec![0, 2, 4]);
487    ///
488    /// let result: Vec<_> = v.titer().vsorted_unique_idx(Keep::Last).collect();
489    /// assert_eq!(result, vec![1, 3, 4]);
490    /// ```
491    fn vsorted_unique_idx<'a>(self, keep: Keep) -> Box<dyn Iterator<Item = usize> + 'a>
492    where
493        T::Inner: PartialEq + 'a + std::fmt::Debug,
494        Self: 'a,
495    {
496        match keep {
497            Keep::First => {
498                let mut last_value = None;
499                let out = self.into_iter().enumerate().filter_map(move |(i, v)| {
500                    if v.not_none() {
501                        let v = v.unwrap();
502                        if last_value == Some(v.clone()) {
503                            None
504                        } else {
505                            last_value = Some(v);
506                            Some(i)
507                        }
508                    } else {
509                        None
510                    }
511                });
512                Box::new(out)
513            },
514            Keep::Last => {
515                let mut iter = self.into_iter();
516                let first_element = iter.next();
517                let mut last_value = if let Some(v) = first_element {
518                    if v.not_none() { Some(v.unwrap()) } else { None }
519                } else {
520                    None
521                };
522                let out = iter
523                    .map(|v| v.to_opt())
524                    .chain(std::iter::once(None))
525                    .enumerate()
526                    .filter_map(move |(i, v)| {
527                        if v.not_none() {
528                            let v = v.unwrap();
529                            if last_value == Some(v.clone()) {
530                                None
531                            } else {
532                                last_value = Some(v);
533                                Some(i)
534                            }
535                        } else {
536                            let out = if last_value.is_some() { Some(i) } else { None };
537                            last_value = None;
538                            out
539                        }
540                    });
541                Box::new(out)
542            },
543        }
544    }
545
546    /// Returns an iterator over unique elements in a sorted iterator.
547    ///
548    /// This method removes consecutive duplicate elements from the iterator.
549    ///
550    /// # Returns
551    ///
552    /// An iterator yielding unique elements.
553    ///
554    /// # Examples
555    ///
556    /// ```
557    /// use tea_core::prelude::*;
558    /// use tea_map::MapValidBasic;
559    ///
560    /// let v = vec![Some(1), Some(1), Some(2), Some(2), Some(3)];
561    /// let result: Vec<_> = v.titer().vsorted_unique().collect();
562    /// assert_eq!(result, vec![Some(1), Some(2), Some(3)]);
563    /// ```
564    #[allow(clippy::unnecessary_filter_map)]
565    fn vsorted_unique<'a>(self) -> impl Iterator<Item = T> + 'a
566    where
567        T::Inner: PartialEq + 'a,
568        Self: 'a,
569    {
570        let mut value: Option<T::Inner> = None;
571        self.into_iter().filter_map(move |v| {
572            if v.not_none() {
573                let v = v.unwrap();
574                if let Some(last_v) = value.as_ref() {
575                    if v != last_v.clone() {
576                        value = Some(v.clone());
577                        Some(T::from_inner(v))
578                    } else {
579                        None
580                    }
581                } else {
582                    value = Some(v.clone());
583                    Some(T::from_inner(v))
584                }
585            } else {
586                None
587            }
588        })
589    }
590}
591
592impl<T: IsNone, I: TrustedLen<Item = T>> MapValidBasic<T> for I {}
593
594#[cfg(test)]
595mod test {
596    use tea_core::testing::assert_vec1d_equal_numeric;
597
598    use super::*;
599
600    #[test]
601    fn test_clip() {
602        let v = vec![1, 2, 3, 4, 5];
603        let res: Vec<_> = v.titer().vclip(2, 4).collect_trusted_vec1();
604        assert_eq!(res, vec![2, 2, 3, 4, 4]);
605        let v = vec![1., 2., 3., 4., 5.];
606        let res: Vec<_> = v.titer().vclip(2., f64::NAN).collect_trusted_vec1();
607        assert_eq!(&res, &vec![2., 2., 3., 4., 5.]);
608        let res: Vec<_> = v.titer().vclip(f64::NAN, 4.).collect_trusted_vec1();
609        assert_eq!(&res, &vec![1., 2., 3., 4., 4.]);
610        let res: Vec<_> = v.titer().vclip(f64::NAN, f64::NAN).collect_trusted_vec1();
611        assert_eq!(&res, &vec![1., 2., 3., 4., 5.]);
612    }
613
614    #[test]
615    fn test_fill() {
616        let v = vec![f64::NAN, 1., 2., f64::NAN, 3., f64::NAN];
617        let res: Vec<_> = v.titer().ffill(None).collect();
618        assert_vec1d_equal_numeric(&res, &vec![f64::NAN, 1., 2., 2., 3., 3.], None);
619        let res: Vec<_> = v.titer().ffill(Some(0.)).collect();
620        assert_vec1d_equal_numeric(&res, &vec![0., 1., 2., 2., 3., 3.], None);
621        let res: Vec<_> = v.titer().bfill(None).collect();
622        assert_vec1d_equal_numeric(&res, &vec![1., 1., 2., 3., 3., f64::NAN], None);
623        let res: Vec<_> = v.titer().bfill(Some(0.)).collect();
624        assert_vec1d_equal_numeric(&res, &vec![1., 1., 2., 3., 3., 0.], None);
625        let res: Vec<_> = v.titer().fill(0.).collect();
626        assert_vec1d_equal_numeric(&res, &vec![0., 1., 2., 0., 3., 0.], None);
627    }
628
629    #[test]
630    fn test_vcut() -> TResult<()> {
631        let v = vec![1, 3, 5, 1, 5, 6, 7, 32, 1];
632        let bins = vec![2, 5, 8];
633        let labels = vec![1, 2, 3, 4];
634        let res1: Vec<_> = v
635            .titer()
636            .vcut(&bins, &labels, true, true)?
637            .try_collect_vec1()?;
638        assert_eq!(res1, vec![1, 2, 2, 1, 2, 3, 3, 4, 1]);
639        let res2: Vec<_> = v
640            .titer()
641            .vcut(&bins, &labels, false, true)?
642            .try_collect_trusted_vec1()?;
643        // bin label mismatch
644        assert_eq!(res2, vec![1, 2, 3, 1, 3, 3, 3, 4, 1]);
645        assert!(v.titer().vcut(&[3], &labels, true, true).is_err());
646        // value not in bins
647        let res: TResult<Vec<_>> = v
648            .titer()
649            .vcut(&[1, 2, 5, 8, 20], &labels, true, false)?
650            .try_collect_vec1();
651        assert!(res.is_err());
652        Ok(())
653    }
654
655    #[test]
656    fn test_sorted_unique() {
657        let v = vec![1, 1, 2, 2, 2, 3, 4, 4, 4, 4, 5, 5, 6];
658        let res: Vec<_> = v.titer().vsorted_unique_idx(Keep::First).collect();
659        assert_eq!(res, vec![0, 2, 5, 6, 10, 12]);
660        let res: Vec<_> = v.titer().vsorted_unique().collect();
661        assert_eq!(res, vec![1, 2, 3, 4, 5, 6]);
662        let res: Vec<_> = v.titer().vsorted_unique_idx(Keep::Last).collect();
663        assert_eq!(res, vec![1, 4, 5, 9, 11, 12]);
664        let v = vec![6, 6, 5, 5, 5, 4, 3, 3, 3, 3, 2, 2, 1];
665        let v2: Vec<_> = v.to_opt_iter().chain(None).collect();
666        let res: Vec<_> = v2.titer().vsorted_unique_idx(Keep::First).collect();
667        assert_eq!(res, vec![0, 2, 5, 6, 10, 12]);
668        let res: Vec<_> = v2.titer().vsorted_unique_idx(Keep::Last).collect();
669        assert_eq!(res, vec![1, 4, 5, 9, 11, 12]);
670        let res: Vec<_> = v2.titer().vsorted_unique().collect();
671        assert_eq!(
672            res,
673            vec![Some(6), Some(5), Some(4), Some(3), Some(2), Some(1)]
674        );
675        let v3: Vec<_> = v
676            .iter_cast::<f64>()
677            .chain(std::iter::once(f64::NAN))
678            .collect();
679        let res: Vec<_> = v3.titer().vsorted_unique_idx(Keep::First).collect();
680        assert_eq!(res, vec![0, 2, 5, 6, 10, 12]);
681        let res: Vec<_> = v3.titer().vsorted_unique_idx(Keep::Last).collect();
682        assert_eq!(res, vec![1, 4, 5, 9, 11, 12]);
683        let res: Vec<_> = v3.titer().vsorted_unique().collect();
684        assert_eq!(res, vec![6., 5., 4., 3., 2., 1.]);
685        let v4 = vec![f64::NAN, f64::NAN, 4., 4., 2., 0., 0.];
686        let res: Vec<_> = v4.titer().vsorted_unique().collect();
687        assert_eq!(res, vec![4., 2., 0.]);
688        let res: Vec<_> = v4.titer().vsorted_unique_idx(Keep::First).collect();
689        assert_eq!(res, vec![2, 4, 5]);
690    }
691}