1#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)]
13pub enum ColMajorArrayError {
14 #[error("Shape mismatch: shape {shape:?} requires {expected} elements, but got {actual}")]
16 ShapeMismatch {
17 shape: Vec<usize>,
19 expected: usize,
21 actual: usize,
23 },
24
25 #[error("Column length mismatch: expected {expected} elements, but got {actual}")]
27 ColumnLengthMismatch {
28 expected: usize,
30 actual: usize,
32 },
33
34 #[error("Expected a 2D array, but ndim = {ndim}")]
36 Not2D {
37 ndim: usize,
39 },
40
41 #[error("Shape product overflow: shape {shape:?} overflows usize")]
43 ShapeOverflow {
44 shape: Vec<usize>,
46 },
47
48 #[error("Column count overflow")]
50 ColumnCountOverflow,
51}
52
53fn checked_shape_numel(shape: &[usize]) -> Option<usize> {
58 shape
59 .iter()
60 .copied()
61 .try_fold(1usize, |acc, d| acc.checked_mul(d))
62}
63
64fn flat_offset(shape: &[usize], index: &[usize]) -> Option<usize> {
67 if index.len() != shape.len() {
68 return None;
69 }
70 let mut offset: usize = 0;
76 for (idx, dim) in index.iter().zip(shape.iter()).rev() {
77 if *idx >= *dim {
78 return None;
79 }
80 offset = offset.checked_mul(*dim)?.checked_add(*idx)?;
81 }
82 Some(offset)
83}
84
85#[derive(Debug, Clone, Copy)]
91pub struct ColMajorArrayRef<'a, T> {
92 data: &'a [T],
93 shape: &'a [usize],
94}
95
96impl<'a, T> ColMajorArrayRef<'a, T> {
97 pub fn new(data: &'a [T], shape: &'a [usize]) -> Result<Self, ColMajorArrayError> {
105 let expected =
106 checked_shape_numel(shape).ok_or_else(|| ColMajorArrayError::ShapeOverflow {
107 shape: shape.to_vec(),
108 })?;
109 if data.len() != expected {
110 return Err(ColMajorArrayError::ShapeMismatch {
111 shape: shape.to_vec(),
112 expected,
113 actual: data.len(),
114 });
115 }
116 Ok(Self { data, shape })
117 }
118
119 pub fn ndim(&self) -> usize {
121 self.shape.len()
122 }
123
124 pub fn shape(&self) -> &[usize] {
126 self.shape
127 }
128
129 pub fn len(&self) -> usize {
131 self.data.len()
132 }
133
134 pub fn is_empty(&self) -> bool {
136 self.data.is_empty()
137 }
138
139 pub fn data(&self) -> &[T] {
141 self.data
142 }
143
144 pub fn get(&self, index: &[usize]) -> Option<&T> {
147 let off = flat_offset(self.shape, index)?;
148 self.data.get(off)
149 }
150}
151
152#[derive(Debug)]
158pub struct ColMajorArrayMut<'a, T> {
159 data: &'a mut [T],
160 shape: &'a [usize],
161}
162
163impl<'a, T> ColMajorArrayMut<'a, T> {
164 pub fn new(data: &'a mut [T], shape: &'a [usize]) -> Result<Self, ColMajorArrayError> {
172 let expected =
173 checked_shape_numel(shape).ok_or_else(|| ColMajorArrayError::ShapeOverflow {
174 shape: shape.to_vec(),
175 })?;
176 if data.len() != expected {
177 return Err(ColMajorArrayError::ShapeMismatch {
178 shape: shape.to_vec(),
179 expected,
180 actual: data.len(),
181 });
182 }
183 Ok(Self { data, shape })
184 }
185
186 pub fn ndim(&self) -> usize {
188 self.shape.len()
189 }
190
191 pub fn shape(&self) -> &[usize] {
193 self.shape
194 }
195
196 pub fn len(&self) -> usize {
198 self.data.len()
199 }
200
201 pub fn is_empty(&self) -> bool {
203 self.data.is_empty()
204 }
205
206 pub fn data(&self) -> &[T] {
208 self.data
209 }
210
211 pub fn data_mut(&mut self) -> &mut [T] {
213 self.data
214 }
215
216 pub fn get(&self, index: &[usize]) -> Option<&T> {
219 let off = flat_offset(self.shape, index)?;
220 self.data.get(off)
221 }
222
223 pub fn get_mut(&mut self, index: &[usize]) -> Option<&mut T> {
226 let off = flat_offset(self.shape, index)?;
227 self.data.get_mut(off)
228 }
229}
230
231#[derive(Debug, Clone, PartialEq, Eq)]
237pub struct ColMajorArray<T> {
238 data: Vec<T>,
239 shape: Vec<usize>,
240}
241
242impl<T> ColMajorArray<T> {
243 pub fn new(data: Vec<T>, shape: Vec<usize>) -> Result<Self, ColMajorArrayError> {
253 let expected =
254 checked_shape_numel(&shape).ok_or_else(|| ColMajorArrayError::ShapeOverflow {
255 shape: shape.clone(),
256 })?;
257 if data.len() != expected {
258 return Err(ColMajorArrayError::ShapeMismatch {
259 shape,
260 expected,
261 actual: data.len(),
262 });
263 }
264 Ok(Self { data, shape })
265 }
266
267 pub fn ndim(&self) -> usize {
269 self.shape.len()
270 }
271
272 pub fn shape(&self) -> &[usize] {
274 &self.shape
275 }
276
277 pub fn len(&self) -> usize {
279 self.data.len()
280 }
281
282 pub fn is_empty(&self) -> bool {
284 self.data.is_empty()
285 }
286
287 pub fn data(&self) -> &[T] {
289 &self.data
290 }
291
292 pub fn data_mut(&mut self) -> &mut [T] {
294 &mut self.data
295 }
296
297 pub fn get(&self, index: &[usize]) -> Option<&T> {
300 let off = flat_offset(&self.shape, index)?;
301 self.data.get(off)
302 }
303
304 pub fn get_mut(&mut self, index: &[usize]) -> Option<&mut T> {
307 let off = flat_offset(&self.shape, index)?;
308 self.data.get_mut(off)
309 }
310
311 pub fn into_data(self) -> Vec<T> {
313 self.data
314 }
315
316 pub fn as_ref(&self) -> ColMajorArrayRef<'_, T> {
318 ColMajorArrayRef {
319 data: &self.data,
320 shape: &self.shape,
321 }
322 }
323
324 pub fn as_mut(&mut self) -> ColMajorArrayMut<'_, T> {
326 ColMajorArrayMut {
327 data: &mut self.data,
328 shape: &self.shape,
329 }
330 }
331
332 pub fn nrows(&self) -> Option<usize> {
336 if self.ndim() == 2 {
337 Some(self.shape[0])
338 } else {
339 None
340 }
341 }
342
343 pub fn ncols(&self) -> Option<usize> {
345 if self.ndim() == 2 {
346 Some(self.shape[1])
347 } else {
348 None
349 }
350 }
351
352 pub fn column(&self, j: usize) -> Option<&[T]> {
355 if self.ndim() != 2 {
356 return None;
357 }
358 let nrows = self.shape[0];
359 if j >= self.shape[1] {
360 return None;
361 }
362 let start = nrows.checked_mul(j)?;
363 let end = start.checked_add(nrows)?;
364 Some(&self.data[start..end])
365 }
366
367 pub fn push_column(&mut self, col: &[T]) -> Result<(), ColMajorArrayError>
380 where
381 T: Clone,
382 {
383 if self.ndim() != 2 {
384 return Err(ColMajorArrayError::Not2D { ndim: self.ndim() });
385 }
386 let nrows = self.shape[0];
387 if col.len() != nrows {
388 return Err(ColMajorArrayError::ColumnLengthMismatch {
389 expected: nrows,
390 actual: col.len(),
391 });
392 }
393 self.data.extend_from_slice(col);
394 self.shape[1] = self.shape[1]
395 .checked_add(1)
396 .ok_or(ColMajorArrayError::ColumnCountOverflow)?;
397 Ok(())
398 }
399}
400
401impl<T: Clone> ColMajorArray<T> {
404 pub fn filled(shape: Vec<usize>, value: T) -> Result<Self, ColMajorArrayError> {
413 let n = checked_shape_numel(&shape).ok_or_else(|| ColMajorArrayError::ShapeOverflow {
414 shape: shape.clone(),
415 })?;
416 Ok(Self {
417 data: vec![value; n],
418 shape,
419 })
420 }
421}
422
423impl<T: Default + Clone> ColMajorArray<T> {
424 pub fn zeros(shape: Vec<usize>) -> Result<Self, ColMajorArrayError> {
433 let n = checked_shape_numel(&shape).ok_or_else(|| ColMajorArrayError::ShapeOverflow {
434 shape: shape.clone(),
435 })?;
436 Ok(Self {
437 data: vec![T::default(); n],
438 shape,
439 })
440 }
441}
442
443#[cfg(test)]
448mod tests {
449 use super::*;
450
451 #[test]
454 fn test_1d_creation_and_get() {
455 let arr = ColMajorArray::new(vec![10, 20, 30], vec![3]).unwrap();
456 assert_eq!(arr.ndim(), 1);
457 assert_eq!(arr.shape(), &[3]);
458 assert_eq!(arr.len(), 3);
459 assert!(!arr.is_empty());
460
461 assert_eq!(arr.get(&[0]), Some(&10));
462 assert_eq!(arr.get(&[1]), Some(&20));
463 assert_eq!(arr.get(&[2]), Some(&30));
464 }
465
466 #[test]
469 fn test_2d_creation_and_get() {
470 let arr = ColMajorArray::new(vec![1, 2, 3, 4, 5, 6], vec![2, 3]).unwrap();
474 assert_eq!(arr.ndim(), 2);
475 assert_eq!(arr.shape(), &[2, 3]);
476 assert_eq!(arr.len(), 6);
477
478 assert_eq!(arr.get(&[0, 0]), Some(&1));
480 assert_eq!(arr.get(&[1, 0]), Some(&2));
481 assert_eq!(arr.get(&[0, 1]), Some(&3));
482 assert_eq!(arr.get(&[1, 1]), Some(&4));
483 assert_eq!(arr.get(&[0, 2]), Some(&5));
484 assert_eq!(arr.get(&[1, 2]), Some(&6));
485 }
486
487 #[test]
490 fn test_3d_creation_and_get() {
491 let data: Vec<i32> = (0..12).collect();
493 let arr = ColMajorArray::new(data.clone(), vec![2, 3, 2]).unwrap();
494 assert_eq!(arr.ndim(), 3);
495 assert_eq!(arr.len(), 12);
496
497 for i2 in 0..2 {
499 for i1 in 0..3 {
500 for i0 in 0..2 {
501 let expected_offset = i0 + 2 * (i1 + 3 * i2);
502 assert_eq!(
503 arr.get(&[i0, i1, i2]),
504 Some(&(expected_offset as i32)),
505 "Mismatch at [{i0}, {i1}, {i2}]"
506 );
507 }
508 }
509 }
510 }
511
512 #[test]
515 fn test_column_major_order_2d() {
516 let nrows = 3;
517 let ncols = 4;
518 let data: Vec<i32> = (0..(nrows * ncols) as i32).collect();
519 let arr = ColMajorArray::new(data.clone(), vec![nrows, ncols]).unwrap();
520
521 for j in 0..ncols {
523 for i in 0..nrows {
524 assert_eq!(arr.get(&[i, j]), Some(&data[i + nrows * j]));
525 }
526 }
527 }
528
529 #[test]
532 fn test_get_mut() {
533 let mut arr = ColMajorArray::new(vec![1, 2, 3, 4], vec![2, 2]).unwrap();
534 if let Some(v) = arr.get_mut(&[1, 0]) {
535 *v = 42;
536 }
537 assert_eq!(arr.get(&[1, 0]), Some(&42));
538 assert_eq!(arr.get(&[0, 0]), Some(&1));
540 assert_eq!(arr.get(&[0, 1]), Some(&3));
541 assert_eq!(arr.get(&[1, 1]), Some(&4));
542 }
543
544 #[test]
547 fn test_push_column() {
548 let mut arr = ColMajorArray::new(vec![1, 2, 3, 4], vec![2, 2]).unwrap();
549 assert_eq!(arr.ncols(), Some(2));
550
551 arr.push_column(&[5, 6]).unwrap();
552 assert_eq!(arr.ncols(), Some(3));
553 assert_eq!(arr.shape(), &[2, 3]);
554 assert_eq!(arr.len(), 6);
555 assert_eq!(arr.get(&[0, 2]), Some(&5));
556 assert_eq!(arr.get(&[1, 2]), Some(&6));
557 }
558
559 #[test]
560 fn test_push_column_wrong_length() {
561 let mut arr = ColMajorArray::new(vec![1, 2, 3, 4], vec![2, 2]).unwrap();
562 let err = arr.push_column(&[5, 6, 7]).unwrap_err();
563 assert_eq!(
564 err,
565 ColMajorArrayError::ColumnLengthMismatch {
566 expected: 2,
567 actual: 3,
568 }
569 );
570 }
571
572 #[test]
573 fn test_push_column_not_2d() {
574 let mut arr = ColMajorArray::new(vec![1, 2, 3], vec![3]).unwrap();
575 let err = arr.push_column(&[4]).unwrap_err();
576 assert_eq!(err, ColMajorArrayError::Not2D { ndim: 1 });
577 }
578
579 #[test]
582 fn test_column_access() {
583 let arr = ColMajorArray::new(vec![1, 2, 3, 4, 5, 6], vec![2, 3]).unwrap();
584 assert_eq!(arr.column(0), Some([1, 2].as_slice()));
585 assert_eq!(arr.column(1), Some([3, 4].as_slice()));
586 assert_eq!(arr.column(2), Some([5, 6].as_slice()));
587 assert_eq!(arr.column(3), None); }
589
590 #[test]
593 fn test_zeros() {
594 let arr: ColMajorArray<f64> = ColMajorArray::zeros(vec![3, 2]).unwrap();
595 assert_eq!(arr.len(), 6);
596 assert!(arr.data().iter().all(|&v| v == 0.0));
597 }
598
599 #[test]
600 fn test_filled() {
601 let arr = ColMajorArray::filled(vec![2, 3], 7i32).unwrap();
602 assert_eq!(arr.len(), 6);
603 assert!(arr.data().iter().all(|&v| v == 7));
604 }
605
606 #[test]
609 fn test_shape_mismatch() {
610 let result = ColMajorArray::new(vec![1, 2, 3], vec![2, 2]);
611 assert_eq!(
612 result.unwrap_err(),
613 ColMajorArrayError::ShapeMismatch {
614 shape: vec![2, 2],
615 expected: 4,
616 actual: 3,
617 }
618 );
619 }
620
621 #[test]
624 fn test_out_of_bounds() {
625 let arr = ColMajorArray::new(vec![1, 2, 3, 4], vec![2, 2]).unwrap();
626 assert_eq!(arr.get(&[2, 0]), None);
628 assert_eq!(arr.get(&[0, 2]), None);
629 assert_eq!(arr.get(&[0]), None);
631 assert_eq!(arr.get(&[0, 0, 0]), None);
632 }
633
634 #[test]
637 fn test_as_ref() {
638 let arr = ColMajorArray::new(vec![10, 20, 30, 40], vec![2, 2]).unwrap();
639 let view = arr.as_ref();
640 assert_eq!(view.ndim(), 2);
641 assert_eq!(view.shape(), &[2, 2]);
642 assert_eq!(view.get(&[1, 1]), Some(&40));
643 assert_eq!(view.data(), arr.data());
644 }
645
646 #[test]
647 fn test_as_mut() {
648 let mut arr = ColMajorArray::new(vec![10, 20, 30, 40], vec![2, 2]).unwrap();
649 {
650 let mut view = arr.as_mut();
651 if let Some(v) = view.get_mut(&[0, 1]) {
652 *v = 99;
653 }
654 }
655 assert_eq!(arr.get(&[0, 1]), Some(&99));
656 }
657
658 #[test]
661 fn test_into_data() {
662 let arr = ColMajorArray::new(vec![1, 2, 3], vec![3]).unwrap();
663 let data = arr.into_data();
664 assert_eq!(data, vec![1, 2, 3]);
665 }
666
667 #[test]
670 fn test_empty_array() {
671 let arr: ColMajorArray<i32> = ColMajorArray::new(vec![], vec![0]).unwrap();
672 assert!(arr.is_empty());
673 assert_eq!(arr.len(), 0);
674 assert_eq!(arr.ndim(), 1);
675 assert_eq!(arr.nrows(), None);
676 assert_eq!(arr.ncols(), None);
677 }
678
679 #[test]
680 fn test_empty_2d_array() {
681 let arr: ColMajorArray<i32> = ColMajorArray::new(vec![], vec![3, 0]).unwrap();
682 assert!(arr.is_empty());
683 assert_eq!(arr.len(), 0);
684 assert_eq!(arr.nrows(), Some(3));
685 assert_eq!(arr.ncols(), Some(0));
686 }
687
688 #[test]
691 fn test_ref_new() {
692 let data = [1, 2, 3, 4, 5, 6];
693 let shape = [2, 3];
694 let view = ColMajorArrayRef::new(&data, &shape).unwrap();
695 assert_eq!(view.ndim(), 2);
696 assert_eq!(view.len(), 6);
697 assert_eq!(view.get(&[1, 2]), Some(&6));
698 }
699
700 #[test]
703 fn test_mut_new() {
704 let mut data = [1, 2, 3, 4, 5, 6];
705 let shape = [2, 3];
706 let mut view = ColMajorArrayMut::new(&mut data, &shape).unwrap();
707 assert_eq!(view.ndim(), 2);
708 assert_eq!(view.len(), 6);
709 *view.get_mut(&[0, 0]).unwrap() = 100;
710 assert_eq!(view.get(&[0, 0]), Some(&100));
711 }
712
713 #[test]
716 fn test_new_rejects_overflow_shape() {
717 let result = ColMajorArray::<u8>::new(vec![], vec![usize::MAX, 2]);
718 assert!(
719 matches!(result, Err(ColMajorArrayError::ShapeOverflow { .. })),
720 "expected ShapeOverflow, got {:?}",
721 result
722 );
723 }
724
725 #[test]
726 fn test_filled_rejects_overflow_shape() {
727 let result = ColMajorArray::filled(vec![usize::MAX, 2], 0u8);
728 assert!(
729 matches!(result, Err(ColMajorArrayError::ShapeOverflow { .. })),
730 "expected ShapeOverflow, got {:?}",
731 result
732 );
733 }
734
735 #[test]
736 fn test_zeros_rejects_overflow_shape() {
737 let result = ColMajorArray::<u8>::zeros(vec![usize::MAX, 2]);
738 assert!(
739 matches!(result, Err(ColMajorArrayError::ShapeOverflow { .. })),
740 "expected ShapeOverflow, got {:?}",
741 result
742 );
743 }
744}