Skip to content

Commit

Permalink
Merge pull request #7 from HHS/indexer_migration
Browse files Browse the repository at this point in the history
Indexer migration
  • Loading branch information
shawnhatch authored Feb 1, 2024
2 parents 6e4e7a8 + 47cf94f commit 7c82a29
Show file tree
Hide file tree
Showing 10 changed files with 672 additions and 0 deletions.
63 changes: 63 additions & 0 deletions src/main/java/util/indexer/Indexer2.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package util.indexer;

import net.jcip.annotations.ThreadSafe;
import util.errors.ContractException;

/**
* A thread safe, immutable index converter that supports treating a 2D array as
* a 1D array.
*/

@ThreadSafe
public final class Indexer2 {

private final int size;

private final int p1;

/**
* Constructs an 2D indexer from the given dimension sizes
*
* @throws ContractException
* <ul>
* <li>{@linkplain IndexerError#NEGATIVE_DIMENSION_SIZE}
* if any dimension has a negative size</li>
* <li>{@linkplain IndexerError#EXCEEDS_MAX_ARRAY_SIZE}
* if the product of the dimensions exceeds max
* int</li>
* </ul>
*
*
*/
public Indexer2(final int dim1, final int dim2) {
if (dim1 < 0) {
throw new ContractException(IndexerError.NEGATIVE_DIMENSION_SIZE);
}
if (dim2 < 0) {
throw new ContractException(IndexerError.NEGATIVE_DIMENSION_SIZE);
}
try {
p1 = dim2;
size = Math.multiplyExact(p1, dim1);
} catch (ArithmeticException e) {
throw new ContractException(IndexerError.EXCEEDS_MAX_ARRAY_SIZE);
}
}

/**
* Returns the combined index = index1*dim2+index2 from the given index values.
* No validation of the indexes or the result is provided.
*/
public int index(final int index1, final int index2) {
return index1 * p1 + index2;
}

/**
* Returns the size (product of the dimensions) of the array supported by this
* index.
*/
public int size() {
return size;
}

}
63 changes: 63 additions & 0 deletions src/main/java/util/indexer/Indexer3.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package util.indexer;

import util.errors.ContractException;

/**
* A thread safe, immutable index converter that supports treating a 3D array as
* a 1D array.
*/
public final class Indexer3 {
private final int size;
private final int p1;
private final int p2;

/**
* Constructs an 3D indexer from the given dimension sizes
*
* @throws ContractException
* <ul>
* <li>{@linkplain IndexerError#NEGATIVE_DIMENSION_SIZE}
* if any dimension has a negative size</li>
* <li>{@linkplain IndexerError#EXCEEDS_MAX_ARRAY_SIZE}
* if the product of the dimensions exceeds max
* int</li>
* </ul>
*
*
*/
public Indexer3(final int dim1, final int dim2, final int dim3) {
if (dim1 < 0) {
throw new ContractException(IndexerError.NEGATIVE_DIMENSION_SIZE);
}
if (dim2 < 0) {
throw new ContractException(IndexerError.NEGATIVE_DIMENSION_SIZE);
}
if (dim3 < 0) {
throw new ContractException(IndexerError.NEGATIVE_DIMENSION_SIZE);
}

try {
p2 = dim3;
p1 = Math.multiplyExact(p2, dim2);
size = Math.multiplyExact(p1, dim1);
} catch (ArithmeticException e) {
throw new ContractException(IndexerError.EXCEEDS_MAX_ARRAY_SIZE);
}
}

/**
* Returns the combined index = (index1*dim2 +index2)*dim3+index3 from the given
* index values. No validation of the indexes or the result is provided.
*/
public int index(final int index1, final int index2, final int index3) {
return index1 * p1 + index2 * p2 + index3;
}

/**
* Returns the size (product of the dimensions) of the array supported by this
* index.
*/
public int size() {
return size;
}
}
70 changes: 70 additions & 0 deletions src/main/java/util/indexer/Indexer4.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package util.indexer;

