From c74ca83beec04684ab741e5737c35d6b406b8b0d Mon Sep 17 00:00:00 2001 From: jacob Date: Mon, 12 Aug 2024 21:14:20 -0600 Subject: [PATCH] Add tests for sparse tensors. --- .../org/flag4j/arrays/sparse/CooCTensor.java | 30 ++- .../org/flag4j/arrays/sparse/CooTensor.java | 24 +- .../CooCTensorHermTransposeTests.java | 252 ++++++++++++++++++ .../CooCTensorReshapeTests.java | 218 +++++++++++++++ .../sparse_tensor/CooTensorReshapeTests.java | 209 +++++++++++++++ .../CooTensorTransposeTests.java | 147 ++++++++++ target/flag4j-v0.1.0-beta.jar | Bin 640987 -> 641230 bytes 7 files changed, 863 insertions(+), 17 deletions(-) create mode 100644 src/test/java/org/flag4j/sparse_complex_tensor/CooCTensorHermTransposeTests.java create mode 100644 src/test/java/org/flag4j/sparse_complex_tensor/CooCTensorReshapeTests.java create mode 100644 src/test/java/org/flag4j/sparse_tensor/CooTensorReshapeTests.java create mode 100644 src/test/java/org/flag4j/sparse_tensor/CooTensorTransposeTests.java diff --git a/src/main/java/org/flag4j/arrays/sparse/CooCTensor.java b/src/main/java/org/flag4j/arrays/sparse/CooCTensor.java index bcd6e0497..b8f5ecfeb 100644 --- a/src/main/java/org/flag4j/arrays/sparse/CooCTensor.java +++ b/src/main/java/org/flag4j/arrays/sparse/CooCTensor.java @@ -276,20 +276,21 @@ public CooCTensor reshape(Shape newShape) { newShape.makeStridesIfNull(); int rank = indices[0].length; + int newRank = newShape.getRank(); int nnz = entries.length; int[] oldStrides = shape.getStrides(); int[] newStrides = newShape.getStrides(); - int[][] newIndices = new int[nnz][rank]; + int[][] newIndices = new int[nnz][newRank]; - for (int i = 0; i < nnz; i++) { + for(int i=0; iComputes the transpose of a tensor. Same as {@link #transpose()}.

+ * + *

This method transposes the tensor by exchanges the first and last index + * of the tensor. Thus, for a rank 2 tensor, this method is equivalent to a matrix transpose.

+ * + *

{@link #T(int, int)} and {@link #T(int...)} offer more general tensor transposes.

* * @return The transpose of this tensor. + * @see #transpose() + * @see #T(int, int) + * @see #T(int...) */ @Override public CooCTensor T() { @@ -870,7 +880,7 @@ public CooCTensor H(int axis1, int axis2) { } // Create sparse coo tensor and sort values lexicographically. - CooCTensor transpose = new CooCTensor(shape, transposeEntries, transposeIndices); + CooCTensor transpose = new CooCTensor(shape.swapAxes(axis1, axis2), transposeEntries, transposeIndices); transpose.sortIndices(); return transpose; @@ -908,7 +918,7 @@ public CooCTensor H(int... axes) { } // Create sparse coo tensor and sort values lexicographically. - CooCTensor transpose = new CooCTensor(shape, transposeEntries, transposeIndices); + CooCTensor transpose = new CooCTensor(shape.swapAxes(axes), transposeEntries, transposeIndices); transpose.sortIndices(); return transpose; diff --git a/src/main/java/org/flag4j/arrays/sparse/CooTensor.java b/src/main/java/org/flag4j/arrays/sparse/CooTensor.java index 3d9872e58..7c1c48ad6 100644 --- a/src/main/java/org/flag4j/arrays/sparse/CooTensor.java +++ b/src/main/java/org/flag4j/arrays/sparse/CooTensor.java @@ -282,12 +282,13 @@ public CooTensor reshape(Shape newShape) { newShape.makeStridesIfNull(); // Ensure this shape object has strides computed. int rank = indices[0].length; + int newRank = newShape.getRank(); int nnz = entries.length; int[] oldStrides = shape.getStrides(); int[] newStrides = newShape.getStrides(); - int[][] newIndices = new int[nnz][rank]; + int[][] newIndices = new int[nnz][newRank]; for (int i = 0; i < nnz; i++) { int flatIndex = 0; @@ -295,7 +296,11 @@ public CooTensor reshape(Shape newShape) { flatIndex += indices[i][j] * oldStrides[j]; } - for (int j = 0; j < rank; j++) { + for (int j = 0; j < newRank; j++) { + int[] arr1 = newIndices[i]; + int v1 = newIndices[i][j]; + int v2 = newStrides[j]; + newIndices[i][j] = flatIndex / newStrides[j]; flatIndex %= newStrides[j]; } @@ -337,6 +342,7 @@ public CooTensor flatten(int axis) { // Compute new shape. int[] destShape = new int[indices[0].length]; + Arrays.fill(destShape, 1); destShape[axis] = shape.totalEntries().intValueExact(); for(int i=0, size=entries.length; iComputes the transpose of a tensor. Same as {@link #transpose()}.

+ * + *

This method transposes the tensor by exchanges the first and last index + * of the tensor. Thus, for a rank 2 tensor, this method is equivalent to a matrix transpose.

+ * + *

{@link #T(int, int)} and {@link #T(int...)} offer more general tensor transposes.

* * @return The transpose of this tensor. * @see #transpose() diff --git a/src/test/java/org/flag4j/sparse_complex_tensor/CooCTensorHermTransposeTests.java b/src/test/java/org/flag4j/sparse_complex_tensor/CooCTensorHermTransposeTests.java new file mode 100644 index 000000000..75340ddff --- /dev/null +++ b/src/test/java/org/flag4j/sparse_complex_tensor/CooCTensorHermTransposeTests.java @@ -0,0 +1,252 @@ +package org.flag4j.sparse_complex_tensor; + +import org.flag4j.arrays.sparse.CooCTensor; +import org.flag4j.complex_numbers.CNumber; +import org.flag4j.core.Shape; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class CooCTensorHermTransposeTests { + static CooCTensor A; + static Shape aShape; + static CNumber[] aEntries; + static int[][] aIndices; + + static CooCTensor exp; + static Shape expShape; + static CNumber[] expEntries; + static int[][] expIndices; + + @Test + void hermTransposeTests() { + // ----------------------- Sub-case 1 ----------------------- + aShape = new Shape(3, 4, 2, 1, 5); + aEntries = new CNumber[]{new CNumber(0.6522, 0.3289), new CNumber(0.7167, 0.9757), new CNumber(0.7091, 0.0283), new CNumber(0.2897, 0.9174), new CNumber(0.2596, 0.4461), new CNumber(0.4028, 0.9296), new CNumber(0.9347, 0.0967), new CNumber(0.4156, 0.7123), new CNumber(0.5299, 0.2536), new CNumber(0.8344, 0.3449), new CNumber(0.3802, 0.6804)}; + aIndices = new int[][]{ + {0, 0, 0, 0, 2}, + {0, 2, 0, 0, 0}, + {1, 0, 0, 0, 3}, + {1, 1, 0, 0, 1}, + {1, 1, 1, 0, 3}, + {1, 3, 0, 0, 3}, + {1, 3, 1, 0, 4}, + {2, 1, 0, 0, 2}, + {2, 1, 0, 0, 3}, + {2, 1, 1, 0, 3}, + {2, 2, 1, 0, 3}}; + A = new CooCTensor(aShape, aEntries, aIndices); + + expShape = new Shape(5, 4, 2, 1, 3); + expEntries = new CNumber[]{new CNumber(0.7167, -0.9757), new CNumber(0.2897, -0.9174), new CNumber(0.6522, -0.3289), new CNumber(0.4156, -0.7123), new CNumber(0.7091, -0.0283), new CNumber(0.5299, -0.2536), new CNumber(0.2596, -0.4461), new CNumber(0.8344, -0.3449), new CNumber(0.3802, -0.6804), new CNumber(0.4028, -0.9296), new CNumber(0.9347, -0.0967)}; + expIndices = new int[][]{ + {0, 2, 0, 0, 0}, + {1, 1, 0, 0, 1}, + {2, 0, 0, 0, 0}, + {2, 1, 0, 0, 2}, + {3, 0, 0, 0, 1}, + {3, 1, 0, 0, 2}, + {3, 1, 1, 0, 1}, + {3, 1, 1, 0, 2}, + {3, 2, 1, 0, 2}, + {3, 3, 0, 0, 1}, + {4, 3, 1, 0, 1}}; + exp = new CooCTensor(expShape, expEntries, expIndices); + assertEquals(exp, A.H()); + + // ----------------------- Sub-case 2 ----------------------- + aShape = new Shape(3, 4, 2, 1, 5); + aEntries = new CNumber[]{new CNumber(0.8546, 0.6631), new CNumber(0.8133, 0.2484), new CNumber(0.0033, 0.7366), new CNumber(0.0844, 0.3648), new CNumber(0.8295, 0.2401), new CNumber(0.1182, 0.7329), new CNumber(0.6894, 0.0494), new CNumber(0.4388, 0.4951), new CNumber(0.8198, 0.6859), new CNumber(0.8987, 0.6718), new CNumber(0.2785, 0.8425)}; + aIndices = new int[][]{ + {0, 2, 0, 0, 1}, + {0, 2, 1, 0, 0}, + {0, 3, 1, 0, 3}, + {1, 0, 0, 0, 3}, + {1, 0, 1, 0, 2}, + {1, 2, 1, 0, 4}, + {1, 3, 1, 0, 3}, + {2, 0, 1, 0, 1}, + {2, 0, 1, 0, 4}, + {2, 1, 1, 0, 0}, + {2, 1, 1, 0, 2}}; + A = new CooCTensor(aShape, aEntries, aIndices); + + expShape = new Shape(2, 4, 3, 1, 5); + expEntries = new CNumber[]{new CNumber(0.0844, -0.3648), new CNumber(0.8546, -0.6631), new CNumber(0.8295, -0.2401), new CNumber(0.4388, -0.4951), new CNumber(0.8198, -0.6859), new CNumber(0.8987, -0.6718), new CNumber(0.2785, -0.8425), new CNumber(0.8133, -0.2484), new CNumber(0.1182, -0.7329), new CNumber(0.0033, -0.7366), new CNumber(0.6894, -0.0494)}; + expIndices = new int[][]{ + {0, 0, 1, 0, 3}, + {0, 2, 0, 0, 1}, + {1, 0, 1, 0, 2}, + {1, 0, 2, 0, 1}, + {1, 0, 2, 0, 4}, + {1, 1, 2, 0, 0}, + {1, 1, 2, 0, 2}, + {1, 2, 0, 0, 0}, + {1, 2, 1, 0, 4}, + {1, 3, 0, 0, 3}, + {1, 3, 1, 0, 3}}; + exp = new CooCTensor(expShape, expEntries, expIndices); + + assertEquals(exp, A.H(0, 2)); + assertEquals(exp, A.H(2, 0)); + + // ----------------------- Sub-case 3 ----------------------- + aShape = new Shape(3, 4, 2, 1, 5); + aEntries = new CNumber[]{new CNumber(0.2252, 0.8303), new CNumber(0.7022, 0.9162), new CNumber(0.6672, 0.2974), new CNumber(0.4421, 0.7193), new CNumber(0.3796, 0.9056), new CNumber(0.2784, 0.3588), new CNumber(0.3264, 0.909), new CNumber(0.1959, 0.2546), new CNumber(0.7772, 0.396), new CNumber(0.2936, 0.491), new CNumber(0.5877, 0.1148)}; + aIndices = new int[][]{ + {0, 0, 0, 0, 2}, + {0, 0, 1, 0, 2}, + {0, 3, 0, 0, 2}, + {1, 0, 1, 0, 1}, + {1, 0, 1, 0, 4}, + {1, 2, 1, 0, 4}, + {2, 0, 0, 0, 0}, + {2, 0, 1, 0, 2}, + {2, 2, 1, 0, 0}, + {2, 3, 1, 0, 2}, + {2, 3, 1, 0, 3}}; + A = new CooCTensor(aShape, aEntries, aIndices); + + expShape = new Shape(3, 1, 5, 4, 2); + expEntries = new CNumber[]{new CNumber(0.2252, -0.8303), new CNumber(0.7022, -0.9162), new CNumber(0.6672, -0.2974), new CNumber(0.4421, -0.7193), new CNumber(0.3796, -0.9056), new CNumber(0.2784, -0.3588), new CNumber(0.3264, -0.909), new CNumber(0.7772, -0.396), new CNumber(0.1959, -0.2546), new CNumber(0.2936, -0.491), new CNumber(0.5877, -0.1148)}; + expIndices = new int[][]{ + {0, 0, 2, 0, 0}, + {0, 0, 2, 0, 1}, + {0, 0, 2, 3, 0}, + {1, 0, 1, 0, 1}, + {1, 0, 4, 0, 1}, + {1, 0, 4, 2, 1}, + {2, 0, 0, 0, 0}, + {2, 0, 0, 2, 1}, + {2, 0, 2, 0, 1}, + {2, 0, 2, 3, 1}, + {2, 0, 3, 3, 1}}; + exp = new CooCTensor(expShape, expEntries, expIndices); + + assertEquals(exp, A.H(0, 3, 4, 1, 2)); + + // ----------------------- Sub-case 4 ----------------------- + assertThrows(IllegalArgumentException.class, ()->A.H(0, 1, 3, 2)); + assertThrows(IllegalArgumentException.class, ()->A.H(0, 3, 4, 1, 2, 5)); + assertThrows(IllegalArgumentException.class, ()->A.H(0, 3, -4, 1, 2)); + assertThrows(IllegalArgumentException.class, ()->A.H(0, 15, 4, 1, 2)); + assertThrows(IndexOutOfBoundsException.class, ()->A.H(5, 1)); + } + + + @Test + void transposeTests() { + // ----------------------- Sub-case 1 ----------------------- + aShape = new Shape(3, 4, 2, 1, 5); + aEntries = new CNumber[]{new CNumber(0.9613, 0.238), new CNumber(0.9947, 0.4532), new CNumber(0.1439, 0.7333), new CNumber(0.2738, 0.7492), new CNumber(0.0159, 0.6496), new CNumber(0.44, 0.5992), new CNumber(0.2544, 0.174), new CNumber(0.0317, 0.3052), new CNumber(0.3788, 0.4169), new CNumber(0.2586, 0.7146), new CNumber(0.148, 0.1819)}; + aIndices = new int[][]{ + {0, 0, 0, 0, 4}, + {0, 0, 1, 0, 1}, + {0, 1, 0, 0, 2}, + {0, 1, 1, 0, 3}, + {1, 1, 1, 0, 0}, + {1, 1, 1, 0, 4}, + {1, 2, 0, 0, 4}, + {1, 2, 1, 0, 1}, + {1, 2, 1, 0, 4}, + {2, 0, 1, 0, 4}, + {2, 1, 1, 0, 4}}; + A = new CooCTensor(aShape, aEntries, aIndices); + + expShape = new Shape(5, 4, 2, 1, 3); + expEntries = new CNumber[]{new CNumber(0.0159, 0.6496), new CNumber(0.9947, 0.4532), new CNumber(0.0317, 0.3052), new CNumber(0.1439, 0.7333), new CNumber(0.2738, 0.7492), new CNumber(0.9613, 0.238), new CNumber(0.2586, 0.7146), new CNumber(0.44, 0.5992), new CNumber(0.148, 0.1819), new CNumber(0.2544, 0.174), new CNumber(0.3788, 0.4169)}; + expIndices = new int[][]{ + {0, 1, 1, 0, 1}, + {1, 0, 1, 0, 0}, + {1, 2, 1, 0, 1}, + {2, 1, 0, 0, 0}, + {3, 1, 1, 0, 0}, + {4, 0, 0, 0, 0}, + {4, 0, 1, 0, 2}, + {4, 1, 1, 0, 1}, + {4, 1, 1, 0, 2}, + {4, 2, 0, 0, 1}, + {4, 2, 1, 0, 1}}; + exp = new CooCTensor(expShape, expEntries, expIndices); + assertEquals(exp, A.T()); + + // ----------------------- Sub-case 2 ----------------------- + aShape = new Shape(3, 4, 2, 1, 5); + aEntries = new CNumber[]{new CNumber(0.5902, 0.9131), new CNumber(0.3367, 0.9463), new CNumber(0.4198, 0.1293), new CNumber(0.6835, 0.2796), new CNumber(0.5134, 0.2389), new CNumber(0.7717, 0.3427), new CNumber(0.7304, 0.4389), new CNumber(0.4974, 0.184), new CNumber(0.1768, 0.9627), new CNumber(0.5433, 0.2314), new CNumber(0.9679, 0.4831)}; + aIndices = new int[][]{ + {0, 1, 0, 0, 3}, + {0, 1, 1, 0, 4}, + {0, 2, 0, 0, 1}, + {1, 0, 0, 0, 1}, + {1, 0, 0, 0, 2}, + {1, 2, 0, 0, 3}, + {1, 3, 0, 0, 0}, + {2, 1, 0, 0, 4}, + {2, 1, 1, 0, 0}, + {2, 2, 0, 0, 3}, + {2, 3, 0, 0, 4}}; + A = new CooCTensor(aShape, aEntries, aIndices); + + expShape = new Shape(2, 4, 3, 1, 5); + expEntries = new CNumber[]{new CNumber(0.6835, 0.2796), new CNumber(0.5134, 0.2389), new CNumber(0.5902, 0.9131), new CNumber(0.4974, 0.184), new CNumber(0.4198, 0.1293), new CNumber(0.7717, 0.3427), new CNumber(0.5433, 0.2314), new CNumber(0.7304, 0.4389), new CNumber(0.9679, 0.4831), new CNumber(0.3367, 0.9463), new CNumber(0.1768, 0.9627)}; + expIndices = new int[][]{ + {0, 0, 1, 0, 1}, + {0, 0, 1, 0, 2}, + {0, 1, 0, 0, 3}, + {0, 1, 2, 0, 4}, + {0, 2, 0, 0, 1}, + {0, 2, 1, 0, 3}, + {0, 2, 2, 0, 3}, + {0, 3, 1, 0, 0}, + {0, 3, 2, 0, 4}, + {1, 1, 0, 0, 4}, + {1, 1, 2, 0, 0}}; + exp = new CooCTensor(expShape, expEntries, expIndices); + + assertEquals(exp, A.T(0, 2)); + assertEquals(exp, A.T(2, 0)); + + // ----------------------- Sub-case 3 ----------------------- + aShape = new Shape(3, 4, 2, 1, 5); + aEntries = new CNumber[]{new CNumber(0.8834, 0.1986), new CNumber(0.3184, 0.3042), new CNumber(0.8551, 0.4776), new CNumber(0.7626, 0.7819), new CNumber(0.1152, 0.4055), new CNumber(0.6564, 0.7552), new CNumber(0.3097, 0.5647), new CNumber(0.3279, 0.7208), new CNumber(0.4838, 0.6065), new CNumber(0.8963, 0.7191), new CNumber(0.2443, 0.4567)}; + aIndices = new int[][]{ + {0, 0, 0, 0, 3}, + {0, 0, 1, 0, 0}, + {0, 1, 0, 0, 4}, + {0, 2, 0, 0, 2}, + {1, 0, 0, 0, 3}, + {1, 2, 0, 0, 0}, + {1, 2, 1, 0, 3}, + {2, 1, 1, 0, 4}, + {2, 2, 0, 0, 4}, + {2, 2, 1, 0, 0}, + {2, 3, 1, 0, 1}}; + A = new CooCTensor(aShape, aEntries, aIndices); + + expShape = new Shape(3, 1, 5, 4, 2); + expEntries = new CNumber[]{new CNumber(0.3184, 0.3042), new CNumber(0.7626, 0.7819), new CNumber(0.8834, 0.1986), new CNumber(0.8551, 0.4776), new CNumber(0.6564, 0.7552), new CNumber(0.1152, 0.4055), new CNumber(0.3097, 0.5647), new CNumber(0.8963, 0.7191), new CNumber(0.2443, 0.4567), new CNumber(0.3279, 0.7208), new CNumber(0.4838, 0.6065)}; + expIndices = new int[][]{ + {0, 0, 0, 0, 1}, + {0, 0, 2, 2, 0}, + {0, 0, 3, 0, 0}, + {0, 0, 4, 1, 0}, + {1, 0, 0, 2, 0}, + {1, 0, 3, 0, 0}, + {1, 0, 3, 2, 1}, + {2, 0, 0, 2, 1}, + {2, 0, 1, 3, 1}, + {2, 0, 4, 1, 1}, + {2, 0, 4, 2, 0}}; + exp = new CooCTensor(expShape, expEntries, expIndices); + + assertEquals(exp, A.T(0, 3, 4, 1, 2)); + + // ----------------------- Sub-case 4 ----------------------- + assertThrows(IllegalArgumentException.class, ()->A.T(0, 1, 3, 2)); + assertThrows(IllegalArgumentException.class, ()->A.T(0, 3, 4, 1, 2, 5)); + assertThrows(IllegalArgumentException.class, ()->A.T(0, 3, -4, 1, 2)); + assertThrows(IllegalArgumentException.class, ()->A.T(0, 15, 4, 1, 2)); + assertThrows(IndexOutOfBoundsException.class, ()->A.T(5, 1)); + } +} diff --git a/src/test/java/org/flag4j/sparse_complex_tensor/CooCTensorReshapeTests.java b/src/test/java/org/flag4j/sparse_complex_tensor/CooCTensorReshapeTests.java new file mode 100644 index 000000000..39df27aba --- /dev/null +++ b/src/test/java/org/flag4j/sparse_complex_tensor/CooCTensorReshapeTests.java @@ -0,0 +1,218 @@ +package org.flag4j.sparse_complex_tensor; + +import org.flag4j.arrays.sparse.CooCTensor; +import org.flag4j.complex_numbers.CNumber; +import org.flag4j.core.Shape; +import org.junit.jupiter.api.Test; + +import static org.junit.Assert.assertThrows; +import static org.junit.jupiter.api.Assertions.assertEquals; + +class CooCTensorReshapeTests { + static CooCTensor A; + static Shape aShape; + static CNumber[] aEntries; + static int[][] aIndices; + + static CooCTensor exp; + static Shape expShape; + static CNumber[] expEntries; + static int[][] expIndices; + + @Test + void reshapeTests() { + // -------------------------- Sub-case 1 -------------------------- + aShape = new Shape(5, 4, 2, 1); + aEntries = new CNumber[]{new CNumber(0.2856, 0.1775), new CNumber(0.2455, 0.6139), new CNumber(0.9386, 0.8602), new CNumber(0.194, 0.921), new CNumber(0.8078, 0.4986), new CNumber(0.359, 0.5673)}; + aIndices = new int[][]{ + {0, 0, 0, 0}, + {0, 0, 1, 0}, + {2, 2, 1, 0}, + {2, 3, 1, 0}, + {3, 3, 0, 0}, + {4, 3, 0, 0}}; + A = new CooCTensor(aShape, aEntries, aIndices); + + expShape = new Shape(2, 5, 2, 2); + expEntries = new CNumber[]{new CNumber(0.2856, 0.1775), new CNumber(0.2455, 0.6139), new CNumber(0.9386, 0.8602), new CNumber(0.194, 0.921), new CNumber(0.8078, 0.4986), new CNumber(0.359, 0.5673)}; + expIndices = new int[][]{ + {0, 0, 0, 0}, + {0, 0, 0, 1}, + {1, 0, 0, 1}, + {1, 0, 1, 1}, + {1, 2, 1, 0}, + {1, 4, 1, 0}}; + exp = new CooCTensor(expShape, expEntries, expIndices); + assertEquals(exp, A.reshape(2, 5, 2, 2)); + + // ----------------------------- Sub-case 2 ----------------------------- + aShape = new Shape(5, 4, 2, 3, 15); + aEntries = new CNumber[]{new CNumber(0.8103, 0.0203), new CNumber(0.5684, 0.4151), new CNumber(0.9044, 0.8734), new CNumber(0.201, 0.7032), new CNumber(0.9682, 0.2723), new CNumber(0.4699, 0.8203), new CNumber(0.3871, 0.3395), new CNumber(0.7851, 0.3768), new CNumber(0.2315, 0.7695), new CNumber(0.8333, 0.8837), new CNumber(0.0398, 0.559), new CNumber(0.0405, 0.9707), new CNumber(0.488, 0.8343), new CNumber(0.2441, 0.7806), new CNumber(0.3995, 0.6793), new CNumber(0.3689, 0.6126), new CNumber(0.0767, 0.9631), new CNumber(0.8007, 0.4023)}; + aIndices = new int[][]{ + {0, 0, 0, 0, 0}, + {0, 1, 0, 0, 7}, + {0, 2, 1, 2, 4}, + {0, 3, 0, 0, 11}, + {0, 3, 1, 0, 10}, + {0, 3, 1, 2, 6}, + {1, 1, 1, 0, 7}, + {2, 0, 0, 0, 8}, + {2, 0, 0, 0, 10}, + {2, 2, 0, 2, 12}, + {2, 2, 1, 1, 6}, + {3, 0, 0, 0, 2}, + {3, 0, 1, 1, 1}, + {4, 0, 0, 2, 10}, + {4, 0, 1, 0, 7}, + {4, 2, 1, 1, 8}, + {4, 3, 0, 0, 6}, + {4, 3, 1, 2, 13}}; + A = new CooCTensor(aShape, aEntries, aIndices); + + expShape = new Shape(15, 2, 4, 15); + expEntries = new CNumber[]{new CNumber(0.8103, 0.0203), new CNumber(0.5684, 0.4151), new CNumber(0.9044, 0.8734), new CNumber(0.201, 0.7032), new CNumber(0.9682, 0.2723), new CNumber(0.4699, 0.8203), new CNumber(0.3871, 0.3395), new CNumber(0.7851, 0.3768), new CNumber(0.2315, 0.7695), new CNumber(0.8333, 0.8837), new CNumber(0.0398, 0.559), new CNumber(0.0405, 0.9707), new CNumber(0.488, 0.8343), new CNumber(0.2441, 0.7806), new CNumber(0.3995, 0.6793), new CNumber(0.3689, 0.6126), new CNumber(0.0767, 0.9631), new CNumber(0.8007, 0.4023)}; + expIndices = new int[][]{ + {0, 0, 0, 0}, + {0, 1, 2, 7}, + {2, 0, 1, 4}, + {2, 0, 2, 11}, + {2, 1, 1, 10}, + {2, 1, 3, 6}, + {4, 0, 1, 7}, + {6, 0, 0, 8}, + {6, 0, 0, 10}, + {7, 1, 2, 12}, + {8, 0, 0, 6}, + {9, 0, 0, 2}, + {9, 1, 0, 1}, + {12, 0, 2, 10}, + {12, 0, 3, 7}, + {14, 0, 0, 8}, + {14, 0, 2, 6}, + {14, 1, 3, 13}}; + exp = new CooCTensor(expShape, expEntries, expIndices); + assertEquals(exp, A.reshape(15, 2, 4, 15)); + + // ----------------------------- Sub-case 3 ----------------------------- + aShape = new Shape(3, 16); + aShape = new Shape(3, 16); + aEntries = new CNumber[]{new CNumber(0.5938, 0.762), new CNumber(0.4295, 0.7988), new CNumber(0.0332, 0.3233), new CNumber(0.7022, 0.1686), new CNumber(0.7114, 0.6353), new CNumber(0.5935, 0.0851), new CNumber(0.7148, 0.5695)}; + aIndices = new int[][]{ + {1, 5}, + {1, 14}, + {1, 15}, + {2, 0}, + {2, 2}, + {2, 3}, + {2, 10}}; + A = new CooCTensor(aShape, aEntries, aIndices); + + expShape = new Shape(2, 4, 3, 2); + expEntries = new CNumber[]{new CNumber(0.5938, 0.762), new CNumber(0.4295, 0.7988), new CNumber(0.0332, 0.3233), new CNumber(0.7022, 0.1686), new CNumber(0.7114, 0.6353), new CNumber(0.5935, 0.0851), new CNumber(0.7148, 0.5695)}; + expIndices = new int[][]{ + {0, 3, 1, 1}, + {1, 1, 0, 0}, + {1, 1, 0, 1}, + {1, 1, 1, 0}, + {1, 1, 2, 0}, + {1, 1, 2, 1}, + {1, 3, 0, 0}}; + exp = new CooCTensor(expShape, expEntries, expIndices); + assertEquals(exp, A.reshape(2, 4, 3, 2)); + + // ----------------------------- Sub-case 4 ----------------------------- + assertThrows(IllegalArgumentException.class, ()->A.reshape(150, 12)); + } + + + @Test + void flattenTests() { + // -------------------------- Sub-case 1 -------------------------- + aShape = new Shape(3, 16); + aEntries = new CNumber[]{ + new CNumber(1, -2), new CNumber(9.145, 0.00013), + new CNumber(234), new CNumber(0, 15)}; + aIndices = new int[][]{ + {0, 4}, + {0, 7}, + {1, 9}, + {2, 10}}; + A = new CooCTensor(aShape, aEntries, aIndices); + + expShape = new Shape(48); + expEntries = new CNumber[]{new CNumber(1, -2), new CNumber(9.145, 0.00013), + new CNumber(234), new CNumber(0, 15)}; + expIndices = new int[][]{ + {4}, + {7}, + {25}, + {42}}; + exp = new CooCTensor(expShape, expEntries, expIndices); + assertEquals(exp, A.flatten()); + + // -------------------------- Sub-case 2 -------------------------- + aShape = new Shape(3, 2, 4); + aEntries = new CNumber[]{new CNumber(0, 15), new CNumber(0), new CNumber(-9, 154)}; + aIndices = new int[][]{ + {0, 1, 3}, + {1, 0, 1}, + {1, 1, 0}}; + A = new CooCTensor(aShape, aEntries, aIndices); + + expShape = new Shape(24); + expEntries = new CNumber[]{new CNumber(0, 15), new CNumber(0), new CNumber(-9, 154)}; + expIndices = new int[][]{ + {7}, + {9}, + {12}}; + exp = new CooCTensor(expShape, expEntries, expIndices); + assertEquals(exp, A.flatten()); + + // -------------------------- Sub-case 3 -------------------------- + aShape = new Shape(3, 2, 4); + aEntries = new CNumber[]{new CNumber(0, 15), new CNumber(2), new CNumber(-9, 154)}; + aIndices = new int[][]{ + {1, 0, 1}, + {1, 1, 1}, + {2, 0, 1}}; + A = new CooCTensor(aShape, aEntries, aIndices); + + expShape = new Shape(1, 24, 1); + expEntries = new CNumber[]{new CNumber(0, 15), new CNumber(2), new CNumber(-9, 154)}; + expIndices = new int[][]{ + {0, 9, 0}, + {0, 13, 0}, + {0, 17, 0}}; + exp = new CooCTensor(expShape, expEntries, expIndices); + assertEquals(exp, A.flatten(1)); + + // -------------------------- Sub-case 4 -------------------------- + aShape = new Shape(3, 2, 4); + aEntries = new CNumber[]{new CNumber(0, 15), new CNumber(234), new CNumber(-9, 154)}; + aIndices = new int[][]{ + {1, 0, 1}, + {1, 1, 1}, + {2, 0, 1}}; + A = new CooCTensor(aShape, aEntries, aIndices); + + expShape = new Shape(1, 1, 24); + expEntries = new CNumber[]{new CNumber(0, 15), new CNumber(234), new CNumber(-9, 154)}; + expIndices = new int[][]{ + {0, 0, 9}, + {0, 0, 13}, + {0, 0, 17}}; + exp = new CooCTensor(expShape, expEntries, expIndices); + assertEquals(exp, A.flatten(2)); + + // -------------------------- Sub-case 5 -------------------------- + aShape = new Shape(3, 2, 4); + aEntries = new CNumber[]{new CNumber(0, 15), new CNumber(234), new CNumber(-9, 154)}; + aIndices = new int[][]{ + {1, 0, 1}, + {1, 1, 1}, + {2, 0, 1}}; + A = new CooCTensor(aShape, aEntries, aIndices); + assertThrows(IndexOutOfBoundsException.class, ()->A.flatten(5)); + assertThrows(IndexOutOfBoundsException.class, ()->A.flatten(-1)); + } +} diff --git a/src/test/java/org/flag4j/sparse_tensor/CooTensorReshapeTests.java b/src/test/java/org/flag4j/sparse_tensor/CooTensorReshapeTests.java new file mode 100644 index 000000000..f2dfe6567 --- /dev/null +++ b/src/test/java/org/flag4j/sparse_tensor/CooTensorReshapeTests.java @@ -0,0 +1,209 @@ +package org.flag4j.sparse_tensor; + +import org.flag4j.arrays.sparse.CooTensor; +import org.flag4j.core.Shape; +import org.junit.jupiter.api.Test; + +import static org.junit.Assert.assertThrows; +import static org.junit.jupiter.api.Assertions.assertEquals; + +class CooTensorReshapeTests { + static CooTensor A; + static Shape aShape; + static double[] aEntries; + static int[][] aIndices; + + static CooTensor exp; + static Shape expShape; + static double[] expEntries; + static int[][] expIndices; + + @Test + void reshapeTests() { + // -------------------------- Sub-case 1 -------------------------- + aShape = new Shape(5, 4, 2, 1); + aEntries = new double[]{-0.2594625644447393, -0.11800739013805872, -1.8499182919471657}; + aIndices = new int[][]{ + {2, 2, 0, 0}, + {2, 3, 0, 0}, + {4, 1, 1, 0}}; + A = new CooTensor(aShape, aEntries, aIndices); + + expShape = new Shape(2, 5, 2, 2); + expEntries = new double[]{-0.2594625644447393, -0.11800739013805872, -1.8499182919471657}; + expIndices = new int[][]{ + {1, 0, 0, 0}, + {1, 0, 1, 0}, + {1, 3, 1, 1}}; + exp = new CooTensor(expShape, expEntries, expIndices); + assertEquals(exp, A.reshape(2, 5, 2, 2)); + + // ----------------------------- Sub-case 2 ----------------------------- + aShape = new Shape(5, 4, 2, 3, 15); + aEntries = new double[]{-1.1499856217218563, -0.33276615768221923, -1.7712698524382784, 0.45988194186997083, -1.0000258840502727, -1.2896045900552038, -1.0495292341142137, 0.5653540034076624, 0.09844833075965526, 1.389726418783007, -0.03253455760212258, 0.8128434562240154, -0.5805363805458708, -0.9687145707590211, 0.005130776492485523, -1.1693463926292427, -0.18736097719279932, -0.588774063376806}; + aIndices = new int[][]{ + {0, 2, 1, 2, 8}, + {1, 0, 0, 0, 2}, + {1, 1, 0, 1, 9}, + {1, 2, 0, 2, 13}, + {1, 3, 1, 1, 3}, + {1, 3, 1, 1, 7}, + {2, 0, 0, 1, 9}, + {2, 2, 1, 0, 11}, + {2, 3, 1, 2, 10}, + {3, 0, 0, 1, 13}, + {3, 0, 1, 0, 6}, + {3, 1, 0, 0, 10}, + {3, 1, 1, 1, 9}, + {4, 0, 1, 0, 9}, + {4, 1, 1, 0, 14}, + {4, 2, 0, 0, 8}, + {4, 3, 0, 2, 6}, + {4, 3, 1, 2, 12}}; + A = new CooTensor(aShape, aEntries, aIndices); + + expShape = new Shape(15, 2, 4, 15); + expEntries = new double[]{-1.1499856217218563, -0.33276615768221923, -1.7712698524382784, 0.45988194186997083, -1.0000258840502727, -1.2896045900552038, -1.0495292341142137, 0.5653540034076624, 0.09844833075965526, 1.389726418783007, -0.03253455760212258, 0.8128434562240154, -0.5805363805458708, -0.9687145707590211, 0.005130776492485523, -1.1693463926292427, -0.18736097719279932, -0.588774063376806}; + expIndices = new int[][]{ + {2, 0, 1, 8}, + {3, 0, 0, 2}, + {3, 1, 3, 9}, + {4, 1, 2, 13}, + {5, 1, 2, 3}, + {5, 1, 2, 7}, + {6, 0, 1, 9}, + {7, 1, 3, 11}, + {8, 1, 3, 10}, + {9, 0, 1, 13}, + {9, 0, 3, 6}, + {9, 1, 2, 10}, + {10, 0, 2, 9}, + {12, 0, 3, 9}, + {13, 0, 1, 14}, + {13, 1, 0, 8}, + {14, 1, 0, 6}, + {14, 1, 3, 12}}; + exp = new CooTensor(expShape, expEntries, expIndices); + assertEquals(exp, A.reshape(15, 2, 4, 15)); + + // ----------------------------- Sub-case 3 ----------------------------- + aShape = new Shape(3, 16); + aEntries = new double[]{-0.5564583772612858, 1.3880160320768695, -0.041746799108138805, 0.22670438356409295}; + aIndices = new int[][]{ + {0, 7}, + {0, 8}, + {0, 15}, + {1, 7}}; + A = new CooTensor(aShape, aEntries, aIndices); + + expShape = new Shape(2, 4, 3, 2); + expEntries = new double[]{-0.5564583772612858, 1.3880160320768695, -0.041746799108138805, 0.22670438356409295}; + expIndices = new int[][]{ + {0, 1, 0, 1}, + {0, 1, 1, 0}, + {0, 2, 1, 1}, + {0, 3, 2, 1}}; + exp = new CooTensor(expShape, expEntries, expIndices); + assertEquals(exp, A.reshape(2, 4, 3, 2)); + + // ----------------------------- Sub-case 4 ----------------------------- + aShape = new Shape(3, 16); + aEntries = new double[]{-0.5564583772612858, 1.3880160320768695, -0.041746799108138805, 0.22670438356409295}; + aIndices = new int[][]{ + {0, 7}, + {0, 8}, + {0, 15}, + {1, 7}}; + A = new CooTensor(aShape, aEntries, aIndices); + assertThrows(IllegalArgumentException.class, ()->A.reshape(150, 12)); + } + + + @Test + void flattenTests() { + // -------------------------- Sub-case 1 -------------------------- + aShape = new Shape(3, 16); + aEntries = new double[]{-0.4396095255063526, -0.008544443239199374, 1.6354416874939133, -0.7535470743266395}; + aIndices = new int[][]{ + {0, 4}, + {0, 7}, + {1, 9}, + {2, 10}}; + A = new CooTensor(aShape, aEntries, aIndices); + + expShape = new Shape(48); + expEntries = new double[]{-0.4396095255063526, -0.008544443239199374, 1.6354416874939133, -0.7535470743266395}; + expIndices = new int[][]{ + {4}, + {7}, + {25}, + {42}}; + exp = new CooTensor(expShape, expEntries, expIndices); + assertEquals(exp, A.flatten()); + + // -------------------------- Sub-case 2 -------------------------- + aShape = new Shape(3, 2, 4); + aEntries = new double[]{0.8809801303815625, 1.2884192212811383, 0.6540684159095426}; + aIndices = new int[][]{ + {0, 1, 3}, + {1, 0, 1}, + {1, 1, 0}}; + A = new CooTensor(aShape, aEntries, aIndices); + + expShape = new Shape(24); + expEntries = new double[]{0.8809801303815625, 1.2884192212811383, 0.6540684159095426}; + expIndices = new int[][]{ + {7}, + {9}, + {12}}; + exp = new CooTensor(expShape, expEntries, expIndices); + assertEquals(exp, A.flatten()); + + // -------------------------- Sub-case 3 -------------------------- + aShape = new Shape(3, 2, 4); + aEntries = new double[]{-0.2100281314624281, 1.4401356481011265, -0.1396976427551165}; + aIndices = new int[][]{ + {1, 0, 1}, + {1, 1, 1}, + {2, 0, 1}}; + A = new CooTensor(aShape, aEntries, aIndices); + + expShape = new Shape(1, 24, 1); + expEntries = new double[]{-0.2100281314624281, 1.4401356481011265, -0.1396976427551165}; + expIndices = new int[][]{ + {0, 9, 0}, + {0, 13, 0}, + {0, 17, 0}}; + exp = new CooTensor(expShape, expEntries, expIndices); + assertEquals(exp, A.flatten(1)); + + // -------------------------- Sub-case 4 -------------------------- + aShape = new Shape(3, 2, 4); + aEntries = new double[]{-0.2100281314624281, 1.4401356481011265, -0.1396976427551165}; + aIndices = new int[][]{ + {1, 0, 1}, + {1, 1, 1}, + {2, 0, 1}}; + A = new CooTensor(aShape, aEntries, aIndices); + + expShape = new Shape(1, 1, 24); + expEntries = new double[]{-0.2100281314624281, 1.4401356481011265, -0.1396976427551165}; + expIndices = new int[][]{ + {0, 0, 9}, + {0, 0, 13}, + {0, 0, 17}}; + exp = new CooTensor(expShape, expEntries, expIndices); + assertEquals(exp, A.flatten(2)); + + // -------------------------- Sub-case 5 -------------------------- + aShape = new Shape(3, 2, 4); + aEntries = new double[]{-0.2100281314624281, 1.4401356481011265, -0.1396976427551165}; + aIndices = new int[][]{ + {1, 0, 1}, + {1, 1, 1}, + {2, 0, 1}}; + A = new CooTensor(aShape, aEntries, aIndices); + assertThrows(IndexOutOfBoundsException.class, ()->A.flatten(5)); + assertThrows(IndexOutOfBoundsException.class, ()->A.flatten(-1)); + } +} diff --git a/src/test/java/org/flag4j/sparse_tensor/CooTensorTransposeTests.java b/src/test/java/org/flag4j/sparse_tensor/CooTensorTransposeTests.java new file mode 100644 index 000000000..b875ee008 --- /dev/null +++ b/src/test/java/org/flag4j/sparse_tensor/CooTensorTransposeTests.java @@ -0,0 +1,147 @@ +package org.flag4j.sparse_tensor; + +import org.flag4j.arrays.sparse.CooTensor; +import org.flag4j.core.Shape; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class CooTensorTransposeTests { + static CooTensor A; + static Shape aShape; + static double[] aEntries; + static int[][] aIndices; + + static CooTensor exp; + static Shape expShape; + static double[] expEntries; + static int[][] expIndices; + + @Test + void transposeTests() { + // ----------------------- Sub-case 1 ----------------------- + aShape = new Shape(3, 4, 2, 1, 5); + aEntries = new double[]{0.3369876012099134, -1.2778944176165061, 0.3003526634585191, -2.871698370528301, -0.7476330328637798, 0.0005862743350540067, 0.27050784380653264, -0.8804057494085602, 0.3433759561302188, -0.6271337465977211, -0.535101554634142, -1.9109729935630098, 0.7218896402785697, 0.8404528180001959, 0.5203214826154133, 0.3371173022767966, -0.6726729311708143, 0.08353784066932919}; + aIndices = new int[][]{ + {0, 0, 1, 0, 1}, + {0, 1, 1, 0, 4}, + {0, 2, 0, 0, 0}, + {0, 2, 1, 0, 1}, + {0, 3, 0, 0, 2}, + {1, 1, 0, 0, 0}, + {1, 1, 1, 0, 0}, + {1, 1, 1, 0, 2}, + {1, 2, 1, 0, 0}, + {1, 2, 1, 0, 4}, + {2, 0, 0, 0, 0}, + {2, 0, 0, 0, 4}, + {2, 0, 1, 0, 4}, + {2, 1, 0, 0, 0}, + {2, 1, 1, 0, 1}, + {2, 1, 1, 0, 3}, + {2, 1, 1, 0, 4}, + {2, 3, 0, 0, 2}}; + A = new CooTensor(aShape, aEntries, aIndices); + + expShape = new Shape(5, 4, 2, 1, 3); + expEntries = new double[]{-0.535101554634142, 0.0005862743350540067, 0.8404528180001959, 0.27050784380653264, 0.3003526634585191, 0.3433759561302188, 0.3369876012099134, 0.5203214826154133, -2.871698370528301, -0.8804057494085602, -0.7476330328637798, 0.08353784066932919, 0.3371173022767966, -1.9109729935630098, 0.7218896402785697, -1.2778944176165061, -0.6726729311708143, -0.6271337465977211}; + expIndices = new int[][]{ + {0, 0, 0, 0, 2}, + {0, 1, 0, 0, 1}, + {0, 1, 0, 0, 2}, + {0, 1, 1, 0, 1}, + {0, 2, 0, 0, 0}, + {0, 2, 1, 0, 1}, + {1, 0, 1, 0, 0}, + {1, 1, 1, 0, 2}, + {1, 2, 1, 0, 0}, + {2, 1, 1, 0, 1}, + {2, 3, 0, 0, 0}, + {2, 3, 0, 0, 2}, + {3, 1, 1, 0, 2}, + {4, 0, 0, 0, 2}, + {4, 0, 1, 0, 2}, + {4, 1, 1, 0, 0}, + {4, 1, 1, 0, 2}, + {4, 2, 1, 0, 1}}; + exp = new CooTensor(expShape, expEntries, expIndices); + assertEquals(exp, A.T()); + + // ----------------------- Sub-case 2 ----------------------- + aShape = new Shape(3, 4, 2, 1, 5); + aEntries = new double[]{0.5495928325292689, -0.4063964781150276, -2.059856306563159, 0.21626315719045072, 0.700470594502127, -0.9551549318514719, 0.20625241662218816, -0.002902831572102285, 0.027379206391192006, -0.048618618464467335, -0.062183293526625626}; + aIndices = new int[][]{ + {0, 1, 0, 0, 1}, + {0, 1, 0, 0, 2}, + {0, 1, 1, 0, 3}, + {1, 0, 1, 0, 0}, + {1, 1, 0, 0, 0}, + {1, 2, 0, 0, 1}, + {2, 0, 1, 0, 1}, + {2, 0, 1, 0, 3}, + {2, 0, 1, 0, 4}, + {2, 1, 1, 0, 3}, + {2, 2, 1, 0, 1}}; + A = new CooTensor(aShape, aEntries, aIndices); + + expShape = new Shape(2, 4, 3, 1, 5); + expEntries = new double[]{0.5495928325292689, -0.4063964781150276, 0.700470594502127, -0.9551549318514719, 0.21626315719045072, 0.20625241662218816, -0.002902831572102285, 0.027379206391192006, -2.059856306563159, -0.048618618464467335, -0.062183293526625626}; + expIndices = new int[][]{ + {0, 1, 0, 0, 1}, + {0, 1, 0, 0, 2}, + {0, 1, 1, 0, 0}, + {0, 2, 1, 0, 1}, + {1, 0, 1, 0, 0}, + {1, 0, 2, 0, 1}, + {1, 0, 2, 0, 3}, + {1, 0, 2, 0, 4}, + {1, 1, 0, 0, 3}, + {1, 1, 2, 0, 3}, + {1, 2, 2, 0, 1}}; + exp = new CooTensor(expShape, expEntries, expIndices); + assertEquals(exp, A.T(0, 2)); + assertEquals(exp, A.T(2, 0)); + + // ----------------------- Sub-case 3 ----------------------- + aShape = new Shape(3, 4, 2, 1, 5); + aEntries = new double[]{0.7755080687938627, -0.35048451266628083, -1.1870851252844012, 0.7738863613063384, 1.486547192285357, 0.2252740863565129, -0.2975058226231608, -0.09472050340155494, -0.15340207810250436, 2.84248590850813, -1.7279407634814916}; + aIndices = new int[][]{ + {0, 1, 0, 0, 2}, + {0, 1, 0, 0, 3}, + {0, 1, 1, 0, 2}, + {0, 3, 1, 0, 1}, + {1, 0, 0, 0, 2}, + {1, 1, 1, 0, 0}, + {2, 0, 0, 0, 0}, + {2, 0, 0, 0, 3}, + {2, 3, 0, 0, 0}, + {2, 3, 0, 0, 1}, + {2, 3, 1, 0, 0}}; + A = new CooTensor(aShape, aEntries, aIndices); + + expShape = new Shape(3, 1, 5, 4, 2); + expEntries = new double[]{0.7738863613063384, 0.7755080687938627, -1.1870851252844012, -0.35048451266628083, 0.2252740863565129, 1.486547192285357, -0.2975058226231608, -0.15340207810250436, -1.7279407634814916, 2.84248590850813, -0.09472050340155494}; + expIndices = new int[][]{ + {0, 0, 1, 3, 1}, + {0, 0, 2, 1, 0}, + {0, 0, 2, 1, 1}, + {0, 0, 3, 1, 0}, + {1, 0, 0, 1, 1}, + {1, 0, 2, 0, 0}, + {2, 0, 0, 0, 0}, + {2, 0, 0, 3, 0}, + {2, 0, 0, 3, 1}, + {2, 0, 1, 3, 0}, + {2, 0, 3, 0, 0}}; + exp = new CooTensor(expShape, expEntries, expIndices); + assertEquals(exp, A.T(0, 3, 4, 1, 2)); + + // ----------------------- Sub-case 4 ----------------------- + assertThrows(IllegalArgumentException.class, ()->A.T(0, 1, 3, 2)); + assertThrows(IllegalArgumentException.class, ()->A.T(0, 3, 4, 1, 2, 5)); + assertThrows(IllegalArgumentException.class, ()->A.T(0, 3, -4, 1, 2)); + assertThrows(IllegalArgumentException.class, ()->A.T(0, 15, 4, 1, 2)); + assertThrows(IndexOutOfBoundsException.class, ()->A.T(5, 1)); + } +} diff --git a/target/flag4j-v0.1.0-beta.jar b/target/flag4j-v0.1.0-beta.jar index 26451c1f65c60a3f5e830f006de4cf8f000952a7..2c1d72bc94a95831f2d644265b6fa173f825555a 100644 GIT binary patch delta 25412 zcmZsD1z1#Vw>HcSFhh4q2uOD~NOzZXcStJ|Bdv4~ARyfcNQWRHNSA^jDIwh|BIrM} zeb4uu_guf%z*^6`SFdO98Qin?=VaE&R90MF4KM^74GoHhmRpsI%M3#P7Q6xFRHgHy z3!#WJx-g0qpo^l&0=giI;DWgR3h5ELJ0Sf3Fg8k>02V~hdxA02pZ?EU9wnXz-$yA=g9V9ve0l$yT22+NY5ExS4$1|h;3*6< zJrM%<$$`kB4CJ7Hy%S&%quelI2%v}wh6suzV@RRM1cu7r6&J!g9)|h9az=uNm@+6Z z0~0WOk138?Nk7E}@-~NwAf6IoVd4F&NTfC(CQN^dhn+qQB}|{kq|Q>o!uoe08ysnr zPdO+X5+h01fdMXWaQP4^D_GEUy1yar;{j_dgf|HkmL3AdPWQ)^{l^Y59SSA+pImNm zf!^Q8Q%9i*c$_GL3!+7wsK7ANKjQ)33()_r5p+WTsynwT{VqNya@VA5;ftV1G`=)) zB29z%Dq1LtX8|E4$VQIu&RepJLpn4xL3_0H8GO1X8E+6hN^ZAgct|n=0?mX z$EEv$fvW$l1Iwf)p-&YKPrIQxBF@;YF|MWg3wbfL#kj7eHITQ(t>&XwYha5}&G}2e zwHLz<=P})iaN&#Nu9H>2sh^V_Ukm(JFFFY5C80K{HG^0bwokZLOO8QR%}^`!RA`VC>h;l@Ti2X$4pTSTkspTAEANY8 z{*0n>y@)t_eA*o~LefNH?%^$Wde4EX|BCD27eBRq2%NH}1Z@+F(OLp6>5OX|g zohie`Vr`b>IiEUne>w$;KdBvKA4S~xQ{4!@SFZNnehnNnHEuZX2KrPVyL(&S6IzS4 z>*rDd{gTV(n$}@x*PzDTsMyO+qU!b*>dd^K7?d5oC^J5z7op#;+Tx-kKvU(0&6*~~ zPqa#{9|Er~LZ{uXw2?_~jER-ut0^DwZQcBSC)HN22

vJ5_F6?b8w{>uzP=^MsJ#d*s{O!&w>l_t;9l?p2bxUqqMCZUh#szmt;X z@QI-RCbk%>v-{wgQq;}(UR2(suCLF%Y@3GR%aVGy1XT|v*O_fqlFk>>{s^H#`<0@{ zd{jwF+D zVvQq){@2MqRWX>Rnkw;0j>WR{3ZJMGywZB+{F{ptJ>&W~xLrNC0u*PG=Ne~Y1^q*) zYoG?R@EtZz3%UfXF8LANkh2)wUo6TzF9S&8SWV88c3lzzqgn~$n#3^|&%$c^oI@Ve zY-;9Zz6L9qQSljE#%PvZsm}|YlVA3Yq-sM?|wdft=v_|x~v#5I4HTz zzdxdE-=_LUT;hw3T(crujw`}6h&sYI*xzHQqx&818}7K_2->4QMg4B7jdqVu4twNm z$|Xu+t@~p16ofY(k>w_2vL+ns$wNvKGV$?2YdQEIg~$kM+70pA4Tl{+vD^{N!d1>H zT|S0KWw7pIvxpjUV?Pp6PA}5l)~bJ-DPvJ)-}T`4gD$x!qK(RICmolOESq5c`3~jU zbu+*1%!v12{VkI3a4C{HC(bA}7P!PduvfEJ-?X0gVe+yonvdPCJr%(CwnZK?6KfuD z_||jaRHK$&K^l+W__5cy>aV=y8H37v2f^2oaQch;WB2_-)P`w4jp(=5SPE4PR(WFh zR3z2gAZYZzVs<6oy5AL|w*KfNdc1MVo=omV^pV_AqAX11YOmd7ctO#ivxs8AXe_e+ z;1d&pG8I8q>=#REwdkhWOD)sAFoxb8wk`U%kup-Ho3>K&F4Tt3XS#g8#_B$GA?a1! zj_`S*)y}>oeEA>4g>ODQ|Dp^2HT`v^W`0MPe!yfCYCw&3wy

ej|^#4cO-G1ZXBZFbB}x9 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 delta 24858 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 BwF>|M