1#[cfg(not(feature = "std"))]
22use alloc::vec::Vec;
23
24use oxiblas_core::memory::AlignedVec;
25use oxiblas_core::scalar::Scalar;
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub enum TriangularKind {
30 Upper,
32 Lower,
34}
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq)]
45pub struct OutOfTriangleError {
46 pub row: usize,
48 pub col: usize,
50 pub dim: usize,
52 pub kind: TriangularKind,
54}
55
56impl core::fmt::Display for OutOfTriangleError {
57 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
58 write!(
59 f,
60 "index ({}, {}) is outside the stored {:?} triangle of a {}x{} packed matrix",
61 self.row, self.col, self.kind, self.dim, self.dim
62 )
63 }
64}
65
66#[cfg(feature = "std")]
67impl std::error::Error for OutOfTriangleError {}
68
69#[derive(Clone)]
95pub struct PackedMat<T: Scalar> {
96 data: AlignedVec<T>,
98 n: usize,
100 kind: TriangularKind,
102}
103
104impl<T: Scalar> PackedMat<T> {
105 pub fn zeros(n: usize, kind: TriangularKind) -> Self
107 where
108 T: bytemuck::Zeroable,
109 {
110 let len = Self::packed_len(n);
111 PackedMat {
112 data: AlignedVec::zeros(len),
113 n,
114 kind,
115 }
116 }
117
118 pub fn filled(n: usize, kind: TriangularKind, value: T) -> Self {
120 let len = Self::packed_len(n);
121 PackedMat {
122 data: AlignedVec::filled(len, value),
123 n,
124 kind,
125 }
126 }
127
128 pub fn from_slice(n: usize, kind: TriangularKind, data: &[T]) -> Self {
133 let len = Self::packed_len(n);
134 assert_eq!(
135 data.len(),
136 len,
137 "Slice length must equal n*(n+1)/2 = {}",
138 len
139 );
140
141 PackedMat {
142 data: AlignedVec::from_slice(data),
143 n,
144 kind,
145 }
146 }
147
148 #[inline]
162 pub const fn packed_len(n: usize) -> usize {
163 match n.checked_add(1) {
164 Some(np1) => match n.checked_mul(np1) {
165 Some(product) => product / 2,
166 None => panic!(
167 "PackedMat: packed length overflow (n*(n+1) exceeds usize::MAX); \
168 requested matrix dimension is too large to allocate"
169 ),
170 },
171 None => panic!(
172 "PackedMat: packed length overflow (n+1 exceeds usize::MAX); \
173 requested matrix dimension is too large to allocate"
174 ),
175 }
176 }
177
178 #[inline]
180 pub fn dim(&self) -> usize {
181 self.n
182 }
183
184 #[inline]
186 pub fn kind(&self) -> TriangularKind {
187 self.kind
188 }
189
190 #[inline]
192 pub fn len(&self) -> usize {
193 self.data.len()
194 }
195
196 #[inline]
198 pub fn is_empty(&self) -> bool {
199 self.n == 0
200 }
201
202 #[inline]
206 pub fn packed_index(&self, row: usize, col: usize) -> Option<usize> {
207 if row >= self.n || col >= self.n {
208 return None;
209 }
210
211 match self.kind {
212 TriangularKind::Upper => {
213 if row <= col {
214 Some(col * (col + 1) / 2 + row)
216 } else {
217 None
218 }
219 }
220 TriangularKind::Lower => {
221 if row >= col {
222 let offset = self.n * col - col * (col.saturating_sub(1)) / 2;
226 Some(offset + (row - col))
227 } else {
228 None
229 }
230 }
231 }
232 }
233
234 #[inline]
238 pub fn get(&self, row: usize, col: usize) -> Option<&T> {
239 self.packed_index(row, col).map(|idx| &self.data[idx])
240 }
241
242 #[inline]
246 pub fn get_mut(&mut self, row: usize, col: usize) -> Option<&mut T> {
247 self.packed_index(row, col).map(|idx| &mut self.data[idx])
248 }
249
250 #[inline]
256 pub fn set(&mut self, row: usize, col: usize, value: T) -> Result<(), OutOfTriangleError> {
257 let idx = self.packed_index(row, col).ok_or(OutOfTriangleError {
258 row,
259 col,
260 dim: self.n,
261 kind: self.kind,
262 })?;
263 self.data[idx] = value;
264 Ok(())
265 }
266
267 #[inline]
269 pub fn as_ptr(&self) -> *const T {
270 self.data.as_ptr()
271 }
272
273 #[inline]
275 pub fn as_mut_ptr(&mut self) -> *mut T {
276 self.data.as_mut_ptr()
277 }
278
279 #[inline]
281 pub fn as_slice(&self) -> &[T] {
282 self.data.as_slice()
283 }
284
285 #[inline]
287 pub fn as_slice_mut(&mut self) -> &mut [T] {
288 self.data.as_mut_slice()
289 }
290
291 pub fn to_dense(&self) -> crate::Mat<T>
293 where
294 T: bytemuck::Zeroable,
295 {
296 let mut mat = crate::Mat::zeros(self.n, self.n);
297
298 for j in 0..self.n {
299 for i in 0..self.n {
300 if let Some(idx) = self.packed_index(i, j) {
301 mat[(i, j)] = self.data[idx];
302 }
303 }
304 }
305
306 mat
307 }
308
309 pub fn from_dense(mat: &crate::MatRef<'_, T>, kind: TriangularKind) -> Self
313 where
314 T: bytemuck::Zeroable,
315 {
316 assert_eq!(mat.nrows(), mat.ncols(), "Matrix must be square");
317 let n = mat.nrows();
318 let mut packed = Self::zeros(n, kind);
319
320 for j in 0..n {
321 for i in 0..n {
322 if let Some(idx) = packed.packed_index(i, j) {
323 packed.data[idx] = mat[(i, j)];
324 }
325 }
326 }
327
328 packed
329 }
330
331 #[inline]
340 fn diagonal_packed_index(&self, i: usize) -> usize {
341 match self.kind {
342 TriangularKind::Upper => i * (i + 1) / 2 + i,
343 TriangularKind::Lower => self.n * i - i * (i.saturating_sub(1)) / 2,
344 }
345 }
346
347 pub fn diagonal(&self) -> Vec<T> {
349 (0..self.n)
350 .map(|i| self.data[self.diagonal_packed_index(i)])
351 .collect()
352 }
353
354 pub fn set_diagonal(&mut self, diag: &[T]) {
356 assert_eq!(
357 diag.len(),
358 self.n,
359 "Diagonal length must match matrix dimension"
360 );
361 for (i, &val) in diag.iter().enumerate() {
362 let idx = self.diagonal_packed_index(i);
363 self.data[idx] = val;
364 }
365 }
366
367 pub fn fill(&mut self, value: T) {
369 for elem in self.data.as_mut_slice() {
370 *elem = value;
371 }
372 }
373
374 pub fn scale(&mut self, alpha: T) {
376 for elem in self.data.as_mut_slice() {
377 *elem *= alpha;
378 }
379 }
380
381 pub fn transpose(&self) -> Self
385 where
386 T: bytemuck::Zeroable,
387 {
388 let new_kind = match self.kind {
389 TriangularKind::Upper => TriangularKind::Lower,
390 TriangularKind::Lower => TriangularKind::Upper,
391 };
392
393 let mut result = Self::zeros(self.n, new_kind);
394
395 for j in 0..self.n {
396 for i in 0..self.n {
397 if let Some(src_idx) = self.packed_index(i, j) {
398 if let Some(dst_idx) = result.packed_index(j, i) {
400 result.data[dst_idx] = self.data[src_idx];
401 }
402 }
403 }
404 }
405
406 result
407 }
408}
409
410impl<T: Scalar + core::fmt::Debug> core::fmt::Debug for PackedMat<T> {
411 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
412 writeln!(f, "PackedMat {}x{} {:?} {{", self.n, self.n, self.kind)?;
413
414 for i in 0..self.n.min(8) {
415 write!(f, " [")?;
416 for j in 0..self.n.min(8) {
417 if j > 0 {
418 write!(f, ", ")?;
419 }
420 match self.get(i, j) {
421 Some(v) => write!(f, "{:8.4?}", v)?,
422 None => write!(f, " * ")?,
423 }
424 }
425 if self.n > 8 {
426 write!(f, ", ...")?;
427 }
428 writeln!(f, "]")?;
429 }
430 if self.n > 8 {
431 writeln!(f, " ...")?;
432 }
433 write!(f, "}}")
434 }
435}
436
437#[derive(Clone, Copy)]
439pub struct PackedRef<'a, T: Scalar> {
440 ptr: *const T,
442 n: usize,
444 kind: TriangularKind,
446 _marker: core::marker::PhantomData<&'a T>,
448}
449
450impl<'a, T: Scalar> PackedRef<'a, T> {
451 #[inline]
467 pub unsafe fn new(ptr: *const T, n: usize, kind: TriangularKind) -> Self {
468 PackedRef {
469 ptr,
470 n,
471 kind,
472 _marker: core::marker::PhantomData,
473 }
474 }
475
476 #[inline]
483 pub fn from_slice(data: &'a [T], n: usize, kind: TriangularKind) -> Self {
484 let expected_len = PackedMat::<T>::packed_len(n);
485 assert_eq!(
486 data.len(),
487 expected_len,
488 "Slice length must equal n*(n+1)/2"
489 );
490 unsafe { PackedRef::new(data.as_ptr(), n, kind) }
495 }
496
497 #[inline]
499 pub fn dim(&self) -> usize {
500 self.n
501 }
502
503 #[inline]
505 pub fn kind(&self) -> TriangularKind {
506 self.kind
507 }
508
509 #[inline]
511 pub fn packed_index(&self, row: usize, col: usize) -> Option<usize> {
512 if row >= self.n || col >= self.n {
513 return None;
514 }
515
516 match self.kind {
517 TriangularKind::Upper => {
518 if row <= col {
519 Some(col * (col + 1) / 2 + row)
520 } else {
521 None
522 }
523 }
524 TriangularKind::Lower => {
525 if row >= col {
526 let offset = self.n * col - col * (col.saturating_sub(1)) / 2;
530 Some(offset + (row - col))
531 } else {
532 None
533 }
534 }
535 }
536 }
537
538 #[inline]
540 pub fn get(&self, row: usize, col: usize) -> Option<&T> {
541 self.packed_index(row, col)
542 .map(|idx| unsafe { &*self.ptr.add(idx) })
543 }
544
545 #[inline]
547 pub fn as_ptr(&self) -> *const T {
548 self.ptr
549 }
550}
551
552unsafe impl<'a, T: Scalar + Send> Send for PackedRef<'a, T> {}
553unsafe impl<'a, T: Scalar + Sync> Sync for PackedRef<'a, T> {}
554
555pub struct PackedMut<'a, T: Scalar> {
557 ptr: *mut T,
559 n: usize,
561 kind: TriangularKind,
563 _marker: core::marker::PhantomData<&'a mut T>,
565}
566
567impl<'a, T: Scalar> PackedMut<'a, T> {
568 #[inline]
585 pub unsafe fn new(ptr: *mut T, n: usize, kind: TriangularKind) -> Self {
586 PackedMut {
587 ptr,
588 n,
589 kind,
590 _marker: core::marker::PhantomData,
591 }
592 }
593
594 #[inline]
601 pub fn from_slice(data: &'a mut [T], n: usize, kind: TriangularKind) -> Self {
602 let expected_len = PackedMat::<T>::packed_len(n);
603 assert_eq!(
604 data.len(),
605 expected_len,
606 "Slice length must equal n*(n+1)/2"
607 );
608 unsafe { PackedMut::new(data.as_mut_ptr(), n, kind) }
613 }
614
615 #[inline]
617 pub fn dim(&self) -> usize {
618 self.n
619 }
620
621 #[inline]
623 pub fn kind(&self) -> TriangularKind {
624 self.kind
625 }
626
627 #[inline]
629 pub fn packed_index(&self, row: usize, col: usize) -> Option<usize> {
630 if row >= self.n || col >= self.n {
631 return None;
632 }
633
634 match self.kind {
635 TriangularKind::Upper => {
636 if row <= col {
637 Some(col * (col + 1) / 2 + row)
638 } else {
639 None
640 }
641 }
642 TriangularKind::Lower => {
643 if row >= col {
644 let offset = self.n * col - col * (col.saturating_sub(1)) / 2;
648 Some(offset + (row - col))
649 } else {
650 None
651 }
652 }
653 }
654 }
655
656 #[inline]
658 pub fn get(&self, row: usize, col: usize) -> Option<&T> {
659 self.packed_index(row, col)
660 .map(|idx| unsafe { &*self.ptr.add(idx) })
661 }
662
663 #[inline]
665 pub fn get_mut(&mut self, row: usize, col: usize) -> Option<&mut T> {
666 self.packed_index(row, col)
667 .map(|idx| unsafe { &mut *self.ptr.add(idx) })
668 }
669
670 #[inline]
676 pub fn set(&mut self, row: usize, col: usize, value: T) -> Result<(), OutOfTriangleError> {
677 let idx = self.packed_index(row, col).ok_or(OutOfTriangleError {
678 row,
679 col,
680 dim: self.n,
681 kind: self.kind,
682 })?;
683 unsafe {
684 *self.ptr.add(idx) = value;
685 }
686 Ok(())
687 }
688
689 #[inline]
691 pub fn as_ptr(&self) -> *const T {
692 self.ptr
693 }
694
695 #[inline]
697 pub fn as_mut_ptr(&mut self) -> *mut T {
698 self.ptr
699 }
700
701 #[inline]
703 pub fn rb(&self) -> PackedRef<'_, T> {
704 unsafe { PackedRef::new(self.ptr, self.n, self.kind) }
708 }
709
710 #[inline]
712 pub fn rb_mut(&mut self) -> PackedMut<'_, T> {
713 unsafe { PackedMut::new(self.ptr, self.n, self.kind) }
716 }
717}
718
719unsafe impl<'a, T: Scalar + Send> Send for PackedMut<'a, T> {}
720unsafe impl<'a, T: Scalar + Sync> Sync for PackedMut<'a, T> {}
721
722#[cfg(test)]
723mod tests {
724 use super::*;
725
726 #[test]
727 fn test_packed_upper_indexing() {
728 let mut p: PackedMat<f64> = PackedMat::zeros(3, TriangularKind::Upper);
734
735 assert_eq!(p.packed_index(0, 0), Some(0));
737 assert_eq!(p.packed_index(0, 1), Some(1));
738 assert_eq!(p.packed_index(1, 1), Some(2));
739 assert_eq!(p.packed_index(0, 2), Some(3));
740 assert_eq!(p.packed_index(1, 2), Some(4));
741 assert_eq!(p.packed_index(2, 2), Some(5));
742
743 assert_eq!(p.packed_index(1, 0), None);
745 assert_eq!(p.packed_index(2, 0), None);
746 assert_eq!(p.packed_index(2, 1), None);
747
748 p.set(0, 0, 1.0).unwrap();
750 p.set(0, 1, 2.0).unwrap();
751 p.set(1, 1, 3.0).unwrap();
752 p.set(0, 2, 4.0).unwrap();
753 p.set(1, 2, 5.0).unwrap();
754 p.set(2, 2, 6.0).unwrap();
755
756 assert_eq!(p.get(0, 0), Some(&1.0));
757 assert_eq!(p.get(0, 1), Some(&2.0));
758 assert_eq!(p.get(1, 1), Some(&3.0));
759 assert_eq!(p.get(0, 2), Some(&4.0));
760 assert_eq!(p.get(1, 2), Some(&5.0));
761 assert_eq!(p.get(2, 2), Some(&6.0));
762 }
763
764 #[test]
765 fn test_packed_lower_indexing() {
766 let mut p: PackedMat<f64> = PackedMat::zeros(3, TriangularKind::Lower);
772
773 assert_eq!(p.packed_index(0, 0), Some(0));
775 assert_eq!(p.packed_index(1, 0), Some(1));
776 assert_eq!(p.packed_index(2, 0), Some(2));
777 assert_eq!(p.packed_index(1, 1), Some(3));
778 assert_eq!(p.packed_index(2, 1), Some(4));
779 assert_eq!(p.packed_index(2, 2), Some(5));
780
781 assert_eq!(p.packed_index(0, 1), None);
783 assert_eq!(p.packed_index(0, 2), None);
784 assert_eq!(p.packed_index(1, 2), None);
785
786 p.set(0, 0, 1.0).unwrap();
788 p.set(1, 0, 2.0).unwrap();
789 p.set(2, 0, 3.0).unwrap();
790 p.set(1, 1, 4.0).unwrap();
791 p.set(2, 1, 5.0).unwrap();
792 p.set(2, 2, 6.0).unwrap();
793
794 assert_eq!(p.get(0, 0), Some(&1.0));
795 assert_eq!(p.get(1, 0), Some(&2.0));
796 assert_eq!(p.get(2, 0), Some(&3.0));
797 assert_eq!(p.get(1, 1), Some(&4.0));
798 assert_eq!(p.get(2, 1), Some(&5.0));
799 assert_eq!(p.get(2, 2), Some(&6.0));
800 }
801
802 #[test]
803 fn test_packed_len() {
804 assert_eq!(PackedMat::<f64>::packed_len(0), 0);
805 assert_eq!(PackedMat::<f64>::packed_len(1), 1);
806 assert_eq!(PackedMat::<f64>::packed_len(2), 3);
807 assert_eq!(PackedMat::<f64>::packed_len(3), 6);
808 assert_eq!(PackedMat::<f64>::packed_len(4), 10);
809 assert_eq!(PackedMat::<f64>::packed_len(10), 55);
810 }
811
812 #[test]
813 fn test_packed_to_dense() {
814 let mut p: PackedMat<f64> = PackedMat::zeros(3, TriangularKind::Upper);
815 p.set(0, 0, 1.0).unwrap();
816 p.set(0, 1, 2.0).unwrap();
817 p.set(1, 1, 3.0).unwrap();
818 p.set(0, 2, 4.0).unwrap();
819 p.set(1, 2, 5.0).unwrap();
820 p.set(2, 2, 6.0).unwrap();
821
822 let dense = p.to_dense();
823 assert_eq!(dense[(0, 0)], 1.0);
824 assert_eq!(dense[(0, 1)], 2.0);
825 assert_eq!(dense[(1, 1)], 3.0);
826 assert_eq!(dense[(0, 2)], 4.0);
827 assert_eq!(dense[(1, 2)], 5.0);
828 assert_eq!(dense[(2, 2)], 6.0);
829
830 assert_eq!(dense[(1, 0)], 0.0);
832 assert_eq!(dense[(2, 0)], 0.0);
833 assert_eq!(dense[(2, 1)], 0.0);
834 }
835
836 #[test]
837 fn test_packed_from_dense() {
838 use crate::Mat;
839
840 let dense = Mat::from_rows(&[&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0], &[7.0, 8.0, 9.0]]);
841
842 let upper = PackedMat::from_dense(&dense.as_ref(), TriangularKind::Upper);
843 assert_eq!(upper.get(0, 0), Some(&1.0));
844 assert_eq!(upper.get(0, 1), Some(&2.0));
845 assert_eq!(upper.get(0, 2), Some(&3.0));
846 assert_eq!(upper.get(1, 1), Some(&5.0));
847 assert_eq!(upper.get(1, 2), Some(&6.0));
848 assert_eq!(upper.get(2, 2), Some(&9.0));
849
850 let lower = PackedMat::from_dense(&dense.as_ref(), TriangularKind::Lower);
851 assert_eq!(lower.get(0, 0), Some(&1.0));
852 assert_eq!(lower.get(1, 0), Some(&4.0));
853 assert_eq!(lower.get(2, 0), Some(&7.0));
854 assert_eq!(lower.get(1, 1), Some(&5.0));
855 assert_eq!(lower.get(2, 1), Some(&8.0));
856 assert_eq!(lower.get(2, 2), Some(&9.0));
857 }
858
859 #[test]
860 fn test_packed_diagonal() {
861 let mut p: PackedMat<f64> = PackedMat::zeros(3, TriangularKind::Upper);
862 p.set(0, 0, 1.0).unwrap();
863 p.set(0, 1, 10.0).unwrap();
864 p.set(1, 1, 2.0).unwrap();
865 p.set(0, 2, 20.0).unwrap();
866 p.set(1, 2, 30.0).unwrap();
867 p.set(2, 2, 3.0).unwrap();
868
869 let diag = p.diagonal();
870 assert_eq!(diag, vec![1.0, 2.0, 3.0]);
871
872 p.set_diagonal(&[10.0, 20.0, 30.0]);
874 let diag2 = p.diagonal();
875 assert_eq!(diag2, vec![10.0, 20.0, 30.0]);
876 }
877
878 #[test]
879 fn test_packed_transpose() {
880 let mut upper: PackedMat<f64> = PackedMat::zeros(3, TriangularKind::Upper);
881 upper.set(0, 0, 1.0).unwrap();
882 upper.set(0, 1, 2.0).unwrap();
883 upper.set(1, 1, 3.0).unwrap();
884 upper.set(0, 2, 4.0).unwrap();
885 upper.set(1, 2, 5.0).unwrap();
886 upper.set(2, 2, 6.0).unwrap();
887
888 let lower = upper.transpose();
889 assert_eq!(lower.kind(), TriangularKind::Lower);
890
891 assert_eq!(lower.get(0, 0), Some(&1.0));
893 assert_eq!(lower.get(1, 0), Some(&2.0)); assert_eq!(lower.get(1, 1), Some(&3.0));
895 assert_eq!(lower.get(2, 0), Some(&4.0)); assert_eq!(lower.get(2, 1), Some(&5.0)); assert_eq!(lower.get(2, 2), Some(&6.0));
898 }
899
900 #[test]
901 fn test_packed_ref() {
902 let data = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0];
903 let pref = PackedRef::from_slice(&data, 3, TriangularKind::Upper);
904
905 assert_eq!(pref.dim(), 3);
906 assert_eq!(pref.get(0, 0), Some(&1.0));
907 assert_eq!(pref.get(0, 1), Some(&2.0));
908 assert_eq!(pref.get(1, 1), Some(&3.0));
909 assert_eq!(pref.get(0, 2), Some(&4.0));
910 assert_eq!(pref.get(1, 2), Some(&5.0));
911 assert_eq!(pref.get(2, 2), Some(&6.0));
912 }
913
914 #[test]
915 fn test_packed_mut() {
916 let mut data = [0.0f64; 6];
917 let mut pmut = PackedMut::from_slice(&mut data, 3, TriangularKind::Lower);
918
919 pmut.set(0, 0, 1.0).unwrap();
920 pmut.set(1, 0, 2.0).unwrap();
921 pmut.set(2, 0, 3.0).unwrap();
922 pmut.set(1, 1, 4.0).unwrap();
923 pmut.set(2, 1, 5.0).unwrap();
924 pmut.set(2, 2, 6.0).unwrap();
925
926 assert_eq!(data, [1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
927 }
928
929 #[test]
930 fn test_packed_scale() {
931 let mut p: PackedMat<f64> = PackedMat::zeros(2, TriangularKind::Upper);
932 p.set(0, 0, 1.0).unwrap();
933 p.set(0, 1, 2.0).unwrap();
934 p.set(1, 1, 3.0).unwrap();
935
936 p.scale(2.0);
937
938 assert_eq!(p.get(0, 0), Some(&2.0));
939 assert_eq!(p.get(0, 1), Some(&4.0));
940 assert_eq!(p.get(1, 1), Some(&6.0));
941 }
942
943 #[test]
949 fn test_packed_mat_set_out_of_triangle_returns_error_not_panic() {
950 let mut upper: PackedMat<f64> = PackedMat::zeros(3, TriangularKind::Upper);
951 let err = upper
952 .set(1, 0, 99.0)
953 .expect_err("(1, 0) is below the diagonal of upper-triangular storage");
954 assert_eq!(
955 err,
956 OutOfTriangleError {
957 row: 1,
958 col: 0,
959 dim: 3,
960 kind: TriangularKind::Upper,
961 }
962 );
963 let message = err.to_string();
965 assert!(message.contains("(1, 0)"), "message was: {message}");
966
967 let mut lower: PackedMat<f64> = PackedMat::zeros(3, TriangularKind::Lower);
968 assert!(lower.set(0, 2, 1.0).is_err());
969
970 assert!(upper.set(5, 5, 1.0).is_err());
972
973 assert_eq!(upper.get(0, 0), Some(&0.0));
975 }
976
977 #[test]
980 fn test_packed_mut_set_out_of_triangle_returns_error_not_panic() {
981 let mut data = [0.0f64; 6];
982 let mut pmut = PackedMut::from_slice(&mut data, 3, TriangularKind::Lower);
983
984 let err = pmut
985 .set(0, 1, 42.0)
986 .expect_err("(0, 1) is above the diagonal of lower-triangular storage");
987 assert_eq!(err.row, 0);
988 assert_eq!(err.col, 1);
989 assert_eq!(err.dim, 3);
990 assert_eq!(err.kind, TriangularKind::Lower);
991
992 pmut.set(0, 0, 7.0).unwrap();
994 assert_eq!(data[0], 7.0);
995 }
996
997 #[test]
1006 #[should_panic(expected = "packed length overflow")]
1007 fn test_packed_len_overflow_panics_not_wraps() {
1008 let _ = PackedMat::<f64>::packed_len(1usize << 32);
1010 }
1011
1012 #[test]
1013 #[should_panic(expected = "packed length overflow")]
1014 fn test_packed_zeros_overflow_panics_not_wraps() {
1015 let _: PackedMat<f64> = PackedMat::zeros(1usize << 32, TriangularKind::Upper);
1016 }
1017
1018 #[test]
1019 fn test_packed_len_is_still_exact_for_sane_dims() {
1020 assert_eq!(PackedMat::<f64>::packed_len(0), 0);
1021 assert_eq!(PackedMat::<f64>::packed_len(1), 1);
1022 assert_eq!(PackedMat::<f64>::packed_len(3), 6);
1023 assert_eq!(PackedMat::<f64>::packed_len(100), 5050);
1024 }
1025
1026 #[test]
1029 #[should_panic(expected = "Slice length must equal")]
1030 fn test_packed_ref_from_slice_rejects_short_slice() {
1031 let data = [0.0f64; 10];
1035 let _ = PackedRef::from_slice(&data, 1000, TriangularKind::Upper);
1036 }
1037
1038 #[test]
1039 #[should_panic(expected = "Slice length must equal")]
1040 fn test_packed_mut_from_slice_rejects_short_slice() {
1041 let mut data = [0.0f64; 10];
1042 let _ = PackedMut::from_slice(&mut data, 1000, TriangularKind::Upper);
1043 }
1044}