1use std::{
2 cmp::Ordering,
3 marker::PhantomData,
4 ops::{Bound, RangeBounds},
5};
6
7#[cfg(feature = "serde")]
8use serde::{Deserialize, Serialize};
9
10use crate::{coordinate::CoordinateLike, IntensityMeasurement};
11
12use super::util::{NonNan, EMPTY_Y, EMPTY_Z};
13use super::{
14 traits::{CoArrayOps, FeatureLike, SplittableFeatureLike, TimeInterval},
15 FeatureLikeMut, TimeArray,
16};
17
18#[derive(Debug, Default, Clone)]
21#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
22pub struct SimpleFeature<X, Y> {
23 pub label: f64,
25 y: Vec<f64>,
26 z: Vec<f32>,
27 _x: PhantomData<X>,
28 _y: PhantomData<Y>,
29}
30
31impl<X, Y> CoArrayOps for SimpleFeature<X, Y> {}
32
33impl<X, Y> SimpleFeature<X, Y> {
34 pub fn new(label: f64, y: Vec<f64>, z: Vec<f32>) -> Self {
35 Self {
36 label,
37 y,
38 z,
39 _x: PhantomData,
40 _y: PhantomData,
41 }
42 }
43
44 pub fn as_view(&self) -> SimpleFeatureView<'_, X, Y> {
45 SimpleFeatureView::new(self.label, &self.y, &self.z)
46 }
47
48 pub fn into_inner(self) -> (f64, Vec<f64>, Vec<f32>) {
49 (self.label, self.y, self.z)
50 }
51
52 pub fn empty(label: f64) -> Self {
54 Self::with_capacity(0, label)
55 }
56
57 pub fn with_capacity(capacity: usize, label: f64) -> Self {
59 Self {
60 label,
61 y: Vec::with_capacity(capacity),
62 z: Vec::with_capacity(capacity),
63 _x: PhantomData,
64 _y: PhantomData,
65 }
66 }
67
68 fn sort_by_y(&mut self) {
69 let n = self.len();
70 let mut mask: Vec<_> = (0..n).collect();
71 mask.sort_by_key(|i| NonNan::new(self.y[*i]));
72
73 const TOMBSTONE: usize = usize::MAX;
74
75 for idx in 0..n {
76 if mask[idx] != TOMBSTONE {
77 let mut current_idx = idx;
78 loop {
79 let next_idx = mask[current_idx];
80 mask[current_idx] = TOMBSTONE;
81 if mask[next_idx] == TOMBSTONE {
82 break;
83 }
84 self.y.swap(current_idx, next_idx);
85 self.z.swap(current_idx, next_idx);
86 current_idx = next_idx;
87 }
88 }
89 }
90 }
91
92 pub fn push<T: CoordinateLike<X> + IntensityMeasurement>(&mut self, pt: &T, time: f64) {
94 self.push_raw(pt.coordinate(), time, pt.intensity())
95 }
96
97 pub fn push_raw(&mut self, _x: f64, y: f64, z: f32) {
99 let needs_sort = self.len() > 0 && self.y.last().copied().unwrap() > y;
100 if !self.is_empty() && y == *self.y.last().unwrap() {
101 *self.z.last_mut().unwrap() += z;
102 } else {
103 self.y.push(y);
104 self.z.push(z);
105 }
106 if needs_sort {
107 self.sort_by_y();
108 }
109 }
110
111 fn apex_y(&self) -> Option<f64> {
112 self.apex_of(&self.y, &self.z)
113 }
114
115 fn find_y(&self, y: f64) -> (Option<usize>, f64) {
116 if self.is_empty() {
117 return (None, y);
118 }
119 match self.y.binary_search_by(|yi| y.total_cmp(yi).reverse()) {
120 Ok(i) => {
121 let low = i.saturating_sub(3);
122 (low..(low + 6).min(self.len()))
123 .map(|i| (Some(i), (self.y[i] - y).abs()))
124 .min_by(|(_, e), (_, d)| e.total_cmp(d))
125 .unwrap()
126 }
127 Err(i) => {
128 let low = i.saturating_sub(3);
129 (low..(low + 6).min(self.len()))
130 .map(|i| (Some(i), (self.y[i] - y).abs()))
131 .min_by(|(_, e), (_, d)| e.total_cmp(d))
132 .unwrap()
133 }
134 }
135 }
136}
137
138impl<X, Y> TimeArray<Y> for SimpleFeature<X, Y> {
139 fn time_view(&self) -> &[f64] {
140 &self.y
141 }
142
143 fn intensity_view(&self) -> &[f32] {
144 &self.z
145 }
146}
147
148impl<X, Y> PartialEq for SimpleFeature<X, Y> {
149 fn eq(&self, other: &Self) -> bool {
150 self.label == other.label
151 && self.y == other.y
152 && self.z == other.z
153 && self._x == other._x
154 && self._y == other._y
155 }
156}
157
158impl<X, Y> PartialOrd for SimpleFeature<X, Y> {
159 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
160 match self.label.partial_cmp(&other.label) {
161 Some(core::cmp::Ordering::Equal) => {}
162 ord => return ord,
163 }
164 match self.start_time().partial_cmp(&other.start_time()) {
165 Some(core::cmp::Ordering::Equal) => {}
166 ord => return ord,
167 }
168 self.z.partial_cmp(&other.z)
169 }
170}
171
172impl<X, Y> CoordinateLike<X> for SimpleFeature<X, Y> {
173 fn coordinate(&self) -> f64 {
174 self.label
175 }
176}
177
178impl<X, Y> TimeInterval<Y> for SimpleFeature<X, Y> {
179 fn start_time(&self) -> Option<f64> {
180 self.y.first().copied()
181 }
182
183 fn end_time(&self) -> Option<f64> {
184 self.y.last().copied()
185 }
186
187 fn apex_time(&self) -> Option<f64> {
188 self.apex_y()
189 }
190
191 fn area(&self) -> f32 {
192 let mut it = self.y.iter().zip(self.z.iter());
193 if let Some((first_y, first_z)) = it.next() {
194 let (_y, _z, acc) =
195 it.fold((first_y, first_z, 0.0), |(last_y, last_z, acc), (y, z)| {
196 let step = (last_z + z) / 2.0;
197 let dy = y - last_y;
198 (y, z, acc + (step as f64 * dy))
199 });
200 acc as f32
201 } else {
202 0.0
203 }
204 }
205
206 fn iter_time(&self) -> impl Iterator<Item = f64> {
207 self.y.iter().copied()
208 }
209
210 fn find_time(&self, time: f64) -> (Option<usize>, f64) {
211 self.find_y(time)
212 }
213}
214
215impl<X, Y> IntensityMeasurement for SimpleFeature<X, Y> {
216 fn intensity(&self) -> f32 {
217 self.z.iter().sum()
218 }
219}
220
221impl<X, Y> FeatureLike<X, Y> for SimpleFeature<X, Y> {
222 fn len(&self) -> usize {
223 self.y.len()
224 }
225
226 fn iter(&self) -> impl Iterator<Item = (f64, f64, f32)> {
227 self.y
228 .iter()
229 .zip(self.z.iter())
230 .map(|(y, z)| (self.label, *y, *z))
231 }
232}
233
234pub struct IntoIter<X, Y> {
235 xval: f64,
236 yiter: std::vec::IntoIter<f64>,
237 ziter: std::vec::IntoIter<f32>,
238 _x: PhantomData<X>,
239 _y: PhantomData<Y>,
240}
241
242impl<X, Y> Iterator for IntoIter<X, Y> {
243 type Item = (f64, f64, f32);
244
245 fn next(&mut self) -> Option<Self::Item> {
246 let y = self.yiter.next();
247 let z = self.ziter.next();
248 match (y, z) {
249 (Some(y), Some(z)) => Some((self.xval, y, z)),
250 _ => None,
251 }
252 }
253}
254
255impl<X, Y> IntoIterator for SimpleFeature<X, Y> {
256 type Item = (f64, f64, f32);
257
258 type IntoIter = IntoIter<X, Y>;
259
260 fn into_iter(self) -> Self::IntoIter {
261 let xval = self.coordinate();
262 IntoIter {
263 xval,
264 yiter: self.y.into_iter(),
265 ziter: self.z.into_iter(),
266 _x: PhantomData::<X>,
267 _y: PhantomData::<Y>,
268 }
269 }
270}
271
272impl<X, Y> Extend<(f64, f64, f32)> for SimpleFeature<X, Y> {
273 fn extend<T: IntoIterator<Item = (f64, f64, f32)>>(&mut self, iter: T) {
274 for (_x, y, z) in iter {
275 self.y.push(y);
276 self.z.push(z);
277 }
278 }
279}
280
281impl<X, Y, P: CoordinateLike<X> + IntensityMeasurement> Extend<(P, f64)> for SimpleFeature<X, Y> {
282 fn extend<T: IntoIterator<Item = (P, f64)>>(&mut self, iter: T) {
283 for (x, t) in iter {
284 self.push(&x, t)
285 }
286 }
287}
288
289impl<X, Y, P: CoordinateLike<X> + IntensityMeasurement> FromIterator<(P, f64)>
290 for SimpleFeature<X, Y>
291{
292 fn from_iter<T: IntoIterator<Item = (P, f64)>>(iter: T) -> Self {
293 let mut it = iter.into_iter();
294 if let Some((peak, time)) = it.next() {
295 let mut this = Self::empty(peak.coordinate());
296 this.push(&peak, time);
297 this.extend(it);
298 this
299 } else {
300 Self::empty(0.0)
301 }
302 }
303}
304
305impl<X, Y> FromIterator<(f64, f64, f32)> for SimpleFeature<X, Y> {
306 fn from_iter<T: IntoIterator<Item = (f64, f64, f32)>>(iter: T) -> Self {
307 let mut it = iter.into_iter();
308 if let Some((x, time, intensity)) = it.next() {
309 let mut this = Self::empty(x);
310 this.push_raw(x, time, intensity);
311 this.extend(it);
312 this
313 } else {
314 Self::empty(0.0)
315 }
316 }
317}
318
319#[derive(Debug)]
320pub struct IterMut<'a, X, Y> {
321 yiter: std::slice::IterMut<'a, f64>,
322 ziter: std::slice::IterMut<'a, f32>,
323 value: f64,
324 _x: PhantomData<X>,
325 _y: PhantomData<Y>,
326}
327
328impl<'a, X, Y> Iterator for IterMut<'a, X, Y> {
329 type Item = (&'a mut f64, &'a mut f64, &'a mut f32);
330
331 fn next(&mut self) -> Option<Self::Item> {
332 let x = std::ptr::addr_of_mut!(self.value);
333 let y = self.yiter.next();
334 let z = self.ziter.next();
335
336 match (y, z) {
337 (Some(y), Some(z)) => Some((unsafe { &mut *x }, y, z)),
338 _ => None,
339 }
340 }
341}
342
343impl<X, Y> FeatureLikeMut<X, Y> for SimpleFeature<X, Y> {
344 fn iter_mut(&mut self) -> impl Iterator<Item = (&mut f64, &mut f64, &mut f32)> {
345 IterMut {
346 yiter: self.y.iter_mut(),
347 ziter: self.z.iter_mut(),
348 value: self.label,
349 _x: PhantomData::<X>,
350 _y: PhantomData::<Y>,
351 }
352 }
353
354 fn push<T: CoordinateLike<X> + IntensityMeasurement>(&mut self, pt: &T, time: f64) {
355 self.push(pt, time);
356 }
357
358 fn push_raw(&mut self, x: f64, y: f64, z: f32) {
359 self.push_raw(x, y, z);
360 }
361
362 fn clear(&mut self) {
363 self.y.clear();
364 self.z.clear();
365 }
366
367 fn reserve(&mut self, capacity: usize) {
368 self.y.reserve(capacity);
369 self.z.reserve(capacity);
370 }
371}
372
373#[derive(Debug, Clone, Copy)]
375#[cfg_attr(feature = "serde", derive(serde::Serialize))]
376pub struct SimpleFeatureView<'a, X, Y> {
377 pub label: f64,
378 y: &'a [f64],
379 z: &'a [f32],
380 _x: PhantomData<X>,
381 _y: PhantomData<Y>,
382}
383
384impl<X, Y> CoArrayOps for SimpleFeatureView<'_, X, Y> {}
385
386impl<'a, X, Y> SimpleFeatureView<'a, X, Y> {
387 pub fn new(label: f64, y: &'a [f64], z: &'a [f32]) -> Self {
388 Self {
389 label,
390 y,
391 z,
392 _x: PhantomData,
393 _y: PhantomData,
394 }
395 }
396
397 pub fn empty(label: f64) -> Self {
398 Self {
399 label,
400 y: EMPTY_Y,
401 z: EMPTY_Z,
402 _x: PhantomData,
403 _y: PhantomData,
404 }
405 }
406
407 fn apex_y(&self) -> Option<f64> {
408 self.apex_of(self.y, self.z)
409 }
410
411 pub fn to_owned(&self) -> SimpleFeature<X, Y> {
412 SimpleFeature::new(self.label, self.y.to_vec(), self.z.to_vec())
413 }
414
415 pub fn into_inner(self) -> (f64, &'a [f64], &'a [f32]) {
416 (self.label, self.y, self.z)
417 }
418
419 fn find_y(&self, y: f64) -> (Option<usize>, f64) {
420 if self.is_empty() {
421 return (None, y);
422 }
423 match self.y.binary_search_by(|yi| y.total_cmp(yi).reverse()) {
424 Ok(i) => {
425 let low = i.saturating_sub(3);
426 (low..(low + 6).min(self.len()))
427 .map(|i| (Some(i), (self.y[i] - y).abs()))
428 .min_by(|(_, e), (_, d)| e.total_cmp(d))
429 .unwrap()
430 }
431 Err(i) => {
432 let low = i.saturating_sub(3);
433 (low..(low + 6).min(self.len()))
434 .map(|i| (Some(i), (self.y[i] - y).abs()))
435 .min_by(|(_, e), (_, d)| e.total_cmp(d))
436 .unwrap()
437 }
438 }
439 }
440}
441
442impl<X, Y> PartialEq for SimpleFeatureView<'_, X, Y> {
443 fn eq(&self, other: &Self) -> bool {
444 self.label == other.label
445 && self.y == other.y
446 && self.z == other.z
447 && self._x == other._x
448 && self._y == other._y
449 }
450}
451
452impl<X, Y> PartialOrd for SimpleFeatureView<'_, X, Y> {
453 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
454 match self.label.partial_cmp(&other.label) {
455 Some(core::cmp::Ordering::Equal) => {}
456 ord => return ord,
457 }
458 match self.start_time().partial_cmp(&other.start_time()) {
459 Some(core::cmp::Ordering::Equal) => {}
460 ord => return ord,
461 }
462 self.z.partial_cmp(other.z)
463 }
464}
465
466impl<X, Y> CoordinateLike<X> for SimpleFeatureView<'_, X, Y> {
467 fn coordinate(&self) -> f64 {
468 self.label
469 }
470}
471
472impl<X, Y> TimeInterval<Y> for SimpleFeatureView<'_, X, Y> {
473 fn start_time(&self) -> Option<f64> {
474 self.y.first().copied()
475 }
476
477 fn end_time(&self) -> Option<f64> {
478 self.y.last().copied()
479 }
480
481 fn apex_time(&self) -> Option<f64> {
482 self.apex_y()
483 }
484
485 fn area(&self) -> f32 {
486 let mut it = self.y.iter().zip(self.z.iter());
487 if let Some((first_y, first_z)) = it.next() {
488 let (_y, _z, acc) =
489 it.fold((first_y, first_z, 0.0), |(last_y, last_z, acc), (y, z)| {
490 let step = (last_z + z) / 2.0;
491 let dy = y - last_y;
492 (y, z, acc + (step as f64 * dy))
493 });
494 acc as f32
495 } else {
496 0.0
497 }
498 }
499
500 fn iter_time(&self) -> impl Iterator<Item = f64> {
501 self.y.iter().copied()
502 }
503
504 fn find_time(&self, time: f64) -> (Option<usize>, f64) {
505 self.find_y(time)
506 }
507}
508
509impl<X, Y> IntensityMeasurement for SimpleFeatureView<'_, X, Y> {
510 fn intensity(&self) -> f32 {
511 self.z.iter().sum()
512 }
513}
514
515impl<X, Y> FeatureLike<X, Y> for SimpleFeatureView<'_, X, Y> {
516 fn len(&self) -> usize {
517 self.y.len()
518 }
519
520 fn iter(&self) -> impl Iterator<Item = (f64, f64, f32)> {
521 self.y
522 .iter()
523 .zip(self.z.iter())
524 .map(|(y, z)| (self.label, *y, *z))
525 }
526}
527
528impl<'a, X, Y> SplittableFeatureLike<'a, X, Y> for SimpleFeature<X, Y> {
529 type ViewType = SimpleFeatureView<'a, X, Y>;
530
531 fn split_at(&'a self, index: usize) -> (Self::ViewType, Self::ViewType) {
532 if !self.is_empty() {
533 let before = Self::ViewType::new(self.label, &self.y[..index], &self.z[..index]);
534 let after = Self::ViewType::new(self.label, &self.y[index..], &self.z[index..]);
535 (before, after)
536 } else {
537 let before = Self::ViewType::new(self.label, EMPTY_Y, EMPTY_Z);
538 let after = Self::ViewType::new(self.label, EMPTY_Y, EMPTY_Z);
539 (before, after)
540 }
541 }
542
543 fn split_at_time(&'a self, point: f64) -> (Self::ViewType, Self::ViewType) {
544 if let Some(point) = self.find_time(point).0 {
545 self.split_at(point)
546 } else {
547 let before = Self::ViewType::new(self.label, EMPTY_Y, EMPTY_Z);
548 let after = Self::ViewType::new(self.label, EMPTY_Y, EMPTY_Z);
549 (before, after)
550 }
551 }
552
553 fn slice<I: RangeBounds<usize> + Clone>(&'a self, bounds: I) -> Self::ViewType {
554 let start = match bounds.start_bound() {
555 Bound::Included(i) => *i,
556 Bound::Excluded(i) => *i,
557 Bound::Unbounded => 0,
558 };
559 let end = match bounds.end_bound() {
560 Bound::Included(i) => *i + 1,
561 Bound::Excluded(i) => *i,
562 Bound::Unbounded => self.len(),
563 };
564 let y = &self.y[start..end];
565 let z = &self.z[start..end];
566 Self::ViewType::new(self.label, y, z)
567 }
568}
569
570impl<'a, X, Y> SplittableFeatureLike<'a, X, Y> for SimpleFeatureView<'a, X, Y> {
571 type ViewType = Self;
572
573 fn split_at_time(&'a self, point: f64) -> (Self::ViewType, Self::ViewType) {
574 if let Some(point) = self.find_time(point).0 {
575 self.split_at(point)
576 } else {
577 let before = Self::ViewType::new(self.label, EMPTY_Y, EMPTY_Z);
578 let after = Self::ViewType::new(self.label, EMPTY_Y, EMPTY_Z);
579 (before, after)
580 }
581 }
582
583 fn slice<I: RangeBounds<usize> + Clone>(&'a self, bounds: I) -> Self::ViewType {
584 let start = bounds.start_bound();
585 let end = bounds.end_bound();
586 match (start, end) {
587 (Bound::Included(i), Bound::Included(j)) => {
588 Self::ViewType::new(self.label, &self.y[*i..=*j], &self.z[*i..=*j])
589 }
590 (Bound::Included(i), Bound::Excluded(j)) => {
591 Self::ViewType::new(self.label, &self.y[*i..*j], &self.z[*i..*j])
592 }
593 (Bound::Included(i), Bound::Unbounded) => {
594 Self::ViewType::new(self.label, &self.y[*i..], &self.z[*i..])
595 }
596 (Bound::Excluded(i), Bound::Included(j)) => {
597 Self::ViewType::new(self.label, &self.y[*i..=*j], &self.z[*i..=*j])
598 }
599 (Bound::Excluded(i), Bound::Excluded(j)) => {
600 Self::ViewType::new(self.label, &self.y[*i..*j], &self.z[*i..*j])
601 }
602 (Bound::Excluded(i), Bound::Unbounded) => {
603 Self::ViewType::new(self.label, &self.y[*i..], &self.z[*i..])
604 }
605 (Bound::Unbounded, Bound::Included(j)) => {
606 Self::ViewType::new(self.label, &self.y[..=*j], &self.z[..=*j])
607 }
608 (Bound::Unbounded, Bound::Excluded(j)) => {
609 Self::ViewType::new(self.label, &self.y[..*j], &self.z[..*j])
610 }
611 (Bound::Unbounded, Bound::Unbounded) => Self::ViewType::new(self.label, self.y, self.z),
612 }
613 }
614
615 fn split_at(&'a self, index: usize) -> (Self::ViewType, Self::ViewType) {
616 if !self.is_empty() {
617 let before = Self::ViewType::new(self.label, &self.y[..index], &self.z[..index]);
618 let after = Self::ViewType::new(self.label, &self.y[index..], &self.z[index..]);
619 (before, after)
620 } else {
621 let before = Self::ViewType::new(self.label, EMPTY_Y, EMPTY_Z);
622 let after = Self::ViewType::new(self.label, EMPTY_Y, EMPTY_Z);
623 (before, after)
624 }
625 }
626}
627
628impl<X, Y> TimeArray<Y> for SimpleFeatureView<'_, X, Y> {
629 fn time_view(&self) -> &[f64] {
630 self.y
631 }
632
633 fn intensity_view(&self) -> &[f32] {
634 self.z
635 }
636}