diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md
index 5b6155b70..f0a9fa284 100644
--- a/.claude/CLAUDE.md
+++ b/.claude/CLAUDE.md
@@ -241,7 +241,9 @@ Tested against NumPy 2.x.
`arange`, `array`, `asanyarray`, `asarray`, `ascontiguousarray`, `asfortranarray`, `copy`, `empty`, `empty_like`, `eye`, `frombuffer`, `full`, `full_like`, `identity`, `linspace`, `meshgrid`, `mgrid`, `ones`, `ones_like`, `zeros`, `zeros_like`
### Shape Manipulation
-`append`, `array_split`, `atleast_1d`, `atleast_2d`, `atleast_3d`, `concatenate`, `delete`, `dsplit`, `dstack`, `expand_dims`, `flatten`, `hsplit`, `hstack`, `insert`, `moveaxis`, `pad`, `ravel`, `repeat`, `reshape`, `roll`, `rollaxis`, `split`, `squeeze`, `stack`, `swapaxes`, `tile`, `transpose`, `unique`, `vsplit`, `vstack`
+`append`, `array_split`, `atleast_1d`, `atleast_2d`, `atleast_3d`, `concatenate`, `delete`, `dsplit`, `dstack`, `expand_dims`, `flatten`, `hsplit`, `hstack`, `insert`, `moveaxis`, `pad`, `ravel`, `repeat`, `reshape`, `resize`, `roll`, `rollaxis`, `split`, `squeeze`, `stack`, `swapaxes`, `tile`, `transpose`, `unique`, `vsplit`, `vstack`
+
+`resize` ships as both `np.resize(a, new_shape)` (function — fills the enlarged output with **repeated copies** of `a` in C-order via an exact-sized doubling byte-tile; empty source / zero new-size → `zeros`; always C-contiguous; any input layout is raveled first) and `ndarray.resize(new_shape, refcheck=true)` (**in-place** — grows with **zeros**, shrinks by truncation, operates on the raw contiguous buffer so an F-contiguous resize relabels memory column-major). The method mirrors NumPy's guards verbatim (`IncorrectShapeException`): single-segment only, and when the byte size changes it must own its data (not a view) and — under `refcheck` — not be shared (`IArraySlice.IsUniquelyReferenced`, backed by the ARC block refcount); `refcheck:false` bypasses. Same-size resize is a pure in-place reshape (no ownership/reference check). See `Manipulation/np.resize.cs`, `Manipulation/NDArray.resize.cs`.
### Broadcasting
`are_broadcastable`, `broadcast`, `broadcast_arrays`, `broadcast_to`
diff --git a/src/NumSharp.Core/Backends/Unmanaged/ArraySlice`1.cs b/src/NumSharp.Core/Backends/Unmanaged/ArraySlice`1.cs
index d28dd3077..46dba5505 100644
--- a/src/NumSharp.Core/Backends/Unmanaged/ArraySlice`1.cs
+++ b/src/NumSharp.Core/Backends/Unmanaged/ArraySlice`1.cs
@@ -587,6 +587,8 @@ public void DangerousFree()
public bool IsReleased => MemoryBlock.IsReleased;
+ public bool IsUniquelyReferenced => MemoryBlock.IsUniquelyReferenced;
+
///
/// Copies the contents of this span into a new array. This heap
diff --git a/src/NumSharp.Core/Backends/Unmanaged/Interfaces/IArraySlice.cs b/src/NumSharp.Core/Backends/Unmanaged/Interfaces/IArraySlice.cs
index 5cdc7f66e..4cc029229 100644
--- a/src/NumSharp.Core/Backends/Unmanaged/Interfaces/IArraySlice.cs
+++ b/src/NumSharp.Core/Backends/Unmanaged/Interfaces/IArraySlice.cs
@@ -99,5 +99,15 @@ public interface IArraySlice : IMemoryBlock, ICloneable, IEnumerable
/// Diagnostic: true once the underlying buffer has been freed.
///
bool IsReleased { get; }
+
+ ///
+ /// true when the underlying is held by
+ /// at most one logical reference (this owner), i.e. no other
+ /// / view shares its buffer. Non-owning wraps
+ /// (external / pinned memory) report true since their refcount is
+ /// immortal and meaningless. Used by ndarray.resize's refcheck to
+ /// mirror NumPy's "references or is referenced by another array" guard.
+ ///
+ bool IsUniquelyReferenced { get; }
}
}
diff --git a/src/NumSharp.Core/Backends/Unmanaged/UnmanagedMemoryBlock`1.cs b/src/NumSharp.Core/Backends/Unmanaged/UnmanagedMemoryBlock`1.cs
index 59da716de..9745a50cd 100644
--- a/src/NumSharp.Core/Backends/Unmanaged/UnmanagedMemoryBlock`1.cs
+++ b/src/NumSharp.Core/Backends/Unmanaged/UnmanagedMemoryBlock`1.cs
@@ -876,6 +876,12 @@ public void Free()
public bool IsReleased => _disposer.IsReleased;
+ ///
+ /// true when this block is held by at most one logical reference.
+ /// Forwards to the inner .
+ ///
+ public bool IsUniquelyReferenced => _disposer.IsUniquelyReferenced;
+
[MethodImpl(OptimizeAndInline)]
public IEnumerator GetEnumerator()
{
@@ -1300,6 +1306,14 @@ public void Release()
/// Diagnostic: true once the buffer has been freed.
public bool IsReleased => Volatile.Read(ref _freed) != 0;
+ ///
+ /// true when at most one logical reference is held (refcount <= 1).
+ /// Non-owning wraps are immortal (refcount is meaningless) and report
+ /// true. Used by resize's refcheck to detect buffer sharing.
+ ///
+ public bool IsUniquelyReferenced =>
+ _type == AllocationType.Wrap || Interlocked.Read(ref _refCount) <= 1;
+
///
/// Backwards-compatible "force free" entry point. Maps to the
/// finalizer's behavior: drops any remaining refs and frees.
diff --git a/src/NumSharp.Core/Manipulation/NDArray.resize.cs b/src/NumSharp.Core/Manipulation/NDArray.resize.cs
new file mode 100644
index 000000000..fc5b16365
--- /dev/null
+++ b/src/NumSharp.Core/Manipulation/NDArray.resize.cs
@@ -0,0 +1,163 @@
+using System;
+using System.Runtime.InteropServices;
+using NumSharp.Backends;
+
+namespace NumSharp
+{
+ public partial class NDArray
+ {
+ ///
+ /// Change shape and size of this array in-place.
+ ///
+ /// If the new array is larger than the original array, the new array is filled with
+ /// zeros (note: this differs from which
+ /// fills with repeated copies). If smaller, the data is truncated (in C-order for
+ /// C-contiguous arrays, memory-order for F-contiguous ones).
+ ///
+ /// Multi-argument form: a.resize(2, 3). A no-argument call a.resize() is a
+ /// no-op (matches NumPy's a.resize() / a.resize(None)).
+ ///
+ /// Shape of resized array (one value per dimension).
+ /// https://numpy.org/doc/stable/reference/generated/numpy.ndarray.resize.html
+ ///
+ /// If this array is not single-segment (contiguous); if growing/shrinking an array that
+ /// does not own its data or is referenced by another array; or if a dimension is negative.
+ ///
+ public void resize(params long[] new_shape)
+ {
+ // NumPy: a.resize() and a.resize(None) return None (no-op). C# collapses both to an
+ // empty/null params array. Explicit scalar (a.resize(())) is reachable via the Shape
+ // overload with a 0-d shape.
+ if (new_shape == null || new_shape.Length == 0)
+ return;
+
+ resize(new Shape(new_shape), refcheck: true);
+ }
+
+ ///
+ /// Change shape and size of this array in-place.
+ /// Primary overload — see for the fill/truncate semantics.
+ ///
+ /// Shape of resized array. A 0-d shape resizes to a scalar.
+ ///
+ /// If true (default), reference counting is used to check that this array's buffer
+ /// is not shared with another array before resizing (when the total size changes).
+ /// Set to false to skip that check.
+ ///
+ /// https://numpy.org/doc/stable/reference/generated/numpy.ndarray.resize.html
+ ///
+ /// If this array is not single-segment (contiguous); if growing/shrinking an array that
+ /// does not own its data or (with ) is referenced by another
+ /// array; or if a dimension is negative.
+ ///
+ public unsafe void resize(Shape new_shape, bool refcheck = true)
+ {
+ var shape = this.Shape;
+
+ // 1. Single-segment (contiguous) requirement — always enforced first, exactly like
+ // NumPy's PyArray_ISONESEGMENT gate. 0-d arrays are trivially single-segment.
+ if (shape.NDim != 0 && !shape.IsContiguous && !shape.IsFContiguous)
+ throw new IncorrectShapeException("resize only works on single-segment arrays");
+
+ // 2. Validate dimensions and compute the new element count. NumPy stops at the first
+ // zero dim (so a negative dim following a zero is NOT reported) and rejects negatives
+ // otherwise, with this exact message (distinct from np.resize's wording).
+ var newDims = new_shape.dimensions ?? System.Array.Empty();
+ long newSize = 1;
+ for (int i = 0; i < newDims.Length; i++)
+ {
+ if (newDims[i] == 0) { newSize = 0; break; }
+ if (newDims[i] < 0)
+ throw new IncorrectShapeException("negative dimensions not allowed");
+ newSize *= newDims[i];
+ }
+
+ long itemsize = this.dtypesize;
+ long oldSize = this.size;
+ long oldBytes = oldSize * itemsize;
+ long newBytes = newSize * itemsize;
+
+ // The physical memory layout to preserve: F only when strictly F-contiguous (and not C),
+ // matching NumPy's _array_fill_strides which keys off the array's current flags. 1-D and
+ // 0-d arrays are C.
+ char order = (shape.IsFContiguous && !shape.IsContiguous) ? 'F' : 'C';
+
+ if (oldBytes != newBytes)
+ {
+ // 3. Reallocation is required. Ownership and reference checks fire ONLY here (a
+ // same-size resize is a pure reshape and skips them, exactly like NumPy).
+
+ // a. Must own its data (not a slice/reshape/transpose view).
+ if (this.Storage.IsView)
+ throw new IncorrectShapeException("cannot resize this array: it does not own its data");
+
+ // b. Must not be shared with another array (unless the caller opts out via refcheck).
+ if (refcheck && !this.Storage.InternalArray.IsUniquelyReferenced)
+ throw new IncorrectShapeException(
+ "cannot resize an array that references or is referenced\n" +
+ "by another array in this way.\n" +
+ "Use the np.resize function or refcheck=False");
+
+ // Allocate the fresh buffer (uninitialized), copy the surviving prefix of the raw
+ // memory (dtype-agnostic — resize operates on the contiguous byte buffer, which is
+ // why an F-contiguous grow re-labels the same bytes with the new column-major
+ // strides), then zero any grown tail.
+ var freshStrides = order == 'F' ? FortranStrides(newDims) : ContiguousStrides(newDims);
+ var fresh = new NDArray(this.typecode, new Shape(newDims, freshStrides), fillZeros: false);
+
+ byte* src = this.Storage.Address + shape.offset * itemsize;
+ byte* dst = fresh.Storage.Address;
+ long copyBytes = Math.Min(oldBytes, newBytes);
+ if (copyBytes > 0)
+ Buffer.MemoryCopy(src, dst, newBytes, copyBytes);
+ if (newBytes > copyBytes)
+ NativeMemory.Clear(dst + copyBytes, (nuint)(newBytes - copyBytes));
+
+ // ARC-correct in-place swap: take a reference on the fresh buffer for `this`, drop
+ // `this`'s reference to the old buffer (freed here when uniquely owned; kept alive
+ // for any other sharer under refcheck:false), then discard the fresh wrapper so the
+ // net effect is `this` solely owning the new buffer.
+ var newStorage = fresh.Storage;
+ newStorage.InternalArray.TryAddRef();
+ this.Storage.InternalArray?.Release();
+ this.Storage = newStorage;
+ fresh.Dispose();
+ }
+ else
+ {
+ // 4. Same total byte size → reshape in place. Preserve the physical order and any
+ // view offset/buffer, so a same-size resize of an F-contiguous array (or a
+ // single-segment slice) relabels the existing memory just as NumPy does.
+ var strides = order == 'F' ? FortranStrides(newDims) : ContiguousStrides(newDims);
+ var target = new Shape(newDims, strides, shape.offset, shape.bufferSize);
+ this.Storage.SetShapeUnsafe(ref target);
+ }
+ }
+
+ /// Row-major (C-order) strides for .
+ private static long[] ContiguousStrides(long[] dims)
+ {
+ var strides = new long[dims.Length];
+ long acc = 1;
+ for (int i = dims.Length - 1; i >= 0; i--)
+ {
+ strides[i] = acc;
+ acc *= dims[i];
+ }
+ return strides;
+ }
+
+ /// Column-major (F-order) strides for .
+ private static long[] FortranStrides(long[] dims)
+ {
+ var strides = new long[dims.Length];
+ long acc = 1;
+ for (int i = 0; i < dims.Length; i++)
+ {
+ strides[i] = acc;
+ acc *= dims[i];
+ }
+ return strides;
+ }
+ }
+}
diff --git a/src/NumSharp.Core/Manipulation/np.resize.cs b/src/NumSharp.Core/Manipulation/np.resize.cs
new file mode 100644
index 000000000..cdfb36aae
--- /dev/null
+++ b/src/NumSharp.Core/Manipulation/np.resize.cs
@@ -0,0 +1,93 @@
+using System;
+using NumSharp.Backends;
+
+namespace NumSharp
+{
+ public static partial class np
+ {
+ ///
+ /// Return a new array with the specified shape.
+ ///
+ /// If the new array is larger than the original array, then the new array is filled
+ /// with repeated copies of (iterating over
+ /// in C-order, cycling back from the start). Note that this behavior is different from
+ /// which fills with zeros instead.
+ ///
+ ///
+ /// NumPy's np.resize takes new_shape as a single argument (an int or a
+ /// sequence) — the multi-argument form np.resize(a, 2, 3) is a NumPy TypeError,
+ /// so it is not offered here. Pass the shape as an int, a value tuple, an array, or a
+ /// ; all resolve through 's implicit conversions:
+ /// np.resize(a, 6), np.resize(a, (2, 3)), np.resize(a, new[]{2, 3}),
+ /// np.resize(a, new Shape(2, 3)).
+ ///
+ ///
+ /// Array to be resized.
+ /// Shape of resized array (int / tuple / array / ).
+ ///
+ /// The new array is formed from the data in the old array, repeated if necessary to
+ /// fill out the required number of elements. The data are repeated iterating over the
+ /// array in C-order. Result is C-contiguous; dtype matches .
+ ///
+ /// https://numpy.org/doc/stable/reference/generated/numpy.resize.html
+ /// If is null.
+ /// If any element of is negative.
+ public static unsafe NDArray resize(NDArray a, Shape new_shape)
+ {
+ if (a is null) throw new ArgumentNullException(nameof(a));
+
+ // Validate dimensions and compute the target element count. NumPy's np.resize
+ // rejects negative dims with this exact message (distinct from ndarray.resize's
+ // "negative dimensions not allowed").
+ long newSize = 1;
+ var newDims = new_shape.dimensions ?? Array.Empty();
+ for (int i = 0; i < newDims.Length; i++)
+ {
+ if (newDims[i] < 0)
+ throw new ArgumentException("all elements of `new_shape` must be non-negative");
+ newSize *= newDims[i];
+ }
+
+ // The result is always C-contiguous (NumPy reshapes the concatenated 1-D read-out),
+ // so normalize to C-order dims — independent of any strides carried by new_shape.
+ var outShape = new Shape(newDims);
+
+ // Flatten to a 1-D C-order read-out. ravel returns a view when a is already
+ // contiguous, else a fresh contiguous copy — either way a dense run of `a.size`
+ // elements starting at flat.Shape.offset. Disposed after the tiling copy.
+ using var flat = ravel(a);
+ long srcSize = flat.size;
+
+ // First case must zero-fill (empty source has nothing to repeat); the second
+ // would repeat zero times. NumPy: np.zeros_like(a, shape=new_shape).
+ if (srcSize == 0 || newSize == 0)
+ return zeros(outShape, a.typecode);
+
+ // Allocate the exact-sized C-contiguous output and tile the source bytes into it.
+ // This beats NumPy's concatenate((a,)*repeats)[:new_size] which over-allocates a
+ // repeats*size buffer then slices — we allocate exactly new_size and fill by
+ // doubling block copies (dtype-agnostic raw memory, O(new_size) bytes,
+ // O(log(new_size/size)) memcpy calls).
+ var result = new NDArray(a.typecode, outShape, fillZeros: false);
+
+ long itemsize = a.dtypesize;
+ byte* src = flat.Storage.Address + (long)flat.Shape.offset * itemsize;
+ byte* dst = result.Storage.Address;
+ long totalBytes = newSize * itemsize;
+ long srcBytes = srcSize * itemsize;
+
+ // Seed with the source (truncated when shrinking), then repeatedly double the
+ // filled region from dst onto itself until the whole output is covered.
+ long filled = Math.Min(srcBytes, totalBytes);
+ Buffer.MemoryCopy(src, dst, totalBytes, filled);
+ while (filled < totalBytes)
+ {
+ long chunk = Math.Min(filled, totalBytes - filled);
+ Buffer.MemoryCopy(dst, dst + filled, totalBytes - filled, chunk);
+ filled += chunk;
+ }
+
+ return result;
+ }
+ }
+}
diff --git a/test/NumSharp.UnitTest/Manipulation/np.resize.Test.cs b/test/NumSharp.UnitTest/Manipulation/np.resize.Test.cs
new file mode 100644
index 000000000..297b03091
--- /dev/null
+++ b/test/NumSharp.UnitTest/Manipulation/np.resize.Test.cs
@@ -0,0 +1,438 @@
+using System;
+using System.Numerics;
+
+namespace NumSharp.UnitTest.Manipulation
+{
+ ///
+ /// Battle tests for np.resize (function — repeats copies) and
+ /// ndarray.resize (in-place method — zero fills). All expected values
+ /// verified against NumPy 2.4.2.
+ ///
+ [TestClass]
+ public class ResizeTests
+ {
+ private static long[] L(NDArray a)
+ {
+ var r = new long[a.size];
+ for (int i = 0; i < a.size; i++) r[i] = Convert.ToInt64(a.GetAtIndex(i));
+ return r;
+ }
+
+ // ==================================================================
+ // np.resize (function) — fills with REPEATED copies of a (C-order)
+ // ==================================================================
+
+ [TestMethod]
+ public void Resize_Func_DocExample_2x3()
+ {
+ // np.resize([[0,1],[2,3]], (2,3)) -> [[0,1,2],[3,0,1]]
+ var a = np.array(new int[,] { { 0, 1 }, { 2, 3 } });
+ var r = np.resize(a, (2, 3));
+ r.shape.Should().Equal(2L, 3L);
+ L(r).Should().Equal(0, 1, 2, 3, 0, 1);
+ }
+
+ [TestMethod]
+ public void Resize_Func_DocExample_1x4()
+ {
+ var a = np.array(new int[,] { { 0, 1 }, { 2, 3 } });
+ var r = np.resize(a, (1, 4));
+ r.shape.Should().Equal(1L, 4L);
+ L(r).Should().Equal(0, 1, 2, 3);
+ }
+
+ [TestMethod]
+ public void Resize_Func_DocExample_2x4_Repeats()
+ {
+ var a = np.array(new int[,] { { 0, 1 }, { 2, 3 } });
+ var r = np.resize(a, (2, 4));
+ r.shape.Should().Equal(2L, 4L);
+ L(r).Should().Equal(0, 1, 2, 3, 0, 1, 2, 3);
+ }
+
+ [TestMethod]
+ public void Resize_Func_IntShape_Truncates()
+ {
+ var a = np.array(new int[,] { { 0, 1 }, { 2, 3 } });
+ var r = np.resize(a, 3);
+ r.shape.Should().Equal(3L);
+ L(r).Should().Equal(0, 1, 2);
+ }
+
+ [TestMethod]
+ public void Resize_Func_Grow_Tiles()
+ {
+ // np.resize([1,2,3], (3,3)) -> rows all [1,2,3]
+ var r = np.resize(np.array(new[] { 1, 2, 3 }), (3, 3));
+ r.shape.Should().Equal(3L, 3L);
+ L(r).Should().Equal(1, 2, 3, 1, 2, 3, 1, 2, 3);
+ }
+
+ [TestMethod]
+ public void Resize_Func_NonMultiple_Tiles()
+ {
+ // np.resize(arange(7), (11,)) cycles: 0..6,0..3
+ var r = np.resize(np.arange(7), 11);
+ r.shape.Should().Equal(11L);
+ L(r).Should().Equal(0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3);
+ }
+
+ [TestMethod]
+ public void Resize_Func_ScalarInput_Repeats()
+ {
+ var r = np.resize(np.array(5), (2, 2));
+ r.shape.Should().Equal(2L, 2L);
+ L(r).Should().Equal(5, 5, 5, 5);
+ }
+
+ [TestMethod]
+ public void Resize_Func_EmptySource_ZeroFills()
+ {
+ // np.resize([], (2,2)) -> zeros
+ var r = np.resize(np.array(new int[] { }), (2, 2));
+ r.shape.Should().Equal(2L, 2L);
+ L(r).Should().Equal(0, 0, 0, 0);
+ }
+
+ [TestMethod]
+ public void Resize_Func_NewSizeZero_ReturnsEmptyShaped()
+ {
+ var r = np.resize(np.array(new[] { 1, 2, 3 }), 0);
+ r.shape.Should().Equal(0L);
+ r.size.Should().Be(0);
+
+ var r2 = np.resize(np.array(new[] { 1, 2, 3 }), (2, 0, 3));
+ r2.shape.Should().Equal(2L, 0L, 3L);
+ r2.size.Should().Be(0);
+ }
+
+ [TestMethod]
+ public void Resize_Func_EmptyShape_ReturnsScalarFirstElement()
+ {
+ // np.resize(a, ()) -> array(0) (0-d, first element)
+ var r = np.resize(np.arange(6), new Shape(new long[0]));
+ r.ndim.Should().Be(0);
+ Convert.ToInt64(r.GetAtIndex(0)).Should().Be(0);
+ }
+
+ [TestMethod]
+ public void Resize_Func_PreservesDtype()
+ {
+ var r = np.resize(np.array(new float[] { 1.5f, 2.5f }), (2, 3));
+ r.dtype.Should().Be(typeof(float));
+ r.shape.Should().Equal(2L, 3L);
+ Convert.ToDouble(r.GetAtIndex(0)).Should().Be(1.5);
+ Convert.ToDouble(r.GetAtIndex(5)).Should().Be(2.5);
+ }
+
+ [TestMethod]
+ public void Resize_Func_NegativeDimension_Throws()
+ {
+ Action act = () => np.resize(np.array(new[] { 1, 2, 3 }), (-1, 2));
+ act.Should().Throw().WithMessage("*non-negative*");
+ }
+
+ [TestMethod]
+ public void Resize_Func_NullArray_Throws()
+ {
+ Action act = () => np.resize((NDArray)null, (2, 2));
+ act.Should().Throw();
+ }
+
+ // ---- np.resize on non-contiguous inputs (ravel materializes C-order) ----
+
+ [TestMethod]
+ public void Resize_Func_Transposed_ReadsCOrder()
+ {
+ // arange(6).reshape(2,3).T ravels C-order to [0,3,1,4,2,5]
+ var r = np.resize(np.arange(6).reshape(2, 3).T, (2, 4));
+ r.shape.Should().Equal(2L, 4L);
+ L(r).Should().Equal(0, 3, 1, 4, 2, 5, 0, 3);
+ }
+
+ [TestMethod]
+ public void Resize_Func_Strided()
+ {
+ var r = np.resize(np.arange(20)["::2"], (2, 3));
+ r.shape.Should().Equal(2L, 3L);
+ L(r).Should().Equal(0, 2, 4, 6, 8, 10);
+ }
+
+ [TestMethod]
+ public void Resize_Func_NegativeStride()
+ {
+ var r = np.resize(np.arange(6)["::-1"], (2, 4));
+ r.shape.Should().Equal(2L, 4L);
+ L(r).Should().Equal(5, 4, 3, 2, 1, 0, 5, 4);
+ }
+
+ [TestMethod]
+ public void Resize_Func_Broadcast()
+ {
+ var r = np.resize(np.broadcast_to(np.arange(3), new Shape(2, 3)), (2, 5));
+ r.shape.Should().Equal(2L, 5L);
+ L(r).Should().Equal(0, 1, 2, 0, 1, 2, 0, 1, 2, 0);
+ }
+
+ [TestMethod]
+ public void Resize_Func_DoesNotMutateSource()
+ {
+ var a = np.arange(4);
+ var r = np.resize(a, (2, 4));
+ a.shape.Should().Equal(4L);
+ L(a).Should().Equal(0, 1, 2, 3);
+ }
+
+ // ==================================================================
+ // ndarray.resize (in-place) — fills with ZEROS when growing
+ // ==================================================================
+
+ [TestMethod]
+ public void Resize_Method_Grow_ZeroFills()
+ {
+ var a = np.array(new[] { 1, 2, 3 });
+ a.resize(2, 3);
+ a.shape.Should().Equal(2L, 3L);
+ L(a).Should().Equal(1, 2, 3, 0, 0, 0);
+ }
+
+ [TestMethod]
+ public void Resize_Method_Shrink_Truncates()
+ {
+ var a = np.arange(1, 7);
+ a.resize(2, 2);
+ a.shape.Should().Equal(2L, 2L);
+ L(a).Should().Equal(1, 2, 3, 4);
+ }
+
+ [TestMethod]
+ public void Resize_Method_SameSize_Reshapes()
+ {
+ var a = np.arange(6);
+ a.resize(2, 3);
+ a.shape.Should().Equal(2L, 3L);
+ L(a).Should().Equal(0, 1, 2, 3, 4, 5);
+ }
+
+ [TestMethod]
+ public void Resize_Method_Grow_3to4()
+ {
+ var a = np.array(new[] { 1, 2, 3 });
+ a.resize(2, 2);
+ a.shape.Should().Equal(2L, 2L);
+ L(a).Should().Equal(1, 2, 3, 0);
+ }
+
+ [TestMethod]
+ public void Resize_Method_ScalarInput_ZeroFills()
+ {
+ var a = np.array(5);
+ a.resize(2, 2);
+ a.shape.Should().Equal(2L, 2L);
+ L(a).Should().Equal(5, 0, 0, 0);
+ }
+
+ [TestMethod]
+ public void Resize_Method_To3D()
+ {
+ var a = np.array(new[] { 1, 2, 3 });
+ a.resize(2, 2, 2);
+ a.shape.Should().Equal(2L, 2L, 2L);
+ L(a).Should().Equal(1, 2, 3, 0, 0, 0, 0, 0);
+ }
+
+ [TestMethod]
+ public void Resize_Method_ToZero()
+ {
+ var a = np.array(new[] { 1, 2, 3 });
+ a.resize(0);
+ a.shape.Should().Equal(0L);
+ a.size.Should().Be(0);
+ }
+
+ [TestMethod]
+ public void Resize_Method_NoArgs_IsNoOp()
+ {
+ var a = np.array(new[] { 1, 2, 3 });
+ a.resize();
+ a.shape.Should().Equal(3L);
+ L(a).Should().Equal(1, 2, 3);
+ }
+
+ [TestMethod]
+ public void Resize_Method_GrowLarge_TailIsZero()
+ {
+ var a = np.arange(10);
+ a.resize(100);
+ a.size.Should().Be(100);
+ for (int i = 0; i < 10; i++) Convert.ToInt64(a.GetAtIndex(i)).Should().Be(i);
+ for (int i = 10; i < 100; i++) Convert.ToInt64(a.GetAtIndex(i)).Should().Be(0);
+ }
+
+ [TestMethod]
+ public void Resize_Method_ReturnsNothing_MutatesInPlace()
+ {
+ var a = np.arange(3);
+ var before = a; // same reference
+ a.resize(5);
+ ReferenceEquals(a, before).Should().BeTrue();
+ a.size.Should().Be(5);
+ }
+
+ // ---- F-contiguous: resize operates on the raw memory buffer ----
+
+ [TestMethod]
+ public void Resize_Method_FContiguous_Grow_RelabelsMemory()
+ {
+ // asfortranarray([[0,1,2],[3,4,5]]) memory = [0,3,1,4,2,5]; grow to (3,3)
+ // -> [0,3,1,4,2,5,0,0,0] read F-order -> [[0,4,0],[3,2,0],[1,5,0]]
+ var a = np.asfortranarray(np.arange(6).reshape(2, 3));
+ a.resize(3, 3);
+ a.Shape.IsFContiguous.Should().BeTrue();
+ // logical [[0,4,0],[3,2,0],[1,5,0]] read in C-order
+ L(a).Should().Equal(0, 4, 0, 3, 2, 0, 1, 5, 0);
+ }
+
+ [TestMethod]
+ public void Resize_Method_FContiguous_SameSize_RelabelsMemory()
+ {
+ // memory [0,3,1,4,2,5] relabeled to (3,2) F-order -> [[0,4],[3,2],[1,5]]
+ var a = np.asfortranarray(np.arange(6).reshape(2, 3));
+ a.resize(3, 2);
+ a.Shape.IsFContiguous.Should().BeTrue();
+ L(a).Should().Equal(0, 4, 3, 2, 1, 5);
+ }
+
+ // ==================================================================
+ // ndarray.resize guards (all match NumPy 2.4.2 ValueError messages)
+ // ==================================================================
+
+ [TestMethod]
+ public void Resize_Method_NonContiguous_Throws()
+ {
+ var nc = np.arange(12).reshape(3, 4)[":, ::2"]; // non-contiguous view
+ Action act = () => nc.resize(3, 2);
+ act.Should().Throw().WithMessage("*single-segment*");
+ }
+
+ [TestMethod]
+ public void Resize_Method_View_DoesNotOwnData_Throws()
+ {
+ var v = np.arange(6).reshape(2, 3); // reshape view
+ Action act = () => v.resize(3, 3); // size change on non-owning view
+ act.Should().Throw().WithMessage("*does not own its data*");
+ }
+
+ [TestMethod]
+ public void Resize_Method_Referenced_Throws()
+ {
+ var b = np.arange(6);
+ var view = b["::"]; // b now referenced by view
+ Action act = () => b.resize(3, 3);
+ act.Should().Throw().WithMessage("*references or is referenced*");
+ GC.KeepAlive(view);
+ }
+
+ [TestMethod]
+ public void Resize_Method_RefcheckFalse_Bypasses()
+ {
+ var b = np.arange(6);
+ var view = b["::"];
+ b.resize(new Shape(new long[] { 3, 3 }), refcheck: false);
+ b.size.Should().Be(9);
+ GC.KeepAlive(view);
+ }
+
+ [TestMethod]
+ public void Resize_Method_NegativeDimension_Throws()
+ {
+ var b = np.arange(6);
+ Action act = () => b.resize(-1, 2);
+ act.Should().Throw().WithMessage("*negative dimensions not allowed*");
+ }
+
+ [TestMethod]
+ public void Resize_Method_ZeroThenNegative_NoError()
+ {
+ // NumPy stops at the first zero dim, so (0,-1) is accepted.
+ var b = np.arange(6);
+ b.resize(new Shape(new long[] { 0, -1 }));
+ b.shape[0].Should().Be(0);
+ b.shape[1].Should().Be(-1);
+ }
+
+ [TestMethod]
+ public void Resize_Method_SameSizeView_AllowedNoOwnershipCheck()
+ {
+ // Same total size skips the ownership check — a view reshapes in place.
+ var v = np.arange(6).reshape(2, 3);
+ v.resize(3, 2);
+ v.shape.Should().Equal(3L, 2L);
+ L(v).Should().Equal(0, 1, 2, 3, 4, 5);
+ }
+
+ // ==================================================================
+ // Dtype coverage — all 15 NumSharp types
+ // ==================================================================
+
+ [TestMethod]
+ public void Resize_Func_AllDtypes_TileAndDtypePreserved()
+ {
+ AssertFuncDtype(np.array(new bool[] { true, false, true }), typeof(bool));
+ AssertFuncDtype(np.array(new byte[] { 1, 2, 3 }), typeof(byte));
+ AssertFuncDtype(np.array(new sbyte[] { 1, 2, 3 }), typeof(sbyte));
+ AssertFuncDtype(np.array(new short[] { 1, 2, 3 }), typeof(short));
+ AssertFuncDtype(np.array(new ushort[] { 1, 2, 3 }), typeof(ushort));
+ AssertFuncDtype(np.array(new int[] { 1, 2, 3 }), typeof(int));
+ AssertFuncDtype(np.array(new uint[] { 1, 2, 3 }), typeof(uint));
+ AssertFuncDtype(np.array(new long[] { 1, 2, 3 }), typeof(long));
+ AssertFuncDtype(np.array(new ulong[] { 1, 2, 3 }), typeof(ulong));
+ AssertFuncDtype(np.array(new char[] { 'a', 'b', 'c' }), typeof(char));
+ AssertFuncDtype(np.array(new Half[] { (Half)1, (Half)2, (Half)3 }), typeof(Half));
+ AssertFuncDtype(np.array(new float[] { 1, 2, 3 }), typeof(float));
+ AssertFuncDtype(np.array(new double[] { 1, 2, 3 }), typeof(double));
+ AssertFuncDtype(np.array(new decimal[] { 1, 2, 3 }), typeof(decimal));
+ AssertFuncDtype(np.array(new Complex[] { 1, 2, 3 }), typeof(Complex));
+ }
+
+ [TestMethod]
+ public void Resize_Method_AllDtypes_GrowZeroFillAndDtypePreserved()
+ {
+ AssertMethodDtype(() => np.array(new bool[] { true, false, true }), typeof(bool));
+ AssertMethodDtype(() => np.array(new byte[] { 1, 2, 3 }), typeof(byte));
+ AssertMethodDtype(() => np.array(new sbyte[] { 1, 2, 3 }), typeof(sbyte));
+ AssertMethodDtype(() => np.array(new short[] { 1, 2, 3 }), typeof(short));
+ AssertMethodDtype(() => np.array(new ushort[] { 1, 2, 3 }), typeof(ushort));
+ AssertMethodDtype(() => np.array(new int[] { 1, 2, 3 }), typeof(int));
+ AssertMethodDtype(() => np.array(new uint[] { 1, 2, 3 }), typeof(uint));
+ AssertMethodDtype(() => np.array(new long[] { 1, 2, 3 }), typeof(long));
+ AssertMethodDtype(() => np.array(new ulong[] { 1, 2, 3 }), typeof(ulong));
+ AssertMethodDtype(() => np.array(new char[] { 'a', 'b', 'c' }), typeof(char));
+ AssertMethodDtype(() => np.array(new Half[] { (Half)1, (Half)2, (Half)3 }), typeof(Half));
+ AssertMethodDtype(() => np.array(new float[] { 1, 2, 3 }), typeof(float));
+ AssertMethodDtype(() => np.array(new double[] { 1, 2, 3 }), typeof(double));
+ AssertMethodDtype(() => np.array(new decimal[] { 1, 2, 3 }), typeof(decimal));
+ AssertMethodDtype(() => np.array(new Complex[] { 1, 2, 3 }), typeof(Complex));
+ }
+
+ private static void AssertFuncDtype(NDArray src, Type dtype)
+ {
+ var r = np.resize(src, (2, 4)); // tile 3 -> 8
+ r.dtype.Should().Be(dtype);
+ r.shape.Should().Equal(2L, 4L);
+ // element i equals src[i % 3]
+ for (int i = 0; i < 8; i++)
+ r.GetAtIndex(i).Should().Be(src.GetAtIndex(i % 3));
+ }
+
+ private static void AssertMethodDtype(Func make, Type dtype)
+ {
+ var a = make();
+ var first = a.GetAtIndex(0);
+ a.resize(2, 4); // grow 3 -> 8, zero-filled tail
+ a.dtype.Should().Be(dtype);
+ a.shape.Should().Equal(2L, 4L);
+ a.GetAtIndex(0).Should().Be(first);
+ }
+ }
+}