1use ndarray::prelude::*;
2use num::complex::Complex64;
3
4#[cfg(feature = "serde")]
5use super::check_channel_labels;
6use super::{DataError, XAxis};
7
8#[derive(Clone, Debug)]
9#[cfg_attr(
10 feature = "serde",
11 derive(serde::Serialize, serde::Deserialize),
12 serde(try_from = "ComplexDataFields")
13)]
14pub struct ComplexData {
15 x_axis: XAxis,
16 y_data: Array2<Complex64>,
17 comment: Option<String>,
18 channel_labels: Vec<Option<String>>,
19}
20
21#[cfg(feature = "serde")]
23#[derive(serde::Deserialize)]
24struct ComplexDataFields {
25 x_axis: XAxis,
26 y_data: Array2<Complex64>,
27 comment: Option<String>,
28 channel_labels: Vec<Option<String>>,
29}
30
31#[cfg(feature = "serde")]
32impl TryFrom<ComplexDataFields> for ComplexData {
33 type Error = DataError;
34
35 fn try_from(fields: ComplexDataFields) -> Result<Self, Self::Error> {
36 if fields.y_data.ncols() != fields.x_axis.len() {
37 return Err(DataError::NotMatching);
38 }
39 check_channel_labels(&fields.channel_labels, fields.y_data.nrows())?;
40 Ok(Self {
43 x_axis: fields.x_axis,
44 y_data: fields.y_data,
45 comment: fields.comment,
46 channel_labels: fields.channel_labels,
47 })
48 }
49}
50
51impl ComplexData {
52 pub fn new(x_data: Array1<f64>, y_data: Array2<Complex64>) -> Result<Self, DataError> {
53 if y_data.ncols() != x_data.len() {
54 return Err(DataError::NotMatching);
55 }
56 Self::from_axis(XAxis::explicit(x_data)?, y_data)
57 }
58
59 pub fn from_axis(x_axis: XAxis, y_data: Array2<Complex64>) -> Result<Self, DataError> {
62 if y_data.ncols() != x_axis.len() {
63 return Err(DataError::NotMatching);
64 }
65 let num_channels = y_data.nrows();
66 Ok(Self {
67 x_axis,
68 y_data,
69 comment: None,
70 channel_labels: vec![None; num_channels],
71 })
72 }
73
74 pub fn zeros(num_channels: usize, num_data_points: usize) -> Self {
75 Self {
76 x_axis: XAxis::indices(num_data_points),
77 y_data: Array2::zeros((num_channels, num_data_points)),
78 comment: None,
79 channel_labels: vec![None; num_channels],
80 }
81 }
82
83 pub fn from_x_data(x_data: Array1<f64>, num_channels: usize) -> Result<Self, DataError> {
84 let x_axis = XAxis::explicit(x_data)?;
85 Ok(Self {
86 y_data: Array2::zeros((num_channels, x_axis.len())),
87 x_axis,
88 comment: None,
89 channel_labels: vec![None; num_channels],
90 })
91 }
92
93 pub fn from_y_data(y_data: Array2<Complex64>) -> Self {
94 let num_channels = y_data.nrows();
95 Self {
96 x_axis: XAxis::indices(y_data.ncols()),
97 y_data,
98 comment: None,
99 channel_labels: vec![None; num_channels],
100 }
101 }
102
103 pub fn comment(&self) -> Option<&str> {
104 self.comment.as_deref()
105 }
106
107 pub fn set_comment(&mut self, comment: Option<&str>) {
108 self.comment = comment.map(Into::into);
109 }
110
111 pub fn channel_label(&self, channel: usize) -> Option<&str> {
112 self.channel_labels.get(channel).and_then(|s| s.as_deref())
113 }
114
115 pub fn set_channel_label(&mut self, channel: usize, label: Option<&str>) {
116 if let Some(slot) = self.channel_labels.get_mut(channel) {
117 *slot = label.map(Into::into);
118 }
119 }
120
121 #[cfg(feature = "serde")]
123 pub(crate) fn channel_labels(&self) -> &[Option<String>] {
124 &self.channel_labels
125 }
126
127 #[cfg(feature = "serde")]
129 pub(crate) fn set_channel_labels(
130 &mut self,
131 channel_labels: Vec<Option<String>>,
132 ) -> Result<(), DataError> {
133 check_channel_labels(&channel_labels, self.num_channels())?;
134 self.channel_labels = channel_labels;
135 Ok(())
136 }
137
138 pub fn num_channels(&self) -> usize {
139 self.y_data.nrows()
140 }
141
142 pub fn num_data_points(&self) -> usize {
143 self.y_data.ncols()
144 }
145
146 pub fn x_axis(&self) -> &XAxis {
149 &self.x_axis
150 }
151
152 pub fn x_data(&self) -> CowArray<'_, f64, Ix1> {
155 self.x_axis.to_cow_array()
156 }
157
158 pub fn set_x_axis(&mut self, x_axis: XAxis) -> Result<(), DataError> {
160 if x_axis.len() != self.num_data_points() {
161 return Err(DataError::NotMatching);
162 }
163 self.x_axis = x_axis;
164 Ok(())
165 }
166
167 pub fn y_data(&self) -> ArrayView2<'_, Complex64> {
168 self.y_data.view()
169 }
170
171 pub fn y_data_mut(&mut self) -> ArrayViewMut2<'_, Complex64> {
172 self.y_data.view_mut()
173 }
174
175 pub fn into_y_data(self) -> Array2<Complex64> {
177 self.y_data
178 }
179
180 pub fn channel(&self, ch: usize) -> ArrayView1<'_, Complex64> {
181 self.y_data.row(ch)
182 }
183
184 pub fn channel_mut(&mut self, ch: usize) -> ArrayViewMut1<'_, Complex64> {
185 self.y_data.row_mut(ch)
186 }
187
188 pub fn channel_iter(&self) -> ndarray::iter::AxisIter<'_, Complex64, Ix1> {
189 self.y_data.outer_iter()
190 }
191
192 pub fn channel_iter_mut(&mut self) -> ndarray::iter::AxisIterMut<'_, Complex64, Ix1> {
193 self.y_data.outer_iter_mut()
194 }
195
196 pub fn iter(&self) -> ndarray::iter::Iter<'_, Complex64, Ix2> {
197 self.y_data.iter()
198 }
199
200 pub fn iter_mut(&mut self) -> ndarray::iter::IterMut<'_, Complex64, Ix2> {
201 self.y_data.iter_mut()
202 }
203
204 pub fn nearest_x_index(&self, x: f64) -> usize {
208 self.x_axis.nearest_index(x)
209 }
210}
211
212impl std::fmt::Display for ComplexData {
213 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
214 let base_info = format!(
215 "Complex data with {} channels and {} data points.",
216 self.num_channels(),
217 self.num_data_points()
218 );
219
220 if let Some(comment) = self.comment() {
221 write!(f, "{}\nComment: {:?}", base_info, comment)
222 } else {
223 write!(f, "{}", base_info)
224 }
225 }
226}
227
228impl PartialEq for ComplexData {
229 fn eq(&self, other: &Self) -> bool {
230 self.x_axis == other.x_axis && self.y_data == other.y_data
231 }
232}
233
234impl approx::AbsDiffEq for ComplexData {
235 type Epsilon = f64;
236
237 fn default_epsilon() -> Self::Epsilon {
238 f64::EPSILON
239 }
240
241 fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
242 if !self.x_axis.abs_diff_eq(&other.x_axis, epsilon) {
243 return false;
244 }
245
246 if !self.y_data().abs_diff_eq(&other.y_data(), epsilon) {
248 return false;
249 }
250
251 true
252 }
253}
254
255impl<'a> IntoIterator for &'a ComplexData {
257 type Item = &'a Complex64;
258 type IntoIter = ndarray::iter::Iter<'a, Complex64, ndarray::Ix2>;
259
260 fn into_iter(self) -> Self::IntoIter {
261 self.iter()
262 }
263}
264
265impl<'a> IntoIterator for &'a mut ComplexData {
267 type Item = &'a mut Complex64;
268 type IntoIter = ndarray::iter::IterMut<'a, Complex64, ndarray::Ix2>;
269
270 fn into_iter(self) -> Self::IntoIter {
271 self.iter_mut()
272 }
273}
274
275impl IntoIterator for ComplexData {
277 type Item = Complex64;
278 type IntoIter = ndarray::iter::IntoIter<Complex64, Ix2>;
279
280 fn into_iter(self) -> Self::IntoIter {
281 self.y_data.into_iter()
282 }
283}
284
285#[cfg(test)]
286mod tests {
287 use super::*;
288
289 #[test]
290 fn complex_data_errors() {
291 let error = ComplexData::new(
292 array![0.0, 1.0, 2.0, 5.0],
293 arr2(&[[Complex64::from(0.0); 3]; 2]),
294 )
295 .err();
296 assert!(matches!(error, Some(DataError::NotMatching)));
297
298 let error =
299 ComplexData::new(array![0.0, 0.0, 0.0], arr2(&[[Complex64::from(0.0); 3]; 2])).err();
300 assert!(matches!(error, Some(DataError::NotIncreasing)));
301
302 let error = ComplexData::from_x_data(array![0.0, 0.0, 0.0], 3).err();
303 assert!(matches!(error, Some(DataError::NotIncreasing)));
304 }
305
306 #[test]
307 fn nearest_x_index_matches_linear_scan() {
308 let data = ComplexData::from_x_data(array![0.0, 1.0, 2.5, 4.0, 10.0], 1).unwrap();
309 let linear_scan = |x: f64| {
310 data.x_data()
311 .iter()
312 .enumerate()
313 .min_by(|(_, a), (_, b)| {
314 (*a - x)
315 .abs()
316 .partial_cmp(&(*b - x).abs())
317 .unwrap_or(std::cmp::Ordering::Equal)
318 })
319 .map(|(index, _)| index)
320 .unwrap()
321 };
322
323 let queries = [
326 -5.0,
327 0.0,
328 0.5,
329 0.6,
330 1.75,
331 2.5,
332 3.25,
333 3.3,
334 7.0,
335 7.1,
336 10.0,
337 42.0,
338 f64::NAN,
339 ];
340 for x in queries {
341 assert_eq!(data.nearest_x_index(x), linear_scan(x), "x = {x}");
342 }
343
344 assert_eq!(ComplexData::zeros(1, 0).nearest_x_index(1.0), 0);
345 }
346
347 #[test]
348 fn test_complex_data() -> Result<(), DataError> {
349 let data = ComplexData::zeros(2, 4);
350 assert_eq!(data.x_data(), arr1(&[0.0, 1.0, 2.0, 3.0]));
351 assert_eq!(data.y_data(), arr2(&[[Complex64::from(0.0); 4]; 2]),);
352
353 let data = ComplexData::from_x_data(arr1(&[0.0, 1.0, 2.0, 5.0]), 2).unwrap();
354 assert_eq!(data.x_data(), arr1(&[0.0, 1.0, 2.0, 5.0]));
355 assert_eq!(data.y_data(), arr2(&[[Complex64::from(0.0); 4]; 2]),);
356
357 let mut i = 0;
358 let mut data = ComplexData::new(
359 Array1::from_iter((0..10).map(|v| v as f64 / 10.0)),
360 Array2::from_shape_fn((2, 10), |_| {
361 let value = Complex64::from(i as f64);
362 i += 1;
363 value
364 }),
365 )?;
366
367 assert_eq!(data.comment(), None);
368
369 data.set_comment(Some("This is just test data"));
370
371 assert_eq!(data.comment(), Some("This is just test data"));
372
373 assert_eq!(data.num_channels(), 2);
374 assert_eq!(data.num_data_points(), 10);
375
376 assert_eq!(
377 data.x_data(),
378 arr1(&[0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9])
379 );
380
381 assert_eq!(
382 data.y_data(),
383 arr2(&[
384 [
385 Complex64::from(0.0),
386 Complex64::from(1.0),
387 Complex64::from(2.0),
388 Complex64::from(3.0),
389 Complex64::from(4.0),
390 Complex64::from(5.0),
391 Complex64::from(6.0),
392 Complex64::from(7.0),
393 Complex64::from(8.0),
394 Complex64::from(9.0)
395 ],
396 [
397 Complex64::from(10.0),
398 Complex64::from(11.0),
399 Complex64::from(12.0),
400 Complex64::from(13.0),
401 Complex64::from(14.0),
402 Complex64::from(15.0),
403 Complex64::from(16.0),
404 Complex64::from(17.0),
405 Complex64::from(18.0),
406 Complex64::from(19.0),
407 ]
408 ])
409 );
410
411 assert_eq!(
412 data.y_data_mut(),
413 arr2(&[
414 [
415 Complex64::from(0.0),
416 Complex64::from(1.0),
417 Complex64::from(2.0),
418 Complex64::from(3.0),
419 Complex64::from(4.0),
420 Complex64::from(5.0),
421 Complex64::from(6.0),
422 Complex64::from(7.0),
423 Complex64::from(8.0),
424 Complex64::from(9.0)
425 ],
426 [
427 Complex64::from(10.0),
428 Complex64::from(11.0),
429 Complex64::from(12.0),
430 Complex64::from(13.0),
431 Complex64::from(14.0),
432 Complex64::from(15.0),
433 Complex64::from(16.0),
434 Complex64::from(17.0),
435 Complex64::from(18.0),
436 Complex64::from(19.0),
437 ]
438 ])
439 );
440
441 let a: Vec<Complex64> = data.clone().into_iter().collect();
442 assert_eq!(
443 a,
444 (0..20)
445 .map(|v| Complex64::from(v as f64))
446 .collect::<Vec<_>>()
447 );
448
449 assert_eq!(
450 data.channel(0),
451 arr1(&[
452 Complex64::from(0.0),
453 Complex64::from(1.0),
454 Complex64::from(2.0),
455 Complex64::from(3.0),
456 Complex64::from(4.0),
457 Complex64::from(5.0),
458 Complex64::from(6.0),
459 Complex64::from(7.0),
460 Complex64::from(8.0),
461 Complex64::from(9.0)
462 ])
463 );
464
465 assert_eq!(
466 data.channel_mut(0),
467 arr1(&[
468 Complex64::from(0.0),
469 Complex64::from(1.0),
470 Complex64::from(2.0),
471 Complex64::from(3.0),
472 Complex64::from(4.0),
473 Complex64::from(5.0),
474 Complex64::from(6.0),
475 Complex64::from(7.0),
476 Complex64::from(8.0),
477 Complex64::from(9.0)
478 ])
479 );
480
481 assert_eq!(
482 data.channel(1),
483 arr1(&[
484 Complex64::from(10.0),
485 Complex64::from(11.0),
486 Complex64::from(12.0),
487 Complex64::from(13.0),
488 Complex64::from(14.0),
489 Complex64::from(15.0),
490 Complex64::from(16.0),
491 Complex64::from(17.0),
492 Complex64::from(18.0),
493 Complex64::from(19.0)
494 ])
495 );
496
497 assert_eq!(
498 data.channel_mut(1),
499 arr1(&[
500 Complex64::from(10.0),
501 Complex64::from(11.0),
502 Complex64::from(12.0),
503 Complex64::from(13.0),
504 Complex64::from(14.0),
505 Complex64::from(15.0),
506 Complex64::from(16.0),
507 Complex64::from(17.0),
508 Complex64::from(18.0),
509 Complex64::from(19.0)
510 ])
511 );
512
513 for (i, ch) in data.channel_iter().enumerate() {
514 match i {
515 0 => assert_eq!(
516 ch,
517 arr1(&[
518 Complex64::from(0.0),
519 Complex64::from(1.0),
520 Complex64::from(2.0),
521 Complex64::from(3.0),
522 Complex64::from(4.0),
523 Complex64::from(5.0),
524 Complex64::from(6.0),
525 Complex64::from(7.0),
526 Complex64::from(8.0),
527 Complex64::from(9.0)
528 ])
529 ),
530 1 => assert_eq!(
531 ch,
532 arr1(&[
533 Complex64::from(10.0),
534 Complex64::from(11.0),
535 Complex64::from(12.0),
536 Complex64::from(13.0),
537 Complex64::from(14.0),
538 Complex64::from(15.0),
539 Complex64::from(16.0),
540 Complex64::from(17.0),
541 Complex64::from(18.0),
542 Complex64::from(19.0)
543 ])
544 ),
545 _ => panic!("Should only have two channels"),
546 }
547 }
548
549 for (i, ch) in data.channel_iter_mut().enumerate() {
550 match i {
551 0 => assert_eq!(
552 ch,
553 arr1(&[
554 Complex64::from(0.0),
555 Complex64::from(1.0),
556 Complex64::from(2.0),
557 Complex64::from(3.0),
558 Complex64::from(4.0),
559 Complex64::from(5.0),
560 Complex64::from(6.0),
561 Complex64::from(7.0),
562 Complex64::from(8.0),
563 Complex64::from(9.0)
564 ])
565 ),
566 1 => assert_eq!(
567 ch,
568 arr1(&[
569 Complex64::from(10.0),
570 Complex64::from(11.0),
571 Complex64::from(12.0),
572 Complex64::from(13.0),
573 Complex64::from(14.0),
574 Complex64::from(15.0),
575 Complex64::from(16.0),
576 Complex64::from(17.0),
577 Complex64::from(18.0),
578 Complex64::from(19.0)
579 ])
580 ),
581 _ => panic!("Should only have two channels"),
582 }
583 }
584
585 Ok(())
586 }
587}