Repository: rust-ndarray/ndarray
Pull request #885 (2021-01-06 08:42:23)

Fix memory continuity judgment when stride is negative
It comes from FIXME in is_cotiguous. Whether the stride is negative will not affect the arrangement of the array in the memory, therefore, sorting and continuity detection can be performed according to the absolute value of the step size. That's what this pr dose in dimension_trait.rs.

Then we need to deal with the as_slice_memory_order function in impl_methods.rs. When stride is negative, although we can know that the array is continuous, because the ptr of the array does not point to the head of the memory data at this time, the ptr value cannot be used directly.

This is why I added the head_ptr_offset function in dimension/mod.rs. It simply calculates the offset of the head address of the array data relative to the current ptr based on the dim and stride of the array. The offset is always less than or equal to 0. After calculating the offset, as_slice_memory_order can get continuous data. as_slice_memory_order_mut is the same.

The last thing to be solved is the to_owned function in impl_method.rs. After calling as_slice_memory_order, it will call from_shape_vec_unchecked function( in impl_constructors.rs ) to generate a new ArrayBase based on the obtained array head pointer. This is incorrect when stride is negative, because the head address of the data array is not the ptr value that ArrayBase should have. Fortunately, we have the head_ptr_offset method, which can simply calculate the offset based on dim and strides, and then get the ptr we need.

SparrowLii: @bluss  This line is a bit strange( tests/oper.rs:775 )
It runs &lhs.to_owned().into_shape(), but  to_owned() does not guarantee that the memory arrangement of the return value is standard C or fortran( which into_shape() needs), regardless of whether the stride is negative.
This is the reason why my test failed, I will try to modify it, but I hope you can provide some suggestions.
SparrowLii: @jturner314 I added the corresponding warning. I think it is a matter to construct an array based on the negative step size, and there are related [issues](https://github.com/rust-ndarray/ndarray/issues/842) in ndarray. I think I can fix them in the next pr. WDYT?
SparrowLii: I added the corresponding test in tests/array.rs, I think it should work well now.
bluss: This ready to merge IMO.

You have fixed `is_contiguous` and the many methods that rely on it, and that's great, I think that's why the maintainers are excited about this PR.

We have left the interface unspecified in the current state of the PR - w.r.t creating array views and raw views with negative strides. For the user, it's not clear if it's allowed and how to specify the pointer - should it be the first-in-memory or the first-in-array pointer that they should pass?

For this reason, I think all the public from-raw-pointer constructors for array views and raw views must have a debug assertion that denies negative strides, and a method doc that states that strides cannot be negative - the reason is that this must be a closed possibility until the interface is decided. This change should not affect our own functionality - we internally use private constructors for array views (that always use the first-in-the-array pointer).

I can do that change that closes down the negative strides in those constructors - if you agree.
jturner314: > For this reason, I think all the public from-raw-pointer constructors for array views and raw views must have a debug assertion that denies negative strides, and a method doc that states that strides cannot be negative - the reason is that this must be a closed possibility until the interface is decided.

I agree.

We could also do this for the public owned constructors such as `from_shape_vec_unchecked`, if there's a possibility that we'll add a way for the user to specify the offset to the first element instead of just assuming that the lowest-memory-address element should be at the start of the `Vec`. (We'd need make sure to change `to_owned`, `map`, and `map_mut` to use `from_vec_dim_stride_unchecked` instead of `from_shape_vec_unchecked` so that they'd still work for the "contiguous with negative strides" case.)
SparrowLii: @bluss I very much agree. Many APIs currently using ptr values do not specify the meaning of ptr. ( from_raw_parts, etc.) A single pointer and the number of elements can only represent a contiguous memory, but cannot represent a data structure that can be directly used by an array. I think that the names and documents of these APIs (and perhaps implementations) should be modified to clarify the definition and use of parameters, like @jturner314 pointed out. But I think it may not be easy to find out all APIs that should be modified. 

I think this pr can be merged, and thank you very much for your reviewing and help. 
bluss: @jturner314 That's an interesting and complicating point. I didn't think of that - with the constructors that start from a Vec, it is already clear where the start of the memory is, I was thinking, so it's not strictly necessary to allow the user to specify a different offset. And `to_owned` and `map` already handle that correctly with this PR if I read it correctly.
jturner314: > with the constructors that start from a Vec, it is already clear where the start of the memory is, I was thinking, so it's not strictly necessary to allow the user to specify a different offset.

I agree that the most obvious use-case is when the lowest-address element corresponds to the start of the owned data. However, once we allow negative strides in the constructors, it'll be possible for users to directly construct everything `ArrayBase` can represent except for the case where the lowest-address element is not at the start of the owned data. While I don't immediately see an obvious use-case for directly constructing an owned array like this, it would be nice to support it in the constructors because the alternative ways to construct an array like this would be painful. (You'd have to create an `Array1` and then perform a complicated series of slicing/reshaping operations. Alternatively, you could drop the elements from the start of the `Vec` that you don't want so that the lowest-address element would be at the start of the `Vec`, but AFAIK, there's not a cheap way to do that.)

> `to_owned` and `map` already handle every applicable case right with this PR if I read it correctly.

That's correct. We'd only need to change them if we added debug assertions for negative strides to `from_shape_vec_unchecked` (since they use `from_shape_vec_unchecked` with strides that can be negative).
bluss: Without a use case, we can decline to implement it 🙂 
jturner314: That would be fine with me; I'd just like to keep this possible functionality in mind while updating the constructor API for negative strides, so that we don't accidentally make it too difficult to add in the future (in case this functionality is desirable for someone). If we do add this functionality in the future, I'd make it optional, similar to how `.strides()` is currently handled; the default would be to place the lowest-address element at the start of the owned data.
jturner314: I just thought of one example where it could be useful -- if you wanted an alignment greater than the alignment of the element type, e.g. for SIMD. (A common way to do this is to allocate a larger `Vec` than needed and ignore the first few elements.)
SparrowLii: I think we should update the description of these constructors, but there is no need to add an offset parameter. As long as the meaning of the ptr parameter in the API is clear (is it a memory head pointer or an array head pointer), we can create it correctly.
Even if we add functions constructed array with negative strides in the future.

For the support of smid, I think we should implement a new data structure for it. Then we can add the offset parameter to it.
bluss: @jturner314 That sounds intriguing, since it can be supported without any extra flags or carrying extra alignment info inside `Array<A, D>`. But it can have its own constructor I think

Patch:
diff --git a/Cargo.toml b/Cargo.toml
index 301d1b56..b312fd4a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -28,23 +28,20 @@ bench = false
 test = true
 
 [dependencies]
-num-integer = { version = "0.1.39", default-features = false }
+num-integer = "0.1.39"
 num-traits = { version = "0.2", default-features = false }
 num-complex = { version = "0.3", default-features = false }
 
-# Use via the `rayon` crate feature!
-rayon_ = { version = "1.0.3", optional = true, package = "rayon" }
+rayon = { version = "1.0.3", optional = true }
 
-approx = { version = "0.4", optional = true , default-features = false }
+approx = { version = "0.4", optional = true }
 
 # Use via the `blas` crate feature!
 cblas-sys = { version = "0.1.4", optional = true, default-features = false }
-blas-src = { version = "0.7.0", optional = true, default-features = false }
-libc = { version = "0.2.82", optional = true }
+blas-src = { version = "0.6.1", optional = true, default-features = false }
 
-matrixmultiply = { version = "0.3.0", default-features = false}
-
-serde = { version = "1.0", optional = true, default-features = false, features = ["alloc"] }
+matrixmultiply = { version = "0.2.0" }
+serde = { version = "1.0", optional = true }
 rawpointer = { version = "0.2" }
 
 [dev-dependencies]
@@ -55,10 +52,9 @@ itertools = { version = "0.9.0", default-features = false, features = ["use_std"
 
 [features]
 default = ["std"]
-
 # Enable blas usage
 # See README for more instructions
-blas = ["cblas-sys", "blas-src", "libc"]
+blas = ["cblas-sys", "blas-src"]
 
 # Old name for the serde feature
 serde-1 = ["serde"]
@@ -70,8 +66,7 @@ test = ["test-blas-openblas-sys"]
 # This feature is used for docs
 docs = ["approx", "serde", "rayon"]
 
-std = ["num-traits/std", "matrixmultiply/std"]
-rayon = ["rayon_", "std"]
+std = ["num-traits/std"]
 
 [profile.release]
 [profile.bench]
diff --git a/README.rst b/README.rst
index 05ff48fb..df416919 100644
--- a/README.rst
+++ b/README.rst
@@ -48,33 +48,22 @@ Crate Feature Flags
 The following crate feature flags are available. They are configured in
 your `Cargo.toml`.
 
-- ``std``
-
-  - Rust standard library (enabled by default)
-
-  - This crate can be used without the standard library by disabling the
-    default `std` feature. To do so, use this in your `Cargo.toml`:
-
-    [dependencies]
-    ndarray = { version = "0.x.y", default-features = false }
-
-  - The `geomspace` `linspace` `logspace` `range` `std` `var` `var_axis` and `std_axis`
-    methods are only available when `std` is enabled.
-
 - ``serde``
 
+  - Optional, compatible with Rust stable
   - Enables serialization support for serde 1.x
 
 - ``rayon``
 
+  - Optional, compatible with Rust stable
   - Enables parallel iterators, parallelized methods and ``par_azip!``.
-  - Implies std
 
 - ``blas``
 
+  - Optional and experimental, compatible with Rust stable
   - Enable transparent BLAS support for matrix multiplication.
     Uses ``blas-src`` for pluggable backend, which needs to be configured
-    separately (see below).
+    separately.
 
 How to use with cargo
 ---------------------
@@ -86,14 +75,14 @@ How to use with cargo
 
 How to enable blas integration. Depend on ``blas-src`` directly to pick a blas
 provider. Depend on the same ``blas-src`` version as ``ndarray`` does, for the
-selection to work.  An example configuration using system openblas is shown
+selection to work.  A proposed configuration using system openblas is shown
 below. Note that only end-user projects (not libraries) should select
 provider::
 
 
     [dependencies]
     ndarray = { version = "0.14.0", features = ["blas"] }
-    blas-src = { version = "0.7.0", default-features = false, features = ["openblas"] }
+    blas-src = { version = "0.6.1", default-features = false, features = ["openblas"] }
     openblas-src = { version = "0.9", default-features = false, features = ["cblas", "system"] }
 
 For official releases of ``ndarray``, the versions are:
@@ -101,11 +90,10 @@ For official releases of ``ndarray``, the versions are:
 =========== ============ ================
 ``ndarray`` ``blas-src`` ``openblas-src``
 =========== ============ ================
-0.15        0.7.0        0.9.0
-0.14        0.6.1        0.9.0
-0.13        0.2.0        0.6.0
-0.12        0.2.0        0.6.0
-0.11        0.1.2        0.5.0
+0.14.0      0.6.1        0.9.0
+0.13.0      0.2.0        0.6.0
+0.12.\*     0.2.0        0.6.0
+0.11.\*     0.1.2        0.5.0
 =========== ============ ================
 
 Recent Changes
@@ -124,3 +112,4 @@ http://opensource.org/licenses/MIT, at your
 option. This file may not be copied, modified, or distributed
 except according to those terms.
 
+
diff --git a/RELEASES.md b/RELEASES.md
index 2f410473..95d82171 100644
--- a/RELEASES.md
+++ b/RELEASES.md
@@ -1,56 +1,3 @@
-Version 0.15.0 (Not released yet)
-=================================
-
-New features
-------------
-
-- Support for compiling ndarray as `no_std` (using core and alloc) by [@xd009642]
-
-  https://github.com/rust-ndarray/ndarray/pull/861
-
-- New methods `.cell_view()` and `ArrayViewMut::into_cell_view` that enable
-  new ways of working with array elements as if they were in Cells - setting
-  elements through shared views and broadcast views.
-
-  https://github.com/rust-ndarray/ndarray/pull/877
-
-
-Enhancements
-------------
-
-- Fix `Zip` for the 0-dimensional case by [@jturner314]
-
-  https://github.com/rust-ndarray/ndarray/pull/862
-
-API changes
------------
-
-- Removed deprecated methods by [@bluss]:
-
-  - Remove deprecated `.all_close()` - use approx feature and methods like  `.abs_diff_eq` instead
-  - Mark `.scalar_sum()` as deprecated - use `.sum()` instead
-  - Remove deprecated `DataClone` - use `Data + RawDataClone` instead
-  - Remove deprecated `ArrayView::into_slice` - use `to_slice()` instead.
-
-  https://github.com/rust-ndarray/ndarray/pull/874
-
-- Remove deprecated methods: rows, cols (for row and column count; the new
-  names are nrows and ncols) by [@bluss]
-
-  https://github.com/rust-ndarray/ndarray/pull/872
-
-- Renamed methods (old names are now deprecated) by [@bluss]
-
-  - `genrows/_mut` -> `rows/_mut`
-  - `gencolumns/_mut` -> `columns/_mut`
-
-  https://github.com/rust-ndarray/ndarray/pull/872
-
-Other changes
--------------
-
-
-
 Version 0.14.0 (2020-11-28)
 ===========================
 
diff --git a/benches/bench1.rs b/benches/bench1.rs
index c446b1e2..de865aa8 100644
--- a/benches/bench1.rs
+++ b/benches/bench1.rs
@@ -433,22 +433,6 @@ fn scalar_add_2(bench: &mut test::Bencher) {
     bench.iter(|| n + &a);
 }
 
-#[bench]
-fn scalar_add_strided_1(bench: &mut test::Bencher) {
-    let a =
-        Array::from_shape_fn((64, 64 * 2), |(i, j)| (i * 64 + j) as f32).slice_move(s![.., ..;2]);
-    let n = 1.;
-    bench.iter(|| &a + n);
-}
-
-#[bench]
-fn scalar_add_strided_2(bench: &mut test::Bencher) {
-    let a =
-        Array::from_shape_fn((64, 64 * 2), |(i, j)| (i * 64 + j) as f32).slice_move(s![.., ..;2]);
-    let n = 1.;
-    bench.iter(|| n + &a);
-}
-
 #[bench]
 fn scalar_sub_1(bench: &mut test::Bencher) {
     let a = Array::<f32, _>::zeros((64, 64));
diff --git a/examples/convo.rs b/examples/convo.rs
index b50ab524..a9f073bd 100644
--- a/examples/convo.rs
+++ b/examples/convo.rs
@@ -1,7 +1,7 @@
 #![allow(unused)]
 extern crate ndarray;
+extern crate num_traits;
 
-#[cfg(feature = "std")]
 use num_traits::Float;
 
 use ndarray::prelude::*;
@@ -13,7 +13,6 @@ const SHARPEN: [[f32; 3]; 3] = [[0., -1., 0.], [-1., 5., -1.], [0., -1., 0.]];
 type Kernel3x3<A> = [[A; 3]; 3];
 
 #[inline(never)]
-#[cfg(feature = "std")]
 fn conv_3x3<F>(a: &ArrayView2<'_, F>, out: &mut ArrayViewMut2<'_, F>, kernel: &Kernel3x3<F>)
 where
     F: Float,
@@ -42,7 +41,6 @@ where
     }
 }
 
-#[cfg(feature = "std")]
 fn main() {
     let n = 16;
     let mut a = Array::zeros((n, n));
@@ -63,5 +61,3 @@ fn main() {
     }
     println!("{:2}", res);
 }
-#[cfg(not(feature = "std"))]
-fn main() {}
diff --git a/examples/sort-axis.rs b/examples/sort-axis.rs
index ab5dfdab..eabf9bb5 100644
--- a/examples/sort-axis.rs
+++ b/examples/sort-axis.rs
@@ -129,7 +129,7 @@ where
         }
     }
 }