import util.errors.ContractException;

/**
* A thread safe, immutable index converter that supports treating a 4D array as
* a 1D array.
*/
public final class Indexer4 {
private final int size;
private final int p1;
private final int p2;
private final int p3;

/**
* Constructs an 4D indexer from the given dimension sizes
*
* @throws ContractException
* <ul>
* <li>{@linkplain IndexerError#NEGATIVE_DIMENSION_SIZE}
* if any dimension has a negative size</li>
* <li>{@linkplain IndexerError#EXCEEDS_MAX_ARRAY_SIZE}
* if the product of the dimensions exceeds max
* int</li>
* </ul>
*
*
*/
public Indexer4(final int dim1, final int dim2, final int dim3, final int dim4) {
if (dim1 < 0) {
throw new ContractException(IndexerError.NEGATIVE_DIMENSION_SIZE);
}
if (dim2 < 0) {
throw new ContractException(IndexerError.NEGATIVE_DIMENSION_SIZE);
}
if (dim3 < 0) {
throw new ContractException(IndexerError.NEGATIVE_DIMENSION_SIZE);
}
if (dim4 < 0) {
throw new ContractException(IndexerError.NEGATIVE_DIMENSION_SIZE);
}

try {
p3 = dim4;
p2 = Math.multiplyExact(p3, dim3);
p1 = Math.multiplyExact(p2, dim2);
size = Math.multiplyExact(p1, dim1);
} catch (ArithmeticException e) {
throw new ContractException(IndexerError.EXCEEDS_MAX_ARRAY_SIZE);
}
}

/**
* Returns the combined index = ((index1*dim2 +index2)*dim3+index3)*dim4+index4
* from the given index values. No validation of the indexes or the result is
* provided.
*/
public int index(final int index1, final int index2, final int index3, final int index4) {
return index1 * p1 + index2 * p2 + index3 * p3 + index4;
}

/**
* Returns the size (product of the dimensions) of the array supported by this
* index.
*/
public int size() {
return size;
}

}
76 changes: 76 additions & 0 deletions src/main/java/util/indexer/Indexer5.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package util.indexer;

import util.errors.ContractException;

/**
* A thread safe, immutable index converter that supports treating a 5D array as
* a 1D array.
*/
public final class Indexer5 {
private final int size;
private final int p1;
private final int p2;
private final int p3;
private final int p4;

/**
* Constructs an 5D indexer from the given dimension sizes
*
* @throws ContractException
* <ul>
* <li>{@linkplain IndexerError#NEGATIVE_DIMENSION_SIZE}
* if any dimension has a negative size</li>
* <li>{@linkplain IndexerError#EXCEEDS_MAX_ARRAY_SIZE}
* if the product of the dimensions exceeds max
* int</li>
* </ul>
*
*
*/
public Indexer5(final int dim1, final int dim2, final int dim3, final int dim4, final int dim5) {
if (dim1 < 0) {
throw new ContractException(IndexerError.NEGATIVE_DIMENSION_SIZE);
}
if (dim2 < 0) {
throw new ContractException(IndexerError.NEGATIVE_DIMENSION_SIZE);
}
if (dim3 < 0) {
throw new ContractException(IndexerError.NEGATIVE_DIMENSION_SIZE);
}
if (dim4 < 0) {
throw new ContractException(IndexerError.NEGATIVE_DIMENSION_SIZE);
}
if (dim5 < 0) {
throw new ContractException(IndexerError.NEGATIVE_DIMENSION_SIZE);
}

try {
p4 = dim5;
p3 = Math.multiplyExact(p4, dim4);
p2 = Math.multiplyExact(p3, dim3);
p1 = Math.multiplyExact(p2, dim2);
size = Math.multiplyExact(p1, dim1);
} catch (ArithmeticException e) {
throw new ContractException(IndexerError.EXCEEDS_MAX_ARRAY_SIZE);
}

}

/**
* Returns the combined index = (((index1*dim2
* +index2)*dim3+index3)*dim4+index4)*dim5+index5 from the given index values.
* No validation of the indexes or the result is provided.
*/
public int index(final int index1, final int index2, final int index3, final int index4, final int index5) {
return index1 * p1 + index2 * p2 + index3 * p3 + index4 * p4 + index5;
}

/**
* Returns the size (product of the dimensions) of the array supported by this
* index.
*/
public int size() {
return size;
}

}
27 changes: 27 additions & 0 deletions src/main/java/util/indexer/IndexerError.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package util.indexer;

