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(value).take(len));
318 }
319 match n {
320 n if n > 0 => Box::new(
321 std::iter::repeat(value)
322 .take(n_abs)
323 .chain(self.take(len - n_abs))
324 .to_trust(len),
325 ),
326 n if n < 0 => Box::new(
327 self.skip(n_abs)
328 .chain(std::iter::repeat(value).take(n_abs))
329 .to_trust(len),
330 ),
331 _ => Box::new(self),
332 }
333 }
334
335 /// Drop None values from the iterator.
336 ///
337 /// # Examples
338 ///
339 /// ```
340 /// use tea_core::prelude::*;
341 /// use tea_map::MapValidBasic;
342 ///
343 /// let v = vec![Some(1), None, Some(3), None, Some(5)];
344 /// let result: Vec<_> = v.titer().drop_none().collect();
345 /// assert_eq!(result, vec![Some(1), Some(3), Some(5)]);
346 /// ```
347 #[inline]
348 fn drop_none(self) -> impl Iterator<Item = T> {
349 self.filter(T::not_none)
350 }
351 /// Categorize values into bins.
352 ///
353 /// This function categorizes the values in the iterator into bins defined by the `bins` parameter.
354 /// It assigns labels to each bin as specified by the `labels` parameter.
355 ///
356 /// # Arguments
357 ///
358 /// * `bins` - A slice of bin edges.
359 /// * `labels` - A slice of labels for each bin.
360 /// * `right` - If true, intervals are closed on the right. If false, intervals are closed on the left.
361 /// * `add_bounds` - If true, adds -∞ and +∞ as the first and last bin edges respectively.
362 ///
363 /// # Returns
364 ///
365 /// Returns a `TResult` containing a boxed `TrustedLen` iterator of `TResult<T2>` items.
366 ///
367 /// # Errors
368 ///
369 /// Returns an error if:
370 /// - The number of labels doesn't match the number of bins (accounting for `add_bounds`).
371 /// - A value falls outside the bin ranges.
372 ///
373 /// # Examples
374 ///
375 /// ```
376 /// use tea_core::prelude::*;
377 /// use tea_map::MapValidBasic;
378 ///
379 /// let v = vec![1, 3, 5, 7, 9];
380 /// let bins = vec![4, 8];
381 /// let labels = vec!["low", "medium", "high"];
382 /// let result: Vec<_> = v.titer().vcut(&bins, &labels, true, true).unwrap().collect::<Result<Vec<_>, _>>().unwrap();
383 /// assert_eq!(result, vec!["low", "low", "medium", "medium", "high"]);
384 /// ```
385 fn vcut<'a, V2, V3, T2>(
386 self,
387 bins: &'a V2,
388 labels: &'a V3,
389 right: bool,
390 add_bounds: bool,
391 ) -> TResult<Box<dyn TrustedLen<Item = TResult<T2>> + 'a>>
392 where
393 Self: 'a,
394 T::Inner: Number + Debug,
395 (T::Inner, T::Inner): itertools::traits::HomogeneousTuple<Item = T::Inner>,
396 T2: IsNone + 'a,
397 V2: Vec1View<T>,
398 V3: Vec1View<T2>,
399 {
400 use itertools::Itertools;
401 let bins: Vec<T::Inner> = if add_bounds {
402 if labels.len() != bins.len() + 1 {
403 tbail!(func=cut, "Number of labels must be one more than the number of bin edges, label: {}, bins: {}", labels.len(), bins.len())
404 }
405 vec![T::Inner::min_()]
406 .into_iter()
407 .chain(bins.titer().map(IsNone::unwrap))
408 .chain(vec![T::Inner::max_()])
409 .collect()
410 } else {
411 if labels.len() + 1 != bins.len() {
412 tbail!(func=cut, "Number of labels must be one fewer than the number of bin edges, label: {}, bins: {}", labels.len(), bins.len())
413 }
414 bins.titer().map(IsNone::unwrap).collect_trusted_vec1()
415 };
416 if right {
417 Ok(Box::new(self.map(move |value| {
418 if value.is_none() {
419 Ok(T2::none())
420 } else {
421 let value = value.unwrap();
422 let mut out = None;
423 for (bound, label) in bins
424 .titer()
425 .tuple_windows::<(T::Inner, T::Inner)>()
426 .zip(labels.titer())
427 {
428 if (bound.0 < value) && (value <= bound.1) {
429 out = Some(label.clone());
430 break;
431 }
432 }
433 out.ok_or_else(|| terr!(func = cut, "value: {:?} not in bins", value))
434 }
435 })))
436 } else {
437 Ok(Box::new(self.map(move |value| {
438 if value.is_none() {
439 Ok(T2::none())
440 } else {
441 let value = value.unwrap();
442 let mut out = None;
443 for (bound, label) in bins
444 .titer()
445 .tuple_windows::<(T::Inner, T::Inner)>()
446 .zip(labels.titer())
447 {
448 if (bound.0 <= value) && (value < bound.1) {
449 out = Some(label.clone());
450 break;
451 }
452 }
453 out.ok_or_else(|| terr!(func = cut, "value: {:?} not in bins", value))
454 }
455 })))
456 }
457 }
458
459 /// Returns indices of unique elements in a sorted iterator, keeping either the first or last occurrence.
460 ///
461 /// # Arguments
462 ///
463 /// * `keep` - Specifies whether to keep the first or last occurrence of each unique element.
464 ///
465 /// # Returns
466 ///
467 /// A boxed iterator yielding indices of unique elements.
468 ///
469 /// # Examples
470 ///
471 /// ```
472 /// use tea_core::prelude::*;
473 /// use tea_map::{MapValidBasic, Keep};
474 ///
475 /// let v = vec![Some(1), Some(1), Some(2), Some(2), Some(3)];
476 /// let result: Vec<_> = v.titer().vsorted_unique_idx(Keep::First).collect();
477 /// assert_eq!(result, vec![0, 2, 4]);
478 ///
479 /// let result: Vec<_> = v.titer().vsorted_unique_idx(Keep::Last).collect();
480 /// assert_eq!(result, vec![1, 3, 4]);
481 /// ```
482 fn vsorted_unique_idx<'a>(self, keep: Keep) -> Box<dyn Iterator<Item = usize> + 'a>
483 where
484 T::Inner: PartialEq + 'a + std::fmt::Debug,
485 Self: 'a,
486 {
487 match keep {
488 Keep::First => {
489 let mut last_value = None;
490 let out = self.into_iter().enumerate().filter_map(move |(i, v)| {
491 if v.not_none() {
492 let v = v.unwrap();
493 if last_value == Some(v.clone()) {
494 None
495 } else {
496 last_value = Some(v);
497 Some(i)
498 }
499 } else {
500 None
501 }
502 });
503 Box::new(out)
504 },
505 Keep::Last => {
506 let mut iter = self.into_iter();
507 let first_element = iter.next();
508 let mut last_value = if let Some(v) = first_element {
509 if v.not_none() {
510 Some(v.unwrap())
511 } else {
512 None
513 }
514 } else {
515 None
516 };
517 let out = iter
518 .map(|v| v.to_opt())
519 .chain(std::iter::once(None))
520 .enumerate()
521 .filter_map(move |(i, v)| {
522 if v.not_none() {
523 let v = v.unwrap();
524 if last_value == Some(v.clone()) {
525 None
526 } else {
527 last_value = Some(v);
528 Some(i)
529 }
530 } else {
531 let out = if last_value.is_some() { Some(i) } else { None };
532 last_value = None;
533 out
534 }
535 });
536 Box::new(out)
537 },
538 }
539 }
540
541 /// Returns an iterator over unique elements in a sorted iterator.
542 ///
543 /// This method removes consecutive duplicate elements from the iterator.
544 ///
545 /// # Returns
546 ///
547 /// An iterator yielding unique elements.
548 ///
549 /// # Examples
550 ///
551 /// ```
552 /// use tea_core::prelude::*;
553 /// use tea_map::MapValidBasic;
554 ///
555 /// let v = vec![Some(1), Some(1), Some(2), Some(2), Some(3)];
556 /// let result: Vec<_> = v.titer().vsorted_unique().collect();
557 /// assert_eq!(result, vec![Some(1), Some(2), Some(3)]);
558 /// ```
559 #[allow(clippy::unnecessary_filter_map)]
560 fn vsorted_unique<'a>(self) -> impl Iterator<Item = T> + 'a
561 where
562 T::Inner: PartialEq + 'a,
563 Self: 'a,
564 {
565 let mut value: Option<T::Inner> = None;
566 self.into_iter().filter_map(move |v| {
567 if v.not_none() {
568 let v = v.unwrap();
569 if let Some(last_v) = value.as_ref() {
570 if v != last_v.clone() {
571 value = Some(v.clone());
572 Some(T::from_inner(v))
573 } else {
574 None
575 }
576 } else {
577 value = Some(v.clone());
578 Some(T::from_inner(v))
579 }
580 } else {
581 None
582 }
583 })
584 }
585}
586
587impl<T: IsNone, I: TrustedLen<Item = T>> MapValidBasic<T> for I {}
588
589#[cfg(test)]
590mod test {
591 use tea_core::testing::assert_vec1d_equal_numeric;
592
593 use super::*;
594
595 #[test]
596 fn test_clip() {
597 let v = vec![1, 2, 3, 4, 5];
598 let res: Vec<_> = v.titer().vclip(2, 4).collect_trusted_vec1();
599 assert_eq!(res, vec![2, 2, 3, 4, 4]);
600 let v = vec![1., 2., 3., 4., 5.];
601 let res: Vec<_> = v.titer().vclip(2., f64::NAN).collect_trusted_vec1();
602 assert_eq!(&res, &vec![2., 2., 3., 4., 5.]);
603 let res: Vec<_> = v.titer().vclip(f64::NAN, 4.).collect_trusted_vec1();
604 assert_eq!(&res, &vec![1., 2., 3., 4., 4.]);
605 let res: Vec<_> = v.titer().vclip(f64::NAN, f64::NAN).collect_trusted_vec1();
606 assert_eq!(&res, &vec![1., 2., 3., 4., 5.]);
607 }
608
609 #[test]
610 fn test_fill() {
611 let v = vec![f64::NAN, 1., 2., f64::NAN, 3., f64::NAN];
612 let res: Vec<_> = v.titer().ffill(None).collect();
613 assert_vec1d_equal_numeric(&res, &vec![f64::NAN, 1., 2., 2., 3., 3.], None);
614 let res: Vec<_> = v.titer().ffill(Some(0.)).collect();
615 assert_vec1d_equal_numeric(&res, &vec![0., 1., 2., 2., 3., 3.], None);
616 let res: Vec<_> = v.titer().bfill(None).collect();
617 assert_vec1d_equal_numeric(&res, &vec![1., 1., 2., 3., 3., f64::NAN], None);
618 let res: Vec<_> = v.titer().bfill(Some(0.)).collect();
619 assert_vec1d_equal_numeric(&res, &vec![1., 1., 2., 3., 3., 0.], None);
620 let res: Vec<_> = v.titer().fill(0.).collect();
621 assert_vec1d_equal_numeric(&res, &vec![0., 1., 2., 0., 3., 0.], None);
622 }
623
624 #[test]
625 fn test_vcut() -> TResult<()> {
626 let v = vec![1, 3, 5, 1, 5, 6, 7, 32, 1];
627 let bins = vec![2, 5, 8];
628 let labels = vec![1, 2, 3, 4];
629 let res1: Vec<_> = v
630 .titer()
631 .vcut(&bins, &labels, true, true)?
632 .try_collect_vec1()?;
633 assert_eq!(res1, vec![1, 2, 2, 1, 2, 3, 3, 4, 1]);
634 let res2: Vec<_> = v
635 .titer()
636 .vcut(&bins, &labels, false, true)?
637 .try_collect_trusted_vec1()?;
638 // bin label mismatch
639 assert_eq!(res2, vec![1, 2, 3, 1, 3, 3, 3, 4, 1]);
640 assert!(v.titer().vcut(&[3], &labels, true, true).is_err());
641 // value not in bins
642 let res: TResult<Vec<_>> = v
643 .titer()
644 .vcut(&[1, 2, 5, 8, 20], &labels, true, false)?
645 .try_collect_vec1();
646 assert!(res.is_err());
647 Ok(())
648 }
649
650 #[test]
651 fn test_sorted_unique() {
652 let v = vec![1, 1, 2, 2, 2, 3, 4, 4, 4, 4, 5, 5, 6];
653 let res: Vec<_> = v.titer().vsorted_unique_idx(Keep::First).collect();
654 assert_eq!(res, vec![0, 2, 5, 6, 10, 12]);
655 let res: Vec<_> = v.titer().vsorted_unique().collect();
656 assert_eq!(res, vec![1, 2, 3, 4, 5, 6]);
657 let res: Vec<_> = v.titer().vsorted_unique_idx(Keep::Last).collect();
658 assert_eq!(res, vec![1, 4, 5, 9, 11, 12]);
659 let v = vec![6, 6, 5, 5, 5, 4, 3, 3, 3, 3, 2, 2, 1];
660 let v2: Vec<_> = v.to_opt_iter().chain(None).collect();
661 let res: Vec<_> = v2.titer().vsorted_unique_idx(Keep::First).collect();
662 assert_eq!(res, vec![0, 2, 5, 6, 10, 12]);
663 let res: Vec<_> = v2.titer().vsorted_unique_idx(Keep::Last).collect();
664 assert_eq!(res, vec![1, 4, 5, 9, 11, 12]);
665 let res: Vec<_> = v2.titer().vsorted_unique().collect();
666 assert_eq!(
667 res,
668 vec![Some(6), Some(5), Some(4), Some(3), Some(2), Some(1)]
669 );
670 let v3: Vec<_> = v
671 .iter_cast::<f64>()
672 .chain(std::iter::once(f64::NAN))
673 .collect();
674 let res: Vec<_> = v3.titer().vsorted_unique_idx(Keep::First).collect();
675 assert_eq!(res, vec![0, 2, 5, 6, 10, 12]);
676 let res: Vec<_> = v3.titer().vsorted_unique_idx(Keep::Last).collect();
677 assert_eq!(res, vec![1, 4, 5, 9, 11, 12]);
678 let res: Vec<_> = v3.titer().vsorted_unique().collect();
679 assert_eq!(res, vec![6., 5., 4., 3., 2., 1.]);
680 let v4 = vec![f64::NAN, f64::NAN, 4., 4., 2., 0., 0.];
681 let res: Vec<_> = v4.titer().vsorted_unique().collect();
682 assert_eq!(res, vec![4., 2., 0.]);
683 let res: Vec<_> = v4.titer().vsorted_unique_idx(Keep::First).collect();
684 assert_eq!(res, vec![2, 4, 5]);
685 }
686}