diff --git a/README.md b/README.md index 0b96953..9a5a7ce 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ Current snapshot date: 2026-04-04. - 2D ndarray support with matrix validation. - Creation helpers: `array`, `zeros`, `arange`. - Addition operations: `add` and `addInPlace` with strict shape checks. +- Optional extensions: scalar addition and `sum()` reduction. - `reshape(int... shape)` with size consistency validation. - NumPy-like string display for 1D arrays (including ellipsis for large arrays). - Defensive copy behavior on constructors and exports. @@ -43,6 +44,8 @@ NdArray c = NdArray.arange(0f, 6f, 2f); NdArray m = NdArray.array(new float[][] {{1f, 2f}, {3f, 4f}}); NdArray sum = m.add(NdArray.array(new float[][] {{10f, 10f}, {10f, 10f}})); NdArray reshaped = NdArray.arange(6f).reshape(2, 3); +NdArray shifted = a.add(10f); +float total = shifted.sum(); ``` ## Tech Stack and Tooling Choices @@ -168,6 +171,7 @@ Implemented: - 1D and 2D ndarray support. - Creation functions (`array`, `zeros`, `arange`). - Addition operations (`add`, `addInPlace`). +- Scalar addition and `sum()` reduction. - Reshape with validation. In progress / next: diff --git a/src/main/java/org/sadisamir/ndarray/NdArray.java b/src/main/java/org/sadisamir/ndarray/NdArray.java index 7e5812e..12fad92 100644 --- a/src/main/java/org/sadisamir/ndarray/NdArray.java +++ b/src/main/java/org/sadisamir/ndarray/NdArray.java @@ -263,6 +263,18 @@ public NdArray add(NdArray other) { return new NdArray(result, numDimensions, shape); } + /** + * Returns a new array with a scalar added to every element. + */ + public NdArray add(float scalar) { + float[] result = new float[totalElements]; + for (int index = 0; index < totalElements; index++) { + result[index] = flatData[index] + scalar; + } + + return new NdArray(result, numDimensions, shape); + } + /** * Adds another array to this one in place. * @@ -278,6 +290,26 @@ public void addInPlace(NdArray other) { } } + /** + * Adds a scalar to this array in place. + */ + public void addInPlace(float scalar) { + for (int index = 0; index < totalElements; index++) { + flatData[index] += scalar; + } + } + + /** + * Returns the sum of all elements in the array. + */ + public float sum() { + double total = 0.0d; + for (float value : flatData) { + total += value; + } + return (float) total; + } + /** * Returns a reshaped view of this array. * diff --git a/src/main/java/org/sadisamir/ndarray/demo/NdArrayDemo.java b/src/main/java/org/sadisamir/ndarray/demo/NdArrayDemo.java index 0f347cc..1a80d53 100644 --- a/src/main/java/org/sadisamir/ndarray/demo/NdArrayDemo.java +++ b/src/main/java/org/sadisamir/ndarray/demo/NdArrayDemo.java @@ -21,6 +21,7 @@ public static void main(String[] args) { demo1DArrayCreation(); demo2DArrayCreation(); demoArithmeticOperations(); + demoOptionalFeatures(); demoReshape(); demoLargeArrayDisplay(); printFooter(); @@ -105,6 +106,21 @@ private static void demoArithmeticOperations() { LOGGER.info(""); } + private static void demoOptionalFeatures() { + printSection("Optional Features"); + + NdArray base = NdArray.array(new float[]{1f, 2f, 3f}); + LOGGER.info("Scalar addition: base.add(10f)"); + LOGGER.info(" Base: {}", base); + NdArray shifted = base.add(10f); + LOGGER.info(" Shifted: {}", shifted); + LOGGER.info(""); + + LOGGER.info("Reduction: shifted.sum()"); + LOGGER.info(" Total: {}", shifted.sum()); + LOGGER.info(""); + } + private static void demoReshape() { printSection("Reshape Operations"); diff --git a/src/test/java/org/sadisamir/ndarray/NdArrayTest.java b/src/test/java/org/sadisamir/ndarray/NdArrayTest.java index 277ad62..2cb7555 100644 --- a/src/test/java/org/sadisamir/ndarray/NdArrayTest.java +++ b/src/test/java/org/sadisamir/ndarray/NdArrayTest.java @@ -372,4 +372,69 @@ void addInPlaceRejectsNullOperand() { assertThrows(NullPointerException.class, () -> left.addInPlace(null)); } + + @Test + void addScalarReturnsNewOneDimensionalArray() { + NdArray source = NdArray.array(new float[] {1.0f, 2.0f, 3.0f}); + + NdArray result = source.add(10.0f); + + assertArrayEquals(new float[] {11.0f, 12.0f, 13.0f}, result.toArray(), 0.0f); + assertArrayEquals(new float[] {1.0f, 2.0f, 3.0f}, source.toArray(), 0.0f); + } + + @Test + void addScalarPreservesTwoDimensionalMetadata() { + NdArray matrix = NdArray.array(new float[][] {{1.0f, 2.0f}, {3.0f, 4.0f}}); + + NdArray result = matrix.add(-1.5f); + + assertEquals(2, result.getNdim()); + assertArrayEquals(new int[] {2, 2}, result.getShape()); + assertEquals(4, result.getSize()); + assertArrayEquals(new float[] {-0.5f, 0.5f}, result.toMatrix()[0], 0.0f); + assertArrayEquals(new float[] {1.5f, 2.5f}, result.toMatrix()[1], 0.0f); + } + + @Test + void addInPlaceScalarMutatesCurrentArray() { + NdArray source = NdArray.array(new float[] {1.0f, 2.0f, 3.0f}); + + source.addInPlace(2.5f); + + assertArrayEquals(new float[] {3.5f, 4.5f, 5.5f}, source.toArray(), 0.0f); + } + + @Test + void addInPlaceScalarPreservesTwoDimensionalShape() { + NdArray matrix = NdArray.array(new float[][] {{1.0f, 2.0f}, {3.0f, 4.0f}}); + + matrix.addInPlace(-1.0f); + + assertEquals(2, matrix.getNdim()); + assertArrayEquals(new int[] {2, 2}, matrix.getShape()); + assertArrayEquals(new float[] {0.0f, 1.0f}, matrix.toMatrix()[0], 0.0f); + assertArrayEquals(new float[] {2.0f, 3.0f}, matrix.toMatrix()[1], 0.0f); + } + + @Test + void sumReturnsTotalForOneDimensionalArray() { + NdArray source = NdArray.array(new float[] {1.0f, 2.5f, 3.5f}); + + assertEquals(7.0f, source.sum(), 0.0f); + } + + @Test + void sumReturnsTotalForTwoDimensionalArray() { + NdArray matrix = NdArray.array(new float[][] {{1.0f, 2.0f}, {3.0f, 4.0f}}); + + assertEquals(10.0f, matrix.sum(), 0.0f); + } + + @Test + void sumOfEmptyArrayIsZero() { + NdArray source = NdArray.array(new float[0]); + + assertEquals(0.0f, source.sum(), 0.0f); + } }