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}