dsfb_semiconductor/input/
residual_stream.rs1#[cfg(not(feature = "std"))]
2use alloc::{string::String, vec::Vec};
3
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
7pub struct ResidualSample {
8 pub timestamp: f64,
9 pub feature_id: String,
10 pub value: f64,
11}
12
13#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
14pub struct ResidualStream {
15 samples: Vec<ResidualSample>,
16}
17
18impl ResidualStream {
19 pub fn new(mut samples: Vec<ResidualSample>) -> Self {
20 samples.sort_by(|left, right| {
21 left.timestamp
22 .total_cmp(&right.timestamp)
23 .then_with(|| left.feature_id.cmp(&right.feature_id))
24 });
25 Self { samples }
26 }
27
28 pub fn from_samples(samples: &[ResidualSample]) -> Self {
29 Self::new(samples.to_vec())
30 }
31
32 pub fn push_clone(&mut self, sample: &ResidualSample) {
33 self.samples.push(sample.clone());
34 self.samples.sort_by(|left, right| {
35 left.timestamp
36 .total_cmp(&right.timestamp)
37 .then_with(|| left.feature_id.cmp(&right.feature_id))
38 });
39 }
40
41 pub fn samples(&self) -> &[ResidualSample] {
42 &self.samples
43 }
44}
45
46#[cfg(test)]
47mod tests {
48 use super::*;
49
50 #[test]
51 fn residual_stream_is_sorted_deterministically() {
52 let stream = ResidualStream::new(vec![
53 ResidualSample {
54 timestamp: 2.0,
55 feature_id: "S002".into(),
56 value: 1.0,
57 },
58 ResidualSample {
59 timestamp: 1.0,
60 feature_id: "S003".into(),
61 value: 1.0,
62 },
63 ResidualSample {
64 timestamp: 1.0,
65 feature_id: "S001".into(),
66 value: 1.0,
67 },
68 ]);
69 assert_eq!(stream.samples()[0].feature_id, "S001");
70 assert_eq!(stream.samples()[1].feature_id, "S003");
71 assert_eq!(stream.samples()[2].feature_id, "S002");
72 }
73}