zenu_matrix/
matrix_format.rs

1use std::fmt;
2
3use crate::{
4    device::DeviceBase,
5    dim::{DimDyn, DimTrait},
6    index::Index0D,
7    matrix::{Matrix, Ref, Repr},
8    num::Num,
9};
10/// Default threshold, below this element count, we don't ellipsize
11const ARRAY_MANY_ELEMENT_LIMIT: usize = 500;
12/// Limit of element count for non-last axes before overflowing with an ellipsis.
13const AXIS_LIMIT_STACKED: usize = 6;
14/// Limit for next to last axis (printed as column)
15/// An odd number because one element uses the same space as the ellipsis.
16const AXIS_LIMIT_COL: usize = 11;
17/// Limit for last axis (printed as row)
18/// An odd number because one element uses approximately the space of the ellipsis.
19const AXIS_LIMIT_ROW: usize = 11;
20
21/// The string used as an ellipsis.
22const ELLIPSIS: &str = "...";
23
24#[expect(clippy::struct_field_names)]
25#[derive(Clone, Debug)]
26struct FormatOptions {
27    axis_collapse_limit: usize,
28    axis_collapse_limit_next_last: usize,
29    axis_collapse_limit_last: usize,
30}
31
32impl FormatOptions {
33    pub(crate) fn default_for_array(nelem: usize, no_limit: bool) -> Self {
34        let default = Self {
35            axis_collapse_limit: AXIS_LIMIT_STACKED,
36            axis_collapse_limit_next_last: AXIS_LIMIT_COL,
37            axis_collapse_limit_last: AXIS_LIMIT_ROW,
38        };
39        default.set_no_limit(no_limit || nelem < ARRAY_MANY_ELEMENT_LIMIT)
40    }
41
42    fn set_no_limit(mut self, no_limit: bool) -> Self {
43        if no_limit {
44            self.axis_collapse_limit = usize::MAX;
45            self.axis_collapse_limit_next_last = usize::MAX;
46            self.axis_collapse_limit_last = usize::MAX;
47        }
48        self
49    }
50
51    /// Axis length collapse limit before ellipsizing, where `axis_rindex` is
52    /// the index of the axis from the back.
53    pub(crate) fn collapse_limit(&self, axis_rindex: usize) -> usize {
54        match axis_rindex {
55            0 => self.axis_collapse_limit_last,
56            1 => self.axis_collapse_limit_next_last,
57            _ => self.axis_collapse_limit,
58        }
59    }
60}
61
62/// Formats the contents of a list of items, using an ellipsis to indicate when
63/// the `length` of the list is greater than `limit`.
64///
65/// # Parameters
66///
67/// * `f`: The formatter.
68/// * `length`: The length of the list.
69/// * `limit`: The maximum number of items before overflow.
70/// * `separator`: Separator to write between items.
71/// * `ellipsis`: Ellipsis for indicating overflow.
72/// * `fmt_elem`: A function that formats an element in the list, given the
73///   formatter and the index of the item in the list.
74fn format_with_overflow(
75    f: &mut fmt::Formatter<'_>,
76    length: usize,
77    limit: usize,
78    separator: &str,
79    ellipsis: &str,
80    fmt_elem: &mut dyn FnMut(&mut fmt::Formatter, usize) -> fmt::Result,
81) -> fmt::Result {
82    if length == 0 {
83        // no-op
84    } else if length <= limit {
85        fmt_elem(f, 0)?;
86        for i in 1..length {
87            f.write_str(separator)?;
88            fmt_elem(f, i)?;
89        }
90    } else {
91        let edge = limit / 2;
92        fmt_elem(f, 0)?;
93        for i in 1..edge {
94            f.write_str(separator)?;
95            fmt_elem(f, i)?;
96        }
97        f.write_str(separator)?;
98        f.write_str(ellipsis)?;
99        for i in length - edge..length {
100            f.write_str(separator)?;
101            fmt_elem(f, i)?;
102        }
103    }
104    Ok(())
105}
106
107fn format_array<A, R, S, D, F>(
108    array: &Matrix<R, S, D>,
109    f: &mut fmt::Formatter<'_>,
110    format: F,
111    fmt_opt: &FormatOptions,
112) -> fmt::Result
113where
114    A: Num,
115    F: FnMut(&A, &mut fmt::Formatter<'_>) -> fmt::Result + Clone,
116    S: DimTrait,
117    R: Repr<Item = A>,
118    D: DeviceBase,
119{
120    // Cast into a dynamically dimensioned view
121    // This is required to be able to use `index_axis` for the recursive case
122    format_array_inner(
123        array.to_ref().into_dyn_dim(),
124        f,
125        format,
126        fmt_opt,
127        0,
128        array.shape().len(),
129    )
130}
131
132fn format_array_inner<T, F, D>(
133    view: Matrix<Ref<&T>, DimDyn, D>,
134    f: &mut fmt::Formatter<'_>,
135    mut format: F,
136    fmt_opt: &FormatOptions,
137    depth: usize,
138    full_ndim: usize,
139) -> fmt::Result
140where
141    T: Num,
142    F: FnMut(&T, &mut fmt::Formatter<'_>) -> fmt::Result + Clone,
143    D: DeviceBase,
144{
145    match view.shape().slice() {
146        // If it's 0 dimensional, we just print out the scalar
147        &[] => format(&view.index_item(&[] as &[usize]), f)?,
148        // We handle 1-D arrays as a special case
149        &[len] => {
150            let view = view.into_dyn_dim();
151            f.write_str("[")?;
152            format_with_overflow(
153                f,
154                len,
155                fmt_opt.collapse_limit(0),
156                ", ",
157                ELLIPSIS,
158                &mut |f, index| format(&view.index_item([index]), f),
159            )?;
160            f.write_str("]")?;
161        }
162        // For n-dimensional arrays, we proceed recursively
163        shape => {
164            let blank_lines = "\n".repeat(shape.len() - 2);
165            let indent = " ".repeat(depth + 1);
166            let separator = format!(",\n{blank_lines}{indent}");
167
168            f.write_str("[")?;
169            let limit = fmt_opt.collapse_limit(full_ndim - depth - 1);
170            format_with_overflow(f, shape[0], limit, &separator, ELLIPSIS, &mut |f, index| {
171                format_array_inner(
172                    view.index_axis_dyn(Index0D::new(index)),
173                    f,
174                    format.clone(),
175                    fmt_opt,
176                    depth + 1,
177                    full_ndim,
178                )
179            })?;
180            f.write_str("]")?;
181        }
182    }
183    Ok(())
184}
185
186// NOTE: We can impl other fmt traits here
187/// Format the array using `Display` and apply the formatting parameters used
188/// to each element.
189///
190/// The array is shown in multiline style.
191impl<A, R, S, D> fmt::Display for Matrix<R, S, D>
192where
193    A: Num + fmt::Display,
194    R: Repr<Item = A>,
195    S: DimTrait,
196    D: DeviceBase,
197{
198    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
199        let fmt_opt = FormatOptions::default_for_array(self.shape().num_elm(), f.alternate());
200        format_array(self, f, <_>::fmt, &fmt_opt)
201    }
202}
203
204/// Format the array using `Debug` and apply the formatting parameters used
205/// to each element.
206///
207/// The array is shown in multiline style.
208impl<A, R, S, D> fmt::Debug for Matrix<R, S, D>
209where
210    A: Num + fmt::Debug,
211    R: Repr<Item = A>,
212    S: DimTrait,
213    D: DeviceBase,
214{
215    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
216        let fmt_opt = FormatOptions::default_for_array(self.shape().num_elm(), f.alternate());
217        format_array(self, f, <_>::fmt, &fmt_opt)?;
218
219        // Add extra information for Debug
220        write!(
221            f,
222            "\n shape={:?}, strides={:?}",
223            // "\nshape={:?}",
224            self.shape(),
225            self.stride(),
226        )?;
227        // match self.shape().len() {
228        //     Some(ndim) => write!(f, ", const ndim={}", ndim)?,
229        //     None => write!(f, ", dynamic ndim={}", self.shape().len())?,
230        // }
231        Ok(())
232    }
233}
234
235#[cfg(test)]
236mod matrix_format_test {
237    #![expect(clippy::uninlined_format_args)]
238    use crate::{
239        device::DeviceBase,
240        dim::{Dim2, Dim4, DimDyn},
241        matrix::{Matrix, Owned},
242    };
243
244    fn assert_str_eq(expected: &str, actual: &str) {
245        // use assert to avoid printing the strings twice on failure
246        assert!(
247            expected == actual,
248            "formatting assertion failed\nexpected:\n{}\nactual:\n{}\n",
249            expected,
250            actual,
251        );
252    }
253
254    fn small_array_1d<D: DeviceBase>() {
255        let a: Matrix<Owned<f32>, DimDyn, D> = Matrix::from_vec(vec![1., 2., 3., 4., 5.], [5]);
256        assert_eq!(format!("{}", a), "[1, 2, 3, 4, 5]");
257    }
258    #[test]
259    fn small_array_1d_cpu() {
260        small_array_1d::<crate::device::cpu::Cpu>();
261    }
262    #[cfg(feature = "nvidia")]
263    #[test]
264    fn small_array_1d_nvidia() {
265        small_array_1d::<crate::device::nvidia::Nvidia>();
266    }
267
268    fn mid_array_1d<D: DeviceBase>() {
269        let a: Matrix<Owned<f32>, DimDyn, D> =
270            Matrix::from_vec(vec![1., 2., 3., 4., 5., 6., 7., 8., 9., 10.], [10]);
271        assert_eq!(format!("{}", a), "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]");
272    }
273    #[test]
274    fn mid_array_1d_cpu() {
275        mid_array_1d::<crate::device::cpu::Cpu>();
276    }
277    #[cfg(feature = "nvidia")]
278    #[test]
279    fn mid_array_1d_nvidia() {
280        mid_array_1d::<crate::device::nvidia::Nvidia>();
281    }
282
283    #[expect(clippy::cast_precision_loss)]
284    fn large_array_1d<D: DeviceBase>() {
285        let mut v = Vec::new();
286        for i in 1..=1000 {
287            v.push(i as f32);
288        }
289        let a: Matrix<Owned<f32>, DimDyn, D> = Matrix::from_vec(v, [1000]);
290        assert_eq!(
291            format!("{}", a),
292            "[1, 2, 3, 4, 5, ..., 996, 997, 998, 999, 1000]"
293        );
294    }
295    #[test]
296    fn large_array_1d_cpu() {
297        large_array_1d::<crate::device::cpu::Cpu>();
298    }
299    #[cfg(feature = "nvidia")]
300    #[test]
301    fn large_array_1d_nvidia() {
302        large_array_1d::<crate::device::nvidia::Nvidia>();
303    }
304
305    fn dim_2_last_axis_overflow<D: DeviceBase>() {
306        let a: Matrix<Owned<f32>, Dim2, D> = Matrix::ones([22, 24]);
307        let actual = format!("{}", a);
308        let expected = "\
309[[1, 1, 1, 1, 1, ..., 1, 1, 1, 1, 1],
310 [1, 1, 1, 1, 1, ..., 1, 1, 1, 1, 1],
311 [1, 1, 1, 1, 1, ..., 1, 1, 1, 1, 1],
312 [1, 1, 1, 1, 1, ..., 1, 1, 1, 1, 1],
313 [1, 1, 1, 1, 1, ..., 1, 1, 1, 1, 1],
314 ...,
315 [1, 1, 1, 1, 1, ..., 1, 1, 1, 1, 1],
316 [1, 1, 1, 1, 1, ..., 1, 1, 1, 1, 1],
317 [1, 1, 1, 1, 1, ..., 1, 1, 1, 1, 1],
318 [1, 1, 1, 1, 1, ..., 1, 1, 1, 1, 1],
319 [1, 1, 1, 1, 1, ..., 1, 1, 1, 1, 1]]";
320        assert_str_eq(expected, &actual);
321    }
322    #[test]
323    fn dim_2_last_axis_overflow_cpu() {
324        dim_2_last_axis_overflow::<crate::device::cpu::Cpu>();
325    }
326    #[cfg(feature = "nvidia")]
327    #[test]
328    fn dim_2_last_axis_overflow_nvidia() {
329        dim_2_last_axis_overflow::<crate::device::nvidia::Nvidia>();
330    }
331
332    #[expect(clippy::cast_lossless)]
333    fn dim_3_overflow_most<D: DeviceBase>() {
334        let mut v = Vec::new();
335        for i in 0..7 {
336            for j in 0..11 {
337                for k in 0..12 {
338                    v.push(
339                        1000.
340                            + (100. * ((i as f64).sqrt() + (j as f64).sin() + k as f64)).round()
341                                / 100.,
342                    );
343                }
344            }
345        }
346        // let a = Array3::from_shape_fn(
347        //     (AXIS_LIMIT_STACKED + 1, AXIS_LIMIT_COL, AXIS_LIMIT_ROW + 1),
348        //     |(i, j, k)| {
349        //         1000. + (100. * ((i as f64).sqrt() + (j as f64).sin() + k as f64)).round() / 100.
350        //     },
351        // );
352        let a: Matrix<Owned<f64>, DimDyn, D> = Matrix::from_vec(v, [7, 11, 12]);
353        let actual = format!("{:6.1}", a);
354        let expected = "\
355[[[1000.0, 1001.0, 1002.0, 1003.0, 1004.0, ..., 1007.0, 1008.0, 1009.0, 1010.0, 1011.0],
356  [1000.8, 1001.8, 1002.8, 1003.8, 1004.8, ..., 1007.8, 1008.8, 1009.8, 1010.8, 1011.8],
357  [1000.9, 1001.9, 1002.9, 1003.9, 1004.9, ..., 1007.9, 1008.9, 1009.9, 1010.9, 1011.9],
358  [1000.1, 1001.1, 1002.1, 1003.1, 1004.1, ..., 1007.1, 1008.1, 1009.1, 1010.1, 1011.1],
359  [ 999.2, 1000.2, 1001.2, 1002.2, 1003.2, ..., 1006.2, 1007.2, 1008.2, 1009.2, 1010.2],
360  [ 999.0, 1000.0, 1001.0, 1002.0, 1003.0, ..., 1006.0, 1007.0, 1008.0, 1009.0, 1010.0],
361  [ 999.7, 1000.7, 1001.7, 1002.7, 1003.7, ..., 1006.7, 1007.7, 1008.7, 1009.7, 1010.7],
362  [1000.7, 1001.7, 1002.7, 1003.7, 1004.7, ..., 1007.7, 1008.7, 1009.7, 1010.7, 1011.7],
363  [1001.0, 1002.0, 1003.0, 1004.0, 1005.0, ..., 1008.0, 1009.0, 1010.0, 1011.0, 1012.0],
364  [1000.4, 1001.4, 1002.4, 1003.4, 1004.4, ..., 1007.4, 1008.4, 1009.4, 1010.4, 1011.4],
365  [ 999.5, 1000.5, 1001.5, 1002.5, 1003.5, ..., 1006.5, 1007.5, 1008.5, 1009.5, 1010.5]],
366
367 [[1001.0, 1002.0, 1003.0, 1004.0, 1005.0, ..., 1008.0, 1009.0, 1010.0, 1011.0, 1012.0],
368  [1001.8, 1002.8, 1003.8, 1004.8, 1005.8, ..., 1008.8, 1009.8, 1010.8, 1011.8, 1012.8],
369  [1001.9, 1002.9, 1003.9, 1004.9, 1005.9, ..., 1008.9, 1009.9, 1010.9, 1011.9, 1012.9],
370  [1001.1, 1002.1, 1003.1, 1004.1, 1005.1, ..., 1008.1, 1009.1, 1010.1, 1011.1, 1012.1],
371  [1000.2, 1001.2, 1002.2, 1003.2, 1004.2, ..., 1007.2, 1008.2, 1009.2, 1010.2, 1011.2],
372  [1000.0, 1001.0, 1002.0, 1003.0, 1004.0, ..., 1007.0, 1008.0, 1009.0, 1010.0, 1011.0],
373  [1000.7, 1001.7, 1002.7, 1003.7, 1004.7, ..., 1007.7, 1008.7, 1009.7, 1010.7, 1011.7],
374  [1001.7, 1002.7, 1003.7, 1004.7, 1005.7, ..., 1008.7, 1009.7, 1010.7, 1011.7, 1012.7],
375  [1002.0, 1003.0, 1004.0, 1005.0, 1006.0, ..., 1009.0, 1010.0, 1011.0, 1012.0, 1013.0],
376  [1001.4, 1002.4, 1003.4, 1004.4, 1005.4, ..., 1008.4, 1009.4, 1010.4, 1011.4, 1012.4],
377  [1000.5, 1001.5, 1002.5, 1003.5, 1004.5, ..., 1007.5, 1008.5, 1009.5, 1010.5, 1011.5]],
378
379 [[1001.4, 1002.4, 1003.4, 1004.4, 1005.4, ..., 1008.4, 1009.4, 1010.4, 1011.4, 1012.4],
380  [1002.3, 1003.3, 1004.3, 1005.3, 1006.3, ..., 1009.3, 1010.3, 1011.3, 1012.3, 1013.3],
381  [1002.3, 1003.3, 1004.3, 1005.3, 1006.3, ..., 1009.3, 1010.3, 1011.3, 1012.3, 1013.3],
382  [1001.6, 1002.6, 1003.6, 1004.6, 1005.6, ..., 1008.6, 1009.6, 1010.6, 1011.6, 1012.6],
383  [1000.7, 1001.7, 1002.7, 1003.7, 1004.7, ..., 1007.7, 1008.7, 1009.7, 1010.7, 1011.7],
384  [1000.5, 1001.5, 1002.5, 1003.5, 1004.5, ..., 1007.5, 1008.5, 1009.5, 1010.5, 1011.5],
385  [1001.1, 1002.1, 1003.1, 1004.1, 1005.1, ..., 1008.1, 1009.1, 1010.1, 1011.1, 1012.1],
386  [1002.1, 1003.1, 1004.1, 1005.1, 1006.1, ..., 1009.1, 1010.1, 1011.1, 1012.1, 1013.1],
387  [1002.4, 1003.4, 1004.4, 1005.4, 1006.4, ..., 1009.4, 1010.4, 1011.4, 1012.4, 1013.4],
388  [1001.8, 1002.8, 1003.8, 1004.8, 1005.8, ..., 1008.8, 1009.8, 1010.8, 1011.8, 1012.8],
389  [1000.9, 1001.9, 1002.9, 1003.9, 1004.9, ..., 1007.9, 1008.9, 1009.9, 1010.9, 1011.9]],
390
391 ...,
392
393 [[1002.0, 1003.0, 1004.0, 1005.0, 1006.0, ..., 1009.0, 1010.0, 1011.0, 1012.0, 1013.0],
394  [1002.8, 1003.8, 1004.8, 1005.8, 1006.8, ..., 1009.8, 1010.8, 1011.8, 1012.8, 1013.8],
395  [1002.9, 1003.9, 1004.9, 1005.9, 1006.9, ..., 1009.9, 1010.9, 1011.9, 1012.9, 1013.9],
396  [1002.1, 1003.1, 1004.1, 1005.1, 1006.1, ..., 1009.1, 1010.1, 1011.1, 1012.1, 1013.1],
397  [1001.2, 1002.2, 1003.2, 1004.2, 1005.2, ..., 1008.2, 1009.2, 1010.2, 1011.2, 1012.2],
398  [1001.0, 1002.0, 1003.0, 1004.0, 1005.0, ..., 1008.0, 1009.0, 1010.0, 1011.0, 1012.0],
399  [1001.7, 1002.7, 1003.7, 1004.7, 1005.7, ..., 1008.7, 1009.7, 1010.7, 1011.7, 1012.7],
400  [1002.7, 1003.7, 1004.7, 1005.7, 1006.7, ..., 1009.7, 1010.7, 1011.7, 1012.7, 1013.7],
401  [1003.0, 1004.0, 1005.0, 1006.0, 1007.0, ..., 1010.0, 1011.0, 1012.0, 1013.0, 1014.0],
402  [1002.4, 1003.4, 1004.4, 1005.4, 1006.4, ..., 1009.4, 1010.4, 1011.4, 1012.4, 1013.4],
403  [1001.5, 1002.5, 1003.5, 1004.5, 1005.5, ..., 1008.5, 1009.5, 1010.5, 1011.5, 1012.5]],
404
405 [[1002.2, 1003.2, 1004.2, 1005.2, 1006.2, ..., 1009.2, 1010.2, 1011.2, 1012.2, 1013.2],
406  [1003.1, 1004.1, 1005.1, 1006.1, 1007.1, ..., 1010.1, 1011.1, 1012.1, 1013.1, 1014.1],
407  [1003.1, 1004.1, 1005.1, 1006.1, 1007.1, ..., 1010.1, 1011.1, 1012.1, 1013.1, 1014.1],
408  [1002.4, 1003.4, 1004.4, 1005.4, 1006.4, ..., 1009.4, 1010.4, 1011.4, 1012.4, 1013.4],
409  [1001.5, 1002.5, 1003.5, 1004.5, 1005.5, ..., 1008.5, 1009.5, 1010.5, 1011.5, 1012.5],
410  [1001.3, 1002.3, 1003.3, 1004.3, 1005.3, ..., 1008.3, 1009.3, 1010.3, 1011.3, 1012.3],
411  [1002.0, 1003.0, 1004.0, 1005.0, 1006.0, ..., 1009.0, 1010.0, 1011.0, 1012.0, 1013.0],
412  [1002.9, 1003.9, 1004.9, 1005.9, 1006.9, ..., 1009.9, 1010.9, 1011.9, 1012.9, 1013.9],
413  [1003.2, 1004.2, 1005.2, 1006.2, 1007.2, ..., 1010.2, 1011.2, 1012.2, 1013.2, 1014.2],
414  [1002.6, 1003.6, 1004.6, 1005.6, 1006.6, ..., 1009.6, 1010.6, 1011.6, 1012.6, 1013.6],
415  [1001.7, 1002.7, 1003.7, 1004.7, 1005.7, ..., 1008.7, 1009.7, 1010.7, 1011.7, 1012.7]],
416
417 [[1002.5, 1003.5, 1004.5, 1005.5, 1006.5, ..., 1009.5, 1010.5, 1011.5, 1012.5, 1013.5],
418  [1003.3, 1004.3, 1005.3, 1006.3, 1007.3, ..., 1010.3, 1011.3, 1012.3, 1013.3, 1014.3],
419  [1003.4, 1004.4, 1005.4, 1006.4, 1007.4, ..., 1010.4, 1011.4, 1012.4, 1013.4, 1014.4],
420  [1002.6, 1003.6, 1004.6, 1005.6, 1006.6, ..., 1009.6, 1010.6, 1011.6, 1012.6, 1013.6],
421  [1001.7, 1002.7, 1003.7, 1004.7, 1005.7, ..., 1008.7, 1009.7, 1010.7, 1011.7, 1012.7],
422  [1001.5, 1002.5, 1003.5, 1004.5, 1005.5, ..., 1008.5, 1009.5, 1010.5, 1011.5, 1012.5],
423  [1002.2, 1003.2, 1004.2, 1005.2, 1006.2, ..., 1009.2, 1010.2, 1011.2, 1012.2, 1013.2],
424  [1003.1, 1004.1, 1005.1, 1006.1, 1007.1, ..., 1010.1, 1011.1, 1012.1, 1013.1, 1014.1],
425  [1003.4, 1004.4, 1005.4, 1006.4, 1007.4, ..., 1010.4, 1011.4, 1012.4, 1013.4, 1014.4],
426  [1002.9, 1003.9, 1004.9, 1005.9, 1006.9, ..., 1009.9, 1010.9, 1011.9, 1012.9, 1013.9],
427  [1001.9, 1002.9, 1003.9, 1004.9, 1005.9, ..., 1008.9, 1009.9, 1010.9, 1011.9, 1012.9]]]";
428        assert_str_eq(expected, &actual);
429    }
430    #[test]
431    fn dim_3_overflow_most_cpu() {
432        dim_3_overflow_most::<crate::device::cpu::Cpu>();
433    }
434    #[cfg(feature = "nvidia")]
435    #[test]
436    fn dim_3_overflow_most_nvidia() {
437        dim_3_overflow_most::<crate::device::nvidia::Nvidia>();
438    }
439
440    #[expect(clippy::too_many_lines, clippy::cast_precision_loss)]
441    fn dim_4_overflow_outer<D: DeviceBase>() {
442        // let a = Array4::from_shape_fn((10, 10, 3, 3), |(i, j, k, l)| i + j + k + l);
443        let mut v = Vec::new();
444        for i in 0..10 {
445            for j in 0..10 {
446                for k in 0..3 {
447                    for l in 0..3 {
448                        v.push((i + j + k + l) as f32);
449                    }
450                }
451            }
452        }
453        let a: Matrix<Owned<f32>, Dim4, D> = Matrix::from_vec(v, [10, 10, 3, 3]);
454        let actual = format!("{:2}", a);
455        // Generated using NumPy with:
456        // np.set_printoptions(threshold=500, edgeitems=3)
457        // np.fromfunction(lambda i, j, k, l: i + j + k + l, (10, 10, 3, 3), dtype=int)
458        //
459        let expected = "\
460[[[[ 0,  1,  2],
461   [ 1,  2,  3],
462   [ 2,  3,  4]],
463
464  [[ 1,  2,  3],
465   [ 2,  3,  4],
466   [ 3,  4,  5]],
467
468  [[ 2,  3,  4],
469   [ 3,  4,  5],
470   [ 4,  5,  6]],
471
472  ...,
473
474  [[ 7,  8,  9],
475   [ 8,  9, 10],
476   [ 9, 10, 11]],
477
478  [[ 8,  9, 10],
479   [ 9, 10, 11],
480   [10, 11, 12]],
481
482  [[ 9, 10, 11],
483   [10, 11, 12],
484   [11, 12, 13]]],
485
486
487 [[[ 1,  2,  3],
488   [ 2,  3,  4],
489   [ 3,  4,  5]],
490
491  [[ 2,  3,  4],
492   [ 3,  4,  5],
493   [ 4,  5,  6]],
494
495  [[ 3,  4,  5],
496   [ 4,  5,  6],
497   [ 5,  6,  7]],
498
499  ...,
500
501  [[ 8,  9, 10],
502   [ 9, 10, 11],
503   [10, 11, 12]],
504
505  [[ 9, 10, 11],
506   [10, 11, 12],
507   [11, 12, 13]],
508
509  [[10, 11, 12],
510   [11, 12, 13],
511   [12, 13, 14]]],
512
513
514 [[[ 2,  3,  4],
515   [ 3,  4,  5],
516   [ 4,  5,  6]],
517
518  [[ 3,  4,  5],
519   [ 4,  5,  6],
520   [ 5,  6,  7]],
521
522  [[ 4,  5,  6],
523   [ 5,  6,  7],
524   [ 6,  7,  8]],
525
526  ...,
527
528  [[ 9, 10, 11],
529   [10, 11, 12],
530   [11, 12, 13]],
531
532  [[10, 11, 12],
533   [11, 12, 13],
534   [12, 13, 14]],
535
536  [[11, 12, 13],
537   [12, 13, 14],
538   [13, 14, 15]]],
539
540
541 ...,
542
543
544 [[[ 7,  8,  9],
545   [ 8,  9, 10],
546   [ 9, 10, 11]],
547
548  [[ 8,  9, 10],
549   [ 9, 10, 11],
550   [10, 11, 12]],
551
552  [[ 9, 10, 11],
553   [10, 11, 12],
554   [11, 12, 13]],
555
556  ...,
557
558  [[14, 15, 16],
559   [15, 16, 17],
560   [16, 17, 18]],
561
562  [[15, 16, 17],
563   [16, 17, 18],
564   [17, 18, 19]],
565
566  [[16, 17, 18],
567   [17, 18, 19],
568   [18, 19, 20]]],
569
570
571 [[[ 8,  9, 10],
572   [ 9, 10, 11],
573   [10, 11, 12]],
574
575  [[ 9, 10, 11],
576   [10, 11, 12],
577   [11, 12, 13]],
578
579  [[10, 11, 12],
580   [11, 12, 13],
581   [12, 13, 14]],
582
583  ...,
584
585  [[15, 16, 17],
586   [16, 17, 18],
587   [17, 18, 19]],
588
589  [[16, 17, 18],
590   [17, 18, 19],
591   [18, 19, 20]],
592
593  [[17, 18, 19],
594   [18, 19, 20],
595   [19, 20, 21]]],
596
597
598 [[[ 9, 10, 11],
599   [10, 11, 12],
600   [11, 12, 13]],
601
602  [[10, 11, 12],
603   [11, 12, 13],
604   [12, 13, 14]],
605
606  [[11, 12, 13],
607   [12, 13, 14],
608   [13, 14, 15]],
609
610  ...,
611
612  [[16, 17, 18],
613   [17, 18, 19],
614   [18, 19, 20]],
615
616  [[17, 18, 19],
617   [18, 19, 20],
618   [19, 20, 21]],
619
620  [[18, 19, 20],
621   [19, 20, 21],
622   [20, 21, 22]]]]";
623        assert_str_eq(expected, &actual);
624    }
625    #[test]
626    fn dim_4_overflow_outer_cpu() {
627        dim_4_overflow_outer::<crate::device::cpu::Cpu>();
628    }
629    #[cfg(feature = "nvidia")]
630    #[test]
631    fn dim_4_overflow_outer_nvidia() {
632        dim_4_overflow_outer::<crate::device::nvidia::Nvidia>();
633    }
634}