1use std::borrow::Cow;
2use std::cmp::Ordering;
3use std::collections::HashMap;
4use std::collections::hash_map::{Iter, IterMut};
5use std::convert::TryFrom;
6use std::ffi::c_void;
7use std::hash::BuildHasherDefault;
8
9use identity_hash::IdentityHasher;
10#[cfg(feature = "parallelism")]
11use rayon::prelude::*;
12
13use mzpeaks::Tolerance;
14
15use mzdata_param::{Param, ParamDescribed, Unit};
16
17use super::BinaryDataArrayType;
18use super::array::DataArray;
19use super::encodings::{ArrayRetrievalError, ArrayType, BinaryCompressionType};
20use super::traits::{ByteArrayView, ByteArrayViewMut};
21
22#[derive(Debug, Default, Clone)]
38#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
39pub struct BinaryArrayMap {
40 pub byte_buffer_map: HashMap<ArrayType, DataArray>,
41}
42
43impl BinaryArrayMap {
44 pub fn new() -> BinaryArrayMap {
45 BinaryArrayMap {
46 ..Default::default()
47 }
48 }
49
50 pub fn len(&self) -> usize {
52 self.byte_buffer_map.len()
53 }
54
55 pub fn is_empty(&self) -> bool {
56 self.byte_buffer_map.is_empty()
57 }
58
59 pub fn has_ion_mobility(&self) -> bool {
61 self.byte_buffer_map.keys().any(|a| a.is_ion_mobility())
62 }
63
64 pub fn iter(&self) -> Iter<'_, ArrayType, DataArray> {
66 self.byte_buffer_map.iter()
67 }
68
69 #[cfg(feature = "parallelism")]
70 pub fn par_iter(&self) -> rayon::collections::hash_map::Iter<'_, ArrayType, DataArray> {
71 self.byte_buffer_map.par_iter()
72 }
73
74 pub fn iter_mut(&mut self) -> IterMut<'_, ArrayType, DataArray> {
76 self.byte_buffer_map.iter_mut()
77 }
78
79 #[cfg(feature = "parallelism")]
80 pub fn par_iter_mut(
81 &mut self,
82 ) -> rayon::collections::hash_map::IterMut<'_, ArrayType, DataArray> {
83 self.byte_buffer_map.par_iter_mut()
84 }
85
86 pub fn encode_array(
93 &mut self,
94 array_type: &ArrayType,
95 compression: BinaryCompressionType,
96 ) -> Result<(), ArrayRetrievalError> {
97 if let Some(arr) = self.get_mut(array_type) {
98 arr.store_compressed(compression)?;
99 Ok(())
100 } else {
101 Err(ArrayRetrievalError::NotFound(array_type.clone()))
102 }
103 }
104
105 pub fn decode_all_arrays(&mut self) -> Result<(), ArrayRetrievalError> {
125 #[cfg(not(feature = "parallelism"))]
126 {
127 self._decode_all_arrays()
128 }
129 #[cfg(feature = "parallelism")]
130 {
131 if self.len() > 2 {
132 self._decode_all_arrays_parallel()
133 } else {
134 self._decode_all_arrays()
135 }
136 }
137 }
138
139 fn _decode_all_arrays(&mut self) -> Result<(), ArrayRetrievalError> {
140 for (_key, value) in self.iter_mut() {
141 match value.compression {
142 BinaryCompressionType::Decoded => {}
143 _ => {
144 value.decode_and_store()?;
145 }
146 }
147 }
148 Ok(())
149 }
150
151 #[cfg(feature = "parallelism")]
152 fn _decode_all_arrays_parallel(&mut self) -> Result<(), ArrayRetrievalError> {
153 let res: Result<(), ArrayRetrievalError> = self
154 .iter_mut()
155 .par_bridge()
156 .map(|(_key, value)| {
157 match value.compression {
158 BinaryCompressionType::Decoded => {}
159 _ => {
160 value.decode_and_store()?;
161 }
162 }
163 Ok(())
164 })
165 .collect::<Result<(), ArrayRetrievalError>>();
166 res
167 }
168
169 pub fn decode_array(&mut self, array_type: &ArrayType) -> Result<(), ArrayRetrievalError> {
175 if let Some(array) = self.get_mut(array_type) {
176 array.decode_and_store()?;
177 Ok(())
178 } else {
179 Err(ArrayRetrievalError::NotFound(array_type.clone()))
180 }
181 }
182
183 pub fn add(&mut self, array: DataArray) {
185 self.byte_buffer_map.insert(array.name.clone(), array);
186 }
187
188 pub fn get(&self, array_type: &ArrayType) -> Option<&DataArray> {
190 self.byte_buffer_map.get(array_type)
191 }
192
193 pub fn get_mut(&mut self, array_type: &ArrayType) -> Option<&mut DataArray> {
195 self.byte_buffer_map.get_mut(array_type)
196 }
197
198 pub fn has_array(&self, array_type: &ArrayType) -> bool {
200 self.byte_buffer_map.contains_key(array_type)
201 }
202
203 pub fn clear(&mut self) {
205 self.byte_buffer_map.clear();
206 }
207
208 pub fn search(&self, query: f64, error_tolerance: Tolerance) -> Option<usize> {
213 if let Ok(mzs) = self.mzs() {
214 let (lower, _upper) = error_tolerance.bounds(query);
215 match mzs[..].binary_search_by(|m| m.partial_cmp(&lower).unwrap()) {
216 Ok(i) => {
217 let mut best_error = error_tolerance.call(query, mzs[i]).abs();
218 let mut best_index = i;
219 let mut index = i + 1;
220 while index < mzs.len() {
221 let error = error_tolerance.call(query, mzs[index]).abs();
222 if error < best_error {
223 best_index = index;
224 best_error = error;
225 }
226 index += 1;
227 }
228 if best_error < error_tolerance.tol() {
229 return Some(best_index);
230 }
231 None
232 }
233 Err(_err) => None,
234 }
235 } else {
236 None
237 }
238 }
239
240 pub fn mzs(&'_ self) -> Result<Cow<'_, [f64]>, ArrayRetrievalError> {
255 let mz_array = self
256 .get(&ArrayType::MZArray)
257 .ok_or(ArrayRetrievalError::NotFound(ArrayType::MZArray))?
258 .to_f64()
259 .inspect_err(|e| log::error!("Failed to decode m/z array: {e}"))?;
260 Ok(mz_array)
261 }
262
263 pub fn sort_from_indices(&mut self, mut mask: Vec<usize>) -> Result<(), ArrayRetrievalError> {
280 let n = mask.len();
281 const TOMBSTONE: usize = usize::MAX;
282 let mut tracks: Vec<(*mut c_void, BinaryDataArrayType)> = Vec::new();
283 for (_, v) in self.iter_mut() {
284 if v.data_len()? != n {
285 continue;
286 }
287 match v.dtype {
288 BinaryDataArrayType::Float64 => {
289 let view = v.coerce_mut::<f64>()?;
290 tracks.push((view.as_mut_ptr() as *mut c_void, v.dtype))
291 }
292 BinaryDataArrayType::Float32 => {
293 let view = v.coerce_mut::<f32>()?;
294 tracks.push((view.as_mut_ptr() as *mut c_void, v.dtype))
295 }
296 BinaryDataArrayType::Int64 => {
297 let view = v.coerce_mut::<i64>()?;
298 tracks.push((view.as_mut_ptr() as *mut c_void, v.dtype))
299 }
300 BinaryDataArrayType::Int32 => {
301 let view = v.coerce_mut::<i32>()?;
302 tracks.push((view.as_mut_ptr() as *mut c_void, v.dtype))
303 }
304 BinaryDataArrayType::ASCII => todo!(),
305 BinaryDataArrayType::Unknown => todo!(),
306 }
307 }
308
309 for idx in 0..n {
310 if mask[idx] != TOMBSTONE {
311 let mut current_idx = idx;
312 loop {
313 let next_idx = mask[current_idx];
314 mask[current_idx] = TOMBSTONE;
315 if mask[next_idx] == TOMBSTONE {
316 break;
317 }
318 for (dat, dtype) in tracks.iter_mut () {
319 match dtype {
320 BinaryDataArrayType::Float64 => {
321 unsafe {
322 let v = core::slice::from_raw_parts_mut(*dat as *mut f64, n);
323 v.swap(current_idx, next_idx);
324 }
325 },
326 BinaryDataArrayType::Float32 => {
327 unsafe {
328 let v = core::slice::from_raw_parts_mut(*dat as *mut f32, n);
329 v.swap(current_idx, next_idx);
330 }
331 },
332 BinaryDataArrayType::Int64 => {
333 unsafe {
334 let v = core::slice::from_raw_parts_mut(*dat as *mut i64, n);
335 v.swap(current_idx, next_idx);
336 }
337 },
338 BinaryDataArrayType::Int32 => {
339 unsafe {
340 let v = core::slice::from_raw_parts_mut(*dat as *mut i32, n);
341 v.swap(current_idx, next_idx);
342 }
343 },
344 BinaryDataArrayType::Unknown => todo!(),
345 BinaryDataArrayType::ASCII => todo!(),
346 }
347 }
348 current_idx = next_idx;
349 }
350 }
351 }
352 Ok(())
393 }
394
395 pub fn sort_by_array(&mut self, name: &ArrayType) -> Result<(), ArrayRetrievalError> {
403 let query_axis = self
404 .get(name)
405 .ok_or_else(|| ArrayRetrievalError::NotFound(name.clone()))?;
406 macro_rules! sort_mask {
407 ($conv:expr, $cmp:expr) => {{
408 let vals = $conv?;
409 if vals.is_sorted() {
410 return Ok(());
411 }
412 let n = vals.len();
413 let mut mask: Vec<usize> = (0..n).into_iter().collect();
414 mask.sort_by(|i, j| {
415 let a = vals[*i];
416 let b = vals[*j];
417 $cmp(&a, &b)
418 });
419 (mask, n)
420 }};
421 }
422 let (mask, _) = match query_axis.dtype() {
423 BinaryDataArrayType::Float64 => {
424 sort_mask!(query_axis.to_f64(), f64::total_cmp)
425 }
426 BinaryDataArrayType::Float32 => {
427 sort_mask!(query_axis.to_f32(), f32::total_cmp)
428 }
429 BinaryDataArrayType::Int64 => {
430 sort_mask!(query_axis.to_i64(), i64::cmp)
431 }
432 BinaryDataArrayType::Int32 => {
433 sort_mask!(query_axis.to_i32(), i32::cmp)
434 }
435 BinaryDataArrayType::ASCII => todo!(),
436 BinaryDataArrayType::Unknown => todo!(),
437 };
438
439 self.sort_from_indices(mask)
440 }
441
442 pub fn mzs_mut(&mut self) -> Result<&mut [f64], ArrayRetrievalError> {
449 if let Some(mz_array) = self.get_mut(&ArrayType::MZArray) {
450 mz_array
451 .decode_and_store()
452 .inspect_err(|e| log::error!("Failed to decode m/z array: {e}"))?;
453 mz_array.store_as(BinaryDataArrayType::Float64)?;
454 mz_array.coerce_mut()
455 } else {
456 Err(ArrayRetrievalError::NotFound(ArrayType::MZArray))
457 }
458 }
459
460 pub fn intensities(&'_ self) -> Result<Cow<'_, [f32]>, ArrayRetrievalError> {
462 let intensities = self
463 .get(&ArrayType::IntensityArray)
464 .ok_or(ArrayRetrievalError::NotFound(ArrayType::IntensityArray))?
465 .to_f32()
466 .inspect_err(|e| log::error!("Failed to decode intensity array: {e:?}"))?;
467 Ok(intensities)
468 }
469
470 pub fn intensities_mut(&mut self) -> Result<&mut [f32], ArrayRetrievalError> {
475 if let Some(arr) = self.get_mut(&ArrayType::IntensityArray) {
476 arr
477 .decode_and_store()
478 .inspect_err(|e| log::error!("Failed to decode intensity array: {e}"))?;
479 arr
480 .store_as(BinaryDataArrayType::Float32)
481 .inspect_err(|e| log::error!("Failed to decode intensity array: {e}"))?;
482 arr.coerce_mut()
483 } else {
484 Err(ArrayRetrievalError::NotFound(ArrayType::IntensityArray))
485 }
486 }
487
488 pub fn charges(&'_ self) -> Result<Cow<'_, [i32]>, ArrayRetrievalError> {
490 match self.get(&ArrayType::ChargeArray) {
491 Some(data_array) => data_array.to_i32(),
492 None => Err(ArrayRetrievalError::NotFound(ArrayType::ChargeArray)),
493 }
494 }
495
496 pub fn charge_mut(&mut self) -> Result<&mut [i32], ArrayRetrievalError> {
501 if let Some(arr) = self.get_mut(&ArrayType::ChargeArray) {
502 arr.decode_and_store()?;
503 arr
504 .store_as(BinaryDataArrayType::Int32)
505 .inspect_err(|e| log::error!("Failed to decode charge array: {e}"))?;
506 arr.coerce_mut()
507 } else {
508 Err(ArrayRetrievalError::NotFound(ArrayType::ChargeArray))
509 }
510 }
511
512 pub fn ion_mobility(&self) -> Result<(Cow<'_, [f64]>, ArrayType), ArrayRetrievalError> {
514 if let Some((array_type, data_array)) = self
515 .byte_buffer_map
516 .iter()
517 .find(|(a, _)| a.is_ion_mobility())
518 {
519 Ok((
520 data_array
521 .to_f64()
522 .inspect_err(|e| log::error!("Failed to decode ion mobility array: {e}"))?,
523 array_type.clone(),
524 ))
525 } else {
526 Err(ArrayRetrievalError::NotFound(ArrayType::IonMobilityArray))
527 }
528 }
529
530 pub fn ion_mobility_mut(&mut self) -> Result<(&mut [f64], ArrayType), ArrayRetrievalError> {
534 if let Some((array_type, data_array)) = self
535 .byte_buffer_map
536 .iter_mut()
537 .find(|(a, _)| a.is_ion_mobility())
538 {
539 data_array
540 .decode_and_store()
541 .inspect_err(|e| log::error!("Failed to decode ion mobility array: {e}"))?;
542 data_array.store_as(BinaryDataArrayType::Float32)?;
543 Ok((data_array.coerce_mut()?, array_type.clone()))
544 } else {
545 Err(ArrayRetrievalError::NotFound(ArrayType::IonMobilityArray))
546 }
547 }
548
549 pub fn stack_ion_mobility(self) -> Result<BinaryArrayMap3D, ArrayRetrievalError> {
553 BinaryArrayMap3D::try_from(self)
554 }
555}
556
557impl IntoIterator for BinaryArrayMap {
558 type Item = (ArrayType, DataArray);
559
560 type IntoIter = <HashMap<ArrayType, DataArray> as IntoIterator>::IntoIter;
561
562 fn into_iter(self) -> Self::IntoIter {
563 self.byte_buffer_map.into_iter()
564 }
565}
566
567#[cfg(feature = "mzsignal")]
568mod mzsignal_impl {
569 use super::*;
570
571 use crate::to_bytes;
572 use mzsignal::ArrayPair;
573
574 impl From<ArrayPair<'_>> for BinaryArrayMap {
575 fn from(value: ArrayPair<'_>) -> Self {
576 let mz_array = DataArray::wrap(
577 &ArrayType::MZArray,
578 BinaryDataArrayType::Float64,
579 to_bytes(&value.mz_array),
580 );
581
582 let intensity_array = DataArray::wrap(
583 &ArrayType::IntensityArray,
584 BinaryDataArrayType::Float32,
585 to_bytes(&value.intensity_array),
586 );
587
588 let mut array_map = BinaryArrayMap::new();
589 array_map.add(mz_array);
590 array_map.add(intensity_array);
591 array_map
592 }
593 }
594}
595
596#[derive(Debug, Default, Clone, Copy, PartialEq)]
597#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
598struct NonNaNF64(f64);
599
600impl std::hash::Hash for NonNaNF64 {
601 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
602 state.write_u64(self.0.to_bits());
603 }
604}
605
606impl identity_hash::IdentityHashable for NonNaNF64 {}
607
608impl From<f64> for NonNaNF64 {
609 fn from(value: f64) -> Self {
610 Self::wrap(value)
611 .unwrap_or_else(|| panic!("Expected an order-able f64 value, but found {}", value))
612 }
613}
614
615impl NonNaNF64 {
616 fn wrap(value: f64) -> Option<Self> {
617 if value.is_nan() {
618 None
619 } else {
620 Some(Self(value))
621 }
622 }
623}
624
625impl PartialOrd for NonNaNF64 {
626 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
627 Some(self.cmp(other))
628 }
629}
630
631impl Eq for NonNaNF64 {}
632
633impl Ord for NonNaNF64 {
634 fn cmp(&self, other: &Self) -> Ordering {
635 self.0.total_cmp(&other.0)
636 }
637}
638
639macro_rules! _populate_stacked_array_from {
640 ($im_dim:ident, $view:ident, $index_map:ident, $array_bins:ident, $array_type:ident, $array:ident) => {
641 for (i_im, im) in $im_dim.iter() {
642 let v = $view[*i_im];
643 let i_axis = $index_map[&NonNaNF64(*im)];
644 let bin = &mut $array_bins[i_axis];
645 if let Some(bin_array) = bin.get_mut($array_type) {
646 bin_array.push(v)?;
647 } else {
648 let mut bin_array = DataArray::from_name_and_type($array_type, $array.dtype());
649 *bin_array.unit_mut() = $array.unit();
650 bin_array.push(v)?;
651 bin.add(bin_array);
652 }
653 }
654 };
655}
656
657#[derive(Debug, Default, Clone)]
670#[cfg_attr(feature = "serde", serde_with::serde_as)]
671#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
672pub struct BinaryArrayMap3D {
673 pub ion_mobility_dimension: Vec<f64>,
674 pub ion_mobility_type: ArrayType,
675 pub ion_mobility_unit: Unit,
676 pub arrays: Vec<BinaryArrayMap>,
677 pub additional_arrays: BinaryArrayMap,
678 #[cfg_attr(feature = "serde", serde_as(as = "Vec<(_, _)>"))]
679 ion_mobility_index: HashMap<NonNaNF64, usize, BuildHasherDefault<IdentityHasher<NonNaNF64>>>,
680 parameters_of: HashMap<ArrayType, mzdata_param::ParamList>
681}
682
683impl BinaryArrayMap3D {
684 pub fn from_ion_mobility_dimension(
690 ion_mobility_dimension: Vec<f64>,
691 ion_mobility_type: ArrayType,
692 ion_mobility_unit: Unit,
693 ) -> BinaryArrayMap3D {
694 let ion_mobility_index: HashMap<_, _, BuildHasherDefault<IdentityHasher<NonNaNF64>>> = ion_mobility_dimension
695 .iter()
696 .copied()
697 .enumerate()
698 .map(|(i, f)| {
699 (
700 NonNaNF64::wrap(f)
701 .unwrap_or_else(|| panic!("Expected non-NaN value for ion mobility")),
702 i,
703 )
704 })
705 .collect();
706 let mut arrays = Vec::new();
707 arrays.resize_with(ion_mobility_dimension.len(), BinaryArrayMap::default);
708 Self {
709 ion_mobility_dimension,
710 ion_mobility_type,
711 ion_mobility_unit,
712 arrays,
713 ion_mobility_index,
714 additional_arrays: Default::default(),
715 parameters_of: Default::default(),
716 }
717 }
718
719 pub fn from_ion_mobility_dimension_and_arrays(
725 ion_mobility_dimension: Vec<f64>,
726 ion_mobility_type: ArrayType,
727 ion_mobility_unit: Unit,
728 arrays: Vec<BinaryArrayMap>,
729 ) -> BinaryArrayMap3D {
730 let ion_mobility_index: HashMap<_, _, _> = ion_mobility_dimension
731 .iter()
732 .copied()
733 .enumerate()
734 .map(|(i, f)| {
735 (
736 NonNaNF64::wrap(f)
737 .unwrap_or_else(|| panic!("Expected non-NaN value for ion mobility")),
738 i,
739 )
740 })
741 .collect();
742
743 assert_eq!(
744 ion_mobility_dimension.len(),
745 arrays.len(),
746 "The number of ion mobility points does not match the number of point-level maps!"
747 );
748
749 Self {
750 ion_mobility_dimension,
751 ion_mobility_type,
752 ion_mobility_unit,
753 arrays,
754 ion_mobility_index,
755 additional_arrays: Default::default(),
756 parameters_of: Default::default(),
757 }
758 }
759
760 pub fn params_of(&self, array_type: &ArrayType) -> Option<&Vec<Param>> {
766 self.parameters_of.get(array_type)
767 }
768
769 pub fn params_of_mut(&mut self, array_type: &ArrayType) -> Option<&mut Vec<Param>> {
777 self.parameters_of.get_mut(array_type)
778 }
779
780 pub fn params_of_entry(&mut self, array_type: ArrayType) -> std::collections::hash_map::Entry<'_, ArrayType, Vec<Param>> {
783 self.parameters_of.entry(array_type)
784 }
785
786 pub fn insert_params_of(&mut self, array_type: ArrayType, params: Vec<Param>) -> Option<Vec<Param>> {
796 self.parameters_of.insert(array_type, params)
797 }
798
799 pub fn get_ion_mobility(&self, ion_mobility: f64) -> Option<&BinaryArrayMap> {
801 if let Some(i) = NonNaNF64::wrap(ion_mobility) {
802 if let Some(i) = self.ion_mobility_index.get(&i) {
803 self.arrays.get(*i)
804 } else {
805 None
806 }
807 } else {
808 None
809 }
810 }
811
812 pub fn search_ion_mobility(
818 &self,
819 ion_mobility: f64,
820 error_tolerance: f64,
821 ) -> Option<(&BinaryArrayMap, f64)> {
822 match self
823 .ion_mobility_dimension
824 .binary_search_by(|x: &f64| x.total_cmp(&ion_mobility))
825 {
826 Ok(i) => {
827 let delta = ion_mobility - self.ion_mobility_dimension[i];
828 if delta.abs() <= error_tolerance {
829 self.arrays.get(i).map(|a| (a, delta))
830 } else {
831 None
832 }
833 }
834 Err(i) => {
835 if self.arrays.is_empty() {
836 return None;
837 }
838 let delta = ion_mobility - self.ion_mobility_dimension[i];
839 if delta.abs() <= error_tolerance {
840 self.arrays.get(i).map(|a| (a, delta))
841 } else {
842 None
843 }
844 }
845 }
846 }
847
848 pub fn search_ion_mobility_index(&self, ion_mobility: f64, error_tolerance: f64) -> Option<(usize, f64)> {
854 match self
855 .ion_mobility_dimension
856 .binary_search_by(|x: &f64| x.total_cmp(&ion_mobility))
857 {
858 Ok(i) => {
859 let delta = ion_mobility - self.ion_mobility_dimension[i];
860 if delta.abs() <= error_tolerance {
861 Some((i, self.ion_mobility_dimension[i]))
862 } else {
863 None
864 }
865 }
866 Err(i) => {
867 if self.arrays.is_empty() {
868 return None;
869 }
870 let delta = ion_mobility - self.ion_mobility_dimension[i];
871 if delta.abs() <= error_tolerance {
872 Some((i, self.ion_mobility_dimension[i]))
873 } else {
874 None
875 }
876 }
877 }
878 }
879
880 pub fn get_ion_mobility_mut(&mut self, ion_mobility: f64) -> Option<&mut BinaryArrayMap> {
884 if let Some(i) = NonNaNF64::wrap(ion_mobility) {
885 if let Some(i) = self.ion_mobility_index.get(&i) {
886 self.arrays.get_mut(*i)
887 } else {
888 None
889 }
890 } else {
891 None
892 }
893 }
894
895 pub fn iter(&self) -> impl Iterator<Item = (f64, &BinaryArrayMap)> {
898 self.ion_mobility_dimension
899 .iter()
900 .copied()
901 .zip(self.arrays.iter())
902 }
903
904 pub fn iter_mut(&mut self) -> impl Iterator<Item = (f64, &mut BinaryArrayMap)> {
907 self.ion_mobility_dimension
908 .iter()
909 .copied()
910 .zip(self.arrays.iter_mut())
911 }
912
913 pub fn unstack(&self) -> Result<BinaryArrayMap, ArrayRetrievalError> {
923 let mut destination = self.additional_arrays.clone();
924
925 let mut im_dim =
926 DataArray::from_name_and_type(&self.ion_mobility_type, BinaryDataArrayType::Float64);
927 im_dim.unit = self.ion_mobility_unit;
928 let mut sizes = Vec::new();
929 let mut mz_size = None;
930 for (layer, im) in self.arrays.iter().zip(self.ion_mobility_dimension.iter()) {
931 sizes.clear();
932 for (key, array) in layer.iter() {
933 match destination.get_mut(key) {
934 Some(sink) => {
935 sink.extend_raw(&array.data).inspect_err(|e| {
936 log::error!("Failed to extend {key:?}: {e}");
937 })?;
938 }
939 None => {
940 destination.add(array.clone());
941 }
942 }
943 if matches!(key, ArrayType::MZArray) {
944 mz_size = Some(array.data_len()?);
945 } else {
946 sizes.push((key, array.data_len()?));
947 }
948 }
949 if let Some(mz_size) = mz_size {
950 im_dim.extend_iter(std::iter::repeat_n(*im, mz_size))?;
951 } else if let Some((_, size)) = sizes.first() {
952 im_dim.extend_iter(std::iter::repeat_n(*im, *size))?;
953 }
954 }
955
956 let sorter = if let Some(mz_array) = destination.get(&ArrayType::MZArray) {
957 let mzs = mz_array.to_f64()?;
958 let ims = im_dim.to_f64()?;
959
960 let mut indices: Vec<usize> = Vec::with_capacity(mzs.len());
961 indices.extend(0..mzs.len());
962 indices.sort_by(|i, j| {
963 mzs[*i]
964 .total_cmp(&mzs[*j])
965 .then_with(|| ims[*i].total_cmp(&ims[*j]))
966 });
967 Some(indices)
968 } else {
969 None
970 };
971
972 let final_size = im_dim.data_len()?;
973
974 destination.add(im_dim);
975
976 if let Some(sorter) = sorter {
977 destination.sort_from_indices(sorter)?;
978 } else if final_size > 0 {
979 log::debug!("Unsorted unstack");
980 }
981
982 for (k, v) in destination.iter_mut(){
983 if let Some(pars) = self.parameters_of.get(k) {
984 v.params_mut().extend(pars.iter().cloned());
985 }
986 }
987 Ok(destination)
988 }
989
990 pub fn stack(source: &BinaryArrayMap) -> Result<Self, ArrayRetrievalError> {
1002 let mut this = Self::default();
1003 if !source.has_ion_mobility() {
1004 return Err(ArrayRetrievalError::NotFound(ArrayType::IonMobilityArray));
1005 }
1006 let (im_dim, im_type) = source.ion_mobility()?;
1007 this.ion_mobility_unit = source.get(&im_type).unwrap().unit;
1008 this.ion_mobility_type = im_type;
1009 if im_dim.is_empty() {
1010 return Ok(this);
1011 }
1012 let mut im_dim: Vec<(usize, f64)> = im_dim.iter().copied().enumerate().collect();
1013 im_dim.sort_by(|(_, va), (_, vb)| va.total_cmp(vb));
1014
1015 let mut im_axis = Vec::with_capacity(200);
1016 let mut last_v = im_dim.first().unwrap().1 - 1.0;
1017 let mut index_map = HashMap::default();
1018 for (_, v) in im_dim.iter() {
1019 if v.total_cmp(&last_v).is_gt() {
1020 last_v = *v;
1021 index_map.insert(NonNaNF64::from(*v), im_axis.len());
1022 im_axis.push(*v);
1023 }
1024 }
1025
1026 let mut array_bins: Vec<BinaryArrayMap> = Vec::with_capacity(im_axis.len());
1027 array_bins.resize(im_axis.len(), BinaryArrayMap::default());
1028 for (array_type, array) in source.iter() {
1029 if array_type.is_ion_mobility() {
1030 continue;
1031 }
1032 if array.data_len()? != im_dim.len() {
1033 this.additional_arrays.add(array.clone());
1034 continue;
1035 }
1036 if !array.params().is_empty() {
1037 this.parameters_of.insert(array_type.clone(), array.params().to_vec());
1038 }
1039 match array.dtype() {
1040 BinaryDataArrayType::Unknown => {
1041 panic!("Cannot re-sort opaque or unknown dimension data types")
1042 }
1043 BinaryDataArrayType::Float64 => {
1044 let view = array.to_f64()?;
1045 _populate_stacked_array_from!(
1046 im_dim, view, index_map, array_bins, array_type, array
1047 );
1048 }
1049 BinaryDataArrayType::Float32 => {
1050 let view = array.to_f32()?;
1051 _populate_stacked_array_from!(
1052 im_dim, view, index_map, array_bins, array_type, array
1053 );
1054 }
1055 BinaryDataArrayType::Int64 => {
1056 let view = array.to_i64()?;
1057 _populate_stacked_array_from!(
1058 im_dim, view, index_map, array_bins, array_type, array
1059 );
1060 }
1061 BinaryDataArrayType::Int32 => {
1062 let view = array.to_i32()?;
1063 _populate_stacked_array_from!(
1064 im_dim, view, index_map, array_bins, array_type, array
1065 );
1066 }
1067 BinaryDataArrayType::ASCII => {
1068 let view = array.decode()?;
1069 _populate_stacked_array_from!(
1070 im_dim, view, index_map, array_bins, array_type, array
1071 );
1072 }
1073 }
1074 }
1075 this.ion_mobility_dimension = im_axis;
1076 this.ion_mobility_index = index_map;
1077 this.arrays = array_bins;
1078 Ok(this)
1079 }
1080}
1081
1082impl TryFrom<BinaryArrayMap> for BinaryArrayMap3D {
1083 type Error = ArrayRetrievalError;
1084
1085 fn try_from(value: BinaryArrayMap) -> Result<Self, Self::Error> {
1087 Self::stack(&value)
1088 }
1089}
1090
1091impl TryFrom<&BinaryArrayMap> for BinaryArrayMap3D {
1093 type Error = ArrayRetrievalError;
1094
1095 fn try_from(value: &BinaryArrayMap) -> Result<Self, Self::Error> {
1096 Self::stack(value)
1097 }
1098}
1099
1100impl TryFrom<BinaryArrayMap3D> for BinaryArrayMap {
1101 type Error = ArrayRetrievalError;
1102
1103 fn try_from(value: BinaryArrayMap3D) -> Result<Self, Self::Error> {
1104 value.unstack()
1105 }
1106}
1107
1108impl TryFrom<&BinaryArrayMap3D> for BinaryArrayMap {
1110 type Error = ArrayRetrievalError;
1111
1112 fn try_from(value: &BinaryArrayMap3D) -> Result<Self, Self::Error> {
1113 value.unstack()
1114 }
1115}
1116
1117
1118
1119#[cfg(test)]
1120mod test {
1121 use crate::BinaryDataArrayType;
1122
1123 use super::*;
1124 use std::fs;
1125 use std::io::{self, prelude::*};
1126
1127 fn make_array_from_file() -> io::Result<DataArray> {
1128 let mut fh = fs::File::open("../../test/data/mz_f64_zlib_bas64.txt")?;
1129 let mut buf = String::new();
1130 fh.read_to_string(&mut buf)?;
1131 let bytes: Vec<u8> = buf.into();
1132 let mut da = DataArray::wrap(&ArrayType::MZArray, BinaryDataArrayType::Float64, bytes);
1133 da.compression = BinaryCompressionType::Zlib;
1134 Ok(da)
1135 }
1136
1137 #[test]
1138 fn test_construction() -> io::Result<()> {
1139 let da = make_array_from_file()?;
1140 let mut map = BinaryArrayMap::new();
1141 assert!(!map.has_array(&ArrayType::MZArray));
1142 map.add(da);
1143 assert!(map.has_array(&ArrayType::MZArray));
1144 Ok(())
1145 }
1146
1147 #[test]
1148 fn test_decode() -> io::Result<()> {
1149 let da = make_array_from_file()?;
1150 let mut map = BinaryArrayMap::new();
1151 map.add(da);
1152 assert_eq!(
1153 map.get(&ArrayType::MZArray).unwrap().compression,
1154 BinaryCompressionType::Zlib
1155 );
1156 map.decode_all_arrays()?;
1157 assert_eq!(
1158 map.get(&ArrayType::MZArray).unwrap().compression,
1159 BinaryCompressionType::Decoded
1160 );
1161 Ok(())
1162 }
1163
1164 #[test]
1165 fn test_sort_from() -> io::Result<()> {
1166 let mut arr = make_array_from_file()?;
1167 let dat = arr.coerce_mut::<f64>()?;
1168 dat.reverse();
1169
1170 let mut arr2 = make_array_from_file()?;
1171 arr2.name = ArrayType::TimeArray;
1172 arr2.store_as(BinaryDataArrayType::Float32)?;
1173 let dat = arr2.coerce_mut::<f32>()?;
1174 dat.reverse();
1175
1176 let mut map = BinaryArrayMap::new();
1177 map.add(arr);
1178 map.add(arr2);
1179 map.sort_by_array(&ArrayType::MZArray)?;
1180 let arr = map.mzs()?;
1181 let (_, state) = arr.iter().copied().fold((-f64::INFINITY, true), |(last, state), next|{
1182 if !state {
1183 (last, state)
1184 } else {
1185 (next, last <= next)
1186 }
1187 });
1188 assert!(state, "Failed to sort m/z array {arr:?}");
1189
1190 let arr = map.get(&ArrayType::TimeArray).unwrap().to_f64()?;
1191 let (_, state) = arr.iter().copied().fold((-f64::INFINITY, true), |(last, state), next|{
1192 if !state {
1193 (last, state)
1194 } else {
1195 (next, last <= next)
1196 }
1197 });
1198 assert!(state, "Failed to sort parallel array {arr:?}");
1199 Ok(())
1200 }
1201}