1use std::ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive};
11use std::panic::Location;
12
13use smallvec::SmallVec;
14
15use crate::ops::imp;
16use crate::scalar::ScalarOrArray;
17use crate::shape::Shape;
18use crate::view::SliceArg;
19use crate::{Array, Category, DType, Error, Result};
20
21mod sealed {
22 pub trait Sealed {}
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
27pub struct Ellipsis;
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
31pub struct NewAxis;
32
33#[derive(Debug, Clone, PartialEq, Eq)]
37pub struct Stepped<R> {
38 range: R,
39 step: i32,
40}
41
42#[doc(hidden)]
44#[derive(Debug, Clone)]
45pub enum IndexItem {
46 Int(i64),
48 Slice {
51 start: Option<i64>,
52 stop: Option<i64>,
53 step: i64,
54 },
55 NewAxis,
57 Ellipsis,
59 Array(Array),
61}
62
63#[doc(hidden)]
65pub trait Bounds: sealed::Sealed {
66 #[doc(hidden)]
67 fn bounds(&self) -> (Option<i64>, Option<i64>);
68}
69
70#[diagnostic::on_unimplemented(
73 message = "`{Self}` cannot take a step",
74 label = "`.step(n)` applies to ranges such as `0..6`, `1..`, `..=4` or `..`"
75)]
76pub trait Step: Bounds + Sized {
77 fn step(self, n: i32) -> Stepped<Self> {
79 Stepped {
80 range: self,
81 step: n,
82 }
83 }
84}
85
86fn inclusive_end(end: i64) -> Option<i64> {
88 if end == -1 {
89 None
90 } else {
91 Some(end.saturating_add(1))
92 }
93}
94
95use crate::axes::wide;
96
97macro_rules! bounds {
98 ($($t:ty => |$r:ident| $e:expr),* $(,)?) => {$(
99 impl sealed::Sealed for $t {}
100 impl Bounds for $t {
101 fn bounds(&self) -> (Option<i64>, Option<i64>) {
102 let $r = self;
103 $e
104 }
105 }
106 impl Step for $t {}
107 )*};
108}
109
110bounds! {
111 Range<i32> => |r| (Some(r.start.into()), Some(r.end.into())),
112 Range<usize> => |r| (Some(wide(r.start)), Some(wide(r.end))),
113 RangeFrom<i32> => |r| (Some(r.start.into()), None),
114 RangeFrom<usize> => |r| (Some(wide(r.start)), None),
115 RangeTo<i32> => |r| (None, Some(r.end.into())),
116 RangeTo<usize> => |r| (None, Some(wide(r.end))),
117 RangeInclusive<i32> => |r| (Some((*r.start()).into()), inclusive_end((*r.end()).into())),
118 RangeInclusive<usize> => |r| (Some(wide(*r.start())), inclusive_end(wide(*r.end()))),
119 RangeToInclusive<i32> => |r| (None, inclusive_end(r.end.into())),
120 RangeToInclusive<usize> => |r| (None, inclusive_end(wide(r.end))),
121 RangeFull => |_r| (None, None),
122}
123
124#[diagnostic::on_unimplemented(
131 message = "`{Self}` is not an index",
132 label = "expected an integer, a range (`1..3`, `..=2`, `(0..6).step(2)`), `..`, `Ellipsis`, `NewAxis`, an index array (`&idx`), a tuple of these, or a slice of ranges"
133)]
134pub trait ArrayIndex: sealed::Sealed {
135 #[doc(hidden)]
136 fn index_items(self, out: &mut SmallVec<[IndexItem; 4]>);
137}
138
139fn slice_item(start: Option<i64>, stop: Option<i64>, step: i64) -> IndexItem {
140 IndexItem::Slice { start, stop, step }
141}
142
143fn python_slice(start: Option<i64>, stop: Option<i64>, step: i64, n: usize) -> SliceArg {
145 let n = n as i64;
146 let norm = |b: i64| (if b < 0 { b + n } else { b }).clamp(0, n);
147 let s = start.map_or(0, norm);
148 let e = stop.map_or(n, norm).max(s);
149 let step = step.clamp(isize::MIN as i64, isize::MAX as i64) as isize;
150 if step > 0 {
151 SliceArg::new(Some(s as isize), Some(e as isize), step)
152 } else if e == s {
153 SliceArg::new(Some(0), Some(0), 1)
154 } else {
155 let stop = if s == 0 { None } else { Some(s as isize - 1) };
156 SliceArg::new(Some(e as isize - 1), stop, step)
157 }
158}
159
160macro_rules! range_index {
161 ($($t:ty),*) => {$(
162 impl ArrayIndex for $t {
163 fn index_items(self, out: &mut SmallVec<[IndexItem; 4]>) {
164 let (start, stop) = self.bounds();
165 out.push(slice_item(start, stop, 1));
166 }
167 }
168 impl sealed::Sealed for Stepped<$t> {}
169 impl ArrayIndex for Stepped<$t> {
170 fn index_items(self, out: &mut SmallVec<[IndexItem; 4]>) {
171 let (start, stop) = self.range.bounds();
172 out.push(slice_item(start, stop, self.step.into()));
173 }
174 }
175 )*};
176}
177
178range_index!(
179 Range<i32>,
180 Range<usize>,
181 RangeFrom<i32>,
182 RangeFrom<usize>,
183 RangeTo<i32>,
184 RangeTo<usize>,
185 RangeInclusive<i32>,
186 RangeInclusive<usize>,
187 RangeToInclusive<i32>,
188 RangeToInclusive<usize>,
189 RangeFull
190);
191
192impl sealed::Sealed for i32 {}
193impl ArrayIndex for i32 {
194 fn index_items(self, out: &mut SmallVec<[IndexItem; 4]>) {
195 out.push(IndexItem::Int(self.into()));
196 }
197}
198
199impl sealed::Sealed for usize {}
200impl ArrayIndex for usize {
201 fn index_items(self, out: &mut SmallVec<[IndexItem; 4]>) {
202 out.push(IndexItem::Int(wide(self)));
203 }
204}
205
206impl sealed::Sealed for Ellipsis {}
207impl ArrayIndex for Ellipsis {
208 fn index_items(self, out: &mut SmallVec<[IndexItem; 4]>) {
209 out.push(IndexItem::Ellipsis);
210 }
211}
212
213impl sealed::Sealed for NewAxis {}
214impl ArrayIndex for NewAxis {
215 fn index_items(self, out: &mut SmallVec<[IndexItem; 4]>) {
216 out.push(IndexItem::NewAxis);
217 }
218}
219
220impl sealed::Sealed for &Array {}
221impl ArrayIndex for &Array {
222 fn index_items(self, out: &mut SmallVec<[IndexItem; 4]>) {
223 out.push(IndexItem::Array(self.clone()));
224 }
225}
226
227impl sealed::Sealed for Array {}
228impl ArrayIndex for Array {
229 fn index_items(self, out: &mut SmallVec<[IndexItem; 4]>) {
230 out.push(IndexItem::Array(self));
231 }
232}
233
234macro_rules! tuple_index {
235 ($($name:ident)+) => {
236 impl<$($name: ArrayIndex),+> sealed::Sealed for ($($name,)+) {}
237 #[diagnostic::do_not_recommend]
238 impl<$($name: ArrayIndex),+> ArrayIndex for ($($name,)+) {
239 #[allow(non_snake_case)]
240 fn index_items(self, out: &mut SmallVec<[IndexItem; 4]>) {
241 let ($($name,)+) = self;
242 $($name.index_items(out);)+
243 }
244 }
245 };
246}
247
248macro_rules! ranges_index {
250 ($($t:ty),*) => {$(
251 impl<R: Bounds> sealed::Sealed for $t {}
252 impl<R: Bounds> ArrayIndex for $t {
253 fn index_items(self, out: &mut SmallVec<[IndexItem; 4]>) {
254 for r in self.iter() {
255 let (start, stop) = r.bounds();
256 out.push(slice_item(start, stop, 1));
257 }
258 }
259 }
260 )*};
261}
262
263ranges_index!(&[R], Vec<R>, &Vec<R>);
264
265tuple_index!(A);
266tuple_index!(A B);
267tuple_index!(A B C);
268tuple_index!(A B C D);
269tuple_index!(A B C D E);
270tuple_index!(A B C D E F);
271tuple_index!(A B C D E F G);
272tuple_index!(A B C D E F G H);
273
274fn items_of(index: impl ArrayIndex) -> SmallVec<[IndexItem; 4]> {
275 let mut items = SmallVec::new();
276 index.index_items(&mut items);
277 items
278}
279
280fn index_arrays(items: &[IndexItem]) -> impl Iterator<Item = &Array> {
282 items.iter().filter_map(|i| match i {
283 IndexItem::Array(a) => Some(a),
284 _ => None,
285 })
286}
287
288#[derive(Debug, Clone)]
290enum Slot {
291 New,
293 Dim(usize),
295 Adv(usize, Option<Array>, i64),
297}
298
299struct Resolved {
302 args: Vec<SliceArg>,
303 slots: Vec<Slot>,
304}
305
306impl Resolved {
307 fn has_arrays(&self) -> bool {
308 self.slots
309 .iter()
310 .any(|s| matches!(s, Slot::Adv(_, Some(_), _)))
311 }
312}
313
314fn resolve(items: &[IndexItem], shape: &[usize]) -> Result<Resolved> {
315 let ndim = shape.len();
316 let ellipses = items
317 .iter()
318 .filter(|i| matches!(i, IndexItem::Ellipsis))
319 .count();
320 if ellipses > 1 {
321 return Err(Error::invalid_index(
322 "index",
323 "an index can only have a single ellipsis",
324 ));
325 }
326 let consuming = items
327 .iter()
328 .filter(|i| !matches!(i, IndexItem::Ellipsis | IndexItem::NewAxis))
329 .count();
330 if consuming > ndim {
331 return Err(Error::invalid_index(
332 "index",
333 format!("too many indices ({consuming}) for an array of {ndim} dimensions"),
334 ));
335 }
336 let mut args = Vec::with_capacity(ndim);
337 let mut slots = Vec::with_capacity(items.len() + ndim);
338 let mut dim = 0usize;
339 let full = |dim: usize, args: &mut Vec<SliceArg>, slots: &mut Vec<Slot>| {
340 args.push(SliceArg::full());
341 slots.push(Slot::Dim(dim));
342 };
343 for item in items {
344 match item {
345 IndexItem::Ellipsis => {
346 for _ in 0..ndim - consuming {
347 full(dim, &mut args, &mut slots);
348 dim += 1;
349 }
350 }
351 IndexItem::NewAxis => slots.push(Slot::New),
352 IndexItem::Slice { start, stop, step } => {
353 if *step == 0 {
354 return Err(Error::invalid_index("index", "slice step must not be zero"));
355 }
356 args.push(python_slice(*start, *stop, *step, shape[dim]));
357 slots.push(Slot::Dim(dim));
358 dim += 1;
359 }
360 IndexItem::Int(i) => {
361 let n = shape[dim] as i64;
362 let k = if *i < 0 { i + n } else { *i };
363 if !(0..n).contains(&k) {
364 return Err(Error::invalid_index(
365 "index",
366 format!("index {i} is out of bounds for axis {dim} with size {n}"),
367 ));
368 }
369 args.push(SliceArg::new(Some(k as isize), Some(k as isize + 1), 1));
370 slots.push(Slot::Adv(dim, None, k));
371 dim += 1;
372 }
373 IndexItem::Array(a) => {
374 if !matches!(a.dtype().category(), Category::Signed | Category::Unsigned) {
375 return Err(Error::dtype_mismatch("index", a.dtype(), DType::I32));
376 }
377 args.push(SliceArg::full());
378 slots.push(Slot::Adv(dim, Some(a.clone()), 0));
379 dim += 1;
380 }
381 }
382 }
383 while dim < ndim {
384 full(dim, &mut args, &mut slots);
385 dim += 1;
386 }
387 Ok(Resolved { args, slots })
388}
389
390fn basic_shape(r: &Resolved, sliced: &[usize]) -> Shape {
392 r.slots
393 .iter()
394 .filter_map(|s| match s {
395 Slot::New => Some(1),
396 Slot::Dim(d) => Some(sliced[*d]),
397 Slot::Adv(..) => None,
398 })
399 .collect()
400}
401
402fn basic(a: &Array, r: &Resolved) -> Result<Array> {
405 let view = imp::slice(a, &r.args)?;
406 let shape = basic_shape(r, view.dims());
407 imp::reshape_to(&view, shape)
408}
409
410fn advanced_indices(a: &Array, r: &Resolved) -> Result<Vec<(usize, Array)>> {
413 r.slots
414 .iter()
415 .filter_map(|s| match s {
416 Slot::Adv(d, idx, k) => Some((*d, idx.clone(), *k)),
417 _ => None,
418 })
419 .map(|(d, idx, k)| {
420 let n = a.dims()[d] as i64;
421 let idx = match idx {
422 Some(idx) => {
423 let idx = imp::astype(&idx, DType::I64)?;
424 let wrapped = imp::where_(imp::less(&idx, 0)?, imp::add(&idx, n)?, &idx)?;
425 imp::minimum(&imp::maximum(&wrapped, 0)?, (n - 1).max(0))?
426 }
427 None => crate::creation::from_data(&[k], &[])?,
428 };
429 Ok((d, idx))
430 })
431 .collect()
432}
433
434fn linear_index(v: &Array, adv: &[(usize, Array)]) -> Result<(Array, Shape, usize)> {
437 let mut bshape: Shape = Shape::new();
438 for (_, idx) in adv {
439 bshape = crate::view::broadcast_shapes("index", &bshape, idx.dims())?;
440 }
441 let sizes: Vec<usize> = adv.iter().map(|&(d, _)| v.dims()[d]).collect();
442 let total: usize = sizes.iter().product();
443 let mut linear: Option<Array> = None;
444 let mut stride = 1usize;
445 for ((_, idx), &n) in adv.iter().zip(&sizes).rev() {
446 let term = imp::multiply(idx, stride as i64)?;
447 linear = Some(match linear {
448 None => term,
449 Some(l) => imp::add(&l, &term)?,
450 });
451 stride *= n;
452 }
453 let linear = imp::broadcast_to(
454 &linear.unwrap_or_else(|| unreachable!("an advanced index has an index array")),
455 &bshape,
456 )?;
457 let index_dtype = if total <= i32::MAX as usize {
458 DType::I32
459 } else {
460 DType::I64
461 };
462 Ok((imp::astype(&linear, index_dtype)?, bshape, total))
463}
464
465fn advanced(a: &Array, r: &Resolved) -> Result<Array> {
469 let mut args = r.args.clone();
472 for slot in &r.slots {
473 if let Slot::Adv(d, None, _) = slot {
474 args[*d] = SliceArg::full();
475 }
476 }
477 let v = imp::slice(a, &args)?;
478 let adv = advanced_indices(&v, r)?;
479 let adv_dims: Vec<usize> = adv.iter().map(|(d, _)| *d).collect();
480 let rest: Vec<usize> = (0..v.ndim()).filter(|d| !adv_dims.contains(d)).collect();
481 let (linear, bshape, total) = linear_index(&v, &adv)?;
482 let perm: Vec<usize> = adv_dims.iter().chain(&rest).copied().collect();
484 let vt = imp::transpose(&v, &perm)?;
485 let mut flat: Shape = Shape::from_elem(total, 1);
486 flat.extend(rest.iter().map(|&d| v.dims()[d]));
487 let vf = imp::reshape_to(&vt, flat)?;
488 let out = crate::indexing::take(&vf, &linear, Some(0))?;
489 let nb = bshape.len();
491 let adv_positions: Vec<usize> = r
492 .slots
493 .iter()
494 .enumerate()
495 .filter(|(_, s)| matches!(s, Slot::Adv(..)))
496 .map(|(i, _)| i)
497 .collect();
498 let adjacent = adv_positions.windows(2).all(|w| w[1] == w[0] + 1);
499 let mut order: Vec<usize> = Vec::with_capacity(out.ndim());
500 let mut shape: Shape = Shape::new();
501 let mut next_rest = nb;
502 let mut placed = !adjacent;
503 if !adjacent {
504 order.extend(0..nb);
505 shape.extend_from_slice(&bshape);
506 }
507 for slot in &r.slots {
508 match slot {
509 Slot::New => shape.push(1),
510 Slot::Dim(_) => {
511 order.push(next_rest);
512 shape.push(out.dims()[next_rest]);
513 next_rest += 1;
514 }
515 Slot::Adv(..) => {
516 if !placed {
517 order.extend(0..nb);
518 shape.extend_from_slice(&bshape);
519 placed = true;
520 }
521 }
522 }
523 }
524 let arranged = imp::transpose(&out, &order)?;
525 imp::reshape_to(&arranged, shape)
526}
527
528fn index_with(a: &Array, items: &[IndexItem]) -> Result<Array> {
529 let r = resolve(items, a.dims())?;
530 if r.has_arrays() {
531 advanced(a, &r)
532 } else {
533 basic(a, &r)
534 }
535}
536
537impl Array {
538 #[track_caller]
560 pub fn i(&self, index: impl ArrayIndex) -> Array {
561 let items = items_of(index);
562 let mut ins: SmallVec<[&Array; 4]> = SmallVec::new();
563 ins.push(self);
564 ins.extend(index_arrays(&items));
565 Array::sticky("index", Location::caller(), &ins, || {
566 index_with(self, &items)
567 })
568 }
569
570 pub fn at(self, index: impl ArrayIndex) -> At {
584 At {
585 base: self,
586 items: items_of(index),
587 }
588 }
589
590 #[track_caller]
594 pub fn slice_update(&self, update: &Array, index: impl ArrayIndex) -> Array {
595 let items = items_of(index);
596 let mut ins: SmallVec<[&Array; 4]> = SmallVec::from_slice(&[self, update]);
597 ins.extend(index_arrays(&items));
598 Array::sticky("slice_update", Location::caller(), &ins, || {
599 update_at(self, &items, ScalarOrArray::Array(update.clone()), false)
600 })
601 }
602}
603
604#[must_use = "an update does nothing until `set` or `add` is called"]
606#[derive(Debug, Clone)]
607pub struct At {
608 base: Array,
609 items: SmallVec<[IndexItem; 4]>,
610}
611
612#[allow(clippy::should_implement_trait)] impl At {
614 #[track_caller]
618 pub fn set(self, value: impl Into<ScalarOrArray>) -> Array {
619 self.apply("at.set", Location::caller(), value.into(), false)
620 }
621
622 #[track_caller]
625 pub fn add(self, value: impl Into<ScalarOrArray>) -> Array {
626 self.apply("at.add", Location::caller(), value.into(), true)
627 }
628
629 fn apply(
630 self,
631 op: &'static str,
632 at: &'static Location<'static>,
633 value: ScalarOrArray,
634 add: bool,
635 ) -> Array {
636 let At { base, items } = self;
637 let failed = std::iter::once(&base)
638 .chain(index_arrays(&items))
639 .chain(match &value {
640 ScalarOrArray::Array(v) => Some(v),
641 ScalarOrArray::Scalar(_) => None,
642 })
643 .find_map(Array::slot_error);
644 if let Some(error) = failed {
645 return Array::failed(error, base.dtype(), base.dims());
646 }
647 match update_at(&base, &items, value, add) {
648 Ok(out) => out,
649 Err(e) => Array::failed(e.locate(op, at), base.dtype(), base.dims()),
650 }
651 }
652}
653
654fn update_along_axis(
659 base: &Array,
660 r: &Resolved,
661 value: &Array,
662 add: bool,
663) -> Result<Option<Array>> {
664 let mut adv_dims = Vec::new();
665 for (slot, arg) in r.slots.iter().zip(&r.args) {
666 match slot {
667 Slot::New => return Ok(None),
668 Slot::Dim(d) => {
669 let n = base.dims()[*d] as isize;
670 let whole = arg.step == 1
671 && arg.start.is_none_or(|s| s == 0 || s <= -n)
672 && arg.stop.is_none_or(|e| e >= n);
673 if !whole {
674 return Ok(None);
675 }
676 }
677 Slot::Adv(d, ..) => adv_dims.push(*d),
678 }
679 }
680 let k = adv_dims[0];
681 if adv_dims.iter().enumerate().any(|(i, &d)| d != k + i) {
682 return Ok(None);
683 }
684 let adv = advanced_indices(base, r)?;
685 let (linear, bshape, total) = linear_index(base, &adv)?;
686 let dims = base.dims();
687 let (pre, post) = (&dims[..k], &dims[k + adv_dims.len()..]);
688 let mut merged: Shape = pre.into();
690 merged.push(total);
691 merged.extend_from_slice(post);
692 let flat_base = imp::reshape_to(base, merged)?;
693 let mut region: Shape = pre.into();
694 region.extend_from_slice(&bshape);
695 region.extend_from_slice(post);
696 let m: usize = bshape.iter().product();
697 let mut along: Shape = pre.into();
698 along.push(m);
699 along.extend_from_slice(post);
700 let update = imp::broadcast_to(&imp::astype(value, base.dtype())?, ®ion)?;
701 let update = imp::reshape_to(&imp::contiguous(&update)?, along.clone())?;
702 let mut index_shape: Shape = Shape::from_elem(1, pre.len());
703 index_shape.push(m);
704 index_shape.extend(std::iter::repeat_n(1, post.len()));
705 let indices = imp::broadcast_to(&imp::reshape_to(&linear, index_shape)?, &along)?;
706 let out = if add {
707 crate::indexing::scatter_add_along_axis(&flat_base, &indices, &update, k as isize)?
708 } else {
709 crate::indexing::put_along_axis(&flat_base, &indices, &update, k as isize)?
710 };
711 Ok(Some(imp::reshape_to(&out, dims.into())?))
712}
713
714fn update_at(base: &Array, items: &[IndexItem], value: ScalarOrArray, add: bool) -> Result<Array> {
716 let r = resolve(items, base.dims())?;
717 let value = value.into_array(Some(base.dtype()))?;
718 if !r.has_arrays() {
719 let region = imp::slice(base, &r.args)?;
721 let indexed = basic_shape(&r, region.dims());
722 let update = imp::broadcast_to(&imp::astype(&value, base.dtype())?, &indexed)?;
723 let update = imp::reshape_to(&update, region.dims().into())?;
724 let update = if add {
725 imp::add(®ion, &update)?
726 } else {
727 update
728 };
729 return imp::slice_update(base, &update, &r.args);
730 }
731 if let Some(out) = update_along_axis(base, &r, &value, add)? {
732 return Ok(out);
733 }
734 let positions_dtype = if base.size() <= i32::MAX as usize {
738 DType::I32
739 } else {
740 DType::I64
741 };
742 let positions = imp::reshape_to(
743 &imp::arange(0.0, base.size() as f64, 1.0, positions_dtype)?,
744 base.dims().into(),
745 )?;
746 let selected = index_with(&positions, items)?;
747 let update = imp::broadcast_to(&imp::astype(&value, base.dtype())?, selected.dims())?;
748 let flat = imp::reshape_to(base, Shape::from_elem(base.size(), 1))?;
749 let n = selected.size();
750 let positions = imp::reshape_to(&selected, Shape::from_elem(n, 1))?;
751 let update = imp::reshape_to(&imp::contiguous(&update)?, Shape::from_elem(n, 1))?;
752 let out = if add {
753 crate::indexing::scatter_add_along_axis(&flat, &positions, &update, 0)?
754 } else {
755 crate::indexing::put_along_axis(&flat, &positions, &update, 0)?
756 };
757 imp::reshape_to(&out, base.dims().into())
758}
759
760#[cfg(test)]
761mod tests {
762 use super::*;
763
764 fn arange(shape: &[usize]) -> Array {
765 let n: usize = shape.iter().product();
766 let data: Vec<i32> = (0..n as i32).collect();
767 Array::from_slice(&data, shape)
768 }
769
770 #[test]
771 fn basic_forms() {
772 let x = arange(&[4, 5, 6]);
773 assert_eq!(x.i(0).shape(), [5, 6]);
774 assert_eq!(x.i(-1).to_vec::<i32>().unwrap()[0], 90);
775 assert_eq!(x.i((.., 1..3)).shape(), [4, 2, 6]);
776 assert_eq!(x.i((Ellipsis, -1)).shape(), [4, 5]);
777 assert_eq!(x.i((.., NewAxis, ..)).shape(), [4, 1, 5, 6]);
778 assert_eq!(x.i((..).step(-1)).to_vec::<i32>().unwrap()[0], 90);
779 assert_eq!(x.i((1.., ..=2, (0..6).step(2))).shape(), [3, 3, 3]);
780 assert_eq!(x.i(1usize..3usize).shape(), [2, 5, 6]);
781 assert!(matches!(
783 x.i((.., 1..3)).primitive_t(),
784 Some(crate::graph::Primitive::Slice { .. })
785 ));
786 }
787
788 #[test]
789 fn invalid_indices() {
790 let x = arange(&[4, 5]);
791 for bad in [
792 x.i(4),
793 x.i(-5),
794 x.i((Ellipsis, 0, Ellipsis)),
795 x.i((0, 0, 0)),
796 x.i((0..2).step(0)),
797 ] {
798 let e = bad.check().unwrap_err();
799 assert!(
800 matches!(e.kind(), Some(crate::OpErrorKind::InvalidIndex(_))),
801 "{e}"
802 );
803 }
804 }
805
806 #[test]
807 fn advanced_placement() {
808 let x = arange(&[2, 3, 4]);
809 let i2 = Array::from_slice(&[0i32, 1], [2]);
810 let j2 = Array::from_slice(&[1i32, 2], [2]);
811 assert_eq!(x.i((&i2, .., &j2)).shape(), [2, 3]);
812 assert_eq!(x.i((.., &i2, &j2)).shape(), [2, 2]);
813 assert_eq!(x.i((&i2, 1)).shape(), [2, 4]);
814 assert_eq!(x.i((1, .., &i2)).shape(), [2, 3]);
815 assert_eq!(x.i((NewAxis, &i2)).shape(), [1, 2, 3, 4]);
816 assert_eq!(x.i((&i2, NewAxis)).shape(), [2, 1, 3, 4]);
817 assert_eq!(x.i((Ellipsis, &i2)).shape(), [2, 3, 2]);
818 assert_eq!(x.i((.., &i2, &j2)).to_vec::<i32>().unwrap(), [1, 6, 13, 18]);
819 }
820
821 #[test]
822 fn updates() {
823 let x = Array::from_slice(&[0i32; 6], [2, 3]);
824 let y = x.clone().at((.., 1..)).set(7);
825 assert_eq!(y.to_vec::<i32>().unwrap(), [0, 7, 7, 0, 7, 7]);
826 let ids = Array::from_slice(&[0i32, 0, 1], [3]);
827 let z = x.at(&ids).add(1);
828 assert_eq!(z.to_vec::<i32>().unwrap(), [2, 2, 2, 1, 1, 1]);
829 }
830}