diff --git a/pom.xml b/pom.xml index 01e4eb3..3f13131 100755 --- a/pom.xml +++ b/pom.xml @@ -102,6 +102,21 @@ + + + + + org.apache.maven.plugins + maven-compiler-plugin + true + + ${version.jdk} + ${version.jdk} + + + + + diff --git a/storage-units-model/src/main/java/wtf/metio/storageunits/model/Byte.java b/storage-units-model/src/main/java/wtf/metio/storageunits/model/Byte.java index f64ddec..a402188 100644 --- a/storage-units-model/src/main/java/wtf/metio/storageunits/model/Byte.java +++ b/storage-units-model/src/main/java/wtf/metio/storageunits/model/Byte.java @@ -107,7 +107,7 @@ public final class Byte extends StorageUnit { } @Override - protected @NotNull BigInteger getNumberOfBytesPerUnit() { + public @NotNull BigInteger getNumberOfBytesPerUnit() { return BigInteger.ONE; } @@ -123,4 +123,9 @@ public final class Byte extends StorageUnit { return StorageUnits::binaryValueOf; } + @Override + protected int conversionScale() { + return 0; + } + } diff --git a/storage-units-model/src/main/java/wtf/metio/storageunits/model/Exabyte.java b/storage-units-model/src/main/java/wtf/metio/storageunits/model/Exabyte.java index 9738d51..1babfba 100755 --- a/storage-units-model/src/main/java/wtf/metio/storageunits/model/Exabyte.java +++ b/storage-units-model/src/main/java/wtf/metio/storageunits/model/Exabyte.java @@ -19,6 +19,8 @@ public final class Exabyte extends StorageUnit { @Serial private static final long serialVersionUID = 6846441733771841250L; + private static final int conversionScale = computeFiniteConversionScale(StorageUnit.BYTES_IN_A_EXABYTE); + Exabyte(final @NotNull BigInteger numberOfBytes) { super(numberOfBytes); } @@ -112,7 +114,7 @@ public final class Exabyte extends StorageUnit { @Override @CheckReturnValue - protected @NotNull BigInteger getNumberOfBytesPerUnit() { + public @NotNull BigInteger getNumberOfBytesPerUnit() { return StorageUnit.BYTES_IN_A_EXABYTE; } @@ -128,4 +130,9 @@ public final class Exabyte extends StorageUnit { return StorageUnits::decimalValueOf; } + @Override + protected int conversionScale() { + return conversionScale; + } + } diff --git a/storage-units-model/src/main/java/wtf/metio/storageunits/model/Exbibyte.java b/storage-units-model/src/main/java/wtf/metio/storageunits/model/Exbibyte.java index aa00d47..56bfdf9 100755 --- a/storage-units-model/src/main/java/wtf/metio/storageunits/model/Exbibyte.java +++ b/storage-units-model/src/main/java/wtf/metio/storageunits/model/Exbibyte.java @@ -19,6 +19,8 @@ public final class Exbibyte extends StorageUnit { @Serial private static final long serialVersionUID = 5993490571003918471L; + private static final int conversionScale = computeFiniteConversionScale(StorageUnit.BYTES_IN_A_EXBIBYTE); + Exbibyte(final @NotNull BigInteger numberOfBytes) { super(numberOfBytes); } @@ -38,7 +40,7 @@ public final class Exbibyte extends StorageUnit { */ @CheckReturnValue public static @NotNull Exbibyte valueOf(final long numberOfBytes) { - return valueOf(BigInteger.valueOf(numberOfBytes)); + return valueOf(java.math.BigInteger.valueOf(numberOfBytes)); } /** @@ -112,7 +114,7 @@ public final class Exbibyte extends StorageUnit { @Override @CheckReturnValue - protected @NotNull BigInteger getNumberOfBytesPerUnit() { + public @NotNull BigInteger getNumberOfBytesPerUnit() { return StorageUnit.BYTES_IN_A_EXBIBYTE; } @@ -128,4 +130,9 @@ public final class Exbibyte extends StorageUnit { return StorageUnits::binaryValueOf; } + @Override + protected int conversionScale() { + return conversionScale; + } + } diff --git a/storage-units-model/src/main/java/wtf/metio/storageunits/model/Gibibyte.java b/storage-units-model/src/main/java/wtf/metio/storageunits/model/Gibibyte.java index 2892325..72eb714 100755 --- a/storage-units-model/src/main/java/wtf/metio/storageunits/model/Gibibyte.java +++ b/storage-units-model/src/main/java/wtf/metio/storageunits/model/Gibibyte.java @@ -19,6 +19,8 @@ public final class Gibibyte extends StorageUnit { @Serial private static final long serialVersionUID = -1104749948510944566L; + private static final int conversionScale = computeFiniteConversionScale(StorageUnit.BYTES_IN_A_GIBIBYTE); + Gibibyte(final @NotNull BigInteger numberOfBytes) { super(numberOfBytes); } @@ -112,7 +114,7 @@ public final class Gibibyte extends StorageUnit { @Override @CheckReturnValue - protected @NotNull BigInteger getNumberOfBytesPerUnit() { + public @NotNull BigInteger getNumberOfBytesPerUnit() { return BYTES_IN_A_GIBIBYTE; } @@ -128,4 +130,9 @@ public final class Gibibyte extends StorageUnit { return StorageUnits::binaryValueOf; } + @Override + protected int conversionScale() { + return conversionScale; + } + } diff --git a/storage-units-model/src/main/java/wtf/metio/storageunits/model/Gigabyte.java b/storage-units-model/src/main/java/wtf/metio/storageunits/model/Gigabyte.java index 3295fb0..147d7aa 100755 --- a/storage-units-model/src/main/java/wtf/metio/storageunits/model/Gigabyte.java +++ b/storage-units-model/src/main/java/wtf/metio/storageunits/model/Gigabyte.java @@ -19,6 +19,8 @@ public final class Gigabyte extends StorageUnit { @Serial private static final long serialVersionUID = 7581075190529125530L; + private static final int conversionScale = computeFiniteConversionScale(StorageUnit.BYTES_IN_A_GIGABYTE); + Gigabyte(final @NotNull BigInteger numberOfBytes) { super(numberOfBytes); } @@ -112,7 +114,7 @@ public final class Gigabyte extends StorageUnit { @Override @CheckReturnValue - protected @NotNull BigInteger getNumberOfBytesPerUnit() { + public @NotNull BigInteger getNumberOfBytesPerUnit() { return BYTES_IN_A_GIGABYTE; } @@ -128,4 +130,9 @@ public final class Gigabyte extends StorageUnit { return StorageUnits::decimalValueOf; } + @Override + protected int conversionScale() { + return conversionScale; + } + } diff --git a/storage-units-model/src/main/java/wtf/metio/storageunits/model/Kibibyte.java b/storage-units-model/src/main/java/wtf/metio/storageunits/model/Kibibyte.java index b7f57c3..d8e670f 100755 --- a/storage-units-model/src/main/java/wtf/metio/storageunits/model/Kibibyte.java +++ b/storage-units-model/src/main/java/wtf/metio/storageunits/model/Kibibyte.java @@ -19,6 +19,8 @@ public final class Kibibyte extends StorageUnit { @Serial private static final long serialVersionUID = 3798828851496657978L; + private static final int conversionScale = computeFiniteConversionScale(StorageUnit.BYTES_IN_A_KIBIBYTE); + Kibibyte(final @NotNull BigInteger numberOfBytes) { super(numberOfBytes); } @@ -112,7 +114,7 @@ public final class Kibibyte extends StorageUnit { @Override @CheckReturnValue - protected @NotNull BigInteger getNumberOfBytesPerUnit() { + public @NotNull BigInteger getNumberOfBytesPerUnit() { return StorageUnit.BYTES_IN_A_KIBIBYTE; } @@ -128,4 +130,9 @@ public final class Kibibyte extends StorageUnit { return StorageUnits::binaryValueOf; } + @Override + protected int conversionScale() { + return conversionScale; + } + } diff --git a/storage-units-model/src/main/java/wtf/metio/storageunits/model/Kilobyte.java b/storage-units-model/src/main/java/wtf/metio/storageunits/model/Kilobyte.java index e536ec3..5665325 100755 --- a/storage-units-model/src/main/java/wtf/metio/storageunits/model/Kilobyte.java +++ b/storage-units-model/src/main/java/wtf/metio/storageunits/model/Kilobyte.java @@ -19,6 +19,8 @@ public final class Kilobyte extends StorageUnit { @Serial private static final long serialVersionUID = 6952239416014811456L; + private static final int conversionScale = computeFiniteConversionScale(StorageUnit.BYTES_IN_A_KILOBYTE); + Kilobyte(final @NotNull BigInteger numberOfBytes) { super(numberOfBytes); } @@ -112,7 +114,7 @@ public final class Kilobyte extends StorageUnit { @Override @CheckReturnValue - protected @NotNull BigInteger getNumberOfBytesPerUnit() { + public @NotNull BigInteger getNumberOfBytesPerUnit() { return BYTES_IN_A_KILOBYTE; } @@ -128,4 +130,9 @@ public final class Kilobyte extends StorageUnit { return StorageUnits::decimalValueOf; } + @Override + protected int conversionScale() { + return conversionScale; + } + } diff --git a/storage-units-model/src/main/java/wtf/metio/storageunits/model/Mebibyte.java b/storage-units-model/src/main/java/wtf/metio/storageunits/model/Mebibyte.java index d9ce27e..7d354fb 100755 --- a/storage-units-model/src/main/java/wtf/metio/storageunits/model/Mebibyte.java +++ b/storage-units-model/src/main/java/wtf/metio/storageunits/model/Mebibyte.java @@ -19,6 +19,8 @@ public final class Mebibyte extends StorageUnit { @Serial private static final long serialVersionUID = 7697583678146919524L; + private static final int conversionScale = computeFiniteConversionScale(StorageUnit.BYTES_IN_A_MEBIBYTE); + Mebibyte(final @NotNull BigInteger numberOfBytes) { super(numberOfBytes); } @@ -112,7 +114,7 @@ public final class Mebibyte extends StorageUnit { @Override @CheckReturnValue - protected @NotNull BigInteger getNumberOfBytesPerUnit() { + public @NotNull BigInteger getNumberOfBytesPerUnit() { return StorageUnit.BYTES_IN_A_MEBIBYTE; } @@ -128,4 +130,9 @@ public final class Mebibyte extends StorageUnit { return StorageUnits::binaryValueOf; } + @Override + protected int conversionScale() { + return conversionScale; + } + } diff --git a/storage-units-model/src/main/java/wtf/metio/storageunits/model/Megabyte.java b/storage-units-model/src/main/java/wtf/metio/storageunits/model/Megabyte.java index ca8debd..12e4e88 100755 --- a/storage-units-model/src/main/java/wtf/metio/storageunits/model/Megabyte.java +++ b/storage-units-model/src/main/java/wtf/metio/storageunits/model/Megabyte.java @@ -19,6 +19,8 @@ public final class Megabyte extends StorageUnit { @Serial private static final long serialVersionUID = 5901923092058760111L; + private static final int conversionScale = computeFiniteConversionScale(StorageUnit.BYTES_IN_A_MEGABYTE); + Megabyte(final @NotNull BigInteger numberOfBytes) { super(numberOfBytes); } @@ -112,7 +114,7 @@ public final class Megabyte extends StorageUnit { @Override @CheckReturnValue - protected @NotNull BigInteger getNumberOfBytesPerUnit() { + public @NotNull BigInteger getNumberOfBytesPerUnit() { return StorageUnit.BYTES_IN_A_MEGABYTE; } @@ -128,4 +130,9 @@ public final class Megabyte extends StorageUnit { return StorageUnits::decimalValueOf; } + @Override + protected int conversionScale() { + return conversionScale; + } + } diff --git a/storage-units-model/src/main/java/wtf/metio/storageunits/model/Pebibyte.java b/storage-units-model/src/main/java/wtf/metio/storageunits/model/Pebibyte.java index acb5691..64f78e2 100755 --- a/storage-units-model/src/main/java/wtf/metio/storageunits/model/Pebibyte.java +++ b/storage-units-model/src/main/java/wtf/metio/storageunits/model/Pebibyte.java @@ -19,6 +19,8 @@ public final class Pebibyte extends StorageUnit { @Serial private static final long serialVersionUID = -6112472064345339882L; + private static final int conversionScale = computeFiniteConversionScale(StorageUnit.BYTES_IN_A_PEBIBYTE); + Pebibyte(final @NotNull BigInteger numberOfBytes) { super(numberOfBytes); } @@ -112,7 +114,7 @@ public final class Pebibyte extends StorageUnit { @Override @CheckReturnValue - protected @NotNull BigInteger getNumberOfBytesPerUnit() { + public @NotNull BigInteger getNumberOfBytesPerUnit() { return StorageUnit.BYTES_IN_A_PEBIBYTE; } @@ -128,4 +130,9 @@ public final class Pebibyte extends StorageUnit { return StorageUnits::binaryValueOf; } + @Override + protected int conversionScale() { + return conversionScale; + } + } diff --git a/storage-units-model/src/main/java/wtf/metio/storageunits/model/Petabyte.java b/storage-units-model/src/main/java/wtf/metio/storageunits/model/Petabyte.java index e61c3f2..9d83211 100755 --- a/storage-units-model/src/main/java/wtf/metio/storageunits/model/Petabyte.java +++ b/storage-units-model/src/main/java/wtf/metio/storageunits/model/Petabyte.java @@ -19,6 +19,8 @@ public final class Petabyte extends StorageUnit { @Serial private static final long serialVersionUID = 5889808368085688387L; + private static final int conversionScale = computeFiniteConversionScale(StorageUnit.BYTES_IN_A_PETABYTE); + Petabyte(final @NotNull BigInteger numberOfBytes) { super(numberOfBytes); } @@ -112,7 +114,7 @@ public final class Petabyte extends StorageUnit { @Override @CheckReturnValue - protected @NotNull BigInteger getNumberOfBytesPerUnit() { + public @NotNull BigInteger getNumberOfBytesPerUnit() { return StorageUnit.BYTES_IN_A_PETABYTE; } @@ -128,4 +130,9 @@ public final class Petabyte extends StorageUnit { return StorageUnits::decimalValueOf; } + @Override + protected int conversionScale() { + return conversionScale; + } + } diff --git a/storage-units-model/src/main/java/wtf/metio/storageunits/model/Qubibyte.java b/storage-units-model/src/main/java/wtf/metio/storageunits/model/Qubibyte.java index 0d4f762..98fc0ba 100755 --- a/storage-units-model/src/main/java/wtf/metio/storageunits/model/Qubibyte.java +++ b/storage-units-model/src/main/java/wtf/metio/storageunits/model/Qubibyte.java @@ -19,6 +19,8 @@ public final class Qubibyte extends StorageUnit { @Serial private static final long serialVersionUID = 8611754914470986560L; + private static final int conversionScale = computeFiniteConversionScale(StorageUnit.BYTES_IN_A_QUBIBYTE); + Qubibyte(final @NotNull BigInteger numberOfBytes) { super(numberOfBytes); } @@ -112,7 +114,7 @@ public final class Qubibyte extends StorageUnit { @Override @CheckReturnValue - protected @NotNull BigInteger getNumberOfBytesPerUnit() { + public @NotNull BigInteger getNumberOfBytesPerUnit() { return StorageUnit.BYTES_IN_A_QUBIBYTE; } @@ -128,4 +130,9 @@ public final class Qubibyte extends StorageUnit { return StorageUnits::binaryValueOf; } + @Override + protected int conversionScale() { + return conversionScale; + } + } diff --git a/storage-units-model/src/main/java/wtf/metio/storageunits/model/Quettabyte.java b/storage-units-model/src/main/java/wtf/metio/storageunits/model/Quettabyte.java index 55e4ba5..585b4e9 100755 --- a/storage-units-model/src/main/java/wtf/metio/storageunits/model/Quettabyte.java +++ b/storage-units-model/src/main/java/wtf/metio/storageunits/model/Quettabyte.java @@ -19,6 +19,8 @@ public final class Quettabyte extends StorageUnit { @Serial private static final long serialVersionUID = -7866123408102424489L; + private static final int conversionScale = computeFiniteConversionScale(StorageUnit.BYTES_IN_A_QUETTABYTE); + Quettabyte(final @NotNull BigInteger numberOfBytes) { super(numberOfBytes); } @@ -112,7 +114,7 @@ public final class Quettabyte extends StorageUnit { @Override @CheckReturnValue - protected @NotNull BigInteger getNumberOfBytesPerUnit() { + public @NotNull BigInteger getNumberOfBytesPerUnit() { return StorageUnit.BYTES_IN_A_QUETTABYTE; } @@ -128,4 +130,9 @@ public final class Quettabyte extends StorageUnit { return StorageUnits::decimalValueOf; } + @Override + protected int conversionScale() { + return conversionScale; + } + } diff --git a/storage-units-model/src/main/java/wtf/metio/storageunits/model/Robibyte.java b/storage-units-model/src/main/java/wtf/metio/storageunits/model/Robibyte.java index 37e09c0..bfd384e 100755 --- a/storage-units-model/src/main/java/wtf/metio/storageunits/model/Robibyte.java +++ b/storage-units-model/src/main/java/wtf/metio/storageunits/model/Robibyte.java @@ -19,6 +19,8 @@ public final class Robibyte extends StorageUnit { @Serial private static final long serialVersionUID = 3553336770900659080L; + private static final int conversionScale = computeFiniteConversionScale(StorageUnit.BYTES_IN_A_ROBIBYTE); + Robibyte(final @NotNull BigInteger numberOfBytes) { super(numberOfBytes); } @@ -112,7 +114,7 @@ public final class Robibyte extends StorageUnit { @Override @CheckReturnValue - protected @NotNull BigInteger getNumberOfBytesPerUnit() { + public @NotNull BigInteger getNumberOfBytesPerUnit() { return StorageUnit.BYTES_IN_A_ROBIBYTE; } @@ -128,4 +130,9 @@ public final class Robibyte extends StorageUnit { return StorageUnits::binaryValueOf; } + @Override + protected int conversionScale() { + return conversionScale; + } + } diff --git a/storage-units-model/src/main/java/wtf/metio/storageunits/model/Ronnabyte.java b/storage-units-model/src/main/java/wtf/metio/storageunits/model/Ronnabyte.java index a700624..ce2b99b 100755 --- a/storage-units-model/src/main/java/wtf/metio/storageunits/model/Ronnabyte.java +++ b/storage-units-model/src/main/java/wtf/metio/storageunits/model/Ronnabyte.java @@ -19,6 +19,8 @@ public final class Ronnabyte extends StorageUnit { @Serial private static final long serialVersionUID = -7866123408102424489L; + private static final int conversionScale = computeFiniteConversionScale(StorageUnit.BYTES_IN_A_RONNABYTE); + Ronnabyte(final @NotNull BigInteger numberOfBytes) { super(numberOfBytes); } @@ -112,7 +114,7 @@ public final class Ronnabyte extends StorageUnit { @Override @CheckReturnValue - protected @NotNull BigInteger getNumberOfBytesPerUnit() { + public @NotNull BigInteger getNumberOfBytesPerUnit() { return StorageUnit.BYTES_IN_A_RONNABYTE; } @@ -128,4 +130,9 @@ public final class Ronnabyte extends StorageUnit { return StorageUnits::decimalValueOf; } + @Override + protected int conversionScale() { + return conversionScale; + } + } diff --git a/storage-units-model/src/main/java/wtf/metio/storageunits/model/StorageUnit.java b/storage-units-model/src/main/java/wtf/metio/storageunits/model/StorageUnit.java index 37d4049..dbf39d4 100755 --- a/storage-units-model/src/main/java/wtf/metio/storageunits/model/StorageUnit.java +++ b/storage-units-model/src/main/java/wtf/metio/storageunits/model/StorageUnit.java @@ -563,6 +563,86 @@ public final long longValue() { return bytes.longValue(); } + /** + * Returns the value in this unit. + * @param roundingMode - the rounding mode for the division that divides the + * underlying byte value by number of bytes in a single measure of this unit. + * This parameter has no effect unless the unit has exuberant bytes per unit value. + * @return value in this unit. + */ + @CheckReturnValue + public final BigDecimal unitValue(RoundingMode roundingMode) { + var divisor = new BigDecimal(getNumberOfBytesPerUnit()); + var result = new BigDecimal(bytes).divide(divisor, conversionScale(), roundingMode).stripTrailingZeros(); + if (result.scale() < 0) { result = result.setScale(0); } + return result; + } + + protected abstract int conversionScale(); + + protected static int computeFiniteConversionScale(BigInteger bpu) { + + // $TODO Use of static is ugly. + // But there is no lazy initialization in this library. + // One can stuff this into a final's field assignment, but + // assigning final fields from methods is evil. Moving to Kotlin, + // would be "better" alternatives. + + // the default implementation is good for units provided by this library, + // because it either x1000 or x1024, where the division always yields a + // regular fraction. If any unit that has an exuberant BPU is introduced, + // they should override this method. + if (bpu.compareTo(BigInteger.ZERO) <= 0) { + throw new ArithmeticException("Bytes per unit must be positive"); + } + + BigInteger current = bpu.abs(); // we only care about factors + int p2 = current.getLowestSetBit(); // exponent of 2 + current = current.shiftRight(p2); // divide by 2^p2 + + int p5 = 0; // exponent of 5 + final BigInteger five = BigInteger.valueOf(5); + while (current.mod(five).signum() == 0) { // while divisible by 5 + current = current.divide(five); + ++p5; + } + + // anything left ≠ 1 ⇒ other prime + if (!current.equals(BigInteger.ONE)) { + throw new ArithmeticException("bytes per unit contains prime factor other than 2 or 5, you can't use this"); + } + + return Math.max(p2, p5); + } + + /** + * Returns the nearest whole value in this unit. + * @param roundingMode - the rounding mode for the division that divides the + * underlying byte value by number of bytes in a single measure of this unit, + * to come to an integer value. + * @return the nearest (according to the specified rounding mode) value in this unit. + */ + public final BigInteger wholeUnitValue(RoundingMode roundingMode) { + return new BigDecimal(bytes).divide(new BigDecimal(getNumberOfBytesPerUnit()), 0, roundingMode).toBigInteger(); + } + + /** + * Returns the amount of bytes that didn't fit in the whole value of this unit. + * @return number of bytes that didn't fill a whole measure of this unit. + */ + public final BigInteger remainder() { + return bytes.remainder(getNumberOfBytesPerUnit()); + } + + /** + * Returns the indication of whether this unit is a whole value, i.e., there is no fractional component, + * and the number of bytes stored fill this unit up to its byte per unit value. + * @return {@code true} if the unit value has no fractional component, {@code false} otherwise + */ + public final boolean isWhole() { + return remainder().equals(BigInteger.ZERO); + } + /** * @param bytesToAdd The amount of bytes to add. * @return The new amount of storage in the appropriate type. @@ -623,7 +703,11 @@ public final long longValue() { */ public abstract @NotNull T subtract(@NotNull StorageUnit storageAmount); - protected abstract @NotNull BigInteger getNumberOfBytesPerUnit(); + /** + * Number of bytes in this unit. + * @return number of bytes in this unit. + */ + public abstract @NotNull BigInteger getNumberOfBytesPerUnit(); protected abstract @NotNull String getSymbol(); diff --git a/storage-units-model/src/main/java/wtf/metio/storageunits/model/Tebibyte.java b/storage-units-model/src/main/java/wtf/metio/storageunits/model/Tebibyte.java index b68be1f..bebbbb3 100755 --- a/storage-units-model/src/main/java/wtf/metio/storageunits/model/Tebibyte.java +++ b/storage-units-model/src/main/java/wtf/metio/storageunits/model/Tebibyte.java @@ -19,6 +19,8 @@ public final class Tebibyte extends StorageUnit { @Serial private static final long serialVersionUID = 3614537130129620881L; + private static final int conversionScale = computeFiniteConversionScale(StorageUnit.BYTES_IN_A_TEBIBYTE); + Tebibyte(final @NotNull BigInteger numberOfBytes) { super(numberOfBytes); } @@ -112,7 +114,7 @@ public final class Tebibyte extends StorageUnit { @Override @CheckReturnValue - protected @NotNull BigInteger getNumberOfBytesPerUnit() { + public @NotNull BigInteger getNumberOfBytesPerUnit() { return BYTES_IN_A_TEBIBYTE; } @@ -128,4 +130,9 @@ public final class Tebibyte extends StorageUnit { return StorageUnits::binaryValueOf; } + @Override + protected int conversionScale() { + return conversionScale; + } + } diff --git a/storage-units-model/src/main/java/wtf/metio/storageunits/model/Terabyte.java b/storage-units-model/src/main/java/wtf/metio/storageunits/model/Terabyte.java index 37df3e8..7e2d6d4 100755 --- a/storage-units-model/src/main/java/wtf/metio/storageunits/model/Terabyte.java +++ b/storage-units-model/src/main/java/wtf/metio/storageunits/model/Terabyte.java @@ -19,6 +19,8 @@ public final class Terabyte extends StorageUnit { @Serial private static final long serialVersionUID = 2160488069631638952L; + private static final int conversionScale = computeFiniteConversionScale(StorageUnit.BYTES_IN_A_TERABYTE); + Terabyte(final @NotNull BigInteger numberOfBytes) { super(numberOfBytes); } @@ -112,7 +114,7 @@ public final class Terabyte extends StorageUnit { @Override @CheckReturnValue - protected @NotNull BigInteger getNumberOfBytesPerUnit() { + public @NotNull BigInteger getNumberOfBytesPerUnit() { return StorageUnit.BYTES_IN_A_TERABYTE; } @@ -128,4 +130,9 @@ public final class Terabyte extends StorageUnit { return StorageUnits::decimalValueOf; } + @Override + protected int conversionScale() { + return conversionScale; + } + } diff --git a/storage-units-model/src/main/java/wtf/metio/storageunits/model/Yobibyte.java b/storage-units-model/src/main/java/wtf/metio/storageunits/model/Yobibyte.java index 633722b..ed15766 100755 --- a/storage-units-model/src/main/java/wtf/metio/storageunits/model/Yobibyte.java +++ b/storage-units-model/src/main/java/wtf/metio/storageunits/model/Yobibyte.java @@ -19,6 +19,8 @@ public final class Yobibyte extends StorageUnit { @Serial private static final long serialVersionUID = -5606322878020884194L; + private static final int conversionScale = computeFiniteConversionScale(StorageUnit.BYTES_IN_A_YOBIBYTE); + Yobibyte(final @NotNull BigInteger numberOfBytes) { super(numberOfBytes); } @@ -112,7 +114,7 @@ public final class Yobibyte extends StorageUnit { @Override @CheckReturnValue - protected @NotNull BigInteger getNumberOfBytesPerUnit() { + public @NotNull BigInteger getNumberOfBytesPerUnit() { return StorageUnit.BYTES_IN_A_YOBIBYTE; } @@ -128,4 +130,9 @@ public final class Yobibyte extends StorageUnit { return StorageUnits::binaryValueOf; } + @Override + protected int conversionScale() { + return conversionScale; + } + } diff --git a/storage-units-model/src/main/java/wtf/metio/storageunits/model/Yottabyte.java b/storage-units-model/src/main/java/wtf/metio/storageunits/model/Yottabyte.java index 52c5aa1..f95c2bb 100755 --- a/storage-units-model/src/main/java/wtf/metio/storageunits/model/Yottabyte.java +++ b/storage-units-model/src/main/java/wtf/metio/storageunits/model/Yottabyte.java @@ -19,6 +19,8 @@ public final class Yottabyte extends StorageUnit { @Serial private static final long serialVersionUID = 2482152459842042316L; + private static final int conversionScale = computeFiniteConversionScale(StorageUnit.BYTES_IN_A_YOTTABYTE); + Yottabyte(final @NotNull BigInteger numberOfBytes) { super(numberOfBytes); } @@ -112,7 +114,7 @@ public final class Yottabyte extends StorageUnit { @Override @CheckReturnValue - protected @NotNull BigInteger getNumberOfBytesPerUnit() { + public @NotNull BigInteger getNumberOfBytesPerUnit() { return StorageUnit.BYTES_IN_A_YOTTABYTE; } @@ -128,4 +130,9 @@ public final class Yottabyte extends StorageUnit { return StorageUnits::decimalValueOf; } + @Override + protected int conversionScale() { + return conversionScale; + } + } diff --git a/storage-units-model/src/main/java/wtf/metio/storageunits/model/Zebibyte.java b/storage-units-model/src/main/java/wtf/metio/storageunits/model/Zebibyte.java index aa65da1..54bd97a 100755 --- a/storage-units-model/src/main/java/wtf/metio/storageunits/model/Zebibyte.java +++ b/storage-units-model/src/main/java/wtf/metio/storageunits/model/Zebibyte.java @@ -19,6 +19,8 @@ public final class Zebibyte extends StorageUnit { @Serial private static final long serialVersionUID = 2192254824473341887L; + private static final int conversionScale = computeFiniteConversionScale(StorageUnit.BYTES_IN_A_ZEBIBYTE); + Zebibyte(final @NotNull BigInteger numberOfBytes) { super(numberOfBytes); } @@ -112,7 +114,7 @@ public final class Zebibyte extends StorageUnit { @Override @CheckReturnValue - protected @NotNull BigInteger getNumberOfBytesPerUnit() { + public @NotNull BigInteger getNumberOfBytesPerUnit() { return StorageUnit.BYTES_IN_A_ZEBIBYTE; } @@ -128,4 +130,9 @@ public final class Zebibyte extends StorageUnit { return StorageUnits::binaryValueOf; } + @Override + protected int conversionScale() { + return conversionScale; + } + } diff --git a/storage-units-model/src/main/java/wtf/metio/storageunits/model/Zettabyte.java b/storage-units-model/src/main/java/wtf/metio/storageunits/model/Zettabyte.java index aa0b7fb..51f054a 100755 --- a/storage-units-model/src/main/java/wtf/metio/storageunits/model/Zettabyte.java +++ b/storage-units-model/src/main/java/wtf/metio/storageunits/model/Zettabyte.java @@ -19,6 +19,8 @@ public final class Zettabyte extends StorageUnit { @Serial private static final long serialVersionUID = 8849006574018911826L; + private static final int conversionScale = computeFiniteConversionScale(StorageUnit.BYTES_IN_A_ZETTABYTE); + Zettabyte(final @NotNull BigInteger numberOfBytes) { super(numberOfBytes); } @@ -112,7 +114,7 @@ public final class Zettabyte extends StorageUnit { @Override @CheckReturnValue - protected @NotNull BigInteger getNumberOfBytesPerUnit() { + public @NotNull BigInteger getNumberOfBytesPerUnit() { return BYTES_IN_A_ZETTABYTE; } @@ -128,4 +130,9 @@ public final class Zettabyte extends StorageUnit { return StorageUnits::decimalValueOf; } + @Override + protected int conversionScale() { + return conversionScale; + } + } diff --git a/storage-units-model/src/test/java/wtf/metio/storageunits/model/StorageUnitArithmeticBigIntegerTest.java b/storage-units-model/src/test/java/wtf/metio/storageunits/model/StorageUnitArithmeticBigIntegerTest.java index 0c635c9..7b9e064 100755 --- a/storage-units-model/src/test/java/wtf/metio/storageunits/model/StorageUnitArithmeticBigIntegerTest.java +++ b/storage-units-model/src/test/java/wtf/metio/storageunits/model/StorageUnitArithmeticBigIntegerTest.java @@ -8,12 +8,17 @@ import org.junit.jupiter.api.DynamicTest; import org.junit.jupiter.api.TestFactory; +import java.math.BigDecimal; import java.math.BigInteger; +import java.math.RoundingMode; +import java.util.Arrays; +import java.util.function.Function; import java.util.stream.LongStream; import java.util.stream.Stream; class StorageUnitArithmeticBigIntegerTest { + private static final int WHOLE_TEST = 42; private static LongStream numberOfBytes() { return LongStream.of(1, 2, 3, 5, 8, 13, 100, 500, -500, 123456789); } @@ -135,4 +140,57 @@ Stream returnNewInstanceAfterSubtractBigInteger() { }); } + @TestFactory + Stream getUnitValue() { + + record D(Function> constructor, RoundingMode rm) {} + + return Arrays.stream(RoundingMode.values()).flatMap(rm-> + TestObjects.bigIntegerBasedConstructors().stream().map(e->new D(e, rm))) + .map(ti -> { + final var initialAmount = BigInteger.valueOf(1); + final var first = ti.constructor.apply(initialAmount); + final var bpu = first.getNumberOfBytesPerUnit(); + final int digits = first.getNumberOfBytesPerUnit().toString().length(); + + return DynamicTest.dynamicTest(String.format("getUnitValue() - %s - %s", first.getClass().getSimpleName(), ti.rm), () -> { + + // fill it up to the brim. + StringBuilder sb = new StringBuilder(digits); + for (int i = 0; i < digits; i++) { sb.append((char)(i%9+ '1')); } + var testValue = new BigInteger(sb.toString()); + final var real = ti.constructor.apply(testValue); + + // do the math ourselves and compare? What else can we do. + // scale value of 1000 - just something that's guaranteed to cover + // all defined units, and isn't too large enough to slow JDK down + var exp = new BigDecimal(testValue).divide(new BigDecimal(bpu), 1000, ti.rm).stripTrailingZeros(); + Assertions.assertEquals(exp, real.unitValue(ti.rm), ()->"unitValue() - "+ti.rm + " on "+testValue + ", bpu="+bpu +", scale="+real.conversionScale()); + + // make sure we didn't miswire any of the implementations. + Assertions.assertEquals(StorageUnit.computeFiniteConversionScale(bpu), real.conversionScale()); + + if (ti.rm != RoundingMode.UNNECESSARY) { + var wholeExp = new BigDecimal(testValue).divide(new BigDecimal(bpu), 0, ti.rm).toBigInteger(); + Assertions.assertEquals(wholeExp, real.wholeUnitValue(ti.rm), () -> "wholeUnitValue() - " + ti.rm + " on " + testValue + ", bpu=" + bpu + ", scale=" + real.conversionScale()); + } + + var remExp = testValue.remainder(bpu); + Assertions.assertEquals(remExp, real.remainder()); + + if (bpu.equals(BigInteger.ONE)) { + Assertions.assertTrue(real.isWhole()); + } else { + Assertions.assertFalse(real.isWhole()); + var whole = ti.constructor.apply(BigInteger.valueOf(WHOLE_TEST).multiply(bpu)); + Assertions.assertTrue(whole.isWhole()); + if (ti.rm == RoundingMode.UNNECESSARY) { + Assertions.assertEquals(BigInteger.valueOf(WHOLE_TEST), whole.wholeUnitValue(ti.rm)); + } + } + + }); + }); + } + }