Skip to content

Commit fc3f836

Browse files
authored
Merge pull request #967 from rust-ndarray/shift-remove
Add method .remove_index(axis, index)
2 parents 37cf919 + b90f1f9 commit fc3f836

File tree

5 files changed

+175
-0
lines changed

5 files changed

+175
-0
lines changed

src/impl_1d.rs

+34
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@
88

99
//! Methods for one-dimensional arrays.
1010
use alloc::vec::Vec;
11+
use std::mem::MaybeUninit;
12+
1113
use crate::imp_prelude::*;
14+
use crate::low_level_util::AbortIfPanic;
1215

1316
/// # Methods For 1-D Arrays
1417
impl<A, S> ArrayBase<S, Ix1>
@@ -27,4 +30,35 @@ where
2730
crate::iterators::to_vec(self.iter().cloned())
2831
}
2932
}
33+
34+
/// Rotate the elements of the array by 1 element towards the front;
35+
/// the former first element becomes the last.
36+
pub(crate) fn rotate1_front(&mut self)
37+
where
38+
S: DataMut,
39+
{
40+
// use swapping to keep all elements initialized (as required by owned storage)
41+
let mut lane_iter = self.iter_mut();
42+
let mut dst = if let Some(dst) = lane_iter.next() { dst } else { return };
43+
44+
// Logically we do a circular swap here, all elements in a chain
45+
// Using MaybeUninit to avoid unecessary writes in the safe swap solution
46+
//
47+
// for elt in lane_iter {
48+
// std::mem::swap(dst, elt);
49+
// dst = elt;
50+
// }
51+
//
52+
let guard = AbortIfPanic(&"rotate1_front: temporarily moving out of owned value");
53+
let mut slot = MaybeUninit::<A>::uninit();
54+
unsafe {
55+
slot.as_mut_ptr().copy_from_nonoverlapping(dst, 1);
56+
for elt in lane_iter {
57+
(dst as *mut A).copy_from_nonoverlapping(elt, 1);
58+
dst = elt;
59+
}
60+
(dst as *mut A).copy_from_nonoverlapping(slot.as_ptr(), 1);
61+
}
62+
guard.defuse();
63+
}
3064
}

src/impl_methods.rs

+24
Original file line numberDiff line numberDiff line change
@@ -2444,6 +2444,30 @@ where
24442444
}
24452445
}
24462446

2447+
/// Remove the `index`th elements along `axis` and shift down elements from higher indexes.
2448+
///
2449+
/// Note that this "removes" the elements by swapping them around to the end of the axis and
2450+
/// shortening the length of the axis; the elements are not deinitialized or dropped by this,
2451+
/// just moved out of view (this only matters for elements with ownership semantics). It's
2452+
/// similar to slicing an owned array in place.
2453+
///
2454+
/// Decreases the length of `axis` by one.
2455+
///
2456+
/// ***Panics*** if `axis` is out of bounds<br>
2457+
/// ***Panics*** if not `index < self.len_of(axis)`.
2458+
pub fn remove_index(&mut self, axis: Axis, index: usize)
2459+
where
2460+
S: DataOwned + DataMut,
2461+
{
2462+
assert!(index < self.len_of(axis), "index {} must be less than length of Axis({})",
2463+
index, axis.index());
2464+
let (_, mut tail) = self.view_mut().split_at(axis, index);
2465+
// shift elements to the front
2466+
Zip::from(tail.lanes_mut(axis)).for_each(|mut lane| lane.rotate1_front());
2467+
// then slice the axis in place to cut out the removed final element
2468+
self.slice_axis_inplace(axis, Slice::new(0, Some(-1), 1));
2469+
}
2470+
24472471
/// Iterates over pairs of consecutive elements along the axis.
24482472
///
24492473
/// The first argument to the closure is an element, and the second

src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ mod shape_builder;
205205
mod slice;
206206
mod split_at;
207207
mod stacking;
208+
mod low_level_util;
208209
#[macro_use]
209210
mod zip;
210211

