1use std::borrow::Cow;
2use std::cmp::Ordering;
3use std::collections::HashMap;
4use std::collections::hash_map::{Iter, IterMut};
5use std::convert::TryFrom;
6
7#[cfg(feature = "parallelism")]
8use rayon::prelude::*;
9
10use mzpeaks::Tolerance;
11
12use mzdata_param::{Param, ParamDescribed, Unit};
13
14use super::BinaryDataArrayType;
15use super::array::DataArray;
16use super::encodings::{ArrayRetrievalError, ArrayType, BinaryCompressionType};
17use super::traits::{ByteArrayView, ByteArrayViewMut};
18
19#[derive(Debug, Default, Clone)]
35#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
36pub struct BinaryArrayMap {
37 pub byte_buffer_map: HashMap<ArrayType, DataArray>,
38}
39
40impl BinaryArrayMap {
41 pub fn new() -> BinaryArrayMap {
42 BinaryArrayMap {
43 ..Default::default()
44 }
45 }
46
47 pub fn len(&self) -> usize {
49 self.byte_buffer_map.len()
50 }
51
52 pub fn is_empty(&self) -> bool {
53 self.byte_buffer_map.is_empty()
54 }
55
56 pub fn has_ion_mobility(&self) -> bool {
58 self.byte_buffer_map.keys().any(|a| a.is_ion_mobility())
59 }
60
61 pub fn iter(&self) -> Iter<'_, ArrayType, DataArray> {
63 self.byte_buffer_map.iter()
64 }
65
66 #[cfg(feature = "parallelism")]
67 pub fn par_iter(&self) -> rayon::collections::hash_map::Iter<'_, ArrayType, DataArray> {
68 self.byte_buffer_map.par_iter()
69 }
70
71 pub fn iter_mut(&mut self) -> IterMut<'_, ArrayType, DataArray> {
73 self.byte_buffer_map.iter_mut()
74 }
75
76 #[cfg(feature = "parallelism")]
77 pub fn par_iter_mut(
78 &mut self,
79 ) -> rayon::collections::hash_map::IterMut<'_, ArrayType, DataArray> {
80 self.byte_buffer_map.par_iter_mut()
81 }
82
83 pub fn encode_array(
90 &mut self,
91 array_type: &ArrayType,
92 compression: BinaryCompressionType,
93 ) -> Result<(), ArrayRetrievalError> {
94 if let Some(arr) = self.get_mut(array_type) {
95 arr.store_compressed(compression)?;
96 Ok(())
97 } else {
98 Err(ArrayRetrievalError::NotFound(array_type.clone()))
99 }
100 }
101
102 pub fn decode_all_arrays(&mut self) -> Result<(), ArrayRetrievalError> {
122 #[cfg(not(feature = "parallelism"))]
123 {
124 self._decode_all_arrays()
125 }
126 #[cfg(feature = "parallelism")]
127 {
128 if self.len() > 2 {
129 self._decode_all_arrays_parallel()
130 } else {
131 self._decode_all_arrays()
132 }
133 }
134 }
135
136 fn _decode_all_arrays(&mut self) -> Result<(), ArrayRetrievalError> {
137 for (_key, value) in self.iter_mut() {
138 match value.compression {
139 BinaryCompressionType::Decoded => {}
140 _ => {
141 value.decode_and_store()?;
142 }
143 }
144 }
145 Ok(())
146 }
147
148 #[cfg(feature = "parallelism")]
149 fn _decode_all_arrays_parallel(&mut self) -> Result<(), ArrayRetrievalError> {
150 let res: Result<(), ArrayRetrievalError> = self
151 .iter_mut()
152 .par_bridge()
153 .map(|(_key, value)| {
154 match value.compression {
155 BinaryCompressionType::Decoded => {}
156 _ => {
157 value.decode_and_store()?;
158 }
159 }
160 Ok(())
161 })
162 .collect::<Result<(), ArrayRetrievalError>>();
163 res
164 }
165
166 pub fn decode_array(&mut self, array_type: &ArrayType) -> Result<(), ArrayRetrievalError> {
172 if let Some(array) = self.get_mut(array_type) {
173 array.decode_and_store()?;
174 Ok(())
175 } else {
176 Err(ArrayRetrievalError::NotFound(array_type.clone()))
177 }
178 }
179
180 pub fn add(&mut self, array: DataArray) {
182 self.byte_buffer_map.insert(array.name.clone(), array);
183 }
184
185 pub fn get(&self, array_type: &ArrayType) -> Option<&DataArray> {
187 self.byte_buffer_map.get(array_type)
188 }
189
190 pub fn get_mut(&mut self, array_type: &ArrayType) -> Option<&mut DataArray> {
192 self.byte_buffer_map.get_mut(array_type)
193 }
194
195 pub fn has_array(&self, array_type: &ArrayType) -> bool {
197 self.byte_buffer_map.contains_key(array_type)
198 }
199
200 pub fn clear(&mut self) {
202 self.byte_buffer_map.clear();
203 }
204
205 pub fn search(&self, query: f64, error_tolerance: Tolerance) -> Option<usize> {
210 if let Ok(mzs) = self.mzs() {
211 let (lower, _upper) = error_tolerance.bounds(query);
212 match mzs[..].binary_search_by(|m| m.partial_cmp(&lower).unwrap()) {
213 Ok(i) => {
214 let mut best_error = error_tolerance.call(query, mzs[i]).abs();
215 let mut best_index = i;
216 let mut index = i + 1;
217 while index < mzs.len() {
218 let error = error_tolerance.call(query, mzs[index]).abs();
219 if error < best_error {
220 best_index = index;
221 best_error = error;
222 }
223 index += 1;
224 }
225 if best_error < error_tolerance.tol() {
226 return Some(best_index);
227 }
228 None
229 }
230 Err(_err) => None,
231 }
232 } else {
233 None
234 }
235 }
236
237 pub fn mzs(&'_ self) -> Result<Cow<'_, [f64]>, ArrayRetrievalError> {
252 let mz_array = self
253 .get(&ArrayType::MZArray)
254 .ok_or(ArrayRetrievalError::NotFound(ArrayType::MZArray))?
255 .to_f64()
256 .inspect_err(|e| log::error!("Failed to decode m/z array: {e}"))?;
257 Ok(mz_array)
258 }
259
260 pub fn sort_from_indices(&mut self, mut mask: Vec<usize>) -> Result<(), ArrayRetrievalError> {
277 let n = mask.len();
278 const TOMBSTONE: usize = usize::MAX;
279 for idx in 0..n {
280 if mask[idx] != TOMBSTONE {
281 let mut current_idx = idx;
282 loop {
283 let next_idx = mask[current_idx];
284 mask[current_idx] = TOMBSTONE;
285 if mask[next_idx] == TOMBSTONE {
286 break;
287 }
288 for (_, v) in self.iter_mut() {
289 if v.data_len()? != n {
290 continue;
291 }
292 match v.dtype {
293 BinaryDataArrayType::Float64 => {
294 let view = v.coerce_mut::<f64>()?;
295 view.swap(current_idx, next_idx);
296 }
297 BinaryDataArrayType::Float32 => {
298 let view = v.coerce_mut::<f32>()?;
299 view.swap(current_idx, next_idx);
300 }
301 BinaryDataArrayType::Int64 => {
302 let view = v.coerce_mut::<i64>()?;
303 view.swap(current_idx, next_idx);
304 }
305 BinaryDataArrayType::Int32 => {
306 let view = v.coerce_mut::<i32>()?;
307 view.swap(current_idx, next_idx);
308 }
309 BinaryDataArrayType::ASCII => todo!(),
310 BinaryDataArrayType::Unknown => todo!(),
311 }
312 }
313 current_idx = next_idx;
314 }
315 }
316 }
317 Ok(())
318 }
319
320 pub fn sort_by_array(&mut self, name: &ArrayType) -> Result<(), ArrayRetrievalError> {
328 let query_axis = self
329 .get(name)
330 .ok_or_else(|| ArrayRetrievalError::NotFound(name.clone()))?;
331 macro_rules! sort_mask {
332 ($conv:expr, $cmp:expr) => {{
333 let vals = $conv?;
334 if vals.is_sorted() {
335 return Ok(());
336 }
337 let n = vals.len();
338 let mut mask: Vec<usize> = (0..n).into_iter().collect();
339 mask.sort_by(|i, j| {
340 let a = vals[*i];
341 let b = vals[*j];
342 $cmp(&a, &b)
343 });
344 (mask, n)
345 }};
346 }
347 let (mask, _) = match query_axis.dtype() {
348 BinaryDataArrayType::Float64 => {
349 sort_mask!(query_axis.to_f64(), f64::total_cmp)
350 }
351 BinaryDataArrayType::Float32 => {
352 sort_mask!(query_axis.to_f32(), f32::total_cmp)
353 }
354 BinaryDataArrayType::Int64 => {
355 sort_mask!(query_axis.to_i64(), i64::cmp)
356 }
357 BinaryDataArrayType::Int32 => {
358 sort_mask!(query_axis.to_i32(), i32::cmp)
359 }
360 BinaryDataArrayType::ASCII => todo!(),
361 BinaryDataArrayType::Unknown => todo!(),
362 };
363
364 self.sort_from_indices(mask)
365 }
366
367 pub fn mzs_mut(&mut self) -> Result<&mut [f64], ArrayRetrievalError> {
374 if let Some(mz_array) = self.get_mut(&ArrayType::MZArray) {
375 mz_array
376 .decode_and_store()
377 .inspect_err(|e| log::error!("Failed to decode m/z array: {e}"))?;
378 mz_array.store_as(BinaryDataArrayType::Float64)?;
379 mz_array.coerce_mut()
380 } else {
381 Err(ArrayRetrievalError::NotFound(ArrayType::MZArray))
382 }
383 }
384
385 pub fn intensities(&'_ self) -> Result<Cow<'_, [f32]>, ArrayRetrievalError> {
387 let intensities = self
388 .get(&ArrayType::IntensityArray)
389 .ok_or(ArrayRetrievalError::NotFound(ArrayType::IntensityArray))?
390 .to_f32()
391 .inspect_err(|e| log::error!("Failed to decode intensity array: {e:?}"))?;
392 Ok(intensities)
393 }
394
395 pub fn intensities_mut(&mut self) -> Result<&mut [f32], ArrayRetrievalError> {
400 if let Some(arr) = self.get_mut(&ArrayType::IntensityArray) {
401 arr
402 .decode_and_store()
403 .inspect_err(|e| log::error!("Failed to decode intensity array: {e}"))?;
404 arr
405 .store_as(BinaryDataArrayType::Float32)
406 .inspect_err(|e| log::error!("Failed to decode intensity array: {e}"))?;
407 arr.coerce_mut()
408 } else {
409 Err(ArrayRetrievalError::NotFound(ArrayType::IntensityArray))
410 }
411 }
412
413 pub fn charges(&'_ self) -> Result<Cow<'_, [i32]>, ArrayRetrievalError> {
415 match self.get(&ArrayType::ChargeArray) {
416 Some(data_array) => data_array.to_i32(),
417 None => Err(ArrayRetrievalError::NotFound(ArrayType::ChargeArray)),
418 }
419 }
420
421 pub fn charge_mut(&mut self) -> Result<&mut [i32], ArrayRetrievalError> {
426 if let Some(arr) = self.get_mut(&ArrayType::ChargeArray) {
427 arr.decode_and_store()?;
428 arr
429 .store_as(BinaryDataArrayType::Int32)
430 .inspect_err(|e| log::error!("Failed to decode charge array: {e}"))?;
431 arr.coerce_mut()
432 } else {
433 Err(ArrayRetrievalError::NotFound(ArrayType::ChargeArray))
434 }
435 }
436
437 pub fn ion_mobility(&self) -> Result<(Cow<'_, [f64]>, ArrayType), ArrayRetrievalError> {
439 if let Some((array_type, data_array)) = self
440 .byte_buffer_map
441 .iter()
442 .find(|(a, _)| a.is_ion_mobility())
443 {
444 Ok((
445 data_array
446 .to_f64()
447 .inspect_err(|e| log::error!("Failed to decode ion mobility array: {e}"))?,
448 array_type.clone(),
449 ))
450 } else {
451 Err(ArrayRetrievalError::NotFound(ArrayType::IonMobilityArray))
452 }
453 }
454
455 pub fn ion_mobility_mut(&mut self) -> Result<(&mut [f64], ArrayType), ArrayRetrievalError> {
459 if let Some((array_type, data_array)) = self
460 .byte_buffer_map
461 .iter_mut()
462 .find(|(a, _)| a.is_ion_mobility())
463 {
464 data_array
465 .decode_and_store()
466 .inspect_err(|e| log::error!("Failed to decode ion mobility array: {e}"))?;
467 data_array.store_as(BinaryDataArrayType::Float32)?;
468 Ok((data_array.coerce_mut()?, array_type.clone()))
469 } else {
470 Err(ArrayRetrievalError::NotFound(ArrayType::IonMobilityArray))
471 }
472 }
473
474 pub fn stack_ion_mobility(self) -> Result<BinaryArrayMap3D, ArrayRetrievalError> {
478 BinaryArrayMap3D::try_from(self)
479 }
480}
481
482impl IntoIterator for BinaryArrayMap {
483 type Item = (ArrayType, DataArray);
484
485 type IntoIter = <HashMap<ArrayType, DataArray> as IntoIterator>::IntoIter;
486
487 fn into_iter(self) -> Self::IntoIter {
488 self.byte_buffer_map.into_iter()
489 }
490}
491
492#[cfg(feature = "mzsignal")]
493mod mzsignal_impl {
494 use super::*;
495
496 use crate::to_bytes;
497 use mzsignal::ArrayPair;
498
499 impl From<ArrayPair<'_>> for BinaryArrayMap {
500 fn from(value: ArrayPair<'_>) -> Self {
501 let mz_array = DataArray::wrap(
502 &ArrayType::MZArray,
503 BinaryDataArrayType::Float64,
504 to_bytes(&value.mz_array),
505 );
506
507 let intensity_array = DataArray::wrap(
508 &ArrayType::IntensityArray,
509 BinaryDataArrayType::Float32,
510 to_bytes(&value.intensity_array),
511 );
512
513 let mut array_map = BinaryArrayMap::new();
514 array_map.add(mz_array);
515 array_map.add(intensity_array);
516 array_map
517 }
518 }
519}
520
521#[derive(Debug, Default, Clone, Copy, PartialEq)]
522#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
523struct NonNaNF64(f64);
524
525impl std::hash::Hash for NonNaNF64 {
526 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
527 self.0.to_bits().hash(state);
528 }
529}
530
531impl From<f64> for NonNaNF64 {
532 fn from(value: f64) -> Self {
533 Self::wrap(value)
534 .unwrap_or_else(|| panic!("Expected an order-able f64 value, but found {}", value))
535 }
536}
537
538impl NonNaNF64 {
539 fn wrap(value: f64) -> Option<Self> {
540 if value.is_nan() {
541 None
542 } else {
543 Some(Self(value))
544 }
545 }
546}
547
548impl PartialOrd for NonNaNF64 {
549 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
550 Some(self.cmp(other))
551 }
552}
553
554impl Eq for NonNaNF64 {}
555
556impl Ord for NonNaNF64 {
557 fn cmp(&self, other: &Self) -> Ordering {
558 self.0.total_cmp(&other.0)
559 }
560}
561
562macro_rules! _populate_stacked_array_from {
563 ($im_dim:ident, $view:ident, $index_map:ident, $array_bins:ident, $array_type:ident, $array:ident) => {
564 for (i_im, im) in $im_dim.iter() {
565 let v = $view[*i_im];
566 let i_axis = $index_map[&NonNaNF64(*im)];
567 let bin = &mut $array_bins[i_axis];
568 if let Some(bin_array) = bin.get_mut($array_type) {
569 bin_array.push(v)?;
570 } else {
571 let mut bin_array = DataArray::from_name_and_type($array_type, $array.dtype());
572 *bin_array.unit_mut() = $array.unit();
573 bin_array.push(v)?;
574 bin.add(bin_array);
575 }
576 }
577 };
578}
579
580#[derive(Debug, Default, Clone)]
593#[cfg_attr(feature = "serde", serde_with::serde_as)]
594#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
595pub struct BinaryArrayMap3D {
596 pub ion_mobility_dimension: Vec<f64>,
597 pub ion_mobility_type: ArrayType,
598 pub ion_mobility_unit: Unit,
599 pub arrays: Vec<BinaryArrayMap>,
600 pub additional_arrays: BinaryArrayMap,
601 #[cfg_attr(feature = "serde", serde_as(as = "Vec<(_, _)>"))]
602 ion_mobility_index: HashMap<NonNaNF64, usize>,
603 parameters_of: HashMap<ArrayType, mzdata_param::ParamList>
604}
605
606impl BinaryArrayMap3D {
607 pub fn from_ion_mobility_dimension(
613 ion_mobility_dimension: Vec<f64>,
614 ion_mobility_type: ArrayType,
615 ion_mobility_unit: Unit,
616 ) -> BinaryArrayMap3D {
617 let ion_mobility_index: HashMap<_, _> = ion_mobility_dimension
618 .iter()
619 .copied()
620 .enumerate()
621 .map(|(i, f)| {
622 (
623 NonNaNF64::wrap(f)
624 .unwrap_or_else(|| panic!("Expected non-NaN value for ion mobility")),
625 i,
626 )
627 })
628 .collect();
629 let mut arrays = Vec::new();
630 arrays.resize_with(ion_mobility_dimension.len(), BinaryArrayMap::default);
631 Self {
632 ion_mobility_dimension,
633 ion_mobility_type,
634 ion_mobility_unit,
635 arrays,
636 ion_mobility_index,
637 additional_arrays: Default::default(),
638 parameters_of: Default::default(),
639 }
640 }
641
642 pub fn from_ion_mobility_dimension_and_arrays(
648 ion_mobility_dimension: Vec<f64>,
649 ion_mobility_type: ArrayType,
650 ion_mobility_unit: Unit,
651 arrays: Vec<BinaryArrayMap>,
652 ) -> BinaryArrayMap3D {
653 let ion_mobility_index: HashMap<_, _> = ion_mobility_dimension
654 .iter()
655 .copied()
656 .enumerate()
657 .map(|(i, f)| {
658 (
659 NonNaNF64::wrap(f)
660 .unwrap_or_else(|| panic!("Expected non-NaN value for ion mobility")),
661 i,
662 )
663 })
664 .collect();
665
666 assert_eq!(
667 ion_mobility_dimension.len(),
668 arrays.len(),
669 "The number of ion mobility points does not match the number of point-level maps!"
670 );
671
672 Self {
673 ion_mobility_dimension,
674 ion_mobility_type,
675 ion_mobility_unit,
676 arrays,
677 ion_mobility_index,
678 additional_arrays: Default::default(),
679 parameters_of: Default::default(),
680 }
681 }
682
683 pub fn params_of(&self, array_type: &ArrayType) -> Option<&Vec<Param>> {
689 self.parameters_of.get(array_type)
690 }
691
692 pub fn params_of_mut(&mut self, array_type: &ArrayType) -> Option<&mut Vec<Param>> {
700 self.parameters_of.get_mut(array_type)
701 }
702
703 pub fn params_of_entry(&mut self, array_type: ArrayType) -> std::collections::hash_map::Entry<'_, ArrayType, Vec<Param>> {
706 self.parameters_of.entry(array_type)
707 }
708
709 pub fn insert_params_of(&mut self, array_type: ArrayType, params: Vec<Param>) -> Option<Vec<Param>> {
719 self.parameters_of.insert(array_type, params)
720 }
721
722 pub fn get_ion_mobility(&self, ion_mobility: f64) -> Option<&BinaryArrayMap> {
724 if let Some(i) = NonNaNF64::wrap(ion_mobility) {
725 if let Some(i) = self.ion_mobility_index.get(&i) {
726 self.arrays.get(*i)
727 } else {
728 None
729 }
730 } else {
731 None
732 }
733 }
734
735 pub fn search_ion_mobility(
741 &self,
742 ion_mobility: f64,
743 error_tolerance: f64,
744 ) -> Option<(&BinaryArrayMap, f64)> {
745 match self
746 .ion_mobility_dimension
747 .binary_search_by(|x: &f64| x.total_cmp(&ion_mobility))
748 {
749 Ok(i) => {
750 let delta = ion_mobility - self.ion_mobility_dimension[i];
751 if delta.abs() <= error_tolerance {
752 self.arrays.get(i).map(|a| (a, delta))
753 } else {
754 None
755 }
756 }
757 Err(i) => {
758 if self.arrays.is_empty() {
759 return None;
760 }
761 let delta = ion_mobility - self.ion_mobility_dimension[i];
762 if delta.abs() <= error_tolerance {
763 self.arrays.get(i).map(|a| (a, delta))
764 } else {
765 None
766 }
767 }
768 }
769 }
770
771 pub fn search_ion_mobility_index(&self, ion_mobility: f64, error_tolerance: f64) -> Option<(usize, f64)> {
777 match self
778 .ion_mobility_dimension
779 .binary_search_by(|x: &f64| x.total_cmp(&ion_mobility))
780 {
781 Ok(i) => {
782 let delta = ion_mobility - self.ion_mobility_dimension[i];
783 if delta.abs() <= error_tolerance {
784 Some((i, self.ion_mobility_dimension[i]))
785 } else {
786 None
787 }
788 }
789 Err(i) => {
790 if self.arrays.is_empty() {
791 return None;
792 }
793 let delta = ion_mobility - self.ion_mobility_dimension[i];
794 if delta.abs() <= error_tolerance {
795 Some((i, self.ion_mobility_dimension[i]))
796 } else {
797 None
798 }
799 }
800 }
801 }
802
803 pub fn get_ion_mobility_mut(&mut self, ion_mobility: f64) -> Option<&mut BinaryArrayMap> {
807 if let Some(i) = NonNaNF64::wrap(ion_mobility) {
808 if let Some(i) = self.ion_mobility_index.get(&i) {
809 self.arrays.get_mut(*i)
810 } else {
811 None
812 }
813 } else {
814 None
815 }
816 }
817
818 pub fn iter(&self) -> impl Iterator<Item = (f64, &BinaryArrayMap)> {
821 self.ion_mobility_dimension
822 .iter()
823 .copied()
824 .zip(self.arrays.iter())
825 }
826
827 pub fn iter_mut(&mut self) -> impl Iterator<Item = (f64, &mut BinaryArrayMap)> {
830 self.ion_mobility_dimension
831 .iter()
832 .copied()
833 .zip(self.arrays.iter_mut())
834 }
835
836 pub fn unstack(&self) -> Result<BinaryArrayMap, ArrayRetrievalError> {
846 let mut destination = self.additional_arrays.clone();
847
848 let mut im_dim =
849 DataArray::from_name_and_type(&self.ion_mobility_type, BinaryDataArrayType::Float64);
850 im_dim.unit = self.ion_mobility_unit;
851 let mut sizes = Vec::new();
852 let mut mz_size = None;
853 for (layer, im) in self.arrays.iter().zip(self.ion_mobility_dimension.iter()) {
854 sizes.clear();
855 for (key, array) in layer.iter() {
856 match destination.get_mut(key) {
857 Some(sink) => {
858 sink.extend_raw(&array.data).inspect_err(|e| {
859 log::error!("Failed to extend {key:?}: {e}");
860 })?;
861 }
862 None => {
863 destination.add(array.clone());
864 }
865 }
866 if matches!(key, ArrayType::MZArray) {
867 mz_size = Some(array.data_len()?);
868 } else {
869 sizes.push((key, array.data_len()?));
870 }
871 }
872 if let Some(mz_size) = mz_size {
873 im_dim.extend_iter(std::iter::repeat_n(*im, mz_size))?;
874 } else if let Some((_, size)) = sizes.first() {
875 im_dim.extend_iter(std::iter::repeat_n(*im, *size))?;
876 }
877 }
878
879 let sorter = if let Some(mz_array) = destination.get(&ArrayType::MZArray) {
880 let mzs = mz_array.to_f64()?;
881 let ims = im_dim.to_f64()?;
882
883 let mut indices: Vec<usize> = Vec::with_capacity(mzs.len());
884 indices.extend(0..mzs.len());
885 indices.sort_by(|i, j| {
886 mzs[*i]
887 .total_cmp(&mzs[*j])
888 .then_with(|| ims[*i].total_cmp(&ims[*j]))
889 });
890 Some(indices)
891 } else {
892 None
893 };
894
895 let final_size = im_dim.data_len()?;
896
897 destination.add(im_dim);
898
899 if let Some(sorter) = sorter {
900 destination.sort_from_indices(sorter)?;
901 } else if final_size > 0 {
902 log::debug!("Unsorted unstack");
903 }
904
905 for (k, v) in destination.iter_mut(){
906 if let Some(pars) = self.parameters_of.get(k) {
907 v.params_mut().extend(pars.iter().cloned());
908 }
909 }
910 Ok(destination)
911 }
912
913 pub fn stack(source: &BinaryArrayMap) -> Result<Self, ArrayRetrievalError> {
925 let mut this = Self::default();
926 if !source.has_ion_mobility() {
927 return Err(ArrayRetrievalError::NotFound(ArrayType::IonMobilityArray));
928 }
929 let (im_dim, im_type) = source.ion_mobility()?;
930 this.ion_mobility_unit = source.get(&im_type).unwrap().unit;
931 this.ion_mobility_type = im_type;
932 if im_dim.is_empty() {
933 return Ok(this);
934 }
935 let mut im_dim: Vec<(usize, f64)> = im_dim.iter().copied().enumerate().collect();
936 im_dim.sort_by(|(_, va), (_, vb)| va.total_cmp(vb));
937
938 let mut im_axis = Vec::with_capacity(200);
939 let mut last_v = im_dim.first().unwrap().1 - 1.0;
940 let mut index_map = HashMap::new();
941 for (_, v) in im_dim.iter() {
942 if v.total_cmp(&last_v).is_gt() {
943 last_v = *v;
944 index_map.insert(NonNaNF64::from(*v), im_axis.len());
945 im_axis.push(*v);
946 }
947 }
948
949 let mut array_bins: Vec<BinaryArrayMap> = Vec::with_capacity(im_axis.len());
950 array_bins.resize(im_axis.len(), BinaryArrayMap::default());
951 for (array_type, array) in source.iter() {
952 if array_type.is_ion_mobility() {
953 continue;
954 }
955 if array.data_len()? != im_dim.len() {
956 this.additional_arrays.add(array.clone());
957 continue;
958 }
959 if !array.params().is_empty() {
960 this.parameters_of.insert(array_type.clone(), array.params().to_vec());
961 }
962 match array.dtype() {
963 BinaryDataArrayType::Unknown => {
964 panic!("Cannot re-sort opaque or unknown dimension data types")
965 }
966 BinaryDataArrayType::Float64 => {
967 let view = array.to_f64()?;
968 _populate_stacked_array_from!(
969 im_dim, view, index_map, array_bins, array_type, array
970 );
971 }
972 BinaryDataArrayType::Float32 => {
973 let view = array.to_f32()?;
974 _populate_stacked_array_from!(
975 im_dim, view, index_map, array_bins, array_type, array
976 );
977 }
978 BinaryDataArrayType::Int64 => {
979 let view = array.to_i64()?;
980 _populate_stacked_array_from!(
981 im_dim, view, index_map, array_bins, array_type, array
982 );
983 }
984 BinaryDataArrayType::Int32 => {
985 let view = array.to_i32()?;
986 _populate_stacked_array_from!(
987 im_dim, view, index_map, array_bins, array_type, array
988 );
989 }
990 BinaryDataArrayType::ASCII => {
991 let view = array.decode()?;
992 _populate_stacked_array_from!(
993 im_dim, view, index_map, array_bins, array_type, array
994 );
995 }
996 }
997 }
998 this.ion_mobility_dimension = im_axis;
999 this.ion_mobility_index = index_map;
1000 this.arrays = array_bins;
1001 Ok(this)
1002 }
1003}
1004
1005impl TryFrom<BinaryArrayMap> for BinaryArrayMap3D {
1006 type Error = ArrayRetrievalError;
1007
1008 fn try_from(value: BinaryArrayMap) -> Result<Self, Self::Error> {
1010 Self::stack(&value)
1011 }
1012}
1013
1014impl TryFrom<&BinaryArrayMap> for BinaryArrayMap3D {
1016 type Error = ArrayRetrievalError;
1017
1018 fn try_from(value: &BinaryArrayMap) -> Result<Self, Self::Error> {
1019 Self::stack(value)
1020 }
1021}
1022
1023impl TryFrom<BinaryArrayMap3D> for BinaryArrayMap {
1024 type Error = ArrayRetrievalError;
1025
1026 fn try_from(value: BinaryArrayMap3D) -> Result<Self, Self::Error> {
1027 value.unstack()
1028 }
1029}
1030
1031impl TryFrom<&BinaryArrayMap3D> for BinaryArrayMap {
1033 type Error = ArrayRetrievalError;
1034
1035 fn try_from(value: &BinaryArrayMap3D) -> Result<Self, Self::Error> {
1036 value.unstack()
1037 }
1038}
1039
1040
1041
1042#[cfg(test)]
1043mod test {
1044 use crate::BinaryDataArrayType;
1045
1046 use super::*;
1047 use std::fs;
1048 use std::io::{self, prelude::*};
1049
1050 fn make_array_from_file() -> io::Result<DataArray> {
1051 let mut fh = fs::File::open("../../test/data/mz_f64_zlib_bas64.txt")?;
1052 let mut buf = String::new();
1053 fh.read_to_string(&mut buf)?;
1054 let bytes: Vec<u8> = buf.into();
1055 let mut da = DataArray::wrap(&ArrayType::MZArray, BinaryDataArrayType::Float64, bytes);
1056 da.compression = BinaryCompressionType::Zlib;
1057 Ok(da)
1058 }
1059
1060 #[test]
1061 fn test_construction() -> io::Result<()> {
1062 let da = make_array_from_file()?;
1063 let mut map = BinaryArrayMap::new();
1064 assert!(!map.has_array(&ArrayType::MZArray));
1065 map.add(da);
1066 assert!(map.has_array(&ArrayType::MZArray));
1067 Ok(())
1068 }
1069
1070 #[test]
1071 fn test_decode() -> io::Result<()> {
1072 let da = make_array_from_file()?;
1073 let mut map = BinaryArrayMap::new();
1074 map.add(da);
1075 assert_eq!(
1076 map.get(&ArrayType::MZArray).unwrap().compression,
1077 BinaryCompressionType::Zlib
1078 );
1079 map.decode_all_arrays()?;
1080 assert_eq!(
1081 map.get(&ArrayType::MZArray).unwrap().compression,
1082 BinaryCompressionType::Decoded
1083 );
1084 Ok(())
1085 }
1086}