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> {
104 let expected =
105 checked_shape_numel(shape).ok_or_else(|| ColMajorArrayError::ShapeOverflow {
106 shape: shape.to_vec(),
107 })?;
108 if data.len() != expected {
109 return Err(ColMajorArrayError::ShapeMismatch {
110 shape: shape.to_vec(),
111 expected,
112 actual: data.len(),
113 });
114 }
115 Ok(Self { data, shape })
116 }
117
118 pub fn ndim(&self) -> usize {
120 self.shape.len()
121 }
122
123 pub fn shape(&self) -> &[usize] {
125 self.shape
126 }
127
128 pub fn len(&self) -> usize {
130 self.data.len()
131 }
132
133 pub fn is_empty(&self) -> bool {
135 self.data.is_empty()
136 }
137
138 pub fn data(&self) -> &[T] {
140 self.data
141 }
142
143 pub fn get(&self, index: &[usize]) -> Option<&T> {
146 let off = flat_offset(self.shape, index)?;
147 self.data.get(off)
148 }
149}
150
151#[derive(Debug)]
157pub struct ColMajorArrayMut<'a, T> {
158 data: &'a mut [T],
159 shape: &'a [usize],
160}
161
162impl<'a, T> ColMajorArrayMut<'a, T> {
163 pub fn new(data: &'a mut [T], shape: &'a [usize]) -> Result<Self, ColMajorArrayError> {
170 let expected =
171 checked_shape_numel(shape).ok_or_else(|| ColMajorArrayError::ShapeOverflow {
172 shape: shape.to_vec(),
173 })?;
174 if data.len() != expected {
175 return Err(ColMajorArrayError::ShapeMismatch {
176 shape: shape.to_vec(),
177 expected,
178 actual: data.len(),
179 });
180 }
181 Ok(Self { data, shape })
182 }
183
184 pub fn ndim(&self) -> usize {
186 self.shape.len()
187 }
188
189 pub fn shape(&self) -> &[usize] {
191 self.shape
192 }
193
194 pub fn len(&self) -> usize {
196 self.data.len()
197 }
198
199 pub fn is_empty(&self) -> bool {
201 self.data.is_empty()
202 }
203
204 pub fn data(&self) -> &[T] {
206 self.data
207 }
208
209 pub fn data_mut(&mut self) -> &mut [T] {
211 self.data
212 }
213
214 pub fn get(&self, index: &[usize]) -> Option<&T> {
217 let off = flat_offset(self.shape, index)?;
218 self.data.get(off)
219 }
220
221 pub fn get_mut(&mut self, index: &[usize]) -> Option<&mut T> {
224 let off = flat_offset(self.shape, index)?;
225 self.data.get_mut(off)
226 }
227}
228
229#[derive(Debug, Clone, PartialEq, Eq)]
235pub struct ColMajorArray<T> {
236 data: Vec<T>,
237 shape: Vec<usize>,
238}
239
240impl<T> ColMajorArray<T> {
241 pub fn new(data: Vec<T>, shape: Vec<usize>) -> Result<Self, ColMajorArrayError> {
246 let expected =
247 checked_shape_numel(&shape).ok_or_else(|| ColMajorArrayError::ShapeOverflow {
248 shape: shape.clone(),
249 })?;
250 if data.len() != expected {
251 return Err(ColMajorArrayError::ShapeMismatch {
252 shape,
253 expected,
254 actual: data.len(),
255 });
256 }
257 Ok(Self { data, shape })
258 }
259
260 pub fn ndim(&self) -> usize {
262 self.shape.len()
263 }
264
265 pub fn shape(&self) -> &[usize] {
267 &self.shape
268 }
269
270 pub fn len(&self) -> usize {
272 self.data.len()
273 }
274
275 pub fn is_empty(&self) -> bool {
277 self.data.is_empty()
278 }
279
280 pub fn data(&self) -> &[T] {
282 &self.data
283 }
284
285 pub fn data_mut(&mut self) -> &mut [T] {
287 &mut self.data
288 }
289
290 pub fn get(&self, index: &[usize]) -> Option<&T> {
293 let off = flat_offset(&self.shape, index)?;
294 self.data.get(off)
295 }
296
297 pub fn get_mut(&mut self, index: &[usize]) -> Option<&mut T> {
300 let off = flat_offset(&self.shape, index)?;
301 self.data.get_mut(off)
302 }
303
304 pub fn into_data(self) -> Vec<T> {
306 self.data
307 }
308
309 pub fn as_ref(&self) -> ColMajorArrayRef<'_, T> {
311 ColMajorArrayRef {
312 data: &self.data,
313 shape: &self.shape,
314 }
315 }
316
317 pub fn as_mut(&mut self) -> ColMajorArrayMut<'_, T> {
319 ColMajorArrayMut {
320 data: &mut self.data,
321 shape: &self.shape,
322 }
323 }
324
325 pub fn nrows(&self) -> Option<usize> {
329 if self.ndim() == 2 {
330 Some(self.shape[0])
331 } else {
332 None
333 }
334 }
335
336 pub fn ncols(&self) -> Option<usize> {
338 if self.ndim() == 2 {
339 Some(self.shape[1])
340 } else {
341 None
342 }
343 }
344
345 pub fn column(&self, j: usize) -> Option<&[T]> {
348 if self.ndim() != 2 {
349 return None;
350 }
351 let nrows = self.shape[0];
352 if j >= self.shape[1] {
353 return None;
354 }
355 let start = nrows.checked_mul(j)?;
356 let end = start.checked_add(nrows)?;
357 Some(&self.data[start..end])
358 }
359
360 pub fn push_column(&mut self, col: &[T]) -> Result<(), ColMajorArrayError>
368 where
369 T: Clone,
370 {
371 if self.ndim() != 2 {
372 return Err(ColMajorArrayError::Not2D { ndim: self.ndim() });
373 }
374 let nrows = self.shape[0];
375 if col.len() != nrows {
376 return Err(ColMajorArrayError::ColumnLengthMismatch {
377 expected: nrows,
378 actual: col.len(),
379 });
380 }
381 self.data.extend_from_slice(col);
382 self.shape[1] = self.shape[1]
383 .checked_add(1)
384 .ok_or(ColMajorArrayError::ColumnCountOverflow)?;
385 Ok(())
386 }
387}
388
389impl<T: Clone> ColMajorArray<T> {
392 pub fn filled(shape: Vec<usize>, value: T) -> Result<Self, ColMajorArrayError> {
396 let n = checked_shape_numel(&shape).ok_or_else(|| ColMajorArrayError::ShapeOverflow {
397 shape: shape.clone(),
398 })?;
399 Ok(Self {
400 data: vec![value; n],
401 shape,
402 })
403 }
404}
405
406impl<T: Default + Clone> ColMajorArray<T> {
407 pub fn zeros(shape: Vec<usize>) -> Result<Self, ColMajorArrayError> {
412 let n = checked_shape_numel(&shape).ok_or_else(|| ColMajorArrayError::ShapeOverflow {
413 shape: shape.clone(),
414 })?;
415 Ok(Self {
416 data: vec![T::default(); n],
417 shape,
418 })
419 }
420}
421
422#[cfg(test)]
427mod tests {
428 use super::*;
429
430 #[test]
433 fn test_1d_creation_and_get() {
434 let arr = ColMajorArray::new(vec![10, 20, 30], vec![3]).unwrap();
435 assert_eq!(arr.ndim(), 1);
436 assert_eq!(arr.shape(), &[3]);
437 assert_eq!(arr.len(), 3);
438 assert!(!arr.is_empty());
439
440 assert_eq!(arr.get(&[0]), Some(&10));
441 assert_eq!(arr.get(&[1]), Some(&20));
442 assert_eq!(arr.get(&[2]), Some(&30));
443 }
444
445 #[test]
448 fn test_2d_creation_and_get() {
449 let arr = ColMajorArray::new(vec![1, 2, 3, 4, 5, 6], vec![2, 3]).unwrap();
453 assert_eq!(arr.ndim(), 2);
454 assert_eq!(arr.shape(), &[2, 3]);
455 assert_eq!(arr.len(), 6);
456
457 assert_eq!(arr.get(&[0, 0]), Some(&1));
459 assert_eq!(arr.get(&[1, 0]), Some(&2));
460 assert_eq!(arr.get(&[0, 1]), Some(&3));
461 assert_eq!(arr.get(&[1, 1]), Some(&4));
462 assert_eq!(arr.get(&[0, 2]), Some(&5));
463 assert_eq!(arr.get(&[1, 2]), Some(&6));
464 }
465
466 #[test]
469 fn test_3d_creation_and_get() {
470 let data: Vec<i32> = (0..12).collect();
472 let arr = ColMajorArray::new(data.clone(), vec![2, 3, 2]).unwrap();
473 assert_eq!(arr.ndim(), 3);
474 assert_eq!(arr.len(), 12);
475
476 for i2 in 0..2 {
478 for i1 in 0..3 {
479 for i0 in 0..2 {
480 let expected_offset = i0 + 2 * (i1 + 3 * i2);
481 assert_eq!(
482 arr.get(&[i0, i1, i2]),
483 Some(&(expected_offset as i32)),
484 "Mismatch at [{i0}, {i1}, {i2}]"
485 );
486 }
487 }
488 }
489 }
490
491 #[test]
494 fn test_column_major_order_2d() {
495 let nrows = 3;
496 let ncols = 4;
497 let data: Vec<i32> = (0..(nrows * ncols) as i32).collect();
498 let arr = ColMajorArray::new(data.clone(), vec![nrows, ncols]).unwrap();
499
500 for j in 0..ncols {
502 for i in 0..nrows {
503 assert_eq!(arr.get(&[i, j]), Some(&data[i + nrows * j]));
504 }
505 }
506 }
507
508 #[test]
511 fn test_get_mut() {
512 let mut arr = ColMajorArray::new(vec![1, 2, 3, 4], vec![2, 2]).unwrap();
513 if let Some(v) = arr.get_mut(&[1, 0]) {
514 *v = 42;
515 }
516 assert_eq!(arr.get(&[1, 0]), Some(&42));
517 assert_eq!(arr.get(&[0, 0]), Some(&1));
519 assert_eq!(arr.get(&[0, 1]), Some(&3));
520 assert_eq!(arr.get(&[1, 1]), Some(&4));
521 }
522
523 #[test]
526 fn test_push_column() {
527 let mut arr = ColMajorArray::new(vec![1, 2, 3, 4], vec![2, 2]).unwrap();
528 assert_eq!(arr.ncols(), Some(2));
529
530 arr.push_column(&[5, 6]).unwrap();
531 assert_eq!(arr.ncols(), Some(3));
532 assert_eq!(arr.shape(), &[2, 3]);
533 assert_eq!(arr.len(), 6);
534 assert_eq!(arr.get(&[0, 2]), Some(&5));
535 assert_eq!(arr.get(&[1, 2]), Some(&6));
536 }
537
538 #[test]
539 fn test_push_column_wrong_length() {
540 let mut arr = ColMajorArray::new(vec![1, 2, 3, 4], vec![2, 2]).unwrap();
541 let err = arr.push_column(&[5, 6, 7]).unwrap_err();
542 assert_eq!(
543 err,
544 ColMajorArrayError::ColumnLengthMismatch {
545 expected: 2,
546 actual: 3,
547 }
548 );
549 }
550
551 #[test]
552 fn test_push_column_not_2d() {
553 let mut arr = ColMajorArray::new(vec![1, 2, 3], vec![3]).unwrap();
554 let err = arr.push_column(&[4]).unwrap_err();
555 assert_eq!(err, ColMajorArrayError::Not2D { ndim: 1 });
556 }
557
558 #[test]
561 fn test_column_access() {
562 let arr = ColMajorArray::new(vec![1, 2, 3, 4, 5, 6], vec![2, 3]).unwrap();
563 assert_eq!(arr.column(0), Some([1, 2].as_slice()));
564 assert_eq!(arr.column(1), Some([3, 4].as_slice()));
565 assert_eq!(arr.column(2), Some([5, 6].as_slice()));
566 assert_eq!(arr.column(3), None); }
568
569 #[test]
572 fn test_zeros() {
573 let arr: ColMajorArray<f64> = ColMajorArray::zeros(vec![3, 2]).unwrap();
574 assert_eq!(arr.len(), 6);
575 assert!(arr.data().iter().all(|&v| v == 0.0));
576 }
577
578 #[test]
579 fn test_filled() {
580 let arr = ColMajorArray::filled(vec![2, 3], 7i32).unwrap();
581 assert_eq!(arr.len(), 6);
582 assert!(arr.data().iter().all(|&v| v == 7));
583 }
584
585 #[test]
588 fn test_shape_mismatch() {
589 let result = ColMajorArray::new(vec![1, 2, 3], vec![2, 2]);
590 assert_eq!(
591 result.unwrap_err(),
592 ColMajorArrayError::ShapeMismatch {
593 shape: vec![2, 2],
594 expected: 4,
595 actual: 3,
596 }
597 );
598 }
599
600 #[test]
603 fn test_out_of_bounds() {
604 let arr = ColMajorArray::new(vec![1, 2, 3, 4], vec![2, 2]).unwrap();
605 assert_eq!(arr.get(&[2, 0]), None);
607 assert_eq!(arr.get(&[0, 2]), None);
608 assert_eq!(arr.get(&[0]), None);
610 assert_eq!(arr.get(&[0, 0, 0]), None);
611 }
612
613 #[test]
616 fn test_as_ref() {
617 let arr = ColMajorArray::new(vec![10, 20, 30, 40], vec![2, 2]).unwrap();
618 let view = arr.as_ref();
619 assert_eq!(view.ndim(), 2);
620 assert_eq!(view.shape(), &[2, 2]);
621 assert_eq!(view.get(&[1, 1]), Some(&40));
622 assert_eq!(view.data(), arr.data());
623 }
624
625 #[test]
626 fn test_as_mut() {
627 let mut arr = ColMajorArray::new(vec![10, 20, 30, 40], vec![2, 2]).unwrap();
628 {
629 let mut view = arr.as_mut();
630 if let Some(v) = view.get_mut(&[0, 1]) {
631 *v = 99;
632 }
633 }
634 assert_eq!(arr.get(&[0, 1]), Some(&99));
635 }
636
637 #[test]
640 fn test_into_data() {
641 let arr = ColMajorArray::new(vec![1, 2, 3], vec![3]).unwrap();
642 let data = arr.into_data();
643 assert_eq!(data, vec![1, 2, 3]);
644 }
645
646 #[test]
649 fn test_empty_array() {
650 let arr: ColMajorArray<i32> = ColMajorArray::new(vec![], vec![0]).unwrap();
651 assert!(arr.is_empty());
652 assert_eq!(arr.len(), 0);
653 assert_eq!(arr.ndim(), 1);
654 assert_eq!(arr.nrows(), None);
655 assert_eq!(arr.ncols(), None);
656 }
657
658 #[test]
659 fn test_empty_2d_array() {
660 let arr: ColMajorArray<i32> = ColMajorArray::new(vec![], vec![3, 0]).unwrap();
661 assert!(arr.is_empty());
662 assert_eq!(arr.len(), 0);
663 assert_eq!(arr.nrows(), Some(3));
664 assert_eq!(arr.ncols(), Some(0));
665 }
666
667 #[test]
670 fn test_ref_new() {
671 let data = [1, 2, 3, 4, 5, 6];
672 let shape = [2, 3];
673 let view = ColMajorArrayRef::new(&data, &shape).unwrap();
674 assert_eq!(view.ndim(), 2);
675 assert_eq!(view.len(), 6);
676 assert_eq!(view.get(&[1, 2]), Some(&6));
677 }
678
679 #[test]
682 fn test_mut_new() {
683 let mut data = [1, 2, 3, 4, 5, 6];
684 let shape = [2, 3];
685 let mut view = ColMajorArrayMut::new(&mut data, &shape).unwrap();
686 assert_eq!(view.ndim(), 2);
687 assert_eq!(view.len(), 6);
688 *view.get_mut(&[0, 0]).unwrap() = 100;
689 assert_eq!(view.get(&[0, 0]), Some(&100));
690 }
691
692 #[test]
695 fn test_new_rejects_overflow_shape() {
696 let result = ColMajorArray::<u8>::new(vec![], vec![usize::MAX, 2]);
697 assert!(
698 matches!(result, Err(ColMajorArrayError::ShapeOverflow { .. })),
699 "expected ShapeOverflow, got {:?}",
700 result
701 );
702 }
703
704 #[test]
705 fn test_filled_rejects_overflow_shape() {
706 let result = ColMajorArray::filled(vec![usize::MAX, 2], 0u8);
707 assert!(
708 matches!(result, Err(ColMajorArrayError::ShapeOverflow { .. })),
709 "expected ShapeOverflow, got {:?}",
710 result
711 );
712 }
713
714 #[test]
715 fn test_zeros_rejects_overflow_shape() {
716 let result = ColMajorArray::<u8>::zeros(vec![usize::MAX, 2]);
717 assert!(
718 matches!(result, Err(ColMajorArrayError::ShapeOverflow { .. })),
719 "expected ShapeOverflow, got {:?}",
720 result
721 );
722 }
723}