src/low_level_util.rs

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright 2021 bluss and ndarray developers.
2+
//
3+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6+
// option. This file may not be copied, modified, or distributed
7+
// except according to those terms.
8+
9+
10+
/// Guard value that will abort if it is dropped.
11+
/// To defuse, this value must be forgotten before the end of the scope.
12+
///
13+
/// The string value is added to the message printed if aborting.
14+
#[must_use]
15+
pub(crate) struct AbortIfPanic(pub(crate) &'static &'static str);
16+
17+
impl AbortIfPanic {
18+
/// Defuse the AbortIfPanic guard. This *must* be done when finished.
19+
#[inline]
20+
pub(crate) fn defuse(self) {
21+
std::mem::forget(self);
22+
}
23+
}
24+
25+
impl Drop for AbortIfPanic {
26+
// The compiler should be able to remove this, if it can see through that there
27+
// is no panic in the code section.
28+
fn drop(&mut self) {
29+
#[cfg(feature="std")]
30+
{
31+
eprintln!("ndarray: panic in no-panic section, aborting: {}", self.0);
32+
std::process::abort()
33+
}
34+
#[cfg(not(feature="std"))]
35+
{
36+
// no-std uses panic-in-panic (should abort)
37+
panic!("ndarray: panic in no-panic section, bailing out: {}", self.0);
38+
}
39+
}
40+
}

tests/array.rs

+76
Original file line numberDiff line numberDiff line change
@@ -2397,3 +2397,79 @@ mod array_cow_tests {
23972397
});
23982398
}
23992399
}
2400+
2401+
#[test]
2402+
fn test_remove_index() {
2403+
let mut a = arr2(&[[1, 2, 3],
2404+
[4, 5, 6],
2405+
[7, 8, 9],
2406+
[10,11,12]]);
2407+
a.remove_index(Axis(0), 1);
2408+
a.remove_index(Axis(1), 2);
2409+
assert_eq!(a.shape(), &[3, 2]);
2410+
assert_eq!(a,
2411+
array![[1, 2],
2412+
[7, 8],
2413+
[10,11]]);
2414+
2415+
let mut a = arr2(&[[1, 2, 3],
2416+
[4, 5, 6],
2417+
[7, 8, 9],
2418+
[10,11,12]]);
2419+
a.invert_axis(Axis(0));
2420+
a.remove_index(Axis(0), 1);
2421+
a.remove_index(Axis(1), 2);
2422+
assert_eq!(a.shape(), &[3, 2]);
2423+
assert_eq!(a,
2424+
array![[10,11],
2425+
[4, 5],
2426+
[1, 2]]);
2427+
2428+
a.remove_index(Axis(1), 1);
2429+
2430+
assert_eq!(a.shape(), &[3, 1]);
2431+
assert_eq!(a,
2432+
array![[10],
2433+
[4],
2434+
[1]]);
2435+
a.remove_index(Axis(1), 0);
2436+
assert_eq!(a.shape(), &[3, 0]);
2437+
assert_eq!(a,
2438+
array![[],
2439+
[],
2440+
[]]);
2441+
}
2442+
2443+
#[should_panic(expected="must be less")]
2444+
#[test]
2445+
fn test_remove_index_oob1() {
2446+
let mut a = arr2(&[[1, 2, 3],
2447+
[4, 5, 6],
2448+
[7, 8, 9],
2449+
[10,11,12]]);
2450+
a.remove_index(Axis(0), 4);
2451+
}
2452+
2453+
#[should_panic(expected="must be less")]
2454+
#[test]
2455+
fn test_remove_index_oob2() {
2456+
let mut a = array![[10], [4], [1]];
2457+
a.remove_index(Axis(1), 0);
2458+
assert_eq!(a.shape(), &[3, 0]);
2459+
assert_eq!(a,
2460+
array![[],
2461+
[],
2462+
[]]);
2463+
a.remove_index(Axis(0), 1); // ok
2464+
assert_eq!(a,
2465+
array![[],
2466+
[]]);
2467+
a.remove_index(Axis(1), 0); // oob
2468+
}
2469+
2470+
#[should_panic(expected="index out of bounds")]
2471+
#[test]
2472+
fn test_remove_index_oob3() {
2473+
let mut a = array![[10], [4], [1]];
2474+
a.remove_index(Axis(2), 0);
2475+
}

0 commit comments

Comments
 (0)