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