-#[cfg(feature = "std")]
+
 fn main() {
     let a = Array::linspace(0., 63., 64).into_shape((8, 8)).unwrap();
     let strings = a.map(|x| x.to_string());
@@ -143,5 +143,3 @@ fn main() {
     let c = strings.permute_axis(Axis(1), &perm);
     println!("{:?}", c);
 }
-#[cfg(not(feature = "std"))]
-fn main() {}
diff --git a/src/argument_traits.rs b/src/argument_traits.rs
index 82d4869a..a93e33a1 100644
--- a/src/argument_traits.rs
+++ b/src/argument_traits.rs
@@ -1,7 +1,6 @@
 use std::cell::Cell;
 use std::mem::MaybeUninit;
 
-use crate::math_cell::MathCell;
 
 /// A producer element that can be assigned to once
 pub trait AssignElem<T> {
@@ -23,13 +22,6 @@ impl<'a, T> AssignElem<T> for &'a Cell<T> {
     }
 }
 
-/// Assignable element, simply `self.set(input)`.
-impl<'a, T> AssignElem<T> for &'a MathCell<T> {
-    fn assign_elem(self, input: T) {
-        self.set(input);
-    }
-}
-
 /// Assignable element, the item in the MaybeUninit is overwritten (prior value, if any, is not
 /// read or dropped).
 impl<'a, T> AssignElem<T> for &'a mut MaybeUninit<T> {
diff --git a/src/array_approx.rs b/src/array_approx.rs
index a2e9c232..82c95a22 100644
--- a/src/array_approx.rs
+++ b/src/array_approx.rs
@@ -81,7 +81,6 @@ where
 #[cfg(test)]
 mod tests {
     use crate::prelude::*;
-    use alloc::vec;
     use approx::{
         assert_abs_diff_eq, assert_abs_diff_ne, assert_relative_eq, assert_relative_ne,
         assert_ulps_eq, assert_ulps_ne,
diff --git a/src/array_serde.rs b/src/array_serde.rs
index 41f7d60c..c8b485d2 100644
--- a/src/array_serde.rs
+++ b/src/array_serde.rs
@@ -11,8 +11,6 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer};
 
 use std::fmt;
 use std::marker::PhantomData;
-use alloc::format;
-use alloc::vec::Vec;
 
 use crate::imp_prelude::*;
 
diff --git a/src/arrayformat.rs b/src/arrayformat.rs
index c1ca352e..a7203b38 100644
--- a/src/arrayformat.rs
+++ b/src/arrayformat.rs
@@ -8,7 +8,6 @@
 use super::{ArrayBase, ArrayView, Axis, Data, Dimension, NdProducer};
 use crate::aliases::{Ix1, IxDyn};
 use std::fmt;
-use alloc::format;
 
 /// Default threshold, below this element count, we don't ellipsize
 const ARRAY_MANY_ELEMENT_LIMIT: usize = 500;
@@ -288,8 +287,6 @@ where
 mod formatting_with_omit {
     use itertools::Itertools;
     use std::fmt;
-    use alloc::string::String;
-    use alloc::vec::Vec;
 
     use super::*;
     use crate::prelude::*;
diff --git a/src/arraytraits.rs b/src/arraytraits.rs
index 449d5778..143bb5fa 100644
--- a/src/arraytraits.rs
+++ b/src/arraytraits.rs
@@ -12,7 +12,6 @@ use std::iter::FromIterator;
 use std::iter::IntoIterator;
 use std::mem;
 use std::ops::{Index, IndexMut};
-use alloc::vec::Vec;
 
 use crate::imp_prelude::*;
 use crate::iter::{Iter, IterMut};
diff --git a/src/data_repr.rs b/src/data_repr.rs
index b34f0a4c..56682d9b 100644
--- a/src/data_repr.rs
+++ b/src/data_repr.rs
@@ -1,9 +1,8 @@
+
 use std::mem;
 use std::mem::ManuallyDrop;
 use std::ptr::NonNull;
-use alloc::slice;
-use alloc::borrow::ToOwned;
-use alloc::vec::Vec;
+use std::slice;
 use crate::extension::nonnull;
 
 /// Array's representation.
diff --git a/src/data_traits.rs b/src/data_traits.rs
index 3d6ce88b..057ef924 100644
--- a/src/data_traits.rs
+++ b/src/data_traits.rs
@@ -11,8 +11,7 @@
 use rawpointer::PointerExt;
 use std::mem::{self, size_of};
 use std::ptr::NonNull;
-use alloc::sync::Arc;
-use alloc::vec::Vec;
+use std::sync::Arc;
 
 use crate::{
     ArrayBase, CowRepr, Dimension, OwnedArcRepr, OwnedRepr, RawViewRepr, ViewRepr,
diff --git a/src/dimension/conversion.rs b/src/dimension/conversion.rs
index 6b53a4ee..bf48dae2 100644
--- a/src/dimension/conversion.rs
+++ b/src/dimension/conversion.rs
@@ -10,7 +10,6 @@
 
 use num_traits::Zero;
 use std::ops::{Index, IndexMut};
-use alloc::vec::Vec;
 
 use crate::{Dim, Dimension, Ix, Ix1, IxDyn, IxDynImpl};
 
diff --git a/src/dimension/dim.rs b/src/dimension/dim.rs
index 97de6d84..3f47e15a 100644
--- a/src/dimension/dim.rs
+++ b/src/dimension/dim.rs
@@ -6,8 +6,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-
 use std::fmt;
+
 use super::Dimension;
 use super::IntoDimension;
 use crate::itertools::zip;
diff --git a/src/dimension/dimension_trait.rs b/src/dimension/dimension_trait.rs
index d98eedd6..4223e9ed 100644
--- a/src/dimension/dimension_trait.rs
+++ b/src/dimension/dimension_trait.rs
@@ -9,7 +9,6 @@
 use std::fmt::Debug;
 use std::ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign};
 use std::ops::{Index, IndexMut};
-use alloc::vec::Vec;
 
 use super::axes_of;
 use super::conversion::Convert;
@@ -287,17 +286,16 @@ pub trait Dimension:
             return true;
         }
         if dim.ndim() == 1 {
-            return false;
+            return strides[0] as isize == -1;
         }
         let order = strides._fastest_varying_stride_order();
         let strides = strides.slice();
 
-        // FIXME: Negative strides
         let dim_slice = dim.slice();
         let mut cstride = 1;
         for &i in order.slice() {
             // a dimension of length 1 can have unequal strides
-            if dim_slice[i] != 1 && strides[i] != cstride {
+            if dim_slice[i] != 1 && (strides[i] as isize).abs() as usize != cstride {
                 return false;
             }
             cstride *= dim_slice[i];
@@ -308,8 +306,7 @@ pub trait Dimension:
     /// Return the axis ordering corresponding to the fastest variation
     /// (in ascending order).
     ///
-    /// Assumes that no stride value appears twice. This cannot yield the correct
-    /// result the strides are not positive.
+    /// Assumes that no stride value appears twice.
     #[doc(hidden)]
     fn _fastest_varying_stride_order(&self) -> Self {
         let mut indices = self.clone();
@@ -317,7 +314,9 @@ pub trait Dimension:
             *elt = i;
         }
         let strides = self.slice();
-        indices.slice_mut().sort_by_key(|&i| strides[i]);
+        indices
+            .slice_mut()
+            .sort_by_key(|&i| (strides[i] as isize).abs());
         indices
     }
 
@@ -646,7 +645,7 @@ impl Dimension for Dim<[Ix; 2]> {
 
     #[inline]
     fn _fastest_varying_stride_order(&self) -> Self {
-        if get!(self, 0) as Ixs <= get!(self, 1) as Ixs {
+        if (get!(self, 0) as Ixs).abs() <= (get!(self, 1) as Ixs).abs() {
             Ix2(0, 1)
         } else {
             Ix2(1, 0)
@@ -806,7 +805,7 @@ impl Dimension for Dim<[Ix; 3]> {
         let mut order = Ix3(0, 1, 2);
         macro_rules! swap {
             ($stride:expr, $order:expr, $x:expr, $y:expr) => {
-                if $stride[$x] > $stride[$y] {
+                if ($stride[$x] as isize).abs() > ($stride[$y] as isize).abs() {
                     $stride.swap($x, $y);
                     $order.ixm().swap($x, $y);
                 }
diff --git a/src/dimension/dynindeximpl.rs b/src/dimension/dynindeximpl.rs
index 5c9cd0d6..76087aa5 100644
--- a/src/dimension/dynindeximpl.rs
+++ b/src/dimension/dynindeximpl.rs
@@ -1,9 +1,7 @@
 use crate::imp_prelude::*;
 use std::hash::{Hash, Hasher};
 use std::ops::{Deref, DerefMut, Index, IndexMut};
-use alloc::vec;
-use alloc::boxed::Box;
-use alloc::vec::Vec;
+
 const CAP: usize = 4;
 
 /// T is usize or isize
diff --git a/src/dimension/mod.rs b/src/dimension/mod.rs
index b4610f0d..f52ba889 100644
--- a/src/dimension/mod.rs
+++ b/src/dimension/mod.rs
@@ -46,15 +46,12 @@ pub fn stride_offset(n: Ix, stride: Ix) -> isize {
 /// There is overlap if, when iterating through the dimensions in order of
 /// increasing stride, the current stride is less than or equal to the maximum
 /// possible offset along the preceding axes. (Axes of length ≤1 are ignored.)
-///
-/// The current implementation assumes that strides of axes with length > 1 are
-/// nonnegative. Additionally, it does not check for overflow.
 pub fn dim_stride_overlap<D: Dimension>(dim: &D, strides: &D) -> bool {
     let order = strides._fastest_varying_stride_order();
     let mut sum_prev_offsets = 0;
     for &index in order.slice() {
         let d = dim[index];
-        let s = strides[index] as isize;
+        let s = (strides[index] as isize).abs();
         match d {
             0 => return false,
             1 => {}
@@ -210,11 +207,7 @@ where
 ///
 /// 2. The product of non-zero axis lengths must not exceed `isize::MAX`.
 ///
-/// 3. For axes with length > 1, the stride must be nonnegative. This is
-///    necessary to make sure the pointer cannot move backwards outside the
-///    slice. For axes with length ≤ 1, the stride can be anything.
-///
-/// 4. If the array will be empty (any axes are zero-length), the difference
+/// 3. If the array will be empty (any axes are zero-length), the difference
 ///    between the least address and greatest address accessible by moving
 ///    along all axes must be ≤ `data.len()`. (It's fine in this case to move
 ///    one byte past the end of the slice since the pointers will be offset but
@@ -225,13 +218,19 @@ where
 ///    `data.len()`. This and #3 ensure that all dereferenceable pointers point
 ///    to elements within the slice.
 ///
-/// 5. The strides must not allow any element to be referenced by two different
+/// 4. The strides must not allow any element to be referenced by two different
 ///    indices.
 ///
 /// Note that since slices cannot contain more than `isize::MAX` bytes,
 /// condition 4 is sufficient to guarantee that the absolute difference in
 /// units of `A` and in units of bytes between the least address and greatest
 /// address accessible by moving along all axes does not exceed `isize::MAX`.
+///
+/// Warning: This function is sufficient to check the invariants of ArrayBase only
+/// if the pointer to the first element of the array is chosen such that the element
+/// with the smallest memory address is at the start of data. (In other words, the
+/// pointer to the first element of the array must be computed using offset_from_ptr_to_memory
+/// so that negative strides are correctly handled.)
 pub(crate) fn can_index_slice<A, D: Dimension>(
     data: &[A],
     dim: &D,
@@ -248,7 +247,7 @@ fn can_index_slice_impl<D: Dimension>(
     dim: &D,
     strides: &D,
 ) -> Result<(), ShapeError> {
-    // Check condition 4.
+    // Check condition 3.
     let is_empty = dim.slice().iter().any(|&d| d == 0);
     if is_empty && max_offset > data_len {
         return Err(from_kind(ErrorKind::OutOfBounds));
@@ -257,15 +256,7 @@ fn can_index_slice_impl<D: Dimension>(
         return Err(from_kind(ErrorKind::OutOfBounds));
     }
 
-    // Check condition 3.
-    for (&d, &s) in izip!(dim.slice(), strides.slice()) {
-        let s = s as isize;
-        if d > 1 && s < 0 {
-            return Err(from_kind(ErrorKind::Unsupported));
-        }
-    }
-
-    // Check condition 5.
+    // Check condition 4.
     if !is_empty && dim_stride_overlap(dim, strides) {
         return Err(from_kind(ErrorKind::Unsupported));
     }
@@ -289,6 +280,19 @@ pub fn stride_offset_checked(dim: &[Ix], strides: &[Ix], index: &[Ix]) -> Option
     Some(offset)
 }
 
+/// Checks if strides are non-negative.
+pub fn strides_non_negative<D>(strides: &D) -> Result<(), ShapeError>
+where
+    D: Dimension,
+{
+    for &stride in strides.slice() {
+        if (stride as isize) < 0 {
+            return Err(from_kind(ErrorKind::Unsupported));
+        }
+    }
+    Ok(())
+}
+
 /// Implementation-specific extensions to `Dimension`
 pub trait DimensionExt {
     // note: many extensions go in the main trait if they need to be special-
@@ -394,6 +398,19 @@ fn to_abs_slice(axis_len: usize, slice: Slice) -> (usize, usize, isize) {
     (start, end, step)
 }
 
+/// This function computes the offset from the logically first element to the first element in
+/// memory of the array. The result is always <= 0.
+pub fn offset_from_ptr_to_memory<D: Dimension>(dim: &D, strides: &D) -> isize {
+    let offset = izip!(dim.slice(), strides.slice()).fold(0, |_offset, (d, s)| {
+        if (*s as isize) < 0 {
+            _offset + *s as isize * (*d as isize - 1)
+        } else {
+            _offset
+        }
+    });
+    offset
+}
+
 /// Modify dimension, stride and return data pointer offset
 ///
 /// **Panics** if stride is 0 or if any index is out of bounds.
@@ -676,7 +693,7 @@ mod test {
 
     #[test]
     fn slice_indexing_uncommon_strides() {
-        let v: alloc::vec::Vec<_> = (0..12).collect();
+        let v: Vec<_> = (0..12).collect();
         let dim = (2, 3, 2).into_dimension();
         let strides = (1, 2, 6).into_dimension();
         assert!(super::can_index_slice(&v, &dim, &strides).is_ok());
@@ -693,13 +710,21 @@ mod test {
         let dim = (2, 3, 2).into_dimension();
         let strides = (5, 2, 1).into_dimension();
         assert!(super::dim_stride_overlap(&dim, &strides));
+        let strides = (-5isize as usize, 2, -1isize as usize).into_dimension();
+        assert!(super::dim_stride_overlap(&dim, &strides));
         let strides = (6, 2, 1).into_dimension();
         assert!(!super::dim_stride_overlap(&dim, &strides));
+        let strides = (6, -2isize as usize, 1).into_dimension();
+        assert!(!super::dim_stride_overlap(&dim, &strides));
         let strides = (6, 0, 1).into_dimension();
         assert!(super::dim_stride_overlap(&dim, &strides));
+        let strides = (-6isize as usize, 0, 1).into_dimension();
+        assert!(super::dim_stride_overlap(&dim, &strides));
         let dim = (2, 2).into_dimension();
         let strides = (3, 2).into_dimension();
         assert!(!super::dim_stride_overlap(&dim, &strides));
+        let strides = (3, -2isize as usize).into_dimension();
+        assert!(!super::dim_stride_overlap(&dim, &strides));
     }
 
     #[test]
@@ -736,7 +761,7 @@ mod test {
         can_index_slice::<i32, _>(&[1], &Ix1(2), &Ix1(1)).unwrap_err();
         can_index_slice::<i32, _>(&[1, 2], &Ix1(2), &Ix1(0)).unwrap_err();
         can_index_slice::<i32, _>(&[1, 2], &Ix1(2), &Ix1(1)).unwrap();
-        can_index_slice::<i32, _>(&[1, 2], &Ix1(2), &Ix1(-1isize as usize)).unwrap_err();
+        can_index_slice::<i32, _>(&[1, 2], &Ix1(2), &Ix1(-1isize as usize)).unwrap();
     }
 
     #[test]
@@ -784,7 +809,7 @@ mod test {
     }
 
     quickcheck! {
-        fn can_index_slice_not_custom_same_as_can_index_slice(data: alloc::vec::Vec<u8>, dim: alloc::vec::Vec<usize>) -> bool {
+        fn can_index_slice_not_custom_same_as_can_index_slice(data: Vec<u8>, dim: Vec<usize>) -> bool {
             let dim = IxDyn(&dim);
             let result = can_index_slice_not_custom(data.len(), &dim);
             if dim.size_checked().is_none() {
@@ -871,7 +896,7 @@ mod test {
             let (min2, max2) = (cmp::min(first2, last2), cmp::max(first2, last2));
 
             // Naively determine if the sequences intersect.
-            let seq1: alloc::vec::Vec<_> = (0..len1)
+            let seq1: Vec<_> = (0..len1)
                 .map(|n| first1 + step1 * n)
                 .collect();
             let intersects = (0..len2)
diff --git a/src/error.rs b/src/error.rs
index c4549614..09093756 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -6,7 +6,6 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 use super::Dimension;
-#[cfg(feature = "std")]
 use std::error::Error;
 use std::fmt;
 
@@ -70,7 +69,6 @@ impl PartialEq for ShapeError {
     }
 }
 
-#[cfg(feature = "std")]
 impl Error for ShapeError {}
 
 impl fmt::Display for ShapeError {
diff --git a/src/extension/nonnull.rs b/src/extension/nonnull.rs
index 5aa50fdc..32fbb07c 100644
--- a/src/extension/nonnull.rs
+++ b/src/extension/nonnull.rs
@@ -1,5 +1,4 @@
 use std::ptr::NonNull;
-use alloc::vec::Vec;
 
 /// Return a NonNull<T> pointer to the vector's data
 pub(crate) fn nonnull_from_vec_data<T>(v: &mut Vec<T>) -> NonNull<T> {
diff --git a/src/free_functions.rs b/src/free_functions.rs
index 95eb7dc8..ff7984ee 100644
--- a/src/free_functions.rs
+++ b/src/free_functions.rs
@@ -7,9 +7,7 @@
 // except according to those terms.
 
 use std::mem::{forget, size_of};
-use alloc::slice;
-use alloc::vec;
-use alloc::vec::Vec;
+use std::slice;
 
 use crate::imp_prelude::*;
 use crate::{dimension, ArcArray1, ArcArray2};
diff --git a/src/geomspace.rs b/src/geomspace.rs
index c1935c71..06242f68 100644
--- a/src/geomspace.rs
+++ b/src/geomspace.rs
@@ -5,7 +5,6 @@
 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
-#![cfg(feature = "std")]
 use num_traits::Float;
 
 /// An iterator of a sequence of geometrically spaced floats.
diff --git a/src/impl_1d.rs b/src/impl_1d.rs
index 704d8643..fa877eff 100644
--- a/src/impl_1d.rs
+++ b/src/impl_1d.rs
@@ -7,7 +7,6 @@
 // except according to those terms.
 
 //! Methods for one-dimensional arrays.
-use alloc::vec::Vec;
 use crate::imp_prelude::*;
 
 /// # Methods For 1-D Arrays
diff --git a/src/impl_constructors.rs b/src/impl_constructors.rs
index 4d76490c..56948cae 100644
--- a/src/impl_constructors.rs
+++ b/src/impl_constructors.rs
@@ -11,26 +11,21 @@
 //!
 
 #![allow(clippy::match_wild_err_arm)]
-#[cfg(feature = "std")]
-use num_traits::Float;
-use num_traits::{One, Zero};
+
+use num_traits::{Float, One, Zero};
 use std::mem::MaybeUninit;
-use alloc::vec;
-use alloc::vec::Vec;
 
 use crate::dimension;
+use crate::dimension::offset_from_ptr_to_memory;
 use crate::error::{self, ShapeError};
 use crate::extension::nonnull::nonnull_from_vec_data;
 use crate::imp_prelude::*;
 use crate::indexes;
 use crate::indices;
-#[cfg(feature = "std")]
-use crate::iterators::to_vec;
-use crate::iterators::to_vec_mapped;
+use crate::iterators::{to_vec, to_vec_mapped};
 use crate::StrideShape;
-#[cfg(feature = "std")]
 use crate::{geomspace, linspace, logspace};
-
+use rawpointer::PointerExt;
 
 /// # Constructor Methods for Owned Arrays
 ///
@@ -73,7 +68,6 @@ where
     /// let array = Array::linspace(0., 1., 5);
     /// assert!(array == arr1(&[0.0, 0.25, 0.5, 0.75, 1.0]))
     /// ```
-    #[cfg(feature = "std")]
     pub fn linspace(start: A, end: A, n: usize) -> Self
     where
         A: Float,
@@ -92,7 +86,6 @@ where
     /// let array = Array::range(0., 5., 1.);
     /// assert!(array == arr1(&[0., 1., 2., 3., 4.]))
     /// ```
-    #[cfg(feature = "std")]
     pub fn range(start: A, end: A, step: A) -> Self
     where
         A: Float,
@@ -121,7 +114,6 @@ where
     /// assert_abs_diff_eq!(array, arr1(&[-1e3, -1e2, -1e1, -1e0]));
     /// # }
     /// ```
-    #[cfg(feature = "std")]
     pub fn logspace(base: A, start: A, end: A, n: usize) -> Self
     where
         A: Float,
@@ -156,7 +148,6 @@ where
     /// #
     /// # example().unwrap();
     /// ```
-    #[cfg(feature = "std")]
     pub fn geomspace(start: A, end: A, n: usize) -> Option<Self>
     where
         A: Float,
@@ -442,7 +433,8 @@ where
     ///
     /// 2. The product of non-zero axis lengths must not exceed `isize::MAX`.
     ///
-    /// 3. For axes with length > 1, the stride must be nonnegative.
+    /// 3. For axes with length > 1, the pointer cannot move outside the
+    ///    slice.
     ///
     /// 4. If the array will be empty (any axes are zero-length), the
     ///    difference between the least address and greatest address accessible
@@ -468,7 +460,7 @@ where
         // debug check for issues that indicates wrong use of this constructor
         debug_assert!(dimension::can_index_slice(&v, &dim, &strides).is_ok());
         ArrayBase {
-            ptr: nonnull_from_vec_data(&mut v),
+            ptr: nonnull_from_vec_data(&mut v).offset(-offset_from_ptr_to_memory(&dim, &strides)),
             data: DataOwned::new(v),
             strides,
             dim,
@@ -494,7 +486,7 @@ where
     ///
     /// This constructor is limited to elements where `A: Copy` (no destructors)
     /// to avoid users shooting themselves too hard in the foot.
-    /// 
+    ///
     /// (Also note that the constructors `from_shape_vec` and
     /// `from_shape_vec_unchecked` allow the user yet more control, in the sense
     /// that Arrays can be created from arbitrary vectors.)
diff --git a/src/impl_methods.rs b/src/impl_methods.rs
index 3afb6814..3921dcbf 100644
--- a/src/impl_methods.rs
+++ b/src/impl_methods.rs
@@ -7,9 +7,8 @@
 // except according to those terms.
 
 use std::ptr as std_ptr;
-use alloc::slice;
-use alloc::vec;
-use alloc::vec::Vec;
+use std::slice;
+
 use rawpointer::PointerExt;
 
 use crate::imp_prelude::*;
@@ -18,10 +17,10 @@ use crate::arraytraits;
 use crate::dimension;
 use crate::dimension::IntoDimension;
 use crate::dimension::{
-    abs_index, axes_of, do_slice, merge_axes, size_of_shape_checked, stride_offset, Axes,
+    abs_index, axes_of, do_slice, merge_axes, offset_from_ptr_to_memory, size_of_shape_checked,
+    stride_offset, Axes,
 };
 use crate::error::{self, ErrorKind, ShapeError};
-use crate::math_cell::MathCell;
 use crate::itertools::zip;
 use crate::zip::Zip;
 
@@ -71,7 +70,7 @@ where
         self.dim.clone().into_pattern()
     }
 
-    /// Return the shape of the array as it's stored in the array.
+    /// Return the shape of the array as it stored in the array.
     ///
     /// This is primarily useful for passing to other `ArrayBase`
     /// functions, such as when creating another array of the same
@@ -153,28 +152,14 @@ where
         unsafe { ArrayViewMut::new(self.ptr, self.dim.clone(), self.strides.clone()) }
     }
 
-    /// Return a shared view of the array with elements as if they were embedded in cells.
-    ///
-    /// The cell view requires a mutable borrow of the array. Once borrowed the
-    /// cell view itself can be copied and accessed without exclusivity.
-    ///
-    /// The view acts "as if" the elements are temporarily in cells, and elements
-    /// can be changed through shared references using the regular cell methods.
-    pub fn cell_view(&mut self) -> ArrayView<'_, MathCell<A>, D>
-    where
-        S: DataMut,
-    {
-        self.view_mut().into_cell_view()
-    }
-
     /// Return an uniquely owned copy of the array.
     ///
-    /// If the input array is contiguous and its strides are positive, then the
-    /// output array will have the same memory layout. Otherwise, the layout of
-    /// the output array is unspecified. If you need a particular layout, you
-    /// can allocate a new array with the desired memory layout and
-    /// [`.assign()`](#method.assign) the data. Alternatively, you can collect
-    /// an iterator, like this for a result in standard layout:
+    /// If the input array is contiguous, then the output array will have the same
+    /// memory layout. Otherwise, the layout of the output array is unspecified.
+    /// If you need a particular layout, you can allocate a new array with the
+    /// desired memory layout and [`.assign()`](#method.assign) the data.
+    /// Alternatively, you can collectan iterator, like this for a result in
+    /// standard layout:
     ///
     /// ```
     /// # use ndarray::prelude::*;
@@ -646,7 +631,7 @@ where
     ///
     /// The caller must ensure that:
     ///
-    /// 1. both `index1` and `index2` are in-bounds and
+    /// 1. both `index1 and `index2` are in-bounds and
     ///
     /// 2. the data is uniquely held by the array. (This property is guaranteed
     ///    for `Array` and `ArrayViewMut`, but not for `ArcArray` or `CowArray`.)
@@ -945,7 +930,7 @@ where
     /// Return a producer and iterable that traverses over all 1D lanes
     /// pointing in the direction of `axis`.
     ///
-    /// When pointing in the direction of the first axis, they are *columns*,
+    /// When the pointing in the direction of the first axis, they are *columns*,
     /// in the direction of the last axis *rows*; in general they are all
     /// *lanes* and are one dimensional.
     ///
@@ -1214,7 +1199,7 @@ where
         (len, stride)
     }
 
-    /// Return a view of the diagonal elements of the array.
+    /// Return an view of the diagonal elements of the array.
     ///
     /// The diagonal is simply the sequence indexed by *(0, 0, .., 0)*,
     /// *(1, 1, ..., 1)* etc as long as all axes have elements.
@@ -1273,7 +1258,7 @@ where
     /// Return `true` if the array data is laid out in contiguous “C order” in
     /// memory (where the last index is the most rapidly varying).
     ///
-    /// Return `false` otherwise, i.e. the array is possibly not
+    /// Return `false` otherwise, i.e the array is possibly not
     /// contiguous in memory, it has custom strides, etc.
     pub fn is_standard_layout(&self) -> bool {
         fn is_standard_layout<D: Dimension>(dim: &D, strides: &D) -> bool {
@@ -1296,9 +1281,6 @@ where
     }
 
     /// Return true if the array is known to be contiguous.
-    ///
-    /// Will detect c- and f-contig arrays correctly, but otherwise
-    /// There are some false negatives.
     pub(crate) fn is_contiguous(&self) -> bool {
         D::is_contiguous(&self.dim, &self.strides)
     }
@@ -1420,14 +1402,18 @@ where
     ///
     /// If this function returns `Some(_)`, then the elements in the slice
     /// have whatever order the elements have in memory.
-    ///
-    /// Implementation notes: Does not yet support negatively strided arrays.
     pub fn as_slice_memory_order(&self) -> Option<&[A]>
     where
         S: Data,
     {
         if self.is_contiguous() {
-            unsafe { Some(slice::from_raw_parts(self.ptr.as_ptr(), self.len())) }
+            let offset = offset_from_ptr_to_memory(&self.dim, &self.strides);
+            unsafe {
+                Some(slice::from_raw_parts(
+                    self.ptr.offset(offset).as_ptr(),
+                    self.len(),
+                ))
+            }
         } else {
             None
         }
@@ -1441,7 +1427,13 @@ where
     {
         if self.is_contiguous() {
             self.ensure_unique();
-            unsafe { Some(slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len())) }
+            let offset = offset_from_ptr_to_memory(&self.dim, &self.strides);
+            unsafe {
+                Some(slice::from_raw_parts_mut(
+                    self.ptr.offset(offset).as_ptr(),
+                    self.len(),
+                ))
+            }
         } else {
             None
         }
diff --git a/src/impl_ops.rs b/src/impl_ops.rs
index 51d432ee..4804356e 100644
--- a/src/impl_ops.rs
+++ b/src/impl_ops.rs
@@ -159,8 +159,8 @@ impl<'a, A, S, D, B> $trt<B> for &'a ArrayBase<S, D>
           B: ScalarOperand,
 {
     type Output = Array<A, D>;
-    fn $mth(self, x: B) -> Self::Output {
-        self.map(move |elt| elt.clone() $operator x.clone())
+    fn $mth(self, x: B) -> Array<A, D> {
+        self.to_owned().$mth(x)
     }
 }
     );
@@ -210,11 +210,11 @@ impl<'a, S, D> $trt<&'a ArrayBase<S, D>> for $scalar
           D: Dimension,
 {
     type Output = Array<$scalar, D>;
-    fn $mth(self, rhs: &ArrayBase<S, D>) -> Self::Output {
+    fn $mth(self, rhs: &ArrayBase<S, D>) -> Array<$scalar, D> {
         if_commutative!($commutative {
             rhs.$mth(self)
         } or {
-            rhs.map(move |elt| self.clone() $operator elt.clone())
+            self.$mth(rhs.to_owned())
         })
     }
 }
diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs
index 41eff2b1..f219b7b6 100644
--- a/src/impl_owned_array.rs
+++ b/src/impl_owned_array.rs
@@ -1,5 +1,4 @@
 
-use alloc::vec::Vec;
 use crate::imp_prelude::*;
 
 /// Methods specific to `Array0`.
diff --git a/src/impl_raw_views.rs b/src/impl_raw_views.rs
index 8377dc93..e3447cac 100644
--- a/src/impl_raw_views.rs
+++ b/src/impl_raw_views.rs
@@ -62,6 +62,11 @@ where
     ///     [`.offset()`] regardless of the starting point due to past offsets.
     ///
     /// * The product of non-zero axis lengths must not exceed `isize::MAX`.
+    /// 
+    /// * Strides must be non-negative.
+    ///
+    /// This function can use debug assertions to check some of these requirements,
+    /// but it's not a complete check.
     ///
     /// [`.offset()`]: https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.offset
     pub unsafe fn from_shape_ptr<Sh>(shape: Sh, ptr: *const A) -> Self
@@ -73,6 +78,7 @@ where
         if cfg!(debug_assertions) {
             assert!(!ptr.is_null(), "The pointer must be non-null.");
             if let Strides::Custom(strides) = &shape.strides {
+                dimension::strides_non_negative(strides).unwrap();
                 dimension::max_abs_offset_check_overflow::<A, _>(&dim, &strides).unwrap();
             } else {
                 dimension::size_of_shape_checked(&dim).unwrap();
@@ -202,6 +208,11 @@ where
     ///     [`.offset()`] regardless of the starting point due to past offsets.
     ///
     /// * The product of non-zero axis lengths must not exceed `isize::MAX`.
+    /// 
+    /// * Strides must be non-negative.
+    ///
+    /// This function can use debug assertions to check some of these requirements,
+    /// but it's not a complete check.
     ///
     /// [`.offset()`]: https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.offset
     pub unsafe fn from_shape_ptr<Sh>(shape: Sh, ptr: *mut A) -> Self
@@ -213,6 +224,7 @@ where
         if cfg!(debug_assertions) {
             assert!(!ptr.is_null(), "The pointer must be non-null.");
             if let Strides::Custom(strides) = &shape.strides {
+                dimension::strides_non_negative(strides).unwrap();
                 dimension::max_abs_offset_check_overflow::<A, _>(&dim, &strides).unwrap();
             } else {
                 dimension::size_of_shape_checked(&dim).unwrap();
diff --git a/src/impl_views/constructors.rs b/src/impl_views/constructors.rs
index bc1602af..c6e5f998 100644
--- a/src/impl_views/constructors.rs
+++ b/src/impl_views/constructors.rs
@@ -96,6 +96,11 @@ where
     ///
     /// * The product of non-zero axis lengths must not exceed `isize::MAX`.
     ///
+    /// * Strides must be non-negative.
+    ///
+    /// This function can use debug assertions to check some of these requirements,
+    /// but it's not a complete check.
+    ///
     /// [`.offset()`]: https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.offset
     pub unsafe fn from_shape_ptr<Sh>(shape: Sh, ptr: *const A) -> Self
     where
@@ -188,6 +193,11 @@ where
     ///
     /// * The product of non-zero axis lengths must not exceed `isize::MAX`.
     ///
+    /// * Strides must be non-negative.
+    ///
+    /// This function can use debug assertions to check some of these requirements,
+    /// but it's not a complete check.
+    ///
     /// [`.offset()`]: https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.offset
     pub unsafe fn from_shape_ptr<Sh>(shape: Sh, ptr: *mut A) -> Self
     where
diff --git a/src/impl_views/conversions.rs b/src/impl_views/conversions.rs
index cfd7f9aa..863d26dd 100644
--- a/src/impl_views/conversions.rs
+++ b/src/impl_views/conversions.rs
@@ -6,14 +6,13 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use alloc::slice;
+use std::slice;
 
 use crate::imp_prelude::*;
 
 use crate::{Baseiter, ElementsBase, ElementsBaseMut, Iter, IterMut};
 
 use crate::iter::{self, AxisIter, AxisIterMut};
-use crate::math_cell::MathCell;
 use crate::IndexLonger;
 
 /// Methods for read-only array views.
@@ -118,21 +117,6 @@ where
     pub fn into_slice(self) -> Option<&'a mut [A]> {
         self.try_into_slice().ok()
     }
-
-    /// Return a shared view of the array with elements as if they were embedded in cells.
-    ///
-    /// The cell view itself can be copied and accessed without exclusivity.
-    ///
-    /// The view acts "as if" the elements are temporarily in cells, and elements
-    /// can be changed through shared references using the regular cell methods.
-    pub fn into_cell_view(self) -> ArrayView<'a, MathCell<A>, D> {
-        // safety: valid because
-        // A and MathCell<A> have the same representation
-        // &'a mut T is interchangeable with &'a Cell<T> -- see method Cell::from_mut in std
-        unsafe {
-            self.into_raw_view_mut().cast::<MathCell<A>>().deref_into_view()
-        }
-    }
 }
 
 /// Private array view methods
diff --git a/src/iterators/mod.rs b/src/iterators/mod.rs
index 595f0897..8cb7a908 100644
--- a/src/iterators/mod.rs
+++ b/src/iterators/mod.rs
@@ -16,7 +16,6 @@ mod windows;
 use std::iter::FromIterator;
 use std::marker::PhantomData;
 use std::ptr;
-use alloc::vec::Vec;
 
 use crate::Ix1;
 
@@ -1447,13 +1446,10 @@ pub unsafe trait TrustedIterator {}
 
 use crate::indexes::IndicesIterF;
 use crate::iter::IndicesIter;
-#[cfg(feature = "std")]
 use crate::{geomspace::Geomspace, linspace::Linspace, logspace::Logspace};
-#[cfg(feature = "std")]
-unsafe impl<F> TrustedIterator for Linspace<F> {}
-#[cfg(feature = "std")]
+
 unsafe impl<F> TrustedIterator for Geomspace<F> {}
-#[cfg(feature = "std")]
+unsafe impl<F> TrustedIterator for Linspace<F> {}
 unsafe impl<F> TrustedIterator for Logspace<F> {}
 unsafe impl<'a, A, D> TrustedIterator for Iter<'a, A, D> {}
 unsafe impl<'a, A, D> TrustedIterator for IterMut<'a, A, D> {}
diff --git a/src/lib.rs b/src/lib.rs
index 11064d32..c0a3b2e8 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -12,7 +12,6 @@
     clippy::deref_addrof,
     clippy::unreadable_literal
 )]
-#![cfg_attr(not(feature = "std"), no_std)]
 
 //! The `ndarray` crate provides an *n*-dimensional container for general elements
 //! and for numerics.
@@ -69,24 +68,20 @@
 //! The following crate feature flags are available. They are configured in your
 //! `Cargo.toml`.
 //!
-//! - `std`
-//!  - Rust standard library (enabled by default)
-//!  - This crate can be used without the standard library by disabling the
-//!    default `std` feature. To do so, use `default-features = false` in
-//!    your `Cargo.toml`.
-//!  - The `geomspace` `linspace` `logspace` `range` `std` `var` `var_axis`
-//!    and `std_axis` methods are only available when `std` is enabled.
 //! - `serde`
+//!   - Optional, compatible with Rust stable
 //!   - Enables serialization support for serde 1.x
 //! - `rayon`
+//!   - Optional, compatible with Rust stable
 //!   - Enables parallel iterators, parallelized methods and [`par_azip!`].
-//!   - Implies std
 //! - `approx`
+//!   - Optional, compatible with Rust stable
 //!   - Enables implementations of traits from the [`approx`] crate.
 //! - `blas`
+//!   - Optional and experimental, compatible with Rust stable
 //!   - Enable transparent BLAS support for matrix multiplication.
 //!     Uses ``blas-src`` for pluggable backend, which needs to be configured
-//!     separately (see the README).
+//!     separately.
 //!
 //! ## Documentation
 //!
@@ -114,14 +109,6 @@
 //! For conversion between `ndarray`, [`nalgebra`](https://crates.io/crates/nalgebra) and
 //! [`image`](https://crates.io/crates/image) check out [`nshare`](https://crates.io/crates/nshare).
 
-
-extern crate alloc;
-
-#[cfg(feature = "std")]
-extern crate std;
-#[cfg(not(feature = "std"))]
-extern crate core as std;
-
 #[cfg(feature = "blas")]
 extern crate blas_src;
 #[cfg(feature = "blas")]
@@ -131,7 +118,7 @@ extern crate cblas_sys;
 pub mod doc;
 
 use std::marker::PhantomData;
-use alloc::sync::Arc;
+use std::sync::Arc;
 
 pub use crate::dimension::dim::*;
 pub use crate::dimension::{Axis, AxisDescription, Dimension, IntoDimension, RemoveAxis};
@@ -148,11 +135,10 @@ use crate::iterators::{ElementsBase, ElementsBaseMut, Iter, IterMut, Lanes, Lane
 pub use crate::arraytraits::AsArray;
 #[cfg(feature = "std")]
 pub use crate::linalg_traits::NdFloat;
-pub use crate::linalg_traits::LinalgScalar;
+pub use crate::linalg_traits::LinalgScalar; 
 
 pub use crate::stacking::{concatenate, stack, stack_new_axis};
 
-pub use crate::math_cell::MathCell;
 pub use crate::impl_views::IndexLonger;
 pub use crate::shape_builder::{Shape, ShapeBuilder, StrideShape};
 
@@ -194,7 +180,6 @@ mod layout;
 mod linalg_traits;
 mod linspace;
 mod logspace;
-mod math_cell;
 mod numeric_util;
 mod partial;
 mod shape_builder;
@@ -315,7 +300,7 @@ pub type Ixs = isize;
 ///
 /// [`ArrayView`] and [`ArrayViewMut`] are read-only and read-write array views
 /// respectively. They use dimensionality, indexing, and almost all other
-/// methods the same way as the other array types.
+/// methods the same was as the other array types.
 ///
 /// Methods for `ArrayBase` apply to array views too, when the trait bounds
 /// allow.
@@ -1590,8 +1575,6 @@ where
 
 // parallel methods
 #[cfg(feature = "rayon")]
-extern crate rayon_ as rayon;
-#[cfg(feature = "rayon")]
 pub mod parallel;
 
 mod impl_1d;
diff --git a/src/linalg/impl_linalg.rs b/src/linalg/impl_linalg.rs
index c227a68a..f54f1443 100644
--- a/src/linalg/impl_linalg.rs
+++ b/src/linalg/impl_linalg.rs
@@ -12,14 +12,13 @@ use crate::numeric_util;
 use crate::{LinalgScalar, Zip};
 
 use std::any::TypeId;
-use alloc::vec::Vec;
 
 #[cfg(feature = "blas")]
 use std::cmp;
 #[cfg(feature = "blas")]
 use std::mem::swap;
 #[cfg(feature = "blas")]
-use libc::c_int;
+use std::os::raw::c_int;
 
 #[cfg(feature = "blas")]
 use cblas_sys as blas_sys;
diff --git a/src/linalg_traits.rs b/src/linalg_traits.rs
index d52e7f54..43fe5f8d 100644
--- a/src/linalg_traits.rs
+++ b/src/linalg_traits.rs
@@ -5,20 +5,14 @@
 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
-
+use crate::ScalarOperand;
 #[cfg(feature = "std")]
 use num_traits::Float;
 use num_traits::{One, Zero};
-
-#[cfg(feature = "std")]
 use std::fmt;
 use std::ops::{Add, Div, Mul, Sub};
-#[cfg(feature = "std")]
 use std::ops::{AddAssign, DivAssign, MulAssign, RemAssign, SubAssign};
 
-#[cfg(feature = "std")]
-use crate::ScalarOperand;
-
 /// Elements that support linear algebra operations.
 ///
 /// `'static` for type-based specialization, `Copy` so that they don't need move
diff --git a/src/linspace.rs b/src/linspace.rs
index 6e9b1203..ca0eae47 100644
--- a/src/linspace.rs
+++ b/src/linspace.rs
@@ -5,7 +5,6 @@
 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
-#![cfg(feature = "std")]
 use num_traits::Float;
 
 /// An iterator of a sequence of evenly spaced floats.
diff --git a/src/logspace.rs b/src/logspace.rs
index 4dc6e1f3..55b5397c 100644
--- a/src/logspace.rs
+++ b/src/logspace.rs
@@ -5,7 +5,6 @@
 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
-#![cfg(feature = "std")]
 use num_traits::Float;
 
 /// An iterator of a sequence of logarithmically spaced number.
diff --git a/src/math_cell.rs b/src/math_cell.rs
deleted file mode 100644
index f0f8da40..00000000
--- a/src/math_cell.rs
+++ /dev/null
@@ -1,102 +0,0 @@
-
-use std::cell::Cell;
-use std::cmp::Ordering;
-use std::fmt;
-
-use std::ops::{Deref, DerefMut};
-
-/// A transparent wrapper of [`Cell<T>`](std::cell::Cell) which is identical in every way, except
-/// it will implement arithmetic operators as well.
-///
-/// The purpose of `MathCell` is to be used from [.cell_view()](crate::ArrayBase::cell_view).
-/// The `MathCell` derefs to `Cell`, so all the cell's methods are available.
-#[repr(transparent)]
-#[derive(Default)]
-pub struct MathCell<T>(Cell<T>);
-
-impl<T> MathCell<T> {
-    /// Create a new cell with the given value
-    #[inline(always)]
-    pub const fn new(value: T) -> Self { MathCell(Cell::new(value)) }
-
-    /// Return the inner value
-    pub fn into_inner(self) -> T { Cell::into_inner(self.0) }
-
-    /// Swap value with another cell
-    pub fn swap(&self, other: &Self) {
-        Cell::swap(&self.0, &other.0)
-    }
-}
-
-impl<T> Deref for MathCell<T> {
-    type Target = Cell<T>;
-    #[inline(always)]
-    fn deref(&self) -> &Self::Target { &self.0 }
-}
-
-impl<T> DerefMut for MathCell<T> {
-    #[inline(always)]
-    fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
-}
-
-impl<T> Clone for MathCell<T>
-    where T: Copy
-{
-    fn clone(&self) -> Self {
-        MathCell::new(self.get())
-    }
-}
-
-impl<T> PartialEq for MathCell<T>
-    where T: Copy + PartialEq
-{
-    fn eq(&self, rhs: &Self) -> bool {
-        self.get() == rhs.get()
-    }
-}
-
-impl<T> Eq for MathCell<T>
-    where T: Copy + Eq
-{ }
-
-impl<T> PartialOrd for MathCell<T>
-    where T: Copy + PartialOrd
-{
-    fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
-        self.get().partial_cmp(&rhs.get())
-    }
-
-    fn lt(&self, rhs: &Self) -> bool { self.get().lt(&rhs.get()) }
-    fn le(&self, rhs: &Self) -> bool { self.get().le(&rhs.get()) }
-    fn gt(&self, rhs: &Self) -> bool { self.get().gt(&rhs.get()) }
-    fn ge(&self, rhs: &Self) -> bool { self.get().ge(&rhs.get()) }
-}
-
-impl<T> Ord for MathCell<T>
-    where T: Copy + Ord
-{
-    fn cmp(&self, rhs: &Self) -> Ordering {
-        self.get().cmp(&rhs.get())
-    }
-}
-
-impl<T> fmt::Debug for MathCell<T>
-    where T: Copy + fmt::Debug
-{
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        self.get().fmt(f)
-    }
-}
-
-
-#[cfg(test)]
-mod tests {
-    use super::MathCell;
-
-    #[test]
-    fn test_basic() {
-        let c = &MathCell::new(0);
-        c.set(1);
-        assert_eq!(c.get(), 1);
-    }
-}
diff --git a/src/numeric/impl_numeric.rs b/src/numeric/impl_numeric.rs
index 9d1cef7e..6fc4f6cf 100644
--- a/src/numeric/impl_numeric.rs
+++ b/src/numeric/impl_numeric.rs
@@ -6,9 +6,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#[cfg(feature = "std")]
-use num_traits::Float;
-use num_traits::{self, FromPrimitive, Zero};
+use num_traits::{self, Float, FromPrimitive, Zero};
 use std::ops::{Add, Div, Mul};
 
 use crate::imp_prelude::*;
@@ -113,118 +111,6 @@ where
         sum
     }
 
-    /// Return variance of elements in the array.
-    ///
-    /// The variance is computed using the [Welford one-pass
-    /// algorithm](https://www.jstor.org/stable/1266577).
-    ///
-    /// The parameter `ddof` specifies the "delta degrees of freedom". For
-    /// example, to calculate the population variance, use `ddof = 0`, or to
-    /// calculate the sample variance, use `ddof = 1`.
-    ///
-    /// The variance is defined as:
-    ///
-    /// ```text
-    ///               1       n
-    /// variance = ――――――――   ∑ (xᵢ - x̅)²
-    ///            n - ddof  i=1
-    /// ```
-    ///
-    /// where
-    ///
-    /// ```text
-    ///     1   n
-    /// x̅ = ―   ∑ xᵢ
-    ///     n  i=1
-    /// ```
-    ///
-    /// and `n` is the length of the array.
-    ///
-    /// **Panics** if `ddof` is less than zero or greater than `n`
-    ///
-    /// # Example
-    ///
-    /// ```
-    /// use ndarray::array;
-    /// use approx::assert_abs_diff_eq;
-    ///
-    /// let a = array![1., -4.32, 1.14, 0.32];
-    /// let var = a.var(1.);
-    /// assert_abs_diff_eq!(var, 6.7331, epsilon = 1e-4);
-    /// ```
-    #[cfg(feature = "std")]
-    pub fn var(&self, ddof: A) -> A
-    where
-        A: Float + FromPrimitive,
-    {
-        let zero = A::from_usize(0).expect("Converting 0 to `A` must not fail.");
-        let n = A::from_usize(self.len()).expect("Converting length to `A` must not fail.");
-        assert!(
-            !(ddof < zero || ddof > n),
-            "`ddof` must not be less than zero or greater than the length of \
-             the axis",
-        );
-        let dof = n - ddof;
-        let mut mean = A::zero();
-        let mut sum_sq = A::zero();
-        let mut i = 0;
-        self.visit(|&x| {
-            let count = A::from_usize(i + 1).expect("Converting index to `A` must not fail.");
-            let delta = x - mean;
-            mean = mean + delta / count;
-            sum_sq = (x - mean).mul_add(delta, sum_sq);
-            i += 1;
-        });
-        sum_sq / dof
-    }
-
-    /// Return standard deviation of elements in the array.
-    ///
-    /// The standard deviation is computed from the variance using
-    /// the [Welford one-pass algorithm](https://www.jstor.org/stable/1266577).
-    ///
-    /// The parameter `ddof` specifies the "delta degrees of freedom". For
-    /// example, to calculate the population standard deviation, use `ddof = 0`,
-    /// or to calculate the sample standard deviation, use `ddof = 1`.
-    ///
-    /// The standard deviation is defined as:
-    ///
-    /// ```text
-    ///               ⎛    1       n          ⎞
-    /// stddev = sqrt ⎜ ――――――――   ∑ (xᵢ - x̅)²⎟
-    ///               ⎝ n - ddof  i=1         ⎠
-    /// ```
-    ///
-    /// where
-    ///
-    /// ```text
-    ///     1   n
-    /// x̅ = ―   ∑ xᵢ
-    ///     n  i=1
-    /// ```
-    ///
-    /// and `n` is the length of the array.
-    ///
-    /// **Panics** if `ddof` is less than zero or greater than `n`
-    ///
-    /// # Example
-    ///
-    /// ```
-    /// use ndarray::array;
-    /// use approx::assert_abs_diff_eq;
-    ///
-    /// let a = array![1., -4.32, 1.14, 0.32];
-    /// let stddev = a.std(1.);
-    /// assert_abs_diff_eq!(stddev, 2.59483, epsilon = 1e-4);
-    /// ```
-    #[cfg(feature = "std")]
-    pub fn std(&self, ddof: A) -> A
-    where
-        A: Float + FromPrimitive,
-    {
-        self.var(ddof).sqrt()
-    }
-
     /// Return sum along `axis`.
     ///
     /// ```
@@ -341,7 +227,6 @@ where
     /// let var = a.var_axis(Axis(0), 1.);
     /// assert_eq!(var, aview1(&[4., 4.]));
     /// ```
-    #[cfg(feature = "std")]
     pub fn var_axis(&self, axis: Axis, ddof: A) -> Array<A, D::Smaller>
     where
         A: Float + FromPrimitive,
@@ -410,7 +295,6 @@ where
     /// let stddev = a.std_axis(Axis(0), 1.);
     /// assert_eq!(stddev, aview1(&[2., 2.]));
     /// ```
-    #[cfg(feature = "std")]
     pub fn std_axis(&self, axis: Axis, ddof: A) -> Array<A, D::Smaller>
     where
         A: Float + FromPrimitive,
diff --git a/src/partial.rs b/src/partial.rs
index a8146aff..887e9382 100644
--- a/src/partial.rs
+++ b/src/partial.rs
@@ -81,7 +81,7 @@ impl<T> Drop for Partial<T> {
     fn drop(&mut self) {
         if !self.ptr.is_null() {
             unsafe {
-                ptr::drop_in_place(alloc::slice::from_raw_parts_mut(self.ptr, self.len));
+                ptr::drop_in_place(std::slice::from_raw_parts_mut(self.ptr, self.len));
             }
         }
     }
diff --git a/src/private.rs b/src/private.rs
index 552b3149..ea13164e 100644
--- a/src/private.rs
+++ b/src/private.rs
@@ -21,5 +21,5 @@ macro_rules! private_impl {
         fn __private__(&self) -> crate::private::PrivateMarker {
             crate::private::PrivateMarker
         }
-    };
+    }
 }
diff --git a/src/zip/mod.rs b/src/zip/mod.rs
index 3c61d811..835ae5d3 100644
--- a/src/zip/mod.rs
+++ b/src/zip/mod.rs
@@ -10,7 +10,6 @@
 mod zipmacro;
 
 use std::mem::MaybeUninit;
-use alloc::vec::Vec;
 
 use crate::imp_prelude::*;
 use crate::AssignElem;


Test patch:
diff --git a/blas-tests/Cargo.toml b/blas-tests/Cargo.toml
index 4d0792fd..da9aad98 100644
--- a/blas-tests/Cargo.toml
+++ b/blas-tests/Cargo.toml
@@ -10,7 +10,7 @@ test = false
 [dev-dependencies]
 approx = "0.4"
 ndarray = { path = "../", features = ["approx", "blas"] }
-blas-src = { version = "0.7.0", default-features = false, features = ["openblas"] }
+blas-src = { version = "0.6.1", default-features = false, features = ["openblas"] }
 openblas-src = { version = "0.9.0", default-features = false, features = ["cblas", "system"] }
 defmac = "0.2"
 num-traits = "0.2"
diff --git a/blas-tests/tests/oper.rs b/blas-tests/tests/oper.rs
index 2741123f..b7c2d769 100644
--- a/blas-tests/tests/oper.rs
+++ b/blas-tests/tests/oper.rs
@@ -173,7 +173,7 @@ where
     S2: Data<Elem = A>,
 {
     let ((m, _), k) = (lhs.dim(), rhs.dim());
-    reference_mat_mul(lhs, &rhs.to_owned().into_shape((k, 1)).unwrap())
+    reference_mat_mul(lhs, &rhs.as_standard_layout().into_shape((k, 1)).unwrap())
         .into_shape(m)
         .unwrap()
 }
@@ -186,7 +186,7 @@ where
     S2: Data<Elem = A>,
 {
     let (m, (_, n)) = (lhs.dim(), rhs.dim());
-    reference_mat_mul(&lhs.to_owned().into_shape((1, m)).unwrap(), rhs)
+    reference_mat_mul(&lhs.as_standard_layout().into_shape((1, m)).unwrap(), rhs)
         .into_shape(n)
         .unwrap()
 }
diff --git a/serialization-tests/Cargo.toml b/serialization-tests/Cargo.toml
index 973a688f..3aaad639 100644
--- a/serialization-tests/Cargo.toml
+++ b/serialization-tests/Cargo.toml
@@ -15,7 +15,6 @@ default = ["ron"]
 
 [dev-dependencies.serde]
 version = "1.0.100"
-default-features = false
 
 [dev-dependencies.serde_json]
 version = "1.0.40"
diff --git a/tests/array-construct.rs b/tests/array-construct.rs
index 4513dd45..738e3b1f 100644
--- a/tests/array-construct.rs
+++ b/tests/array-construct.rs
@@ -51,7 +51,6 @@ fn test_arcarray_thread_safe() {
 }
 
 #[test]
-#[cfg(feature = "std")]
 fn test_uninit() {
     unsafe {
         let mut a = Array::<f32, _>::uninitialized((3, 4).f());
diff --git a/tests/array.rs b/tests/array.rs
index c1976511..9523a8d2 100644
--- a/tests/array.rs
+++ b/tests/array.rs
@@ -67,7 +67,6 @@ fn arrayviewmut_shrink_lifetime<'a, 'b: 'a>(
 }
 
 #[test]
-#[cfg(feature = "std")]
 fn test_mat_mul() {
     // smoke test, a big matrix multiplication of uneven size
     let (n, m) = (45, 33);
@@ -610,7 +609,6 @@ fn test_cow_shrink() {
 }
 
 #[test]
-#[cfg(feature = "std")]
 fn test_sub() {
     let mat = ArcArray::linspace(0., 15., 16).reshape((2, 4, 2));
     let s1 = mat.index_axis(Axis(0), 0);
@@ -625,7 +623,6 @@ fn test_sub() {
 
 #[should_panic]
 #[test]
-#[cfg(feature = "std")]
 fn test_sub_oob_1() {
     let mat = ArcArray::linspace(0., 15., 16).reshape((2, 4, 2));
     mat.index_axis(Axis(0), 2);
@@ -1613,7 +1610,6 @@ fn scalar_ops() {
 }
 
 #[test]
-#[cfg(feature = "std")]
 fn split_at() {
     let mut a = arr2(&[[1., 2.], [3., 4.]]);
 
@@ -1665,7 +1661,6 @@ fn deny_split_at_index_out_of_bounds() {
 }
 
 #[test]
-#[cfg(feature = "std")]
 fn test_range() {
     let a = Array::range(0., 5., 1.);
     assert_eq!(a.len(), 5);
@@ -1722,10 +1717,18 @@ fn to_owned_memory_order() {
     // input.
     let c = arr2(&[[1, 2, 3], [4, 5, 6]]);
     let mut f = c.view();
+
+    // transposed array
     f.swap_axes(0, 1);
     let fo = f.to_owned();
     assert_eq!(f, fo);
     assert_eq!(f.strides(), fo.strides());
+
+    // negated stride axis
+    f.invert_axis(Axis(1));
+    let fo2 = f.to_owned();
+    assert_eq!(f, fo2);
+    assert_eq!(f.strides(), fo2.strides());
 }
 
 #[test]
@@ -1734,6 +1737,7 @@ fn to_owned_neg_stride() {
     c.slice_collapse(s![.., ..;-1]);
     let co = c.to_owned();
     assert_eq!(c, co);
+    assert_eq!(c.strides(), co.strides());
 }
 
 #[test]
@@ -1791,6 +1795,64 @@ fn test_contiguous() {
     assert!(b.as_slice_memory_order().is_some());
 }
 
+#[test]
+fn test_contiguous_neg_strides() {
+    let s = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
+    let mut a = ArrayView::from_shape((2, 3, 2).strides((1, 4, 2)), &s).unwrap();
+    assert_eq!(
+        a,
+        arr3(&[[[0, 2], [4, 6], [8, 10]], [[1, 3], [5, 7], [9, 11]]])
+    );
+    assert!(a.as_slice_memory_order().is_some());
+
+    let mut b = a.slice(s![..;1, ..;-1, ..;-1]);
+    assert_eq!(
+        b,
+        arr3(&[[[10, 8], [6, 4], [2, 0]], [[11, 9], [7, 5], [3, 1]]])
+    );
+    assert!(b.as_slice_memory_order().is_some());
+
+    b.swap_axes(1, 2);
+    assert_eq!(b, arr3(&[[[10, 6, 2], [8, 4, 0]], [[11, 7, 3], [9, 5, 1]]]));
+    assert!(b.as_slice_memory_order().is_some());
+
+    b.invert_axis(Axis(0));
+    assert_eq!(b, arr3(&[[[11, 7, 3], [9, 5, 1]], [[10, 6, 2], [8, 4, 0]]]));
+    assert!(b.as_slice_memory_order().is_some());
+
+    let mut c = b.reversed_axes();
+    assert_eq!(
+        c,
+        arr3(&[[[11, 10], [9, 8]], [[7, 6], [5, 4]], [[3, 2], [1, 0]]])
+    );
+    assert!(c.as_slice_memory_order().is_some());
+
+    c.merge_axes(Axis(1), Axis(2));
+    assert_eq!(c, arr3(&[[[11, 10, 9, 8]], [[7, 6, 5, 4]], [[3, 2, 1, 0]]]));
+    assert!(c.as_slice_memory_order().is_some());
+
+    let d = b.remove_axis(Axis(1));
+    assert_eq!(d, arr2(&[[11, 7, 3], [10, 6, 2]]));
+    assert!(d.as_slice_memory_order().is_none());
+
+    let e = b.remove_axis(Axis(2));
+    assert_eq!(e, arr2(&[[11, 9], [10, 8]]));
+    assert!(e.as_slice_memory_order().is_some());
+
+    let f = e.insert_axis(Axis(2));
+    assert_eq!(f, arr3(&[[[11], [9]], [[10], [8]]]));
+    assert!(f.as_slice_memory_order().is_some());
+
+    let mut g = b.clone();
+    g.collapse_axis(Axis(1), 0);
+    assert_eq!(g, arr3(&[[[11, 7, 3]], [[10, 6, 2]]]));
+    assert!(g.as_slice_memory_order().is_none());
+
+    b.collapse_axis(Axis(2), 0);
+    assert_eq!(b, arr3(&[[[11], [9]], [[10], [8]]]));
+    assert!(b.as_slice_memory_order().is_some());
+}
+
 #[test]
 fn test_swap() {
     let mut a = arr2(&[[1, 2, 3], [4, 5, 6], [7, 8, 9]]);
diff --git a/tests/broadcast.rs b/tests/broadcast.rs
index 5416e901..6840947b 100644
--- a/tests/broadcast.rs
+++ b/tests/broadcast.rs
@@ -1,7 +1,6 @@
 use ndarray::prelude::*;
 
 #[test]
-#[cfg(feature = "std")]
 fn broadcast_1() {
     let a_dim = Dim([2, 4, 2, 2]);
     let b_dim = Dim([2, 1, 2, 1]);
@@ -27,7 +26,6 @@ fn broadcast_1() {
 }
 
 #[test]
-#[cfg(feature = "std")]
 fn test_add() {
     let a_dim = Dim([2, 4, 2, 2]);
     let b_dim = Dim([2, 1, 2, 1]);
@@ -40,7 +38,6 @@ fn test_add() {
 
 #[test]
 #[should_panic]
-#[cfg(feature = "std")]
 fn test_add_incompat() {
     let a_dim = Dim([2, 4, 2, 2]);
     let mut a = ArcArray::linspace(0.0, 1., a_dim.size()).reshape(a_dim);
diff --git a/tests/dimension.rs b/tests/dimension.rs
index cfc122e4..86a7e5ed 100644
--- a/tests/dimension.rs
+++ b/tests/dimension.rs
@@ -118,19 +118,36 @@ fn fastest_varying_order() {
     let order = strides._fastest_varying_stride_order();
     assert_eq!(order.slice(), &[3, 0, 2, 1]);
 
+    let strides = Dim([-2isize as usize, 8, -4isize as usize, -1isize as usize]);
+    let order = strides._fastest_varying_stride_order();
+    assert_eq!(order.slice(), &[3, 0, 2, 1]);
+
     assert_eq!(Dim([1, 3])._fastest_varying_stride_order(), Dim([0, 1]));
+    assert_eq!(
+        Dim([1, -3isize as usize])._fastest_varying_stride_order(),
+        Dim([0, 1])
+    );
     assert_eq!(Dim([7, 2])._fastest_varying_stride_order(), Dim([1, 0]));
+    assert_eq!(
+        Dim([-7isize as usize, 2])._fastest_varying_stride_order(),
+        Dim([1, 0])
+    );
     assert_eq!(
         Dim([6, 1, 3])._fastest_varying_stride_order(),
         Dim([1, 2, 0])
     );
+    assert_eq!(
+        Dim([-6isize as usize, 1, -3isize as usize])._fastest_varying_stride_order(),
+        Dim([1, 2, 0])
+    );
 
     // it's important that it produces distinct indices. Prefer the stable order
     // where 0 is before 1 when they are equal.
     assert_eq!(Dim([2, 2])._fastest_varying_stride_order(), [0, 1]);
     assert_eq!(Dim([2, 2, 1])._fastest_varying_stride_order(), [2, 0, 1]);
     assert_eq!(
-        Dim([2, 2, 3, 1, 2])._fastest_varying_stride_order(),
+        Dim([-2isize as usize, -2isize as usize, 3, 1, -2isize as usize])
+            ._fastest_varying_stride_order(),
         [3, 0, 1, 4, 2]
     );
 }
@@ -287,7 +304,6 @@ fn test_array_view() {
 }
 
 #[test]
-#[cfg(feature = "std")]
 #[allow(clippy::cognitive_complexity)]
 fn test_all_ndindex() {
     macro_rules! ndindex {
diff --git a/tests/iterator_chunks.rs b/tests/iterator_chunks.rs
index 99555717..8ac88502 100644
--- a/tests/iterator_chunks.rs
+++ b/tests/iterator_chunks.rs
@@ -10,7 +10,6 @@ use ndarray::prelude::*;
 use ndarray::NdProducer;
 
 #[test]
-#[cfg(feature = "std")]
 fn chunks() {
     let a = <Array1<f32>>::linspace(1., 100., 10 * 10)
         .into_shape((10, 10))
diff --git a/tests/iterators.rs b/tests/iterators.rs
index 7bb856ad..7a30003c 100644
--- a/tests/iterators.rs
+++ b/tests/iterators.rs
@@ -28,7 +28,6 @@ macro_rules! assert_panics {
 }
 
 #[test]
-#[cfg(feature = "std")]
 fn double_ended() {
     let a = ArcArray::linspace(0., 7., 8);
     let mut it = a.iter().cloned();
@@ -59,7 +58,6 @@ fn iter_size_hint() {
 }
 
 #[test]
-#[cfg(feature = "std")]
 fn indexed() {
     let a = ArcArray::linspace(0., 7., 8);
     for (i, elt) in a.indexed_iter() {
@@ -92,7 +90,6 @@ where
 }
 
 #[test]
-#[cfg(feature = "std")]
 fn as_slice() {
     let a = ArcArray::linspace(0., 7., 8);
     let a = a.reshape((2, 4, 1));
@@ -527,7 +524,6 @@ fn axis_iter_mut_zip_partially_consumed_discontiguous() {
 }
 
 #[test]
-#[cfg(feature = "std")]
 fn axis_chunks_iter_corner_cases() {
     // examples provided by @bluss in PR #65
     // these tests highlight corner cases of the axis_chunks_iter implementation
diff --git a/tests/ixdyn.rs b/tests/ixdyn.rs
index 3c96cd74..3d96967a 100644
--- a/tests/ixdyn.rs
+++ b/tests/ixdyn.rs
@@ -155,7 +155,6 @@ fn test_0_add_broad() {
 }
 
 #[test]
-#[cfg(feature = "std")]
 fn test_into_dimension() {
     let a = Array::linspace(0., 41., 6 * 7).into_shape((6, 7)).unwrap();
     let a2 = a.clone().into_shape(IxDyn(&[6, 7])).unwrap();
diff --git a/tests/numeric.rs b/tests/numeric.rs
index 15df5328..7c6f1441 100644
--- a/tests/numeric.rs
+++ b/tests/numeric.rs
@@ -64,82 +64,6 @@ fn sum_mean_empty() {
     assert_eq!(a, None);
 }
 
-#[test]
-#[cfg(feature = "std")]
-fn var() {
-    let a = array![1., -4.32, 1.14, 0.32];
-    assert_abs_diff_eq!(a.var(0.), 5.049875, epsilon = 1e-8);
-}
-
-#[test]
-#[cfg(feature = "std")]
-#[should_panic]
-fn var_negative_ddof() {
-    let a = array![1., 2., 3.];
-    a.var(-1.);
-}
-
-#[test]
-#[cfg(feature = "std")]
-#[should_panic]
-fn var_too_large_ddof() {
-    let a = array![1., 2., 3.];
-    a.var(4.);
-}
-
-#[test]
-#[cfg(feature = "std")]
-fn var_nan_ddof() {
-    let a = Array2::<f64>::zeros((2, 3));
-    let v = a.var(::std::f64::NAN);
-    assert!(v.is_nan());
-}
-
-#[test]
-#[cfg(feature = "std")]
-fn var_empty_arr() {
-    let a: Array1<f64> = array![];
-    assert!(a.var(0.0).is_nan());
-}
-
-#[test]
-#[cfg(feature = "std")]
-fn std() {
-    let a = array![1., -4.32, 1.14, 0.32];
-    assert_abs_diff_eq!(a.std(0.), 2.24719, epsilon = 1e-5);
-}
-
-#[test]
-#[cfg(feature = "std")]
-#[should_panic]
-fn std_negative_ddof() {
-    let a = array![1., 2., 3.];
-    a.std(-1.);
-}
-
-#[test]
-#[cfg(feature = "std")]
-#[should_panic]
-fn std_too_large_ddof() {
-    let a = array![1., 2., 3.];
-    a.std(4.);
-}
-
-#[test]
-#[cfg(feature = "std")]
-fn std_nan_ddof() {
-    let a = Array2::<f64>::zeros((2, 3));
-    let v = a.std(::std::f64::NAN);
-    assert!(v.is_nan());
-}
-
-#[test]
-#[cfg(feature = "std")]
-fn std_empty_arr() {
-    let a: Array1<f64> = array![];
-    assert!(a.std(0.0).is_nan());
-}
-
 #[test]
 #[cfg(feature = "approx")]
 fn var_axis() {
@@ -259,7 +183,6 @@ fn std_axis() {
 
 #[test]
 #[should_panic]
-#[cfg(feature = "std")]
 fn var_axis_negative_ddof() {
     let a = array![1., 2., 3.];
     a.var_axis(Axis(0), -1.);
@@ -267,14 +190,12 @@ fn var_axis_negative_ddof() {
 
 #[test]
 #[should_panic]
-#[cfg(feature = "std")]
 fn var_axis_too_large_ddof() {
     let a = array![1., 2., 3.];
     a.var_axis(Axis(0), 4.);
 }
 
 #[test]
-#[cfg(feature = "std")]
 fn var_axis_nan_ddof() {
     let a = Array2::<f64>::zeros((2, 3));
     let v = a.var_axis(Axis(1), ::std::f64::NAN);
@@ -283,7 +204,6 @@ fn var_axis_nan_ddof() {
 }
 
 #[test]
-#[cfg(feature = "std")]
 fn var_axis_empty_axis() {
     let a = Array2::<f64>::zeros((2, 0));
     let v = a.var_axis(Axis(1), 0.);
@@ -293,14 +213,12 @@ fn var_axis_empty_axis() {
 
 #[test]
 #[should_panic]
-#[cfg(feature = "std")]
 fn std_axis_bad_dof() {
     let a = array![1., 2., 3.];
     a.std_axis(Axis(0), 4.);
 }
 
 #[test]
-#[cfg(feature = "std")]
 fn std_axis_empty_axis() {
     let a = Array2::<f64>::zeros((2, 0));
     let v = a.std_axis(Axis(1), 0.);
diff --git a/tests/oper.rs b/tests/oper.rs
index a3d17976..91205c49 100644
--- a/tests/oper.rs
+++ b/tests/oper.rs
@@ -4,7 +4,7 @@
     clippy::unreadable_literal,
     clippy::many_single_char_names
 )]
-#![cfg(feature = "std")]
+
 use ndarray::linalg::general_mat_mul;
 use ndarray::prelude::*;
 use ndarray::{rcarr1, rcarr2};
@@ -707,7 +707,7 @@ fn gen_mat_vec_mul() {
         S2: Data<Elem = A>,
     {
         let ((m, _), k) = (lhs.dim(), rhs.dim());
-        reference_mat_mul(lhs, &rhs.to_owned().into_shape((k, 1)).unwrap())
+        reference_mat_mul(lhs, &rhs.as_standard_layout().into_shape((k, 1)).unwrap())
             .into_shape(m)
             .unwrap()
     }
@@ -772,7 +772,7 @@ fn vec_mat_mul() {
         S2: Data<Elem = A>,
     {
         let (m, (_, n)) = (lhs.dim(), rhs.dim());
-        reference_mat_mul(&lhs.to_owned().into_shape((1, m)).unwrap(), rhs)
+        reference_mat_mul(&lhs.as_standard_layout().into_shape((1, m)).unwrap(), rhs)
             .into_shape(n)
             .unwrap()
     }
diff --git a/tests/raw_views.rs b/tests/raw_views.rs
index b63e4292..b955cac6 100644
--- a/tests/raw_views.rs
+++ b/tests/raw_views.rs
@@ -81,3 +81,18 @@ fn raw_view_deref_into_view_misaligned() {
     let data: [u16; 2] = [0x0011, 0x2233];
     misaligned_deref(&data);
 }
+
+#[test]
+#[cfg(debug_assertions)]
+#[should_panic = "Unsupported"]
+fn raw_view_negative_strides() {
+    fn misaligned_deref(data: &[u16; 2]) -> ArrayView1<'_, u16> {
+        let ptr: *const u16 = data.as_ptr();
+        unsafe {
+            let raw_view = RawArrayView::from_shape_ptr(1.strides((-1isize) as usize), ptr);
+            raw_view.deref_into_view()
+        }
+    }
+    let data: [u16; 2] = [0x0011, 0x2233];
+    misaligned_deref(&data);
+}
diff --git a/tests/views.rs b/tests/views.rs
deleted file mode 100644
index 216e5540..00000000
--- a/tests/views.rs
+++ /dev/null
@@ -1,16 +0,0 @@
-use ndarray::prelude::*;
-use ndarray::Zip;
-
-#[test]
-fn cell_view() {
-    let mut a = Array::from_shape_fn((10, 5), |(i, j)| (i * j) as f32);
-    let answer = &a + 1.;
-
-    {
-        let cv1 = a.cell_view();
-        let cv2 = cv1;
-
-        Zip::from(cv1).and(cv2).apply(|a, b| a.set(b.get() + 1.));
-    }
-    assert_eq!(a, answer);
-}