Skip to main content

scirs2_core/memory_efficient/
memmap_slice.rs

1//! Slicing operations for memory-mapped arrays.
2//!
3//! This module provides functionality for efficiently slicing memory-mapped arrays
4//! without loading the entire array into memory. These slicing operations maintain
5//! the memory-mapping and only load the required data when accessed.
6
7use super::memmap::MemoryMappedArray;
8use crate::error::{CoreError, CoreResult, ErrorContext};
9use ::ndarray::{ArrayBase, Dimension, IxDyn, SliceInfo, SliceInfoElem};
10use std::marker::PhantomData;
11use std::ops::RangeBounds;
12
13/// A slice of a memory-mapped array that maintains memory-mapping.
14///
15/// This provides a view into a subset of a memory-mapped array without
16/// loading the entire array into memory. Data is only loaded when
17/// accessed through the slice.
18pub struct MemoryMappedSlice<A, D>
19where
20    A: Clone + Copy + 'static + Send + Sync,
21    D: Dimension,
22{
23    /// The source memory-mapped array
24    source: MemoryMappedArray<A>,
25
26    /// The slice information
27    slice_info: SliceInfo<Vec<SliceInfoElem>, D, D>,
28
29    /// Phantom data for dimension type
30    phantom: PhantomData<D>,
31}
32
33impl<A, D> MemoryMappedSlice<A, D>
34where
35    A: Clone + Copy + 'static + Send + Sync,
36    D: Dimension,
37{
38    /// Creates a new slice from a memory-mapped array and slice information.
39    pub fn new(
40        source: MemoryMappedArray<A>,
41        slice_info: SliceInfo<Vec<SliceInfoElem>, D, D>,
42    ) -> Self {
43        Self {
44            source,
45            slice_info,
46            phantom: PhantomData,
47        }
48    }
49
50    /// Returns the shape of the slice.
51    ///
52    /// Note: This is a simplified version for backward compatibility.
53    /// For the accurate calculated shape, use `calculatedshape()`.
54    pub fn shape(&self) -> D {
55        // Simplified approach for backward compatibility
56        // This might not be 100% accurate but prevents breaking existing code
57        self.calculate_slicedshape().unwrap_or_default()
58    }
59
60    /// Returns the accurately calculated shape of the slice.
61    ///
62    /// Calculates the actual shape based on the slice parameters and source shape.
63    pub fn calculatedshape(&self) -> CoreResult<D> {
64        self.calculate_slicedshape()
65    }
66
67    /// Calculate the actual shape after slicing
68    fn calculate_slicedshape(&self) -> CoreResult<D> {
69        let sourceshape = &self.source.shape;
70        let slice_elements = self.slice_info.as_ref();
71
72        let mut result_dims = Vec::new();
73
74        // Process each dimension up to the source dimensions
75        for (dim_idx, &dim_size) in sourceshape.iter().enumerate() {
76            if dim_idx < slice_elements.len() {
77                match &slice_elements[dim_idx] {
78                    SliceInfoElem::Slice { start, end, step } => {
79                        // Calculate the size of this sliced dimension
80                        let start_idx = if *start < 0 {
81                            (dim_size as isize + start).max(0) as usize
82                        } else {
83                            (*start as usize).min(dim_size)
84                        };
85
86                        let end_idx = if let Some(e) = end {
87                            if *e < 0 {
88                                (dim_size as isize + e).max(0) as usize
89                            } else {
90                                (*e as usize).min(dim_size)
91                            }
92                        } else {
93                            dim_size
94                        };
95
96                        let step_size = step.max(&1).unsigned_abs();
97                        let slice_size = if end_idx > start_idx {
98                            (end_idx - start_idx).div_ceil(step_size)
99                        } else {
100                            0
101                        };
102
103                        result_dims.push(slice_size);
104                    }
105                    SliceInfoElem::Index(_) => {
106                        // Index operations reduce dimensionality by 1
107                        // Don't add this dimension to result
108                    }
109                    _ => {
110                        // NewAxis or other slice types - for now, treat as full dimension
111                        result_dims.push(dim_size);
112                    }
113                }
114            } else {
115                // Dimensions beyond slice elements are included in full
116                result_dims.push(dim_size);
117            }
118        }
119
120        // Convert to target dimension type using a more robust approach
121        Self::convert_dims_to_target_type(&result_dims)
122    }
123
124    /// Convert dimensions vector to target dimension type D
125    fn convert_dims_to_target_type(resultdims: &[usize]) -> CoreResult<D> {
126        let source_ndim = resultdims.len();
127        let target_ndim = D::NDIM;
128
129        // Handle dynamic dimensions (IxDyn) - always accept
130        if target_ndim.is_none() {
131            // For dynamic dimensions, create IxDyn directly
132            let dyn_dim = IxDyn(resultdims);
133            // This is safe because IxDyn can always be converted to itself or any Dimension type
134            // We use unsafe transmute as a last resort since we know D is IxDyn in this case
135            let converted_dim = unsafe { std::mem::transmute_copy(&dyn_dim) };
136            return Ok(converted_dim);
137        }
138
139        let target_ndim = target_ndim.expect("Operation failed");
140
141        // Check if dimensions match exactly
142        if source_ndim == target_ndim {
143            match target_ndim {
144                1 if resultdims.len() == 1 => {
145                    let dim1 = crate::ndarray::Ix1(resultdims[0]);
146                    let converted_dim = unsafe { std::mem::transmute_copy(&dim1) };
147                    return Ok(converted_dim);
148                }
149                2 if resultdims.len() == 2 => {
150                    let dim2 = crate::ndarray::Ix2(resultdims[0], resultdims[1]);
151                    let converted_dim = unsafe { std::mem::transmute_copy(&dim2) };
152                    return Ok(converted_dim);
153                }
154                3 if resultdims.len() == 3 => {
155                    let dim3 = crate::ndarray::Ix3(resultdims[0], resultdims[1], resultdims[2]);
156                    let converted_dim = unsafe { std::mem::transmute_copy(&dim3) };
157                    return Ok(converted_dim);
158                }
159                4 if resultdims.len() == 4 => {
160                    let dim4 = crate::ndarray::Ix4(
161                        resultdims[0],
162                        resultdims[1],
163                        resultdims[2],
164                        resultdims[3],
165                    );
166                    let converted_dim = unsafe { std::mem::transmute_copy(&dim4) };
167                    return Ok(converted_dim);
168                }
169                _ => {}
170            }
171
172            return Err(CoreError::DimensionError(ErrorContext::new(format!(
173                "Cannot convert {source_ndim} dimensions to target dimension type"
174            ))));
175        }
176
177        // Handle dimension mismatches
178        if source_ndim < target_ndim {
179            // Add singleton dimensions at the end
180            let mut expanded_dims = resultdims.to_vec();
181            expanded_dims.resize(target_ndim, 1);
182
183            match target_ndim {
184                1 => {
185                    if expanded_dims.len() == 1 {
186                        let dim1 = crate::ndarray::Ix1(expanded_dims[0]);
187                        let converted_dim = unsafe { std::mem::transmute_copy(&dim1) };
188                        Ok(converted_dim)
189                    } else {
190                        Err(CoreError::DimensionError(ErrorContext::new(format!(
191                            "Cannot expand to 1D from dimensions: {expanded_dims:?}"
192                        ))))
193                    }
194                }
195                2 => {
196                    if expanded_dims.len() == 2 {
197                        let dim2 = crate::ndarray::Ix2(expanded_dims[0], expanded_dims[1]);
198                        let converted_dim = unsafe { std::mem::transmute_copy(&dim2) };
199                        Ok(converted_dim)
200                    } else {
201                        Err(CoreError::DimensionError(ErrorContext::new(format!(
202                            "Cannot expand to 2D from dimensions: {expanded_dims:?}"
203                        ))))
204                    }
205                }
206                3 => {
207                    if expanded_dims.len() == 3 {
208                        let dim3 = crate::ndarray::Ix3(
209                            expanded_dims[0],
210                            expanded_dims[1],
211                            expanded_dims[2],
212                        );
213                        let converted_dim = unsafe { std::mem::transmute_copy(&dim3) };
214                        Ok(converted_dim)
215                    } else {
216                        Err(CoreError::DimensionError(ErrorContext::new(format!(
217                            "Cannot expand to 3D from dimensions: {expanded_dims:?}"
218                        ))))
219                    }
220                }
221                4 => {
222                    if expanded_dims.len() == 4 {
223                        let dim4 = crate::ndarray::Ix4(
224                            expanded_dims[0],
225                            expanded_dims[1],
226                            expanded_dims[2],
227                            expanded_dims[3],
228                        );
229                        let converted_dim = unsafe { std::mem::transmute_copy(&dim4) };
230                        Ok(converted_dim)
231                    } else {
232                        Err(CoreError::DimensionError(ErrorContext::new(format!(
233                            "Cannot expand to 4D from dimensions: {expanded_dims:?}"
234                        ))))
235                    }
236                }
237                _ => Err(CoreError::DimensionError(ErrorContext::new(format!(
238                    "Unsupported target dimension: {target_ndim}"
239                )))),
240            }
241        } else {
242            // Try to remove singleton dimensions
243            let mut squeezed_dims = Vec::new();
244            let mut removed_count = 0;
245            let dims_to_remove = source_ndim - target_ndim;
246
247            for &dim_size in resultdims {
248                if dim_size == 1 && removed_count < dims_to_remove {
249                    removed_count += 1;
250                } else {
251                    squeezed_dims.push(dim_size);
252                }
253            }
254
255            if squeezed_dims.len() != target_ndim {
256                return Err(CoreError::DimensionError(ErrorContext::new(format!(
257                    "Sliced shape has {} dimensions but target type expects {} dimensions. \
258                     Sliced shape: {:?}, source shape: {:?}, available singleton dimensions: {}",
259                    source_ndim,
260                    target_ndim,
261                    resultdims,
262                    resultdims,
263                    resultdims.iter().filter(|&&x| x == 1).count()
264                ))));
265            }
266
267            match target_ndim {
268                1 => {
269                    if squeezed_dims.len() == 1 {
270                        let dim1 = crate::ndarray::Ix1(squeezed_dims[0]);
271                        let converted_dim = unsafe { std::mem::transmute_copy(&dim1) };
272                        Ok(converted_dim)
273                    } else {
274                        Err(CoreError::DimensionError(ErrorContext::new(format!(
275                            "Cannot squeeze to 1D from dimensions: {squeezed_dims:?}"
276                        ))))
277                    }
278                }
279                2 => {
280                    if squeezed_dims.len() == 2 {
281                        let dim2 = crate::ndarray::Ix2(squeezed_dims[0], squeezed_dims[1]);
282                        let converted_dim = unsafe { std::mem::transmute_copy(&dim2) };
283                        Ok(converted_dim)
284                    } else {
285                        Err(CoreError::DimensionError(ErrorContext::new(format!(
286                            "Cannot squeeze to 2D from dimensions: {squeezed_dims:?}"
287                        ))))
288                    }
289                }
290                3 => {
291                    if squeezed_dims.len() == 3 {
292                        let dim3 = crate::ndarray::Ix3(
293                            squeezed_dims[0],
294                            squeezed_dims[1],
295                            squeezed_dims[2],
296                        );
297                        let converted_dim = unsafe { std::mem::transmute_copy(&dim3) };
298                        Ok(converted_dim)
299                    } else {
300                        Err(CoreError::DimensionError(ErrorContext::new(format!(
301                            "Cannot squeeze to 3D from dimensions: {squeezed_dims:?}"
302                        ))))
303                    }
304                }
305                4 => {
306                    if squeezed_dims.len() == 4 {
307                        let dim4 = crate::ndarray::Ix4(
308                            squeezed_dims[0],
309                            squeezed_dims[1],
310                            squeezed_dims[2],
311                            squeezed_dims[3],
312                        );
313                        let converted_dim = unsafe { std::mem::transmute_copy(&dim4) };
314                        Ok(converted_dim)
315                    } else {
316                        Err(CoreError::DimensionError(ErrorContext::new(format!(
317                            "Cannot squeeze to 4D from dimensions: {squeezed_dims:?}"
318                        ))))
319                    }
320                }
321                _ => Err(CoreError::DimensionError(ErrorContext::new(format!(
322                    "Unsupported target dimension: {target_ndim}"
323                )))),
324            }
325        }
326    }
327
328    /// Returns a reference to the source memory-mapped array.
329    pub const fn source(&self) -> &MemoryMappedArray<A> {
330        &self.source
331    }
332
333    /// Returns the slice information.
334    pub const fn slice_info(&self) -> &SliceInfo<Vec<SliceInfoElem>, D, D> {
335        &self.slice_info
336    }
337
338    /// Safely convert an array to the target dimension type with detailed error reporting.
339    fn safe_dimensionality_conversion(
340        array: crate::ndarray::ArrayBase<crate::ndarray::OwnedRepr<A>, crate::ndarray::IxDyn>,
341        context: &str,
342    ) -> CoreResult<ArrayBase<crate::ndarray::OwnedRepr<A>, D>> {
343        let sourceshape = array.shape().to_vec();
344        let source_ndim = sourceshape.len();
345        let target_ndim = D::NDIM;
346
347        // Handle dynamic dimensions (IxDyn) first
348        if target_ndim.is_none() {
349            return array.into_dimensionality::<D>().map_err(|_| {
350                CoreError::DimensionError(ErrorContext::new(format!(
351                    "Failed to convert {context} array to dynamic dimension type. Source shape: {sourceshape:?}"
352                )))
353            });
354        }
355
356        let target_ndim = target_ndim.expect("Operation failed");
357
358        // Try direct conversion first for exact matches
359        if source_ndim == target_ndim {
360            return array.into_dimensionality::<D>().map_err(|_| {
361                CoreError::DimensionError(ErrorContext::new(format!(
362                    "Dimension conversion failed for {} array despite matching dimensions ({} -> {}). Source shape: {:?}, target dimension type: {}",
363                    context, source_ndim, target_ndim, sourceshape, std::any::type_name::<D>()
364                )))
365            });
366        }
367
368        // Handle dimension mismatches with robust strategies
369        match source_ndim.cmp(&target_ndim) {
370            std::cmp::Ordering::Less => {
371                // Fewer dimensions than target - try to expand
372                Self::try_expand_dimensions(array, context, source_ndim, target_ndim)
373            }
374            std::cmp::Ordering::Greater => {
375                // More dimensions than target - try to squeeze
376                Self::try_squeeze_dimensions(array, context, source_ndim, target_ndim)
377            }
378            std::cmp::Ordering::Equal => {
379                // This case is already handled above, but for completeness
380                array.into_dimensionality::<D>().map_err(|_| {
381                    CoreError::DimensionError(ErrorContext::new(format!(
382                        "Unexpected dimension conversion failure for {context} array with matching dimensions. Source shape: {sourceshape:?}"
383                    )))
384                })
385            }
386        }
387    }
388
389    /// Try to expand dimensions by adding singleton dimensions.
390    fn try_expand_dimensions(
391        array: crate::ndarray::ArrayBase<crate::ndarray::OwnedRepr<A>, crate::ndarray::IxDyn>,
392        context: &str,
393        source_dims: usize,
394        target_dims: usize,
395    ) -> CoreResult<ArrayBase<crate::ndarray::OwnedRepr<A>, D>> {
396        let sourceshape = array.shape().to_vec();
397        let dims_to_add = target_dims - source_dims;
398
399        if dims_to_add == 0 {
400            return array.into_dimensionality::<D>().map_err(|_| {
401                CoreError::DimensionError(ErrorContext::new(format!(
402                    "Failed to convert {context} array despite equal dimensions"
403                )))
404            });
405        }
406
407        // Create expanded shape by adding singleton dimensions at the end
408        let mut expandedshape = sourceshape.clone();
409        expandedshape.resize(source_dims + dims_to_add, 1);
410
411        // Try to reshape to expanded shape
412        match array
413            .clone()
414            .into_shape_with_order(crate::ndarray::IxDyn(&expandedshape))
415        {
416            Ok(reshaped) => reshaped.into_dimensionality::<D>().map_err(|_| {
417                CoreError::DimensionError(ErrorContext::new(format!(
418                    "Failed to convert expanded {context} array to target dimension type"
419                )))
420            }),
421            Err(_) => {
422                // Try adding singleton dimensions at the beginning instead
423                let mut altshape = vec![1; dims_to_add];
424                altshape.extend_from_slice(&sourceshape);
425
426                array
427                    .into_shape_with_order(crate::ndarray::IxDyn(&altshape))
428                    .map_err(|_| {
429                        CoreError::DimensionError(ErrorContext::new(format!(
430                            "Cannot reshape {context} array from shape {sourceshape:?} to any expanded shape"
431                        )))
432                    })?
433                    .into_dimensionality::<D>()
434                    .map_err(|_| {
435                        CoreError::DimensionError(ErrorContext::new(format!(
436                            "Cannot expand {context} array from {source_dims} to {target_dims} dimensions"
437                        )))
438                    })
439            }
440        }
441    }
442
443    /// Try to squeeze singleton dimensions.
444    fn try_squeeze_dimensions(
445        array: crate::ndarray::ArrayBase<crate::ndarray::OwnedRepr<A>, crate::ndarray::IxDyn>,
446        context: &str,
447        source_dims: usize,
448        target_dims: usize,
449    ) -> CoreResult<ArrayBase<crate::ndarray::OwnedRepr<A>, D>> {
450        let sourceshape = array.shape().to_vec();
451
452        // Find and remove singleton dimensions
453        let mut squeezedshape = Vec::new();
454        let mut removed_dims = 0;
455        let dims_to_remove = source_dims - target_dims;
456
457        for &dim_size in &sourceshape {
458            if dim_size == 1 && removed_dims < dims_to_remove {
459                // Skip singleton dimension
460                removed_dims += 1;
461            } else {
462                squeezedshape.push(dim_size);
463            }
464        }
465
466        if squeezedshape.len() != target_dims {
467            return Err(CoreError::DimensionError(ErrorContext::new(format!(
468                "Cannot squeeze {} array from {} to {} dimensions. Source shape: {:?}, only {} singleton dimensions available",
469                context, source_dims, target_dims, sourceshape,
470                sourceshape.iter().filter(|&&x| x == 1).count()
471            ))));
472        }
473
474        // Reshape to squeezed shape and convert
475        array
476            .into_shape_with_order(crate::ndarray::IxDyn(&squeezedshape))
477            .map_err(|_| {
478                CoreError::DimensionError(ErrorContext::new(format!(
479                    "Cannot reshape {context} array from shape {sourceshape:?} to squeezed shape {squeezedshape:?}"
480                )))
481            })?
482            .into_dimensionality::<D>()
483            .map_err(|_| {
484                CoreError::DimensionError(ErrorContext::new(format!(
485                    "Cannot convert squeezed {context} array from {source_dims} to {target_dims} dimensions"
486                )))
487            })
488    }
489
490    /// Loads the slice data into memory.
491    ///
492    /// This method materializes the slice by loading only the necessary data
493    /// from the memory-mapped file.
494    pub fn load(&self) -> CoreResult<ArrayBase<crate::ndarray::OwnedRepr<A>, D>> {
495        // Get the raw data slice
496        let data_slice = self.source.as_slice();
497
498        // Use generic approach that works for all dimension types
499        self.load_slice_generic(data_slice)
500    }
501
502    /// Generic slice loading that works for all dimension types
503    fn load_slice_generic(
504        &self,
505        data_slice: &[A],
506    ) -> CoreResult<ArrayBase<crate::ndarray::OwnedRepr<A>, D>> {
507        use ::ndarray::IxDyn;
508
509        // Validate dimension compatibility first
510        self.validate_dimension_compatibility()?;
511
512        // Create dynamic array view from source
513        let sourceshape = IxDyn(&self.source.shape);
514        let source_array =
515            crate::ndarray::ArrayView::from_shape(sourceshape, data_slice).map_err(|e| {
516                CoreError::ShapeError(ErrorContext::new(format!(
517                    "Failed to create array view from source shape {:?}: {}",
518                    self.source.shape, e
519                )))
520            })?;
521
522        // Apply the slice using ndarray's generic slicing
523        let slice_elements = self.slice_info.as_ref();
524        let sliced = self.apply_slice_safely_owned(source_array, slice_elements)?;
525
526        // Convert to target dimension with robust error handling
527        Self::safe_dimensionality_conversion(sliced, "sliced array")
528    }
529
530    /// Validate that the slice operation is compatible with target dimension
531    fn validate_dimension_compatibility(&self) -> CoreResult<()> {
532        let source_ndim = self.source.shape.len();
533        let slice_elements = self.slice_info.as_ref();
534
535        // Calculate the resulting dimensions more accurately
536        let mut resulting_dims = 0;
537        let mut index_operations = 0;
538
539        // Count dimensions that will remain after slicing
540        for (i, elem) in slice_elements.iter().enumerate() {
541            if i >= source_ndim {
542                // Beyond source dimensions - may be NewAxis or other slice types
543                // For safety, assume it adds a dimension
544                resulting_dims += 1;
545            } else {
546                match elem {
547                    SliceInfoElem::Index(_) => {
548                        // Index reduces dimensionality by 1
549                        index_operations += 1;
550                    }
551                    SliceInfoElem::Slice { .. } => {
552                        // Slice preserves the dimension
553                        resulting_dims += 1;
554                    }
555                    // Note: NewAxis might not be available in all ndarray versions
556                    // Handle other slice types defensively
557                    _ => {
558                        // Default case - preserve dimension
559                        resulting_dims += 1;
560                    }
561                }
562            }
563        }
564
565        // Add dimensions beyond slice elements (they are preserved)
566        if slice_elements.len() < source_ndim {
567            resulting_dims += source_ndim - slice_elements.len();
568        }
569
570        // Check if target dimension is compatible
571        if let Some(target_ndim) = D::NDIM {
572            if resulting_dims != target_ndim {
573                return Err(CoreError::DimensionError(ErrorContext::new(format!(
574                    "Dimension mismatch: slice operation will result in {}D array, but target type expects {}D. Source shape: {:?} ({}D), slice elements: {}, index operations: {}",
575                    resulting_dims, target_ndim, self.source.shape, source_ndim,
576                    slice_elements.len(), index_operations
577                ))));
578            }
579        }
580
581        Ok(())
582    }
583
584    /// Safely apply slice to array view with proper error handling, returning owned array
585    fn apply_slice_safely_owned(
586        &self,
587        source_array: crate::ndarray::ArrayView<A, IxDyn>,
588        slice_elements: &[SliceInfoElem],
589    ) -> CoreResult<crate::ndarray::Array<A, IxDyn>> {
590        if slice_elements.is_empty() {
591            return Ok(source_array.to_owned());
592        }
593
594        // Apply the slice using ndarray's slicing
595        let sliced = source_array.slice_each_axis(|ax| {
596            if ax.axis.index() < slice_elements.len() {
597                match &slice_elements[ax.axis.index()] {
598                    SliceInfoElem::Slice { start, end, step } => {
599                        // Handle negative indices and bounds checking
600                        let dim_size = ax.len as isize;
601                        let safe_start = self.handle_negative_index(*start, dim_size);
602                        let safe_end = if let Some(e) = end {
603                            self.handle_negative_index(*e, dim_size)
604                        } else {
605                            dim_size
606                        };
607
608                        // Ensure indices are within bounds
609                        let clamped_start = safe_start.max(0).min(dim_size) as usize;
610                        let clamped_end = safe_end.max(0).min(dim_size) as usize;
611
612                        // Validate step
613                        let safe_step = step.max(&1).unsigned_abs();
614
615                        crate::ndarray::Slice::new(
616                            clamped_start as isize,
617                            Some(clamped_end as isize),
618                            safe_step as isize,
619                        )
620                    }
621                    SliceInfoElem::Index(idx) => {
622                        let dim_size = ax.len as isize;
623                        let safe_idx = self.handle_negative_index(*idx, dim_size);
624                        let clamped_idx = safe_idx.max(0).min(dim_size - 1) as usize;
625                        crate::ndarray::Slice::new(
626                            clamped_idx as isize,
627                            Some((clamped_idx + 1) as isize),
628                            1,
629                        )
630                    }
631                    _ => crate::ndarray::Slice::new(0, None, 1),
632                }
633            } else {
634                crate::ndarray::Slice::new(0, None, 1)
635            }
636        });
637
638        Ok(sliced.to_owned())
639    }
640
641    /// Handle negative indices properly  
642    fn handle_negative_index(&self, index: isize, dimsize: isize) -> isize {
643        if index < 0 {
644            dimsize + index
645        } else {
646            index
647        }
648    }
649}
650
651/// Extension trait for adding slicing functionality to MemoryMappedArray.
652pub trait MemoryMappedSlicing<A: Clone + Copy + 'static + Send + Sync> {
653    /// Creates a slice of the memory-mapped array using standard slice syntax.
654    fn slice<I, E>(&self, sliceinfo: I) -> CoreResult<MemoryMappedSlice<A, E>>
655    where
656        I: crate::ndarray::SliceArg<E>,
657        E: Dimension;
658
659    /// Creates a 1D slice using a range.
660    fn slice_1d(
661        &self,
662        range: impl RangeBounds<usize>,
663    ) -> CoreResult<MemoryMappedSlice<A, crate::ndarray::Ix1>>;
664
665    /// Creates a 2D slice using ranges for each dimension.
666    fn slice_2d(
667        &self,
668        row_range: impl RangeBounds<usize>,
669        col_range: impl RangeBounds<usize>,
670    ) -> CoreResult<MemoryMappedSlice<A, crate::ndarray::Ix2>>;
671}
672
673impl<A: Clone + Copy + 'static + Send + Sync> MemoryMappedSlicing<A> for MemoryMappedArray<A> {
674    fn slice<I, E>(&self, sliceinfo: I) -> CoreResult<MemoryMappedSlice<A, E>>
675    where
676        I: crate::ndarray::SliceArg<E>,
677        E: Dimension,
678    {
679        // For now, we'll implement specific cases and improve later
680        // This is a limitation of the current API
681
682        // Create a default slice that returns the whole array
683        // This is a limitation - we can't properly convert generic SliceArg to SliceInfo
684        // without knowing the specific slice type at compile time
685        let slicedshape = self.shape.clone();
686
687        // Create SliceInfo that represents the identity slice on the sliced data
688        // This is because we're creating a new MemoryMappedArray that contains just the sliced data
689        let mut elems = Vec::new();
690        for &dim_size in &slicedshape {
691            elems.push(SliceInfoElem::Slice {
692                start: 0,
693                end: Some(dim_size as isize),
694                step: 1,
695            });
696        }
697
698        let slice_info = unsafe { SliceInfo::new(elems) }
699            .map_err(|_| CoreError::ShapeError(ErrorContext::new("Failed to create slice info")))?;
700
701        // Create a slice that references the original memory-mapped array
702        // This is an identity slice for now
703        let source = MemoryMappedArray::new::<crate::ndarray::OwnedRepr<A>, E>(
704            None,
705            &self.file_path,
706            self.mode,
707            self.offset,
708        )?;
709
710        Ok(MemoryMappedSlice::new(source, slice_info))
711    }
712
713    fn slice_1d(
714        &self,
715        range: impl RangeBounds<usize>,
716    ) -> CoreResult<MemoryMappedSlice<A, crate::ndarray::Ix1>> {
717        // Convert to explicit range
718        let start = match range.start_bound() {
719            std::ops::Bound::Included(&n) => n,
720            std::ops::Bound::Excluded(&n) => n + 1,
721            std::ops::Bound::Unbounded => 0,
722        };
723
724        let end = match range.end_bound() {
725            std::ops::Bound::Included(&n) => n + 1,
726            std::ops::Bound::Excluded(&n) => n,
727            std::ops::Bound::Unbounded => self.shape[0],
728        };
729
730        if start >= end || end > self.shape[0] {
731            return Err(CoreError::ShapeError(ErrorContext::new(format!(
732                "Invalid slice range {}..{} for array of shape {:?}",
733                start, end, self.shape
734            ))));
735        }
736
737        // Create SliceInfo for 1D array
738        let slice_info = unsafe {
739            SliceInfo::<Vec<SliceInfoElem>, crate::ndarray::Ix1, crate::ndarray::Ix1>::new(vec![
740                SliceInfoElem::Slice {
741                    start: start as isize,
742                    end: Some(end as isize),
743                    step: 1,
744                },
745            ])
746            .map_err(|e| {
747                CoreError::ShapeError(ErrorContext::new(format!(
748                    "Failed to create slice info: {e}"
749                )))
750            })?
751        };
752
753        // Create a new reference to the same memory-mapped file
754        let source = self.clone_ref()?;
755        Ok(MemoryMappedSlice::new(source, slice_info))
756    }
757
758    fn slice_2d(
759        &self,
760        row_range: impl RangeBounds<usize>,
761        col_range: impl RangeBounds<usize>,
762    ) -> CoreResult<MemoryMappedSlice<A, crate::ndarray::Ix2>> {
763        // Ensure we're working with a 2D array
764        if self.shape.len() != 2 {
765            return Err(CoreError::ShapeError(ErrorContext::new(format!(
766                "Expected 2D array, got {}D",
767                self.shape.len()
768            ))));
769        }
770
771        // Convert row _range to explicit _range
772        let row_start = match row_range.start_bound() {
773            std::ops::Bound::Included(&n) => n,
774            std::ops::Bound::Excluded(&n) => n + 1,
775            std::ops::Bound::Unbounded => 0,
776        };
777
778        let row_end = match row_range.end_bound() {
779            std::ops::Bound::Included(&n) => n + 1,
780            std::ops::Bound::Excluded(&n) => n,
781            std::ops::Bound::Unbounded => self.shape[0],
782        };
783
784        // Convert column _range to explicit _range
785        let col_start = match col_range.start_bound() {
786            std::ops::Bound::Included(&n) => n,
787            std::ops::Bound::Excluded(&n) => n + 1,
788            std::ops::Bound::Unbounded => 0,
789        };
790
791        let col_end = match col_range.end_bound() {
792            std::ops::Bound::Included(&n) => n + 1,
793            std::ops::Bound::Excluded(&n) => n,
794            std::ops::Bound::Unbounded => self.shape[1],
795        };
796
797        // Validate ranges
798        if row_start >= row_end || row_end > self.shape[0] {
799            return Err(CoreError::ShapeError(ErrorContext::new(format!(
800                "Invalid row slice _range {}..{} for array of shape {:?}",
801                row_start, row_end, self.shape
802            ))));
803        }
804
805        if col_start >= col_end || col_end > self.shape[1] {
806            return Err(CoreError::ShapeError(ErrorContext::new(format!(
807                "Invalid column slice _range {}..{} for array of shape {:?}",
808                col_start, col_end, self.shape
809            ))));
810        }
811
812        // Create SliceInfo for 2D array
813        let slice_info = unsafe {
814            SliceInfo::<Vec<SliceInfoElem>, crate::ndarray::Ix2, crate::ndarray::Ix2>::new(vec![
815                SliceInfoElem::Slice {
816                    start: row_start as isize,
817                    end: Some(row_end as isize),
818                    step: 1,
819                },
820                SliceInfoElem::Slice {
821                    start: col_start as isize,
822                    end: Some(col_end as isize),
823                    step: 1,
824                },
825            ])
826            .map_err(|e| {
827                CoreError::ShapeError(ErrorContext::new(format!(
828                    "Failed to create slice info: {e}"
829                )))
830            })?
831        };
832
833        // Create a new reference to the same memory-mapped file
834        let source = self.clone_ref()?;
835        Ok(MemoryMappedSlice::new(source, slice_info))
836    }
837}
838
839// Tests temporarily removed due to Rust compiler prefix parsing issue