1pub mod extended;
7
8use crate::array::owned::Array;
9use crate::dimension::{Dimension, Ix1, IxDyn};
10use crate::dtype::Element;
11use crate::error::{FerrayError, FerrayResult};
12
13pub fn reshape<T: Element, D: Dimension>(
31 a: &Array<T, D>,
32 new_shape: &[usize],
33) -> FerrayResult<Array<T, IxDyn>> {
34 let old_size = a.size();
35 let new_size: usize = new_shape.iter().product();
36 if old_size != new_size {
37 return Err(FerrayError::shape_mismatch(format!(
38 "cannot reshape array of size {old_size} into shape {new_shape:?} (size {new_size})",
39 )));
40 }
41 let view = a.inner.view().into_dyn();
42 let reshaped = view
43 .to_shape(ndarray::IxDyn(new_shape))
44 .map_err(|e| FerrayError::shape_mismatch(e.to_string()))?;
45 Ok(Array::from_ndarray(
49 reshaped.as_standard_layout().into_owned(),
50 ))
51}
52
53pub fn ravel<T: Element, D: Dimension>(a: &Array<T, D>) -> FerrayResult<Array<T, Ix1>> {
58 let n = a.size();
59 let view = a.inner.view().into_dyn();
60 let reshaped = view
61 .to_shape(ndarray::IxDyn(&[n]))
62 .expect("1-D reshape always succeeds for a size-preserving target");
63 let standard = reshaped.as_standard_layout().into_owned();
64 let one_d = standard
65 .into_dimensionality::<ndarray::Ix1>()
66 .expect("reshape result has ndim == 1 by construction");
67 Ok(Array::from_ndarray(one_d))
68}
69
70pub fn flatten<T: Element, D: Dimension>(a: &Array<T, D>) -> FerrayResult<Array<T, Ix1>> {
74 ravel(a)
75}
76
77pub fn squeeze<T: Element, D: Dimension>(
88 a: &Array<T, D>,
89 axis: Option<usize>,
90) -> FerrayResult<Array<T, IxDyn>> {
91 let shape = a.shape();
92 if let Some(ax) = axis {
93 if ax >= shape.len() {
94 return Err(FerrayError::axis_out_of_bounds(ax, shape.len()));
95 }
96 if shape[ax] != 1 {
97 return Err(FerrayError::invalid_value(format!(
98 "cannot select axis {} with size {} for squeeze (must be 1)",
99 ax, shape[ax],
100 )));
101 }
102 let new_shape: Vec<usize> = shape
103 .iter()
104 .enumerate()
105 .filter(|&(i, _)| i != ax)
106 .map(|(_, &s)| s)
107 .collect();
108 let data: Vec<T> = a.iter().cloned().collect();
109 Array::from_vec(IxDyn::new(&new_shape), data)
110 } else {
111 let new_shape: Vec<usize> = shape.iter().copied().filter(|&s| s != 1).collect();
112 let new_shape = if new_shape.is_empty() && !shape.is_empty() {
115 vec![1]
116 } else if new_shape.is_empty() {
117 vec![]
118 } else {
119 new_shape
120 };
121 let data: Vec<T> = a.iter().cloned().collect();
122 Array::from_vec(IxDyn::new(&new_shape), data)
123 }
124}
125
126pub fn expand_dims<T: Element, D: Dimension>(
133 a: &Array<T, D>,
134 axis: usize,
135) -> FerrayResult<Array<T, IxDyn>> {
136 let ndim = a.ndim();
137 if axis > ndim {
138 return Err(FerrayError::axis_out_of_bounds(axis, ndim + 1));
139 }
140 let mut new_shape: Vec<usize> = a.shape().to_vec();
141 new_shape.insert(axis, 1);
142 let data: Vec<T> = a.iter().cloned().collect();
143 Array::from_vec(IxDyn::new(&new_shape), data)
144}
145
146pub fn broadcast_to<T: Element, D: Dimension>(
159 a: &Array<T, D>,
160 new_shape: &[usize],
161) -> FerrayResult<Array<T, IxDyn>> {
162 let src_shape = a.shape();
163 let dyn_view = a.inner.view().into_dyn();
164 let broadcast_view = dyn_view
165 .broadcast(ndarray::IxDyn(new_shape))
166 .ok_or_else(|| FerrayError::BroadcastFailure {
167 shape_a: src_shape.to_vec(),
168 shape_b: new_shape.to_vec(),
169 })?;
170 Ok(Array::from_ndarray(
174 broadcast_view.as_standard_layout().into_owned(),
175 ))
176}
177
178pub fn concatenate<T: Element>(
191 arrays: &[Array<T, IxDyn>],
192 axis: usize,
193) -> FerrayResult<Array<T, IxDyn>> {
194 if arrays.is_empty() {
195 return Err(FerrayError::invalid_value(
196 "concatenate: need at least one array",
197 ));
198 }
199 let ndim = arrays[0].ndim();
200 if axis >= ndim {
201 return Err(FerrayError::axis_out_of_bounds(axis, ndim));
202 }
203 let base_shape = arrays[0].shape();
204
205 let mut total_along_axis = 0usize;
207 for arr in arrays {
208 if arr.ndim() != ndim {
209 return Err(FerrayError::shape_mismatch(format!(
210 "all arrays must have same ndim; got {} and {}",
211 ndim,
212 arr.ndim(),
213 )));
214 }
215 for (i, (&s, &base)) in arr.shape().iter().zip(base_shape.iter()).enumerate() {
216 if i != axis && s != base {
217 return Err(FerrayError::shape_mismatch(format!(
218 "shape mismatch on axis {i}: {s} vs {base}",
219 )));
220 }
221 }
222 total_along_axis += arr.shape()[axis];
223 }
224
225 let mut new_shape = base_shape.to_vec();
227 new_shape[axis] = total_along_axis;
228 let total: usize = new_shape.iter().product();
229 let mut data = Vec::with_capacity(total);
230
231 let src_vecs: Vec<Vec<T>> = arrays.iter().map(|a| a.iter().cloned().collect()).collect();
233
234 let mut out_strides = vec![1usize; ndim];
236 for i in (0..ndim - 1).rev() {
237 out_strides[i] = out_strides[i + 1] * new_shape[i + 1];
238 }
239
240 for flat_idx in 0..total {
242 let mut rem = flat_idx;
244 let mut nd_idx = vec![0usize; ndim];
245 for i in 0..ndim {
246 nd_idx[i] = rem / out_strides[i];
247 rem %= out_strides[i];
248 }
249
250 let concat_idx = nd_idx[axis];
252 let mut offset = 0;
253 let mut src_arr_idx = 0;
254 for (k, arr) in arrays.iter().enumerate() {
255 let len_along = arr.shape()[axis];
256 if concat_idx < offset + len_along {
257 src_arr_idx = k;
258 break;
259 }
260 offset += len_along;
261 }
262 let local_concat_idx = concat_idx - offset;
263
264 let src_shape = arrays[src_arr_idx].shape();
266 let mut src_flat = 0usize;
267 let mut src_mul = 1usize;
268 for i in (0..ndim).rev() {
269 let idx = if i == axis {
270 local_concat_idx
271 } else {
272 nd_idx[i]
273 };
274 src_flat += idx * src_mul;
275 src_mul *= src_shape[i];
276 }
277
278 let elem = src_vecs[src_arr_idx].get(src_flat).ok_or_else(|| {
279 FerrayError::invalid_value(format!(
280 "concatenate: internal index {} out of range for source array of length {}",
281 src_flat,
282 src_vecs[src_arr_idx].len(),
283 ))
284 })?;
285 data.push(elem.clone());
286 }
287
288 Array::from_vec(IxDyn::new(&new_shape), data)
289}
290
291pub fn stack<T: Element>(arrays: &[Array<T, IxDyn>], axis: usize) -> FerrayResult<Array<T, IxDyn>> {
303 if arrays.is_empty() {
304 return Err(FerrayError::invalid_value("stack: need at least one array"));
305 }
306 let base_shape = arrays[0].shape();
307 let ndim = base_shape.len();
308
309 if axis > ndim {
310 return Err(FerrayError::axis_out_of_bounds(axis, ndim + 1));
311 }
312
313 for arr in &arrays[1..] {
314 if arr.shape() != base_shape {
315 return Err(FerrayError::shape_mismatch(format!(
316 "all input arrays must have the same shape; got {:?} and {:?}",
317 base_shape,
318 arr.shape(),
319 )));
320 }
321 }
322
323 let mut expanded = Vec::with_capacity(arrays.len());
325 for arr in arrays {
326 expanded.push(expand_dims(arr, axis)?);
327 }
328 concatenate(&expanded, axis)
329}
330
331pub fn vstack<T: Element>(arrays: &[Array<T, IxDyn>]) -> FerrayResult<Array<T, IxDyn>> {
336 if arrays.is_empty() {
337 return Err(FerrayError::invalid_value(
338 "vstack: need at least one array",
339 ));
340 }
341 let ndim = arrays[0].ndim();
343 if ndim == 1 {
344 let mut reshaped = Vec::with_capacity(arrays.len());
345 for arr in arrays {
346 let n = arr.shape()[0];
347 reshaped.push(reshape(arr, &[1, n])?);
348 }
349 concatenate(&reshaped, 0)
350 } else {
351 concatenate(arrays, 0)
352 }
353}
354
355pub fn hstack<T: Element>(arrays: &[Array<T, IxDyn>]) -> FerrayResult<Array<T, IxDyn>> {
360 if arrays.is_empty() {
361 return Err(FerrayError::invalid_value(
362 "hstack: need at least one array",
363 ));
364 }
365 let ndim = arrays[0].ndim();
366 if ndim == 1 {
367 concatenate(arrays, 0)
368 } else {
369 concatenate(arrays, 1)
370 }
371}
372
373pub fn dstack<T: Element>(arrays: &[Array<T, IxDyn>]) -> FerrayResult<Array<T, IxDyn>> {
381 if arrays.is_empty() {
382 return Err(FerrayError::invalid_value(
383 "dstack: need at least one array",
384 ));
385 }
386 let mut expanded = Vec::with_capacity(arrays.len());
387 for arr in arrays {
388 let shape = arr.shape();
389 match shape.len() {
390 1 => {
391 let n = shape[0];
392 expanded.push(reshape(arr, &[1, n, 1])?);
393 }
394 2 => {
395 let (m, n) = (shape[0], shape[1]);
396 expanded.push(reshape(arr, &[m, n, 1])?);
397 }
398 _ => {
399 let data: Vec<T> = arr.iter().cloned().collect();
401 expanded.push(Array::from_vec(IxDyn::new(shape), data)?);
402 }
403 }
404 }
405 concatenate(&expanded, 2)
406}
407
408pub fn column_stack<T: Element>(arrays: &[Array<T, IxDyn>]) -> FerrayResult<Array<T, IxDyn>> {
419 if arrays.is_empty() {
420 return Err(FerrayError::invalid_value(
421 "column_stack: need at least one array",
422 ));
423 }
424 let first_ndim = arrays[0].ndim();
425 if first_ndim == 1 {
426 let n = arrays[0].shape()[0];
428 let mut reshaped = Vec::with_capacity(arrays.len());
429 for arr in arrays {
430 if arr.ndim() != 1 {
431 return Err(FerrayError::shape_mismatch(
432 "column_stack: all inputs must have the same ndim",
433 ));
434 }
435 if arr.shape()[0] != n {
436 return Err(FerrayError::shape_mismatch(format!(
437 "column_stack: 1-D inputs must have the same length; got {} and {}",
438 n,
439 arr.shape()[0],
440 )));
441 }
442 reshaped.push(reshape(arr, &[n, 1])?);
443 }
444 concatenate(&reshaped, 1)
445 } else {
446 hstack(arrays)
448 }
449}
450
451pub fn row_stack<T: Element>(arrays: &[Array<T, IxDyn>]) -> FerrayResult<Array<T, IxDyn>> {
456 vstack(arrays)
457}
458
459pub fn block<T: Element>(blocks: &[Vec<Array<T, IxDyn>>]) -> FerrayResult<Array<T, IxDyn>> {
469 if blocks.is_empty() {
470 return Err(FerrayError::invalid_value("block: empty input"));
471 }
472 let mut rows = Vec::with_capacity(blocks.len());
473 for row in blocks {
474 if row.is_empty() {
475 return Err(FerrayError::invalid_value("block: empty row"));
476 }
477 let row_arr = if row.len() == 1 {
479 let data: Vec<T> = row[0].iter().cloned().collect();
480 Array::from_vec(IxDyn::new(row[0].shape()), data)?
481 } else {
482 hstack(row)?
483 };
484 rows.push(row_arr);
485 }
486 if rows.len() == 1 {
487 Ok(rows.pop().unwrap_or_else(|| unreachable!()))
489 } else {
490 vstack(&rows)
491 }
492}
493
494pub fn split<T: Element>(
503 a: &Array<T, IxDyn>,
504 n_sections: usize,
505 axis: usize,
506) -> FerrayResult<Vec<Array<T, IxDyn>>> {
507 let shape = a.shape();
508 if axis >= shape.len() {
509 return Err(FerrayError::axis_out_of_bounds(axis, shape.len()));
510 }
511 let axis_len = shape[axis];
512 if n_sections == 0 {
513 return Err(FerrayError::invalid_value("split: n_sections must be > 0"));
514 }
515 if axis_len % n_sections != 0 {
516 return Err(FerrayError::invalid_value(format!(
517 "array of size {axis_len} along axis {axis} cannot be evenly split into {n_sections} sections",
518 )));
519 }
520 let chunk_size = axis_len / n_sections;
521 let indices: Vec<usize> = (1..n_sections).map(|i| i * chunk_size).collect();
522 array_split(a, &indices, axis)
523}
524
525pub fn array_split<T: Element>(
534 a: &Array<T, IxDyn>,
535 indices: &[usize],
536 axis: usize,
537) -> FerrayResult<Vec<Array<T, IxDyn>>> {
538 let shape = a.shape();
539 let ndim = shape.len();
540 if axis >= ndim {
541 return Err(FerrayError::axis_out_of_bounds(axis, ndim));
542 }
543 let axis_len = shape[axis];
544 let src_data: Vec<T> = a.iter().cloned().collect();
545
546 let mut splits = Vec::with_capacity(indices.len() + 2);
548 splits.push(0);
549 for &idx in indices {
550 splits.push(idx.min(axis_len));
551 }
552 splits.push(axis_len);
553
554 let mut src_strides = vec![1usize; ndim];
556 for i in (0..ndim - 1).rev() {
557 src_strides[i] = src_strides[i + 1] * shape[i + 1];
558 }
559
560 let mut result = Vec::with_capacity(splits.len() - 1);
561 for w in splits.windows(2) {
562 let start = w[0];
563 let end = w[1];
564 let chunk_len = end - start;
565
566 let mut sub_shape = shape.to_vec();
567 sub_shape[axis] = chunk_len;
568 let sub_total: usize = sub_shape.iter().product();
569
570 let mut sub_strides = vec![1usize; ndim];
572 for i in (0..ndim - 1).rev() {
573 sub_strides[i] = sub_strides[i + 1] * sub_shape[i + 1];
574 }
575
576 let mut sub_data = Vec::with_capacity(sub_total);
577 for flat in 0..sub_total {
578 let mut rem = flat;
580 let mut src_flat = 0usize;
581 for i in 0..ndim {
582 let idx = rem / sub_strides[i];
583 rem %= sub_strides[i];
584 let src_idx = if i == axis { idx + start } else { idx };
585 src_flat += src_idx * src_strides[i];
586 }
587 sub_data.push(src_data[src_flat].clone());
588 }
589 result.push(Array::from_vec(IxDyn::new(&sub_shape), sub_data)?);
590 }
591
592 Ok(result)
593}
594
595pub fn array_split_n<T: Element>(
605 a: &Array<T, IxDyn>,
606 n: usize,
607 axis: usize,
608) -> FerrayResult<Vec<Array<T, IxDyn>>> {
609 if n == 0 {
610 return Err(FerrayError::invalid_value("array_split_n: n must be > 0"));
611 }
612 let shape = a.shape();
613 if axis >= shape.len() {
614 return Err(FerrayError::axis_out_of_bounds(axis, shape.len()));
615 }
616 let axis_len = shape[axis];
617
618 let base = axis_len / n;
622 let extra = axis_len % n;
623 let mut indices = Vec::with_capacity(n.saturating_sub(1));
624 let mut cum = 0usize;
625 for i in 0..n - 1 {
626 cum += if i < extra { base + 1 } else { base };
627 indices.push(cum);
628 }
629 array_split(a, &indices, axis)
630}
631
632pub fn vsplit<T: Element>(
636 a: &Array<T, IxDyn>,
637 n_sections: usize,
638) -> FerrayResult<Vec<Array<T, IxDyn>>> {
639 split(a, n_sections, 0)
640}
641
642pub fn hsplit<T: Element>(
646 a: &Array<T, IxDyn>,
647 n_sections: usize,
648) -> FerrayResult<Vec<Array<T, IxDyn>>> {
649 split(a, n_sections, 1)
650}
651
652pub fn dsplit<T: Element>(
656 a: &Array<T, IxDyn>,
657 n_sections: usize,
658) -> FerrayResult<Vec<Array<T, IxDyn>>> {
659 split(a, n_sections, 2)
660}
661
662pub fn transpose<T: Element, D: Dimension>(
677 a: &Array<T, D>,
678 axes: Option<&[usize]>,
679) -> FerrayResult<Array<T, IxDyn>> {
680 let ndim = a.ndim();
681 let perm: Vec<usize> = match axes {
682 Some(ax) => {
683 if ax.len() != ndim {
684 return Err(FerrayError::invalid_value(format!(
685 "axes must have length {} but got {}",
686 ndim,
687 ax.len(),
688 )));
689 }
690 let mut seen = vec![false; ndim];
692 for &a in ax {
693 if a >= ndim {
694 return Err(FerrayError::axis_out_of_bounds(a, ndim));
695 }
696 if seen[a] {
697 return Err(FerrayError::invalid_value(format!(
698 "duplicate axis {a} in transpose",
699 )));
700 }
701 seen[a] = true;
702 }
703 ax.to_vec()
704 }
705 None => (0..ndim).rev().collect(),
706 };
707
708 let permuted = a
717 .inner
718 .view()
719 .into_dyn()
720 .permuted_axes(ndarray::IxDyn(&perm));
721 Ok(Array::from_ndarray(
722 permuted.as_standard_layout().into_owned(),
723 ))
724}
725
726pub fn swapaxes<T: Element, D: Dimension>(
733 a: &Array<T, D>,
734 axis1: usize,
735 axis2: usize,
736) -> FerrayResult<Array<T, IxDyn>> {
737 let ndim = a.ndim();
738 if axis1 >= ndim {
739 return Err(FerrayError::axis_out_of_bounds(axis1, ndim));
740 }
741 if axis2 >= ndim {
742 return Err(FerrayError::axis_out_of_bounds(axis2, ndim));
743 }
744 let mut perm: Vec<usize> = (0..ndim).collect();
745 perm.swap(axis1, axis2);
746 transpose(a, Some(&perm))
747}
748
749pub fn moveaxis<T: Element, D: Dimension>(
756 a: &Array<T, D>,
757 source: usize,
758 destination: usize,
759) -> FerrayResult<Array<T, IxDyn>> {
760 let ndim = a.ndim();
761 if source >= ndim {
762 return Err(FerrayError::axis_out_of_bounds(source, ndim));
763 }
764 if destination >= ndim {
765 return Err(FerrayError::axis_out_of_bounds(destination, ndim));
766 }
767 let mut order: Vec<usize> = (0..ndim).filter(|&x| x != source).collect();
769 order.insert(destination, source);
770 transpose(a, Some(&order))
771}
772
773pub fn rollaxis<T: Element, D: Dimension>(
780 a: &Array<T, D>,
781 axis: usize,
782 start: usize,
783) -> FerrayResult<Array<T, IxDyn>> {
784 let ndim = a.ndim();
785 if axis >= ndim {
786 return Err(FerrayError::axis_out_of_bounds(axis, ndim));
787 }
788 if start > ndim {
789 return Err(FerrayError::axis_out_of_bounds(start, ndim + 1));
790 }
791 let dst = if start > axis { start - 1 } else { start };
792 if axis == dst {
793 let data: Vec<T> = a.iter().cloned().collect();
795 return Array::from_vec(IxDyn::new(a.shape()), data);
796 }
797 moveaxis(a, axis, dst)
798}
799
800pub fn flip<T: Element, D: Dimension>(
807 a: &Array<T, D>,
808 axis: usize,
809) -> FerrayResult<Array<T, IxDyn>> {
810 let shape = a.shape();
811 let ndim = shape.len();
812 if axis >= ndim {
813 return Err(FerrayError::axis_out_of_bounds(axis, ndim));
814 }
815 let src_data: Vec<T> = a.iter().cloned().collect();
816 let total = src_data.len();
817
818 let mut strides = vec![1usize; ndim];
820 for i in (0..ndim.saturating_sub(1)).rev() {
821 strides[i] = strides[i + 1] * shape[i + 1];
822 }
823
824 let mut data = Vec::with_capacity(total);
825 for flat in 0..total {
826 let mut rem = flat;
827 let mut src_flat = 0usize;
828 for i in 0..ndim {
829 let idx = rem / strides[i];
830 rem %= strides[i];
831 let src_idx = if i == axis { shape[i] - 1 - idx } else { idx };
832 src_flat += src_idx * strides[i];
833 }
834 data.push(src_data[src_flat].clone());
835 }
836 Array::from_vec(IxDyn::new(shape), data)
837}
838
839pub fn fliplr<T: Element, D: Dimension>(a: &Array<T, D>) -> FerrayResult<Array<T, IxDyn>> {
846 if a.ndim() < 2 {
847 return Err(FerrayError::invalid_value(
848 "fliplr: array must be at least 2-D",
849 ));
850 }
851 flip(a, 1)
852}
853
854pub fn flipud<T: Element, D: Dimension>(a: &Array<T, D>) -> FerrayResult<Array<T, IxDyn>> {
861 if a.ndim() < 1 {
862 return Err(FerrayError::invalid_value(
863 "flipud: array must be at least 1-D",
864 ));
865 }
866 flip(a, 0)
867}
868
869pub fn rot90<T: Element, D: Dimension>(a: &Array<T, D>, k: i32) -> FerrayResult<Array<T, IxDyn>> {
878 if a.ndim() < 2 {
879 return Err(FerrayError::invalid_value(
880 "rot90: array must be at least 2-D",
881 ));
882 }
883 let k = k.rem_euclid(4);
885 let shape = a.shape();
886 let data: Vec<T> = a.iter().cloned().collect();
887
888 let as_dyn = Array::from_vec(IxDyn::new(shape), data)?;
890
891 match k {
892 0 => Ok(as_dyn),
893 1 => {
894 let flipped = flip(&as_dyn, 1)?;
896 swapaxes(&flipped, 0, 1)
897 }
898 2 => {
899 let f1 = flip(&as_dyn, 0)?;
901 flip(&f1, 1)
902 }
903 3 => {
904 let transposed = swapaxes(&as_dyn, 0, 1)?;
906 flip(&transposed, 1)
907 }
908 _ => unreachable!(),
909 }
910}
911
912pub fn roll<T: Element, D: Dimension>(
922 a: &Array<T, D>,
923 shift: isize,
924 axis: Option<usize>,
925) -> FerrayResult<Array<T, IxDyn>> {
926 match axis {
927 None => {
928 let data: Vec<T> = a.iter().cloned().collect();
930 let n = data.len();
931 if n == 0 {
932 return Array::from_vec(IxDyn::new(a.shape()), data);
933 }
934 let shift = ((shift % n as isize) + n as isize) as usize % n;
935 let mut rolled = Vec::with_capacity(n);
936 for i in 0..n {
937 rolled.push(data[(n + i - shift) % n].clone());
938 }
939 Array::from_vec(IxDyn::new(a.shape()), rolled)
940 }
941 Some(ax) => {
942 let shape = a.shape();
943 let ndim = shape.len();
944 if ax >= ndim {
945 return Err(FerrayError::axis_out_of_bounds(ax, ndim));
946 }
947 let axis_len = shape[ax];
948 if axis_len == 0 {
949 let data: Vec<T> = a.iter().cloned().collect();
950 return Array::from_vec(IxDyn::new(shape), data);
951 }
952 let shift = ((shift % axis_len as isize) + axis_len as isize) as usize % axis_len;
953 let src_data: Vec<T> = a.iter().cloned().collect();
954 let total = src_data.len();
955
956 let mut strides = vec![1usize; ndim];
958 for i in (0..ndim.saturating_sub(1)).rev() {
959 strides[i] = strides[i + 1] * shape[i + 1];
960 }
961
962 let mut data = Vec::with_capacity(total);
963 for flat in 0..total {
964 let mut rem = flat;
965 let mut src_flat = 0usize;
966 #[allow(clippy::needless_range_loop)]
967 for i in 0..ndim {
968 let idx = rem / strides[i];
969 rem %= strides[i];
970 let src_idx = if i == ax {
971 (axis_len + idx - shift) % axis_len
972 } else {
973 idx
974 };
975 src_flat += src_idx * strides[i];
976 }
977 data.push(src_data[src_flat].clone());
978 }
979 Array::from_vec(IxDyn::new(shape), data)
980 }
981 }
982}
983
984pub fn r_<T: Element>(arrays: &[Array<T, Ix1>]) -> FerrayResult<Array<T, Ix1>> {
1000 if arrays.is_empty() {
1001 return Err(FerrayError::invalid_value("r_: need at least one array"));
1002 }
1003 let total: usize = arrays.iter().map(|a| a.shape()[0]).sum();
1004 let mut data = Vec::with_capacity(total);
1005 for a in arrays {
1006 data.extend(a.iter().cloned());
1007 }
1008 Array::from_vec(Ix1::new([total]), data)
1009}
1010
1011pub fn c_<T: Element>(arrays: &[Array<T, Ix1>]) -> FerrayResult<Array<T, IxDyn>> {
1022 if arrays.is_empty() {
1023 return Err(FerrayError::invalid_value("c_: need at least one array"));
1024 }
1025 let n = arrays[0].shape()[0];
1026 for a in &arrays[1..] {
1027 if a.shape()[0] != n {
1028 return Err(FerrayError::shape_mismatch(format!(
1029 "c_: all 1-D inputs must have the same length; got {} and {}",
1030 n,
1031 a.shape()[0],
1032 )));
1033 }
1034 }
1035 let k = arrays.len();
1036 let cols: Vec<Vec<T>> = arrays.iter().map(|a| a.iter().cloned().collect()).collect();
1038 let mut data = Vec::with_capacity(n * k);
1039 for i in 0..n {
1040 for col in &cols {
1041 data.push(col[i].clone());
1042 }
1043 }
1044 Array::from_vec(IxDyn::new(&[n, k]), data)
1045}
1046
1047#[cfg(test)]
1052mod tests {
1053 use super::*;
1054
1055 fn dyn_arr(shape: &[usize], data: Vec<f64>) -> Array<f64, IxDyn> {
1056 Array::from_vec(IxDyn::new(shape), data).unwrap()
1057 }
1058
1059 #[test]
1062 fn test_reshape() {
1063 let a = dyn_arr(&[2, 3], vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
1064 let b = reshape(&a, &[3, 2]).unwrap();
1065 assert_eq!(b.shape(), &[3, 2]);
1066 let data: Vec<f64> = b.iter().copied().collect();
1067 assert_eq!(data, vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
1068 }
1069
1070 #[test]
1071 fn test_reshape_size_mismatch() {
1072 let a = dyn_arr(&[2, 3], vec![1.0; 6]);
1073 assert!(reshape(&a, &[2, 4]).is_err());
1074 }
1075
1076 #[test]
1077 fn test_ravel() {
1078 let a = dyn_arr(&[2, 3], vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
1079 let b = ravel(&a).unwrap();
1080 assert_eq!(b.shape(), &[6]);
1081 assert_eq!(b.as_slice().unwrap(), &[1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
1082 }
1083
1084 #[test]
1085 fn test_flatten() {
1086 let a = dyn_arr(&[2, 3], vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
1087 let b = flatten(&a).unwrap();
1088 assert_eq!(b.shape(), &[6]);
1089 }
1090
1091 #[test]
1092 fn test_squeeze() {
1093 let a = dyn_arr(&[1, 3, 1], vec![1.0, 2.0, 3.0]);
1094 let b = squeeze(&a, None).unwrap();
1095 assert_eq!(b.shape(), &[3]);
1096 }
1097
1098 #[test]
1099 fn test_squeeze_specific_axis() {
1100 let a = dyn_arr(&[1, 3, 1], vec![1.0, 2.0, 3.0]);
1101 let b = squeeze(&a, Some(0)).unwrap();
1102 assert_eq!(b.shape(), &[3, 1]);
1103 }
1104
1105 #[test]
1106 fn test_squeeze_not_size_1() {
1107 let a = dyn_arr(&[2, 3], vec![1.0; 6]);
1108 assert!(squeeze(&a, Some(0)).is_err());
1109 }
1110
1111 #[test]
1112 fn test_expand_dims() {
1113 let a = dyn_arr(&[3], vec![1.0, 2.0, 3.0]);
1114 let b = expand_dims(&a, 0).unwrap();
1115 assert_eq!(b.shape(), &[1, 3]);
1116 let c = expand_dims(&a, 1).unwrap();
1117 assert_eq!(c.shape(), &[3, 1]);
1118 }
1119
1120 #[test]
1121 fn test_expand_dims_oob() {
1122 let a = dyn_arr(&[3], vec![1.0, 2.0, 3.0]);
1123 assert!(expand_dims(&a, 3).is_err());
1124 }
1125
1126 #[test]
1127 fn test_broadcast_to() {
1128 let a = dyn_arr(&[1, 3], vec![1.0, 2.0, 3.0]);
1129 let b = broadcast_to(&a, &[3, 3]).unwrap();
1130 assert_eq!(b.shape(), &[3, 3]);
1131 let data: Vec<f64> = b.iter().copied().collect();
1132 assert_eq!(data, vec![1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0]);
1133 }
1134
1135 #[test]
1136 fn test_broadcast_to_1d_to_2d() {
1137 let a = dyn_arr(&[3], vec![1.0, 2.0, 3.0]);
1138 let b = broadcast_to(&a, &[2, 3]).unwrap();
1139 assert_eq!(b.shape(), &[2, 3]);
1140 }
1141
1142 #[test]
1143 fn test_broadcast_to_incompatible() {
1144 let a = dyn_arr(&[4], vec![1.0, 2.0, 3.0, 4.0]);
1145 assert!(broadcast_to(&a, &[3]).is_err());
1146 }
1147
1148 #[test]
1149 fn test_broadcast_to_from_non_contiguous_source() {
1150 let a = dyn_arr(&[2, 3], vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
1155 let t = transpose(&a, None).unwrap();
1157 let b = broadcast_to(&t, &[2, 3, 2]).unwrap();
1158 assert_eq!(b.shape(), &[2, 3, 2]);
1159 let data: Vec<f64> = b.iter().copied().collect();
1161 assert_eq!(&data[..6], &data[6..12]);
1162 }
1163
1164 #[test]
1167 fn test_concatenate() {
1168 let a = dyn_arr(&[2, 3], vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
1169 let b = dyn_arr(&[2, 3], vec![7.0, 8.0, 9.0, 10.0, 11.0, 12.0]);
1170 let c = concatenate(&[a, b], 0).unwrap();
1171 assert_eq!(c.shape(), &[4, 3]);
1172 }
1173
1174 #[test]
1175 fn test_concatenate_axis1() {
1176 let a = dyn_arr(&[2, 2], vec![1.0, 2.0, 3.0, 4.0]);
1177 let b = dyn_arr(&[2, 3], vec![5.0, 6.0, 7.0, 8.0, 9.0, 10.0]);
1178 let c = concatenate(&[a, b], 1).unwrap();
1179 assert_eq!(c.shape(), &[2, 5]);
1180 }
1181
1182 #[test]
1183 fn test_concatenate_shape_mismatch() {
1184 let a = dyn_arr(&[2, 3], vec![1.0; 6]);
1185 let b = dyn_arr(&[3, 3], vec![1.0; 9]);
1186 let c = concatenate(&[a, b], 0).unwrap();
1190 assert_eq!(c.shape(), &[5, 3]);
1191 }
1192
1193 #[test]
1194 fn test_concatenate_empty() {
1195 let v: Vec<Array<f64, IxDyn>> = vec![];
1196 assert!(concatenate(&v, 0).is_err());
1197 }
1198
1199 #[test]
1200 fn test_stack() {
1201 let a = dyn_arr(&[3], vec![1.0, 2.0, 3.0]);
1202 let b = dyn_arr(&[3], vec![4.0, 5.0, 6.0]);
1203 let c = stack(&[a, b], 0).unwrap();
1204 assert_eq!(c.shape(), &[2, 3]);
1205 let data: Vec<f64> = c.iter().copied().collect();
1206 assert_eq!(data, vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
1207 }
1208
1209 #[test]
1210 fn test_stack_axis1() {
1211 let a = dyn_arr(&[3], vec![1.0, 2.0, 3.0]);
1212 let b = dyn_arr(&[3], vec![4.0, 5.0, 6.0]);
1213 let c = stack(&[a, b], 1).unwrap();
1214 assert_eq!(c.shape(), &[3, 2]);
1215 let data: Vec<f64> = c.iter().copied().collect();
1216 assert_eq!(data, vec![1.0, 4.0, 2.0, 5.0, 3.0, 6.0]);
1217 }
1218
1219 #[test]
1220 fn test_vstack() {
1221 let a = dyn_arr(&[3], vec![1.0, 2.0, 3.0]);
1222 let b = dyn_arr(&[3], vec![4.0, 5.0, 6.0]);
1223 let c = vstack(&[a, b]).unwrap();
1224 assert_eq!(c.shape(), &[2, 3]);
1225 }
1226
1227 #[test]
1228 fn test_hstack() {
1229 let a = dyn_arr(&[3], vec![1.0, 2.0, 3.0]);
1230 let b = dyn_arr(&[3], vec![4.0, 5.0, 6.0]);
1231 let c = hstack(&[a, b]).unwrap();
1232 assert_eq!(c.shape(), &[6]);
1233 }
1234
1235 #[test]
1236 fn test_hstack_2d() {
1237 let a = dyn_arr(&[2, 2], vec![1.0, 2.0, 3.0, 4.0]);
1238 let b = dyn_arr(&[2, 3], vec![5.0, 6.0, 7.0, 8.0, 9.0, 10.0]);
1239 let c = hstack(&[a, b]).unwrap();
1240 assert_eq!(c.shape(), &[2, 5]);
1241 }
1242
1243 #[test]
1244 fn test_dstack() {
1245 let a = dyn_arr(&[2, 2], vec![1.0, 2.0, 3.0, 4.0]);
1246 let b = dyn_arr(&[2, 2], vec![5.0, 6.0, 7.0, 8.0]);
1247 let c = dstack(&[a, b]).unwrap();
1248 assert_eq!(c.shape(), &[2, 2, 2]);
1249 }
1250
1251 #[test]
1252 fn test_block() {
1253 let a = dyn_arr(&[2, 2], vec![1.0, 2.0, 3.0, 4.0]);
1254 let b = dyn_arr(&[2, 2], vec![5.0, 6.0, 7.0, 8.0]);
1255 let c = dyn_arr(&[2, 2], vec![9.0, 10.0, 11.0, 12.0]);
1256 let d = dyn_arr(&[2, 2], vec![13.0, 14.0, 15.0, 16.0]);
1257 let result = block(&[vec![a, b], vec![c, d]]).unwrap();
1258 assert_eq!(result.shape(), &[4, 4]);
1259 }
1260
1261 #[test]
1262 fn test_split() {
1263 let a = dyn_arr(&[6], vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
1264 let parts = split(&a, 3, 0).unwrap();
1265 assert_eq!(parts.len(), 3);
1266 assert_eq!(parts[0].shape(), &[2]);
1267 assert_eq!(parts[1].shape(), &[2]);
1268 assert_eq!(parts[2].shape(), &[2]);
1269 }
1270
1271 #[test]
1272 fn test_split_uneven() {
1273 let a = dyn_arr(&[5], vec![1.0, 2.0, 3.0, 4.0, 5.0]);
1274 assert!(split(&a, 3, 0).is_err()); }
1276
1277 #[test]
1278 fn test_array_split() {
1279 let a = dyn_arr(&[5], vec![1.0, 2.0, 3.0, 4.0, 5.0]);
1280 let parts = array_split(&a, &[2, 4], 0).unwrap();
1281 assert_eq!(parts.len(), 3);
1282 assert_eq!(parts[0].shape(), &[2]); assert_eq!(parts[1].shape(), &[2]); assert_eq!(parts[2].shape(), &[1]); }
1286
1287 #[test]
1288 fn test_vsplit() {
1289 let a = dyn_arr(&[4, 2], vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0]);
1290 let parts = vsplit(&a, 2).unwrap();
1291 assert_eq!(parts.len(), 2);
1292 assert_eq!(parts[0].shape(), &[2, 2]);
1293 }
1294
1295 #[test]
1296 fn test_hsplit() {
1297 let a = dyn_arr(&[2, 4], vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0]);
1298 let parts = hsplit(&a, 2).unwrap();
1299 assert_eq!(parts.len(), 2);
1300 assert_eq!(parts[0].shape(), &[2, 2]);
1301 }
1302
1303 #[test]
1306 fn test_transpose_2d() {
1307 let a = dyn_arr(&[2, 3], vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
1308 let b = transpose(&a, None).unwrap();
1309 assert_eq!(b.shape(), &[3, 2]);
1310 let data: Vec<f64> = b.iter().copied().collect();
1311 assert_eq!(data, vec![1.0, 4.0, 2.0, 5.0, 3.0, 6.0]);
1312 }
1313
1314 #[test]
1315 fn test_transpose_explicit() {
1316 let a = dyn_arr(&[2, 3], vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
1317 let b = transpose(&a, Some(&[1, 0])).unwrap();
1318 assert_eq!(b.shape(), &[3, 2]);
1319 }
1320
1321 #[test]
1322 fn test_transpose_bad_axes() {
1323 let a = dyn_arr(&[2, 3], vec![1.0; 6]);
1324 assert!(transpose(&a, Some(&[0])).is_err()); }
1326
1327 #[test]
1328 fn test_swapaxes() {
1329 let a = dyn_arr(&[2, 3, 4], vec![0.0; 24]);
1330 let b = swapaxes(&a, 0, 2).unwrap();
1331 assert_eq!(b.shape(), &[4, 3, 2]);
1332 }
1333
1334 #[test]
1335 fn test_moveaxis() {
1336 let a = dyn_arr(&[2, 3, 4], vec![0.0; 24]);
1337 let b = moveaxis(&a, 0, 2).unwrap();
1338 assert_eq!(b.shape(), &[3, 4, 2]);
1339 }
1340
1341 #[test]
1342 fn test_rollaxis() {
1343 let a = dyn_arr(&[2, 3, 4], vec![0.0; 24]);
1344 let b = rollaxis(&a, 2, 0).unwrap();
1345 assert_eq!(b.shape(), &[4, 2, 3]);
1346 }
1347
1348 #[test]
1349 fn test_flip() {
1350 let a = dyn_arr(&[3], vec![1.0, 2.0, 3.0]);
1351 let b = flip(&a, 0).unwrap();
1352 let data: Vec<f64> = b.iter().copied().collect();
1353 assert_eq!(data, vec![3.0, 2.0, 1.0]);
1354 }
1355
1356 #[test]
1357 fn test_flip_2d() {
1358 let a = dyn_arr(&[2, 3], vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
1359 let b = flip(&a, 0).unwrap();
1360 let data: Vec<f64> = b.iter().copied().collect();
1361 assert_eq!(data, vec![4.0, 5.0, 6.0, 1.0, 2.0, 3.0]);
1362
1363 let c = flip(&a, 1).unwrap();
1364 let data2: Vec<f64> = c.iter().copied().collect();
1365 assert_eq!(data2, vec![3.0, 2.0, 1.0, 6.0, 5.0, 4.0]);
1366 }
1367
1368 #[test]
1369 fn test_fliplr() {
1370 let a = dyn_arr(&[2, 3], vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
1371 let b = fliplr(&a).unwrap();
1372 let data: Vec<f64> = b.iter().copied().collect();
1373 assert_eq!(data, vec![3.0, 2.0, 1.0, 6.0, 5.0, 4.0]);
1374 }
1375
1376 #[test]
1377 fn test_flipud() {
1378 let a = dyn_arr(&[2, 3], vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
1379 let b = flipud(&a).unwrap();
1380 let data: Vec<f64> = b.iter().copied().collect();
1381 assert_eq!(data, vec![4.0, 5.0, 6.0, 1.0, 2.0, 3.0]);
1382 }
1383
1384 #[test]
1385 fn test_fliplr_1d_err() {
1386 let a = dyn_arr(&[3], vec![1.0, 2.0, 3.0]);
1387 assert!(fliplr(&a).is_err());
1388 }
1389
1390 #[test]
1391 fn test_rot90_once() {
1392 let a = dyn_arr(&[2, 2], vec![1.0, 2.0, 3.0, 4.0]);
1394 let b = rot90(&a, 1).unwrap();
1395 assert_eq!(b.shape(), &[2, 2]);
1396 let data: Vec<f64> = b.iter().copied().collect();
1397 assert_eq!(data, vec![2.0, 4.0, 1.0, 3.0]);
1398 }
1399
1400 #[test]
1401 fn test_rot90_twice() {
1402 let a = dyn_arr(&[2, 2], vec![1.0, 2.0, 3.0, 4.0]);
1403 let b = rot90(&a, 2).unwrap();
1404 let data: Vec<f64> = b.iter().copied().collect();
1405 assert_eq!(data, vec![4.0, 3.0, 2.0, 1.0]);
1406 }
1407
1408 #[test]
1409 fn test_rot90_four_is_identity() {
1410 let a = dyn_arr(&[2, 3], vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
1411 let b = rot90(&a, 4).unwrap();
1412 let data_a: Vec<f64> = a.iter().copied().collect();
1413 let data_b: Vec<f64> = b.iter().copied().collect();
1414 assert_eq!(data_a, data_b);
1415 assert_eq!(a.shape(), b.shape());
1416 }
1417
1418 #[test]
1419 fn test_roll_flat() {
1420 let a = dyn_arr(&[5], vec![1.0, 2.0, 3.0, 4.0, 5.0]);
1421 let b = roll(&a, 2, None).unwrap();
1422 let data: Vec<f64> = b.iter().copied().collect();
1423 assert_eq!(data, vec![4.0, 5.0, 1.0, 2.0, 3.0]);
1424 }
1425
1426 #[test]
1427 fn test_roll_negative() {
1428 let a = dyn_arr(&[5], vec![1.0, 2.0, 3.0, 4.0, 5.0]);
1429 let b = roll(&a, -2, None).unwrap();
1430 let data: Vec<f64> = b.iter().copied().collect();
1431 assert_eq!(data, vec![3.0, 4.0, 5.0, 1.0, 2.0]);
1432 }
1433
1434 #[test]
1435 fn test_roll_axis() {
1436 let a = dyn_arr(&[2, 3], vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
1437 let b = roll(&a, 1, Some(1)).unwrap();
1438 let data: Vec<f64> = b.iter().copied().collect();
1439 assert_eq!(data, vec![3.0, 1.0, 2.0, 6.0, 4.0, 5.0]);
1440 }
1441
1442 #[test]
1447 fn test_column_stack_1d() {
1448 let a = dyn_arr(&[4], vec![1.0, 2.0, 3.0, 4.0]);
1450 let b = dyn_arr(&[4], vec![10.0, 20.0, 30.0, 40.0]);
1451 let c = dyn_arr(&[4], vec![100.0, 200.0, 300.0, 400.0]);
1452 let result = column_stack(&[a, b, c]).unwrap();
1453 assert_eq!(result.shape(), &[4, 3]);
1454 assert_eq!(
1455 result.iter().copied().collect::<Vec<_>>(),
1456 vec![
1457 1.0, 10.0, 100.0, 2.0, 20.0, 200.0, 3.0, 30.0, 300.0, 4.0, 40.0, 400.0, ]
1462 );
1463 }
1464
1465 #[test]
1466 fn test_column_stack_2d_same_as_hstack() {
1467 let a = dyn_arr(&[2, 2], vec![1.0, 2.0, 3.0, 4.0]);
1468 let b = dyn_arr(&[2, 2], vec![5.0, 6.0, 7.0, 8.0]);
1469 let result = column_stack(&[a, b]).unwrap();
1470 assert_eq!(result.shape(), &[2, 4]);
1471 assert_eq!(
1472 result.iter().copied().collect::<Vec<_>>(),
1473 vec![1.0, 2.0, 5.0, 6.0, 3.0, 4.0, 7.0, 8.0]
1474 );
1475 }
1476
1477 #[test]
1478 fn test_column_stack_length_mismatch() {
1479 let a = dyn_arr(&[3], vec![1.0, 2.0, 3.0]);
1480 let b = dyn_arr(&[4], vec![1.0, 2.0, 3.0, 4.0]);
1481 assert!(column_stack(&[a, b]).is_err());
1482 }
1483
1484 #[test]
1485 fn test_row_stack_is_vstack() {
1486 let a = dyn_arr(&[3], vec![1.0, 2.0, 3.0]);
1487 let b = dyn_arr(&[3], vec![4.0, 5.0, 6.0]);
1488 let row = row_stack(&[a.clone(), b.clone()]).unwrap();
1489 let v = vstack(&[a, b]).unwrap();
1490 assert_eq!(row.shape(), v.shape());
1491 assert_eq!(
1492 row.iter().copied().collect::<Vec<_>>(),
1493 v.iter().copied().collect::<Vec<_>>()
1494 );
1495 }
1496
1497 #[test]
1498 fn test_array_split_n_uneven() {
1499 let a = dyn_arr(&[7], vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0]);
1501 let parts = array_split_n(&a, 3, 0).unwrap();
1502 assert_eq!(parts.len(), 3);
1503 assert_eq!(
1504 parts[0].iter().copied().collect::<Vec<_>>(),
1505 vec![1.0, 2.0, 3.0]
1506 );
1507 assert_eq!(parts[1].iter().copied().collect::<Vec<_>>(), vec![4.0, 5.0]);
1508 assert_eq!(parts[2].iter().copied().collect::<Vec<_>>(), vec![6.0, 7.0]);
1509 }
1510
1511 #[test]
1512 fn test_array_split_n_even() {
1513 let a = dyn_arr(&[6], vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
1514 let parts = array_split_n(&a, 3, 0).unwrap();
1515 assert_eq!(parts.len(), 3);
1516 for (i, expected) in [vec![1.0, 2.0], vec![3.0, 4.0], vec![5.0, 6.0]]
1517 .iter()
1518 .enumerate()
1519 {
1520 assert_eq!(&parts[i].iter().copied().collect::<Vec<_>>(), expected);
1521 }
1522 }
1523
1524 #[test]
1525 fn test_array_split_n_more_sections_than_elements() {
1526 let a = dyn_arr(&[3], vec![1.0, 2.0, 3.0]);
1529 let parts = array_split_n(&a, 5, 0).unwrap();
1530 assert_eq!(parts.len(), 5);
1531 assert_eq!(parts[0].iter().copied().collect::<Vec<_>>(), vec![1.0]);
1532 assert_eq!(parts[1].iter().copied().collect::<Vec<_>>(), vec![2.0]);
1533 assert_eq!(parts[2].iter().copied().collect::<Vec<_>>(), vec![3.0]);
1534 assert_eq!(
1535 parts[3].iter().copied().collect::<Vec<_>>(),
1536 Vec::<f64>::new()
1537 );
1538 assert_eq!(
1539 parts[4].iter().copied().collect::<Vec<_>>(),
1540 Vec::<f64>::new()
1541 );
1542 }
1543
1544 #[test]
1545 fn test_to_dyn_from_typed() {
1546 use crate::Array;
1547 use crate::dimension::Ix2;
1548 let typed =
1549 Array::<f64, Ix2>::from_vec(Ix2::new([2, 3]), vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0])
1550 .unwrap();
1551 let dy = typed.to_dyn();
1552 assert_eq!(dy.shape(), &[2, 3]);
1553 assert_eq!(
1554 dy.iter().copied().collect::<Vec<_>>(),
1555 vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]
1556 );
1557 }
1558
1559 #[test]
1560 fn test_concatenate_typed_via_to_dyn() {
1561 use crate::Array;
1564 use crate::dimension::Ix2;
1565 let a = Array::<f64, Ix2>::from_vec(Ix2::new([2, 2]), vec![1.0, 2.0, 3.0, 4.0]).unwrap();
1566 let b = Array::<f64, Ix2>::from_vec(Ix2::new([2, 2]), vec![5.0, 6.0, 7.0, 8.0]).unwrap();
1567 let result = concatenate(&[a.to_dyn(), b.to_dyn()], 0).unwrap();
1568 assert_eq!(result.shape(), &[4, 2]);
1569 assert_eq!(
1570 result.iter().copied().collect::<Vec<_>>(),
1571 vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0]
1572 );
1573 }
1574
1575 #[test]
1576 fn test_r_concatenates_1d() {
1577 use crate::Array;
1578 let a = Array::<i32, Ix1>::from_vec(Ix1::new([3]), vec![1, 2, 3]).unwrap();
1579 let b = Array::<i32, Ix1>::from_vec(Ix1::new([2]), vec![4, 5]).unwrap();
1580 let c = Array::<i32, Ix1>::from_vec(Ix1::new([1]), vec![6]).unwrap();
1581 let r = r_(&[a, b, c]).unwrap();
1582 assert_eq!(
1583 r.iter().copied().collect::<Vec<_>>(),
1584 vec![1, 2, 3, 4, 5, 6]
1585 );
1586 }
1587
1588 #[test]
1589 fn test_r_empty_input_errs() {
1590 let r = r_::<f64>(&[]);
1591 assert!(r.is_err());
1592 }
1593
1594 #[test]
1595 fn test_c_columns_to_2d() {
1596 use crate::Array;
1597 let a = Array::<i32, Ix1>::from_vec(Ix1::new([3]), vec![1, 2, 3]).unwrap();
1598 let b = Array::<i32, Ix1>::from_vec(Ix1::new([3]), vec![10, 20, 30]).unwrap();
1599 let r = c_(&[a, b]).unwrap();
1600 assert_eq!(r.shape(), &[3, 2]);
1601 assert_eq!(
1603 r.iter().copied().collect::<Vec<_>>(),
1604 vec![1, 10, 2, 20, 3, 30],
1605 );
1606 }
1607
1608 #[test]
1609 fn test_c_length_mismatch_errs() {
1610 use crate::Array;
1611 let a = Array::<i32, Ix1>::from_vec(Ix1::new([3]), vec![1, 2, 3]).unwrap();
1612 let b = Array::<i32, Ix1>::from_vec(Ix1::new([2]), vec![10, 20]).unwrap();
1613 assert!(c_(&[a, b]).is_err());
1614 }
1615}