import util.errors.ContractError;
import util.errors.ContractException;

/**
* An enumeration supporting {@link ContractException} that acts as a general
* description of the exception.
*/
public enum IndexerError implements ContractError {
NEGATIVE_DIMENSION_SIZE("Negative dimension size"),
EXCEEDS_MAX_ARRAY_SIZE("The size exceeds max int"),
;



private final String description;

private IndexerError(final String description) {
this.description = description;
}

@Override
public String getDescription() {
return description;
}
}
73 changes: 73 additions & 0 deletions src/test/java/util/indexer/AT_Indexer2.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package util.indexer;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

import org.apache.commons.math3.random.RandomGenerator;
import org.apache.commons.math3.util.FastMath;
import org.junit.jupiter.api.Test;

import util.annotations.UnitTestConstructor;
import util.annotations.UnitTestMethod;
import util.errors.ContractException;
import util.random.RandomGeneratorProvider;

public class AT_Indexer2 {
@Test
@UnitTestConstructor(target = Indexer2.class, args = { int.class, int.class })
public void testIndexer2() {

// precondition test: if any dimension has a negative size

ContractException contractException = assertThrows(ContractException.class, () -> new Indexer2(-1, 1));
assertEquals(IndexerError.NEGATIVE_DIMENSION_SIZE, contractException.getErrorType());

contractException = assertThrows(ContractException.class, () -> new Indexer2(1, -1));
assertEquals(IndexerError.NEGATIVE_DIMENSION_SIZE, contractException.getErrorType());

// precondition test: if the product of the dimensions exceeds max int
int n = (int) FastMath.pow(Integer.MAX_VALUE, 1.0 / 2) + 1;

contractException = assertThrows(ContractException.class, () -> new Indexer2(n, n));
assertEquals(IndexerError.EXCEEDS_MAX_ARRAY_SIZE, contractException.getErrorType());

}

@Test
@UnitTestMethod(target = Indexer2.class, name = "index", args = { int.class, int.class })
public void testIndex() {
RandomGenerator randomGenerator = RandomGeneratorProvider.getRandomGenerator(6937151566492160140L);

//show that index = index1*dim2+index2

for (int i = 0; i < 30; i++) {
int dim1 = randomGenerator.nextInt(30) + 1;
int dim2 = randomGenerator.nextInt(30) + 1;

Indexer2 indexer2 = new Indexer2(dim1, dim2);

for (int j = 0; j < 100; j++) {

int index1 = randomGenerator.nextInt(dim1);
int index2 = randomGenerator.nextInt(dim2);

assertEquals(index1 * dim2 + index2, indexer2.index(index1, index2));
}
}
}

@Test
@UnitTestMethod(target = Indexer2.class, name = "size", args = {})
public void testSize() {
RandomGenerator randomGenerator = RandomGeneratorProvider.getRandomGenerator(1286993379829775736L);

for (int i = 0; i < 30; i++) {
int dim1 = randomGenerator.nextInt(30) + 1;
int dim2 = randomGenerator.nextInt(30) + 1;

Indexer2 indexer2 = new Indexer2(dim1, dim2);

assertEquals(dim1*dim2, indexer2.size());
}
}
}
Loading

0 comments on commit 7c82a29

Please sign in to comment.