This is an automated email from the ASF dual-hosted git repository. toulmean pushed a commit to branch 1.0 in repository https://gitbox.apache.org/repos/asf/incubator-tuweni.git
commit 04d5d2f62237579308096bb7e0780a762942afae Author: Antoine Toulme <[email protected]> AuthorDate: Thu Apr 25 16:22:41 2019 -0700 Add UInt32 --- .../tuweni/units/bigints/BaseUInt32Value.java | 334 +++++++++ .../org/apache/tuweni/units/bigints/UInt32.java | 533 +++++++++++++++ .../apache/tuweni/units/bigints/UInt32Domain.java | 52 ++ .../apache/tuweni/units/bigints/UInt32Value.java | 375 +++++++++++ .../tuweni/units/bigints/UInt32ValueDomain.java | 65 ++ .../org/apache/tuweni/units/bigints/UInt32s.java | 42 ++ .../tuweni/units/bigints/BaseUInt32ValueTest.java | 736 ++++++++++++++++++++ .../apache/tuweni/units/bigints/UInt32Test.java | 746 +++++++++++++++++++++ 8 files changed, 2883 insertions(+) diff --git a/units/src/main/java/org/apache/tuweni/units/bigints/BaseUInt32Value.java b/units/src/main/java/org/apache/tuweni/units/bigints/BaseUInt32Value.java new file mode 100644 index 0000000..ab82058 --- /dev/null +++ b/units/src/main/java/org/apache/tuweni/units/bigints/BaseUInt32Value.java @@ -0,0 +1,334 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE + * file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file + * to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package org.apache.tuweni.units.bigints; + +import static java.util.Objects.requireNonNull; + +import org.apache.tuweni.bytes.Bytes; + +import java.math.BigInteger; +import java.util.function.Function; + +/** + * Base class for {@link UInt32Value}. + * + * <p> + * This class is abstract as it is not meant to be used directly, but it has no abstract methods. As mentioned in + * {@link UInt32Value}, this is used to create strongly-typed type aliases of {@link UInt32}. In other words, this allow + * to "tag" numbers with the unit of what they represent for the type-system, which can help clarity, but also forbid + * mixing numbers that are mean to be of different units (the strongly-typed part). + * + * <p> + * This class implements {@link UInt32Value}, but also adds a few operations that take a {@link UInt32} directly, for + * instance {@link #multiply(UInt32)}. The rational is that multiplying a given quantity of something by a "raw" number + * is always meaningful, and return a new quantity of the same thing. + * + * @param <T> The concrete type of the value. + */ +public abstract class BaseUInt32Value<T extends UInt32Value<T>> implements UInt32Value<T> { + + private final UInt32 value; + private final Function<UInt32, T> ctor; + + /** + * @param value The value to instantiate this {@code UInt32Value} with. + * @param ctor A constructor for the concrete type. + */ + protected BaseUInt32Value(UInt32 value, Function<UInt32, T> ctor) { + requireNonNull(value); + requireNonNull(ctor); + this.value = value; + this.ctor = ctor; + } + + /** + * @param value An unsigned value to instantiate this {@code UInt32Value} with. + * @param ctor A constructor for the concrete type. + */ + protected BaseUInt32Value(int value, Function<UInt32, T> ctor) { + requireNonNull(ctor); + this.value = UInt32.valueOf(value); + this.ctor = ctor; + } + + /** + * @param value An unsigned value to instantiate this {@code UInt32Value} with. + * @param ctor A constructor for the concrete type. + */ + protected BaseUInt32Value(BigInteger value, Function<UInt32, T> ctor) { + requireNonNull(value); + requireNonNull(ctor); + this.value = UInt32.valueOf(value); + this.ctor = ctor; + } + + /** + * Return a copy of this value, or itself if immutable. + * + * <p> + * The default implementation of this method returns a copy using the constructor for the concrete type and the bytes + * returned from {@link #toBytes()}. Most implementations will want to override this method to instead return + * {@code this}. + * + * @return A copy of this value, or itself if immutable. + */ + protected T copy() { + return ctor.apply(value); + } + + /** + * Return the zero value for this type. + * + * <p> + * The default implementation of this method returns a value obtained from calling the concrete type constructor with + * an argument of {@link UInt32#ZERO}. Most implementations will want to override this method to instead return a + * static constant. + * + * @return The zero value for this type. + */ + protected T zero() { + return ctor.apply(UInt32.ZERO); + } + + @Override + public T add(T value) { + return add(value.toUInt32()); + } + + /** + * Returns a value that is {@code (this + value)}. + * + * @param value The amount to be added to this value. + * @return {@code this + value} + */ + public T add(UInt32 value) { + if (value.isZero()) { + return copy(); + } + return ctor.apply(this.value.add(value)); + } + + @Override + public T add(int value) { + if (value == 0) { + return copy(); + } + return ctor.apply(this.value.add(value)); + } + + @Override + public T addMod(T value, UInt32 modulus) { + return addMod(value.toUInt32(), modulus); + } + + /** + * Returns a value equivalent to {@code ((this + value) mod modulus)}. + * + * @param value The amount to be added to this value. + * @param modulus The modulus. + * @return {@code (this + value) mod modulus} + * @throws ArithmeticException {@code modulus} == 0. + */ + public T addMod(UInt32 value, UInt32 modulus) { + return ctor.apply(this.value.addMod(value, modulus)); + } + + @Override + public T addMod(long value, UInt32 modulus) { + return ctor.apply(this.value.addMod(value, modulus)); + } + + @Override + public T addMod(long value, long modulus) { + return ctor.apply(this.value.addMod(value, modulus)); + } + + @Override + public T subtract(T value) { + return subtract(value.toUInt32()); + } + + /** + * Returns a value that is {@code (this - value)}. + * + * @param value The amount to be subtracted from this value. + * @return {@code this - value} + */ + public T subtract(UInt32 value) { + if (value.isZero()) { + return copy(); + } + return ctor.apply(this.value.subtract(value)); + } + + @Override + public T subtract(int value) { + if (value == 0) { + return copy(); + } + return ctor.apply(this.value.subtract(value)); + } + + @Override + public T multiply(T value) { + return multiply(value.toUInt32()); + } + + /** + * Returns a value that is {@code (this * value)}. + * + * @param value The amount to multiply this value by. + * @return {@code this * value} + */ + public T multiply(UInt32 value) { + if (isZero() || value.isZero()) { + return zero(); + } + if (value.equals(UInt32.ONE)) { + return copy(); + } + return ctor.apply(this.value.multiply(value)); + } + + @Override + public T multiply(int value) { + if (value == 0 || isZero()) { + return zero(); + } + if (value == 1) { + return copy(); + } + return ctor.apply(this.value.multiply(value)); + } + + @Override + public T multiplyMod(T value, UInt32 modulus) { + return multiplyMod(value.toUInt32(), modulus); + } + + /** + * Returns a value that is {@code ((this * value) mod modulus)}. + * + * @param value The amount to multiply this value by. + * @param modulus The modulus. + * @return {@code (this * value) mod modulus} + * @throws ArithmeticException {@code value} < 0 or {@code modulus} == 0. + */ + public T multiplyMod(UInt32 value, UInt32 modulus) { + return ctor.apply(this.value.multiplyMod(value, modulus)); + } + + @Override + public T multiplyMod(int value, UInt32 modulus) { + return ctor.apply(this.value.multiplyMod(value, modulus)); + } + + @Override + public T multiplyMod(int value, int modulus) { + return ctor.apply(this.value.multiplyMod(value, modulus)); + } + + @Override + public T divide(T value) { + return divide(value.toUInt32()); + } + + /** + * Returns a value that is {@code (this / value)}. + * + * @param value The amount to divide this value by. + * @return {@code this / value} + * @throws ArithmeticException {@code value} == 0. + */ + public T divide(UInt32 value) { + return ctor.apply(this.value.divide(value)); + } + + @Override + public T divide(int value) { + return ctor.apply(this.value.divide(value)); + } + + @Override + public T pow(UInt32 exponent) { + return ctor.apply(this.value.pow(exponent)); + } + + @Override + public T pow(long exponent) { + return ctor.apply(this.value.pow(exponent)); + } + + @Override + public T mod(UInt32 modulus) { + return ctor.apply(this.value.mod(modulus)); + } + + @Override + public T mod(int modulus) { + return ctor.apply(this.value.mod(modulus)); + } + + @Override + public int compareTo(T other) { + return compareTo(other.toUInt32()); + } + + /** + * Compare two {@link UInt32} values. + * + * @param other The value to compare to. + * @return A negative integer, zero, or a positive integer as this value is less than, equal to, or greater than the + * specified value. + */ + public int compareTo(UInt32 other) { + return this.value.compareTo(other); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof UInt32Value)) { + return false; + } + UInt32Value<?> other = (UInt32Value<?>) obj; + return this.value.equals(other.toUInt32()); + } + + @Override + public int hashCode() { + return value.hashCode(); + } + + @Override + public String toString() { + return value.toString(); + } + + @Override + public UInt32 toUInt32() { + return value; + } + + @Override + public Bytes toBytes() { + return value.toBytes(); + } + + @Override + public Bytes toMinimalBytes() { + return value.toMinimalBytes(); + } +} diff --git a/units/src/main/java/org/apache/tuweni/units/bigints/UInt32.java b/units/src/main/java/org/apache/tuweni/units/bigints/UInt32.java new file mode 100644 index 0000000..fa56ee8 --- /dev/null +++ b/units/src/main/java/org/apache/tuweni/units/bigints/UInt32.java @@ -0,0 +1,533 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE + * file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file + * to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package org.apache.tuweni.units.bigints; + +import static com.google.common.base.Preconditions.checkArgument; + +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.MutableBytes; + +import java.math.BigInteger; + +/** + * An unsigned 32-bit precision number. + * + * This is a raw {@link UInt32Value} - a 32-bit precision unsigned number of no particular unit. + */ +public final class UInt32 implements UInt32Value<UInt32> { + private final static int MAX_CONSTANT = 32; + private static UInt32[] CONSTANTS = new UInt32[MAX_CONSTANT + 1]; + static { + CONSTANTS[0] = new UInt32(0); + for (int i = 1; i <= MAX_CONSTANT; ++i) { + CONSTANTS[i] = new UInt32(i); + } + } + + /** The minimum value of a UInt32 */ + public final static UInt32 MIN_VALUE = valueOf(0); + /** The maximum value of a UInt32 */ + public final static UInt32 MAX_VALUE = new UInt32(~0); + /** The value 0 */ + public final static UInt32 ZERO = valueOf(0); + /** The value 1 */ + public final static UInt32 ONE = valueOf(1); + + private static final BigInteger P_2_32 = BigInteger.valueOf(2).pow(32); + + private final int value; + + /** + * Return a {@code UInt32} containing the specified value. + * + * @param value The value to create a {@code UInt32} for. + * @return A {@code UInt32} containing the specified value. + * @throws IllegalArgumentException If the value is negative. + */ + public static UInt32 valueOf(int value) { + checkArgument(value >= 0, "Argument must be positive"); + return create(value); + } + + /** + * Return a {@link UInt32} containing the specified value. + * + * @param value the value to create a {@link UInt32} for + * @return a {@link UInt32} containing the specified value + * @throws IllegalArgumentException if the value is negative or too large to be represented as a UInt32 + */ + public static UInt32 valueOf(BigInteger value) { + checkArgument(value.signum() >= 0, "Argument must be positive"); + checkArgument(value.bitLength() <= 32, "Argument is too large to represent a UInt32"); + return create(value.intValue()); + } + + /** + * Return a {@link UInt32} containing the value described by the specified bytes. + * + * @param bytes The bytes containing a {@link UInt32}. + * @return A {@link UInt32} containing the specified value. + * @throws IllegalArgumentException if {@code bytes.size() > 8}. + */ + public static UInt32 fromBytes(Bytes bytes) { + checkArgument(bytes.size() <= 8, "Argument is greater than 8 bytes"); + return create(bytes.toInt()); + } + + /** + * Parse a hexadecimal string into a {@link UInt32}. + * + * @param str The hexadecimal string to parse, which may or may not start with "0x". That representation may contain + * less than 8 bytes, in which case the result is left padded with zeros. + * @return The value corresponding to {@code str}. + * @throws IllegalArgumentException if {@code str} does not correspond to a valid hexadecimal representation or + * contains more than 8 bytes. + */ + public static UInt32 fromHexString(String str) { + return fromBytes(Bytes.fromHexStringLenient(str)); + } + + private static UInt32 create(int value) { + if (value >= 0 && value <= MAX_CONSTANT) { + return CONSTANTS[value]; + } + return new UInt32(value); + } + + private UInt32(int value) { + this.value = value; + } + + @Override + public boolean isZero() { + return this.value == 0; + } + + @Override + public UInt32 add(UInt32 value) { + if (value.value == 0) { + return this; + } + if (this.value == 0) { + return value; + } + return create(this.value + value.value); + } + + @Override + public UInt32 add(int value) { + if (value == 0) { + return this; + } + return create(this.value + value); + } + + @Override + public UInt32 addMod(UInt32 value, UInt32 modulus) { + if (modulus.isZero()) { + throw new ArithmeticException("addMod with zero modulus"); + } + return create(toBigInteger().add(value.toBigInteger()).mod(modulus.toBigInteger()).intValue()); + } + + @Override + public UInt32 addMod(long value, UInt32 modulus) { + if (modulus.isZero()) { + throw new ArithmeticException("addMod with zero modulus"); + } + return create(toBigInteger().add(BigInteger.valueOf(value)).mod(modulus.toBigInteger()).intValue()); + } + + @Override + public UInt32 addMod(long value, long modulus) { + if (modulus == 0) { + throw new ArithmeticException("addMod with zero modulus"); + } + if (modulus < 0) { + throw new ArithmeticException("addMod unsigned with negative modulus"); + } + return create(toBigInteger().add(BigInteger.valueOf(value)).mod(BigInteger.valueOf(modulus)).intValue()); + } + + @Override + public UInt32 subtract(UInt32 value) { + if (value.isZero()) { + return this; + } + return create(this.value - value.value); + } + + @Override + public UInt32 subtract(int value) { + return add(-value); + } + + @Override + public UInt32 multiply(UInt32 value) { + if (this.value == 0 || value.value == 0) { + return ZERO; + } + if (value.value == 1) { + return this; + } + return create(this.value * value.value); + } + + @Override + public UInt32 multiply(int value) { + if (value < 0) { + throw new ArithmeticException("multiply unsigned by negative"); + } + if (value == 0 || this.value == 0) { + return ZERO; + } + if (value == 1) { + return this; + } + return create(this.value * value); + } + + @Override + public UInt32 multiplyMod(UInt32 value, UInt32 modulus) { + if (modulus.isZero()) { + throw new ArithmeticException("multiplyMod with zero modulus"); + } + if (this.value == 0 || value.value == 0) { + return ZERO; + } + if (value.value == 1) { + return mod(modulus); + } + return create(toBigInteger().multiply(value.toBigInteger()).mod(modulus.toBigInteger()).intValue()); + } + + @Override + public UInt32 multiplyMod(int value, UInt32 modulus) { + if (modulus.isZero()) { + throw new ArithmeticException("multiplyMod with zero modulus"); + } + if (value == 0 || this.value == 0) { + return ZERO; + } + if (value == 1) { + return mod(modulus); + } + if (value < 0) { + throw new ArithmeticException("multiplyMod unsigned by negative"); + } + return create(toBigInteger().multiply(BigInteger.valueOf(value)).mod(modulus.toBigInteger()).intValue()); + } + + @Override + public UInt32 multiplyMod(int value, int modulus) { + if (modulus == 0) { + throw new ArithmeticException("multiplyMod with zero modulus"); + } + if (modulus < 0) { + throw new ArithmeticException("multiplyMod unsigned with negative modulus"); + } + if (value == 0 || this.value == 0) { + return ZERO; + } + if (value == 1) { + return mod(modulus); + } + if (value < 0) { + throw new ArithmeticException("multiplyMod unsigned by negative"); + } + return create(toBigInteger().multiply(BigInteger.valueOf(value)).mod(BigInteger.valueOf(modulus)).intValue()); + } + + @Override + public UInt32 divide(UInt32 value) { + if (value.value == 0) { + throw new ArithmeticException("divide by zero"); + } + if (value.value == 1) { + return this; + } + return create(toBigInteger().divide(value.toBigInteger()).intValue()); + } + + @Override + public UInt32 divide(int value) { + if (value == 0) { + throw new ArithmeticException("divide by zero"); + } + if (value < 0) { + throw new ArithmeticException("divide unsigned by negative"); + } + if (value == 1) { + return this; + } + if (isPowerOf2(value)) { + return shiftRight(log2(value)); + } + return create(toBigInteger().divide(BigInteger.valueOf(value)).intValue()); + } + + @Override + public UInt32 pow(UInt32 exponent) { + return create(toBigInteger().modPow(exponent.toBigInteger(), P_2_32).intValue()); + } + + @Override + public UInt32 pow(long exponent) { + return create(toBigInteger().modPow(BigInteger.valueOf(exponent), P_2_32).intValue()); + } + + @Override + public UInt32 mod(UInt32 modulus) { + if (modulus.isZero()) { + throw new ArithmeticException("mod by zero"); + } + return create(toBigInteger().mod(modulus.toBigInteger()).intValue()); + } + + @Override + public UInt32 mod(int modulus) { + if (modulus == 0) { + throw new ArithmeticException("mod by zero"); + } + if (modulus < 0) { + throw new ArithmeticException("mod by negative"); + } + return create(this.value % modulus); + } + + /** + * Return a bit-wise AND of this value and the supplied value. + * + * @param value the value to perform the operation with + * @return the result of a bit-wise AND + */ + public UInt32 and(UInt32 value) { + if (this.value == 0 || value.value == 0) { + return ZERO; + } + return create(this.value & value.value); + } + + /** + * Return a bit-wise AND of this value and the supplied bytes. + * + * @param bytes the bytes to perform the operation with + * @return the result of a bit-wise AND + * @throws IllegalArgumentException if more than 8 bytes are supplied + */ + public UInt32 and(Bytes bytes) { + checkArgument(bytes.size() <= 4, "and with more than 4 bytes"); + if (this.value == 0) { + return ZERO; + } + int value = bytes.toInt(); + if (value == 0) { + return ZERO; + } + return create(this.value & value); + } + + /** + * Return a bit-wise OR of this value and the supplied value. + * + * @param value the value to perform the operation with + * @return the result of a bit-wise OR + */ + public UInt32 or(UInt32 value) { + return create(this.value | value.value); + } + + /** + * Return a bit-wise OR of this value and the supplied bytes. + * + * @param bytes the bytes to perform the operation with + * @return the result of a bit-wise OR + * @throws IllegalArgumentException if more than 8 bytes are supplied + */ + public UInt32 or(Bytes bytes) { + checkArgument(bytes.size() <= 4, "or with more than 4 bytes"); + return create(this.value | bytes.toInt()); + } + + /** + * Return a bit-wise XOR of this value and the supplied value. + * + * If this value and the supplied value are different lengths, then the shorter will be zero-padded to the left. + * + * @param value the value to perform the operation with + * @return the result of a bit-wise XOR + * @throws IllegalArgumentException if more than 8 bytes are supplied + */ + public UInt32 xor(UInt32 value) { + return create(this.value ^ value.value); + } + + /** + * Return a bit-wise XOR of this value and the supplied bytes. + * + * @param bytes the bytes to perform the operation with + * @return the result of a bit-wise XOR + * @throws IllegalArgumentException if more than 8 bytes are supplied + */ + public UInt32 xor(Bytes bytes) { + checkArgument(bytes.size() <= 4, "xor with more than 4 bytes"); + return create(this.value ^ bytes.toInt()); + } + + /** + * Return a bit-wise NOT of this value. + * + * @return the result of a bit-wise NOT + */ + public UInt32 not() { + return create(~this.value); + } + + /** + * Shift all bits in this value to the right. + * + * @param distance The number of bits to shift by. + * @return A value containing the shifted bits. + */ + public UInt32 shiftRight(int distance) { + if (distance == 0) { + return this; + } + if (distance >= 32) { + return ZERO; + } + return create(this.value >>> distance); + } + + /** + * Shift all bits in this value to the left. + * + * @param distance The number of bits to shift by. + * @return A value containing the shifted bits. + */ + public UInt32 shiftLeft(int distance) { + if (distance == 0) { + return this; + } + if (distance >= 32) { + return ZERO; + } + return create(this.value << distance); + } + + @Override + public boolean equals(Object object) { + if (object == this) { + return true; + } + if (!(object instanceof UInt32)) { + return false; + } + UInt32 other = (UInt32) object; + return this.value == other.value; + } + + @Override + public int hashCode() { + return Long.hashCode(this.value); + } + + @Override + public int compareTo(UInt32 other) { + return Long.compareUnsigned(this.value, other.value); + } + + @Override + public int intValue() { + if (!fitsInt()) { + throw new ArithmeticException("Value does not fit a 4 byte int"); + } + return this.value; + } + + @Override + public long toLong() { + if (!fitsLong()) { + throw new ArithmeticException("Value does not fit a 8 byte long"); + } + return toBigInteger().longValue(); + } + + @Override + public String toString() { + return String.valueOf(value); + } + + @Override + public BigInteger toBigInteger() { + byte[] mag = new byte[4]; + mag[0] = (byte) ((this.value >>> 24) & 0xFF); + mag[1] = (byte) ((this.value >>> 16) & 0xFF); + mag[2] = (byte) ((this.value >>> 8) & 0xFF); + mag[3] = (byte) (this.value & 0xFF); + return new BigInteger(1, mag); + } + + @Override + public UInt32 toUInt32() { + return this; + } + + @Override + public Bytes toBytes() { + MutableBytes bytes = MutableBytes.create(4); + bytes.setInt(0, this.value); + return bytes; + } + + @Override + public Bytes toMinimalBytes() { + int requiredBytes = 4 - (Integer.numberOfLeadingZeros(this.value) / 8); + MutableBytes bytes = MutableBytes.create(requiredBytes); + int j = 0; + switch (requiredBytes) { + case 4: + bytes.set(j++, (byte) ((this.value >>> 24) & 0xFF)); + // fall through + case 3: + bytes.set(j++, (byte) ((this.value >>> 16) & 0xFF)); + // fall through + case 2: + bytes.set(j++, (byte) ((this.value >>> 8) & 0xFF)); + // fall through + case 1: + bytes.set(j, (byte) (this.value & 0xFF)); + } + return bytes; + } + + @Override + public int numberOfLeadingZeros() { + return Integer.numberOfLeadingZeros(this.value); + } + + @Override + public int bitLength() { + return 32 - Integer.numberOfLeadingZeros(this.value); + } + + private static boolean isPowerOf2(long n) { + assert n > 0; + return (n & (n - 1)) == 0; + } + + private static int log2(int v) { + assert v > 0; + return 63 - Long.numberOfLeadingZeros(v); + } +} diff --git a/units/src/main/java/org/apache/tuweni/units/bigints/UInt32Domain.java b/units/src/main/java/org/apache/tuweni/units/bigints/UInt32Domain.java new file mode 100644 index 0000000..b339c1a --- /dev/null +++ b/units/src/main/java/org/apache/tuweni/units/bigints/UInt32Domain.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE + * file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file + * to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package org.apache.tuweni.units.bigints; + +import com.google.common.collect.DiscreteDomain; + +/** + * A {@link DiscreteDomain} over {@link UInt32}. + */ +public final class UInt32Domain extends DiscreteDomain<UInt32> { + + @Override + public UInt32 next(UInt32 value) { + return value.add(1); + } + + @Override + public UInt32 previous(UInt32 value) { + return value.subtract(1); + } + + @Override + public long distance(UInt32 start, UInt32 end) { + boolean negativeDistance = start.compareTo(end) < 0; + UInt32 distance = negativeDistance ? end.subtract(start) : start.subtract(end); + if (!distance.fitsLong()) { + return negativeDistance ? Long.MIN_VALUE : Long.MAX_VALUE; + } + long distanceLong = distance.toLong(); + return negativeDistance ? -distanceLong : distanceLong; + } + + @Override + public UInt32 minValue() { + return UInt32.MIN_VALUE; + } + + @Override + public UInt32 maxValue() { + return UInt32.MAX_VALUE; + } +} diff --git a/units/src/main/java/org/apache/tuweni/units/bigints/UInt32Value.java b/units/src/main/java/org/apache/tuweni/units/bigints/UInt32Value.java new file mode 100644 index 0000000..bf24643 --- /dev/null +++ b/units/src/main/java/org/apache/tuweni/units/bigints/UInt32Value.java @@ -0,0 +1,375 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE + * file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file + * to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package org.apache.tuweni.units.bigints; + + +import org.apache.tuweni.bytes.Bytes; + +import java.math.BigInteger; + +/** + * Represents a 32-bit (8 bytes) unsigned integer value. + * + * <p> + * A {@link UInt32Value} is an unsigned integer value whose value can range between 0 and 2^32-1. + * + * <p> + * This interface defines operations for value types with a 32-bit precision range. The methods provided by this + * interface take parameters of the same type (and also {@code long}. This provides type safety by ensuring calculations + * cannot mix different {@code UInt32Value} types. + * + * <p> + * Where only a pure numerical 32-bit value is required, {@link UInt32} should be used. + * + * <p> + * It is strongly advised to extend {@link BaseUInt32Value} rather than implementing this interface directly. Doing so + * provides type safety in that quantities of different units cannot be mixed accidentally. + * + * @param <T> The concrete type of the value. + */ +public interface UInt32Value<T extends UInt32Value<T>> extends Comparable<T> { + + /** + * @return True if this is the value 0. + */ + default boolean isZero() { + return toBytes().isZero(); + } + + /** + * Returns a value that is {@code (this + value)}. + * + * @param value The amount to be added to this value. + * @return {@code this + value} + */ + T add(T value); + + /** + * Returns a value that is {@code (this + value)}. + * + * @param value the amount to be added to this value + * @return {@code this + value} + * @throws ArithmeticException if the result of the addition overflows + */ + default T addExact(T value) { + T result = add(value); + if (compareTo(result) > 0) { + throw new ArithmeticException("UInt32 overflow"); + } + return result; + } + + /** + * Returns a value that is {@code (this + value)}. + * + * @param value The amount to be added to this value. + * @return {@code this + value} + */ + T add(int value); + + /** + * Returns a value that is {@code (this + value)}. + * + * @param value the amount to be added to this value + * @return {@code this + value} + * @throws ArithmeticException if the result of the addition overflows + */ + default T addExact(int value) { + T result = add(value); + if ((value > 0 && compareTo(result) > 0) || (value < 0 && compareTo(result) < 0)) { + throw new ArithmeticException("UInt32 overflow"); + } + return result; + } + + /** + * Returns a value equivalent to {@code ((this + value) mod modulus)}. + * + * @param value The amount to be added to this value. + * @param modulus The modulus. + * @return {@code (this + value) mod modulus} + * @throws ArithmeticException {@code modulus} == 0. + */ + T addMod(T value, UInt32 modulus); + + /** + * Returns a value equivalent to {@code ((this + value) mod modulus)}. + * + * @param value The amount to be added to this value. + * @param modulus The modulus. + * @return {@code (this + value) mod modulus} + * @throws ArithmeticException {@code modulus} == 0. + */ + T addMod(long value, UInt32 modulus); + + /** + * Returns a value equivalent to {@code ((this + value) mod modulus)}. + * + * @param value The amount to be added to this value. + * @param modulus The modulus. + * @return {@code (this + value) mod modulus} + * @throws ArithmeticException {@code modulus} ≤ 0. + */ + T addMod(long value, long modulus); + + /** + * Returns a value that is {@code (this - value)}. + * + * @param value The amount to be subtracted from this value. + * @return {@code this - value} + */ + T subtract(T value); + + /** + * Returns a value that is {@code (this - value)}. + * + * @param value the amount to be subtracted to this value + * @return {@code this - value} + * @throws ArithmeticException if the result of the subtraction overflows + */ + default T subtractExact(T value) { + T result = subtract(value); + if (compareTo(result) < 0) { + throw new ArithmeticException("UInt32 overflow"); + } + return result; + } + + /** + * Returns a value that is {@code (this - value)}. + * + * @param value The amount to be subtracted from this value. + * @return {@code this - value} + */ + T subtract(int value); + + /** + * Returns a value that is {@code (this - value)}. + * + * @param value the amount to be subtracted to this value + * @return {@code this - value} + * @throws ArithmeticException if the result of the subtraction overflows + */ + default T subtractExact(int value) { + T result = subtract(value); + if ((value > 0 && compareTo(result) < 0) || (value < 0 && compareTo(result) > 0)) { + throw new ArithmeticException("UInt32 overflow"); + } + return result; + } + + /** + * Returns a value that is {@code (this * value)}. + * + * @param value The amount to multiply this value by. + * @return {@code this * value} + */ + T multiply(T value); + + /** + * Returns a value that is {@code (this * value)}. + * + * @param value The amount to multiply this value by. + * @return {@code this * value} + * @throws ArithmeticException {@code value} < 0. + */ + T multiply(int value); + + /** + * Returns a value that is {@code ((this * value) mod modulus)}. + * + * @param value The amount to multiply this value by. + * @param modulus The modulus. + * @return {@code (this * value) mod modulus} + * @throws ArithmeticException {@code value} < 0 or {@code modulus} == 0. + */ + T multiplyMod(T value, UInt32 modulus); + + /** + * Returns a value that is {@code ((this * value) mod modulus)}. + * + * @param value The amount to multiply this value by. + * @param modulus The modulus. + * @return {@code (this * value) mod modulus} + * @throws ArithmeticException {@code value} < 0 or {@code modulus} == 0. + */ + T multiplyMod(int value, UInt32 modulus); + + /** + * Returns a value that is {@code ((this * value) mod modulus)}. + * + * @param value The amount to multiply this value by. + * @param modulus The modulus. + * @return {@code (this * value) mod modulus} + * @throws ArithmeticException {@code value} < 0 or {@code modulus} ≤ 0. + */ + T multiplyMod(int value, int modulus); + + /** + * Returns a value that is {@code (this / value)}. + * + * @param value The amount to divide this value by. + * @return {@code this / value} + * @throws ArithmeticException {@code value} == 0. + */ + T divide(T value); + + /** + * Returns a value that is {@code (this / value)}. + * + * @param value The amount to divide this value by. + * @return {@code this / value} + * @throws ArithmeticException {@code value} ≤ 0. + */ + T divide(int value); + + /** + * Returns a value that is {@code (this<sup>exponent</sup> mod 2<sup>32</sup>)} + * + * <p> + * This calculates an exponentiation over the modulus of {@code 2^32}. + * + * <p> + * Note that {@code exponent} is an {@link UInt32} rather than of the type {@code T}. + * + * @param exponent The exponent to which this value is to be raised. + * @return {@code this<sup>exponent</sup> mod 2<sup>32</sup>} + */ + T pow(UInt32 exponent); + + /** + * Returns a value that is {@code (this<sup>exponent</sup> mod 2<sup>32</sup>)} + * + * <p> + * This calculates an exponentiation over the modulus of {@code 2^32}. + * + * @param exponent The exponent to which this value is to be raised. + * @return {@code this<sup>exponent</sup> mod 2<sup>32</sup>} + */ + T pow(long exponent); + + /** + * Returns a value that is {@code (this mod modulus)}. + * + * @param modulus The modulus. + * @return {@code this mod modulus}. + * @throws ArithmeticException {@code modulus} == 0. + */ + T mod(UInt32 modulus); + + /** + * Returns a value that is {@code (this mod modulus)}. + * + * @param modulus The modulus. + * @return {@code this mod modulus}. + * @throws ArithmeticException {@code modulus} ≤ 0. + */ + T mod(int modulus); + + /** + * @return True if this value fits a java {@code int} (i.e. is less or equal to {@code Integer.MAX_VALUE}). + */ + default boolean fitsInt() { + return true; + } + + /** + * @return This value as a java {@code int} assuming it is small enough to fit an {@code int}. + * @throws ArithmeticException If the value does not fit an {@code int}, that is if {@code + * !fitsInt()}. + */ + default int intValue() { + if (!fitsInt()) { + throw new ArithmeticException("Value does not fit a 4 byte int"); + } + return toBytes().getInt(4); + } + + /** + * @return True if this value fits a java {@code long} (i.e. is less or equal to {@code Long.MAX_VALUE}). + */ + default boolean fitsLong() { + return true; + } + + /** + * @return This value as a java {@code long} assuming it is small enough to fit a {@code long}. + * @throws ArithmeticException If the value does not fit a {@code long}, that is if {@code + * !fitsLong()}. + */ + default long toLong() { + if (!fitsLong()) { + throw new ArithmeticException("Value does not fit a 8 byte long"); + } + return toBytes().getLong(0); + } + + /** + * @return This value as a {@link BigInteger}. + */ + default BigInteger toBigInteger() { + return toBytes().toUnsignedBigInteger(); + } + + /** + * This value represented as an hexadecimal string. + * + * <p> + * Note that this representation includes all the 8 underlying bytes, no matter what the integer actually represents + * (in other words, it can have many leading zeros). For a shorter representation that don't include leading zeros, + * use {@link #toShortHexString}. + * + * @return This value represented as an hexadecimal string. + */ + default String toHexString() { + return toBytes().toHexString(); + } + + /** @return This value represented as a minimal hexadecimal string (without any leading zero). */ + default String toShortHexString() { + return toBytes().toShortHexString(); + } + + /** + * Convert this value to a {@link UInt32}. + * + * @return This value as a {@link UInt32}. + */ + UInt32 toUInt32(); + + /** + * @return The value as bytes. + */ + Bytes toBytes(); + + /** + * @return The value as bytes without any leading zero bytes. + */ + Bytes toMinimalBytes(); + + /** + * @return the number of zero bits preceding the highest-order ("leftmost") one-bit in the binary representation of + * this value, or 32 if the value is equal to zero. + */ + default int numberOfLeadingZeros() { + return toBytes().numberOfLeadingZeros(); + } + + /** + * @return The number of bits following and including the highest-order ("leftmost") one-bit in the binary + * representation of this value, or zero if all bits are zero. + */ + default int bitLength() { + return toBytes().bitLength(); + } +} diff --git a/units/src/main/java/org/apache/tuweni/units/bigints/UInt32ValueDomain.java b/units/src/main/java/org/apache/tuweni/units/bigints/UInt32ValueDomain.java new file mode 100644 index 0000000..06a43e1 --- /dev/null +++ b/units/src/main/java/org/apache/tuweni/units/bigints/UInt32ValueDomain.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE + * file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file + * to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package org.apache.tuweni.units.bigints; + +import java.util.function.Function; + +import com.google.common.collect.DiscreteDomain; + +/** + * A {@link DiscreteDomain} over a {@link UInt32Value}. + */ +public final class UInt32ValueDomain<T extends UInt32Value<T>> extends DiscreteDomain<T> { + + private final T minValue; + private final T maxValue; + + /** + * @param ctor The constructor for the {@link UInt32Value} type. + */ + public UInt32ValueDomain(Function<UInt32, T> ctor) { + this.minValue = ctor.apply(UInt32.MIN_VALUE); + this.maxValue = ctor.apply(UInt32.MAX_VALUE); + } + + @Override + public T next(T value) { + return value.add(1); + } + + @Override + public T previous(T value) { + return value.subtract(1); + } + + @Override + public long distance(T start, T end) { + boolean negativeDistance = start.compareTo(end) < 0; + T distance = negativeDistance ? end.subtract(start) : start.subtract(end); + if (!distance.fitsLong()) { + return negativeDistance ? Long.MIN_VALUE : Long.MAX_VALUE; + } + long distanceLong = distance.toLong(); + return negativeDistance ? -distanceLong : distanceLong; + } + + @Override + public T minValue() { + return minValue; + } + + @Override + public T maxValue() { + return maxValue; + } +} diff --git a/units/src/main/java/org/apache/tuweni/units/bigints/UInt32s.java b/units/src/main/java/org/apache/tuweni/units/bigints/UInt32s.java new file mode 100644 index 0000000..b4f3324 --- /dev/null +++ b/units/src/main/java/org/apache/tuweni/units/bigints/UInt32s.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE + * file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file + * to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package org.apache.tuweni.units.bigints; + +/** Static utility methods on UInt32 values. */ +public final class UInt32s { + private UInt32s() {} + + /** + * Returns the maximum of two UInt32 values. + * + * @param v1 The first value. + * @param v2 The second value. + * @return The maximum of {@code v1} and {@code v2}. + * @param <T> The concrete type of the two values. + */ + public static <T extends UInt32Value<T>> T max(T v1, T v2) { + return (v1.compareTo(v2)) >= 0 ? v1 : v2; + } + + /** + * Returns the minimum of two UInt32 values. + * + * @param v1 The first value. + * @param v2 The second value. + * @return The minimum of {@code v1} and {@code v2}. + * @param <T> The concrete type of the two values. + */ + public static <T extends UInt32Value<T>> T min(T v1, T v2) { + return (v1.compareTo(v2)) < 0 ? v1 : v2; + } +} diff --git a/units/src/test/java/org/apache/tuweni/units/bigints/BaseUInt32ValueTest.java b/units/src/test/java/org/apache/tuweni/units/bigints/BaseUInt32ValueTest.java new file mode 100644 index 0000000..44c0e27 --- /dev/null +++ b/units/src/test/java/org/apache/tuweni/units/bigints/BaseUInt32ValueTest.java @@ -0,0 +1,736 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE + * file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file + * to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package org.apache.tuweni.units.bigints; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.apache.tuweni.bytes.Bytes; + +import java.util.stream.Stream; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +// This test is in a `test` sub-package to ensure that it does not have access to package-private +// methods within the bigints package, as it should be testing the usage of the public API. +class BaseUInt32ValueTest { + + private static class Value extends BaseUInt32Value<Value> { + static final Value MAX_VALUE = new Value(UInt32.MAX_VALUE); + + Value(UInt32 v) { + super(v, Value::new); + } + + Value(int v) { + super(v, Value::new); + } + } + + private static Value v(int v) { + return new Value(v); + } + + private static Value hv(String s) { + return new Value(UInt32.fromHexString(s)); + } + + @ParameterizedTest + @MethodSource("addProvider") + void add(Value v1, Value v2, Value expected) { + assertValueEquals(expected, v1.add(v2)); + } + + private static Stream<Arguments> addProvider() { + return Stream.of( + Arguments.of(v(1), v(0), v(1)), + Arguments.of(v(5), v(0), v(5)), + Arguments.of(v(0), v(1), v(1)), + Arguments.of(v(0), v(100), v(100)), + Arguments.of(v(2), v(2), v(4)), + Arguments.of(v(100), v(90), v(190)), + Arguments.of(Value.MAX_VALUE, v(1), v(0)), + Arguments.of(Value.MAX_VALUE, v(2), v(1)), + Arguments.of(hv("0xFFFFFFF0"), v(1), hv("0xFFFFFFF1")), + Arguments.of(hv("0xFFFFFFFE"), v(1), Value.MAX_VALUE)); + } + + @ParameterizedTest + @MethodSource("addUInt32Provider") + void addUInt32(Value v1, UInt32 v2, Value expected) { + assertValueEquals(expected, v1.add(v2)); + } + + private static Stream<Arguments> addUInt32Provider() { + return Stream.of( + Arguments.of(v(1), UInt32.ZERO, v(1)), + Arguments.of(v(5), UInt32.ZERO, v(5)), + Arguments.of(v(0), UInt32.ONE, v(1)), + Arguments.of(v(0), UInt32.valueOf(100), v(100)), + Arguments.of(v(2), UInt32.valueOf(2), v(4)), + Arguments.of(v(100), UInt32.valueOf(90), v(190)), + Arguments.of(Value.MAX_VALUE, UInt32.valueOf(1), v(0)), + Arguments.of(Value.MAX_VALUE, UInt32.valueOf(2), v(1)), + Arguments.of(hv("0xFFFFFFF0"), UInt32.valueOf(1), hv("0xFFFFFFF1")), + Arguments.of(hv("0xFFFFFFFE"), UInt32.valueOf(1), Value.MAX_VALUE)); + } + + @ParameterizedTest + @MethodSource("addLongProvider") + void addLong(Value v1, int v2, Value expected) { + assertValueEquals(expected, v1.add(v2)); + } + + private static Stream<Arguments> addLongProvider() { + return Stream.of( + Arguments.of(v(1), 0, v(1)), + Arguments.of(v(5), 0, v(5)), + Arguments.of(v(0), 1, v(1)), + Arguments.of(v(0), 100, v(100)), + Arguments.of(v(2), 2, v(4)), + Arguments.of(v(100), 90, v(190)), + Arguments.of(Value.MAX_VALUE, 1, v(0)), + Arguments.of(Value.MAX_VALUE, 2, v(1)), + Arguments.of(hv("0xFFFFFFF0"), 1, hv("0xFFFFFFF1")), + Arguments.of(hv("0xFFFFFFFE"), 1, Value.MAX_VALUE), + Arguments.of(v(10), -5, v(5)), + Arguments.of(v(0), -1, Value.MAX_VALUE)); + } + + @ParameterizedTest + @MethodSource("addModProvider") + void addMod(Value v1, Value v2, UInt32 m, Value expected) { + assertValueEquals(expected, v1.addMod(v2, m)); + } + + private static Stream<Arguments> addModProvider() { + return Stream.of( + Arguments.of(v(0), v(1), UInt32.valueOf(2), v(1)), + Arguments.of(v(1), v(1), UInt32.valueOf(2), v(0)), + Arguments.of(Value.MAX_VALUE.subtract(2), v(1), UInt32.MAX_VALUE, Value.MAX_VALUE.subtract(1)), + Arguments.of(Value.MAX_VALUE.subtract(1), v(1), UInt32.MAX_VALUE, v(0)), + Arguments.of(v(2), v(1), UInt32.valueOf(2), v(1)), + Arguments.of(v(3), v(2), UInt32.valueOf(6), v(5)), + Arguments.of(v(3), v(4), UInt32.valueOf(2), v(1))); + } + + @Test + void shouldThrowForAddModOfZero() { + Throwable exception = assertThrows(ArithmeticException.class, () -> v(0).addMod(v(1), UInt32.ZERO)); + assertEquals("addMod with zero modulus", exception.getMessage()); + } + + @ParameterizedTest + @MethodSource("addModUInt32UInt32Provider") + void addModUInt32UInt32(Value v1, UInt32 v2, UInt32 m, Value expected) { + assertValueEquals(expected, v1.addMod(v2, m)); + } + + private static Stream<Arguments> addModUInt32UInt32Provider() { + return Stream.of( + Arguments.of(v(0), UInt32.ONE, UInt32.valueOf(2), v(1)), + Arguments.of(v(1), UInt32.ONE, UInt32.valueOf(2), v(0)), + Arguments.of(Value.MAX_VALUE.subtract(2), UInt32.ONE, UInt32.MAX_VALUE, Value.MAX_VALUE.subtract(1)), + Arguments.of(Value.MAX_VALUE.subtract(1), UInt32.ONE, UInt32.MAX_VALUE, v(0)), + Arguments.of(v(2), UInt32.ONE, UInt32.valueOf(2), v(1)), + Arguments.of(v(3), UInt32.valueOf(2), UInt32.valueOf(6), v(5)), + Arguments.of(v(3), UInt32.valueOf(4), UInt32.valueOf(2), v(1))); + } + + @Test + void shouldThrowForAddModLongUInt32OfZero() { + Throwable exception = assertThrows(ArithmeticException.class, () -> v(0).addMod(1, UInt32.ZERO)); + assertEquals("addMod with zero modulus", exception.getMessage()); + } + + @ParameterizedTest + @MethodSource("addModLongUInt32Provider") + void addModLongUInt32(Value v1, int v2, UInt32 m, Value expected) { + assertValueEquals(expected, v1.addMod(v2, m)); + } + + private static Stream<Arguments> addModLongUInt32Provider() { + return Stream.of( + Arguments.of(v(0), 1, UInt32.valueOf(2), v(1)), + Arguments.of(v(1), 1, UInt32.valueOf(2), v(0)), + Arguments.of(Value.MAX_VALUE.subtract(2), 1, UInt32.MAX_VALUE, Value.MAX_VALUE.subtract(1)), + Arguments.of(Value.MAX_VALUE.subtract(1), 1, UInt32.MAX_VALUE, v(0)), + Arguments.of(v(2), 1, UInt32.valueOf(2), v(1)), + Arguments.of(v(2), -1, UInt32.valueOf(2), v(1)), + Arguments.of(v(1), -7, UInt32.valueOf(5), v(4))); + } + + @Test + void shouldThrowForAddModUInt32UInt32OfZero() { + Throwable exception = assertThrows(ArithmeticException.class, () -> v(0).addMod(UInt32.ONE, UInt32.ZERO)); + assertEquals("addMod with zero modulus", exception.getMessage()); + } + + @ParameterizedTest + @MethodSource("addModLongLongProvider") + void addModLongLong(Value v1, int v2, int m, Value expected) { + assertValueEquals(expected, v1.addMod(v2, m)); + } + + private static Stream<Arguments> addModLongLongProvider() { + return Stream.of(Arguments.of(v(0), 1, 2, v(1)), Arguments.of(v(1), 1, 2, v(0)), Arguments.of(v(2), 1, 2, v(1))); + } + + @Test + void shouldThrowForAddModLongLongOfZero() { + Throwable exception = assertThrows(ArithmeticException.class, () -> v(0).addMod(1, 0)); + assertEquals("addMod with zero modulus", exception.getMessage()); + } + + @Test + void shouldThrowForAddModLongLongOfNegative() { + Throwable exception = assertThrows(ArithmeticException.class, () -> v(0).addMod(1, -5)); + assertEquals("addMod unsigned with negative modulus", exception.getMessage()); + } + + @ParameterizedTest + @MethodSource("subtractProvider") + void subtract(Value v1, Value v2, Value expected) { + assertValueEquals(expected, v1.subtract(v2)); + } + + private static Stream<Arguments> subtractProvider() { + return Stream.of( + Arguments.of(v(1), v(0), v(1)), + Arguments.of(v(5), v(0), v(5)), + Arguments.of(v(2), v(1), v(1)), + Arguments.of(v(100), v(100), v(0)), + Arguments.of(v(0), v(1), Value.MAX_VALUE), + Arguments.of(v(1), v(2), Value.MAX_VALUE), + Arguments.of(Value.MAX_VALUE, v(1), hv("0xFFFFFFFE"))); + } + + @ParameterizedTest + @MethodSource("subtractUInt32Provider") + void subtractUInt32(Value v1, UInt32 v2, Value expected) { + assertValueEquals(expected, v1.subtract(v2)); + } + + private static Stream<Arguments> subtractUInt32Provider() { + return Stream.of( + Arguments.of(v(1), UInt32.ZERO, v(1)), + Arguments.of(v(5), UInt32.ZERO, v(5)), + Arguments.of(v(2), UInt32.ONE, v(1)), + Arguments.of(v(100), UInt32.valueOf(100), v(0)), + Arguments.of(v(0), UInt32.ONE, Value.MAX_VALUE), + Arguments.of(v(1), UInt32.valueOf(2), Value.MAX_VALUE), + Arguments.of(Value.MAX_VALUE, UInt32.ONE, hv("0xFFFFFFFE"))); + } + + @ParameterizedTest + @MethodSource("subtractLongProvider") + void subtractLong(Value v1, int v2, Value expected) { + assertValueEquals(expected, v1.subtract(v2)); + } + + private static Stream<Arguments> subtractLongProvider() { + return Stream.of( + Arguments.of(v(1), 0, v(1)), + Arguments.of(v(5), 0, v(5)), + Arguments.of(v(2), 1, v(1)), + Arguments.of(v(100), 100, v(0)), + Arguments.of(v(0), 1, Value.MAX_VALUE), + Arguments.of(v(1), 2, Value.MAX_VALUE), + Arguments.of(Value.MAX_VALUE, 1, hv("0xFFFFFFFE")), + Arguments.of(v(0), -1, v(1)), + Arguments.of(v(0), -100, v(100)), + Arguments.of(v(2), -2, v(4))); + } + + @ParameterizedTest + @MethodSource("multiplyProvider") + void multiply(Value v1, Value v2, Value expected) { + assertValueEquals(expected, v1.multiply(v2)); + } + + private static Stream<Arguments> multiplyProvider() { + return Stream.of( + Arguments.of(v(0), v(2), v(0)), + Arguments.of(v(1), v(2), v(2)), + Arguments.of(v(2), v(2), v(4)), + Arguments.of(v(3), v(2), v(6)), + Arguments.of(v(4), v(2), v(8)), + Arguments.of(v(10), v(18), v(180)), + Arguments.of(v(2), v(8), v(16)), + Arguments.of(v(7), v(8), v(56)), + Arguments.of(v(8), v(8), v(64)), + Arguments.of(v(17), v(8), v(136)), + Arguments.of(v(22), v(0), v(0))); + } + + @ParameterizedTest + @MethodSource("multiplyUInt32Provider") + void multiplyUInt32(Value v1, UInt32 v2, Value expected) { + assertValueEquals(expected, v1.multiply(v2)); + } + + private static Stream<Arguments> multiplyUInt32Provider() { + return Stream.of( + Arguments.of(v(0), UInt32.valueOf(2), v(0)), + Arguments.of(v(1), UInt32.valueOf(2), v(2)), + Arguments.of(v(2), UInt32.valueOf(2), v(4)), + Arguments.of(v(3), UInt32.valueOf(2), v(6)), + Arguments.of(v(4), UInt32.valueOf(2), v(8)), + Arguments.of(v(10), UInt32.valueOf(18), v(180)), + Arguments.of(v(2), UInt32.valueOf(8), v(16)), + Arguments.of(v(7), UInt32.valueOf(8), v(56)), + Arguments.of(v(8), UInt32.valueOf(8), v(64)), + Arguments.of(v(17), UInt32.valueOf(8), v(136)), + Arguments.of(v(22), UInt32.ZERO, v(0)), + Arguments.of(hv("0xFFFFFFFF"), UInt32.valueOf(2), hv("0xFFFFFFFE")), + Arguments.of(hv("0xFFFFFFFF"), UInt32.valueOf(2), hv("0xFFFFFFFE"))); + } + + @ParameterizedTest + @MethodSource("multiplyLongProvider") + void multiplyLong(Value v1, int v2, Value expected) { + assertValueEquals(expected, v1.multiply(v2)); + } + + private static Stream<Arguments> multiplyLongProvider() { + return Stream.of( + Arguments.of(v(0), 2, v(0)), + Arguments.of(v(1), 2, v(2)), + Arguments.of(v(2), 2, v(4)), + Arguments.of(v(3), 2, v(6)), + Arguments.of(v(4), 2, v(8)), + Arguments.of(v(10), 18, v(180)), + Arguments.of(v(2), 8, v(16)), + Arguments.of(v(7), 8, v(56)), + Arguments.of(v(8), 8, v(64)), + Arguments.of(v(17), 8, v(136)), + Arguments.of(v(22), 0, v(0)), + Arguments.of(hv("0xFFFFFFFF"), 2, hv("0xFFFFFFFE")), + Arguments.of(hv("0xFFFFFFFF"), 2, hv("0xFFFFFFFE"))); + } + + @Test + void shouldThrowForMultiplyLongOfNegative() { + Throwable exception = assertThrows(ArithmeticException.class, () -> v(2).multiply(-5)); + assertEquals("multiply unsigned by negative", exception.getMessage()); + } + + @ParameterizedTest + @MethodSource("multiplyModProvider") + void multiplyMod(Value v1, Value v2, UInt32 m, Value expected) { + assertValueEquals(expected, v1.multiplyMod(v2, m)); + } + + private static Stream<Arguments> multiplyModProvider() { + return Stream.of( + Arguments.of(v(0), v(5), UInt32.valueOf(2), v(0)), + Arguments.of(v(2), v(3), UInt32.valueOf(7), v(6)), + Arguments.of(v(2), v(3), UInt32.valueOf(6), v(0)), + Arguments.of(v(2), v(0), UInt32.valueOf(6), v(0)), + Arguments.of(hv("0xFFFFFFFE"), v(2), UInt32.MAX_VALUE, hv("0xFFFFFFFD"))); + } + + @Test + void shouldThrowForMultiplyModOfModZero() { + Throwable exception = assertThrows(ArithmeticException.class, () -> v(0).multiplyMod(v(1), UInt32.ZERO)); + assertEquals("multiplyMod with zero modulus", exception.getMessage()); + } + + @ParameterizedTest + @MethodSource("multiplyModUInt32UInt32Provider") + void multiplyModUInt32UInt32(Value v1, UInt32 v2, UInt32 m, Value expected) { + assertValueEquals(expected, v1.multiplyMod(v2, m)); + } + + private static Stream<Arguments> multiplyModUInt32UInt32Provider() { + return Stream.of( + Arguments.of(v(0), UInt32.valueOf(5), UInt32.valueOf(2), v(0)), + Arguments.of(v(2), UInt32.valueOf(3), UInt32.valueOf(7), v(6)), + Arguments.of(v(2), UInt32.valueOf(3), UInt32.valueOf(6), v(0)), + Arguments.of(v(2), UInt32.ZERO, UInt32.valueOf(6), v(0)), + Arguments.of(hv("0xFFFFFFFE"), UInt32.valueOf(2), UInt32.MAX_VALUE, hv("0xFFFFFFFD"))); + } + + @Test + void shouldThrowForMultiplyModUInt32UInt32OfModZero() { + Throwable exception = + assertThrows(ArithmeticException.class, () -> v(0).multiplyMod(UInt32.valueOf(5), UInt32.ZERO)); + assertEquals("multiplyMod with zero modulus", exception.getMessage()); + } + + @ParameterizedTest + @MethodSource("multiplyModLongUInt32Provider") + void multiplyModLongUInt32(Value v1, int v2, UInt32 m, Value expected) { + assertValueEquals(expected, v1.multiplyMod(v2, m)); + } + + private static Stream<Arguments> multiplyModLongUInt32Provider() { + return Stream.of( + Arguments.of(v(0), 5, UInt32.valueOf(2), v(0)), + Arguments.of(v(2), 3, UInt32.valueOf(7), v(6)), + Arguments.of(v(2), 3, UInt32.valueOf(6), v(0)), + Arguments.of(v(2), 0, UInt32.valueOf(6), v(0)), + Arguments.of(hv("0xFFFFFFFE"), 2, UInt32.MAX_VALUE, hv("0xFFFFFFFD"))); + } + + @Test + void shouldThrowForMultiplyModLongUInt32OfModZero() { + Throwable exception = assertThrows(ArithmeticException.class, () -> v(3).multiplyMod(1, UInt32.ZERO)); + assertEquals("multiplyMod with zero modulus", exception.getMessage()); + } + + @Test + void shouldThrowForMultiplyModLongUInt32OfNegative() { + Throwable exception = assertThrows(ArithmeticException.class, () -> v(5).multiplyMod(-1, UInt32.valueOf(2))); + assertEquals("multiplyMod unsigned by negative", exception.getMessage()); + } + + @ParameterizedTest + @MethodSource("multiplyModLongLongProvider") + void multiplyModLongLong(Value v1, int v2, int m, Value expected) { + assertValueEquals(expected, v1.multiplyMod(v2, m)); + } + + private static Stream<Arguments> multiplyModLongLongProvider() { + return Stream.of( + Arguments.of(v(0), 5, 2, v(0)), + Arguments.of(v(2), 3, 7, v(6)), + Arguments.of(v(2), 3, 6, v(0)), + Arguments.of(v(2), 0, 6, v(0)), + Arguments.of(hv("0x0FFFFFFE"), 2, Integer.MAX_VALUE, hv("0x1FFFFFFC"))); + } + + @Test + void shouldThrowForMultiplyModLongLongOfModZero() { + Throwable exception = assertThrows(ArithmeticException.class, () -> v(5).multiplyMod(1, 0)); + assertEquals("multiplyMod with zero modulus", exception.getMessage()); + } + + @Test + void shouldThrowForMultiplyModLongLongOfModNegative() { + Throwable exception = assertThrows(ArithmeticException.class, () -> v(2).multiplyMod(5, -7)); + assertEquals("multiplyMod unsigned with negative modulus", exception.getMessage()); + } + + @Test + void shouldThrowForMultiplyModLongLongOfNegative() { + Throwable exception = assertThrows(ArithmeticException.class, () -> v(3).multiplyMod(-1, 2)); + assertEquals("multiplyMod unsigned by negative", exception.getMessage()); + } + + @ParameterizedTest + @MethodSource("divideProvider") + void divide(Value v1, Value v2, Value expected) { + assertValueEquals(expected, v1.divide(v2)); + } + + private static Stream<Arguments> divideProvider() { + return Stream.of( + Arguments.of(v(0), v(2), v(0)), + Arguments.of(v(1), v(2), v(0)), + Arguments.of(v(2), v(2), v(1)), + Arguments.of(v(3), v(2), v(1)), + Arguments.of(v(4), v(2), v(2)), + Arguments.of(v(2), v(8), v(0)), + Arguments.of(v(7), v(8), v(0)), + Arguments.of(v(8), v(8), v(1)), + Arguments.of(v(9), v(8), v(1)), + Arguments.of(v(17), v(8), v(2)), + Arguments.of(v(1024), v(8), v(128)), + Arguments.of(v(1026), v(8), v(128))); + } + + @Test + void shouldThrowForDivideByZero() { + Throwable exception = assertThrows(ArithmeticException.class, () -> v(5).divide(v(0))); + assertEquals("divide by zero", exception.getMessage()); + } + + @ParameterizedTest + @MethodSource("divideUInt32Provider") + void divideUInt32(Value v1, UInt32 v2, Value expected) { + assertValueEquals(expected, v1.divide(v2)); + } + + private static Stream<Arguments> divideUInt32Provider() { + return Stream.of( + Arguments.of(v(0), UInt32.valueOf(2), v(0)), + Arguments.of(v(1), UInt32.valueOf(2), v(0)), + Arguments.of(v(2), UInt32.valueOf(2), v(1)), + Arguments.of(v(3), UInt32.valueOf(2), v(1)), + Arguments.of(v(4), UInt32.valueOf(2), v(2)), + Arguments.of(v(2), UInt32.valueOf(8), v(0)), + Arguments.of(v(7), UInt32.valueOf(8), v(0)), + Arguments.of(v(8), UInt32.valueOf(8), v(1)), + Arguments.of(v(9), UInt32.valueOf(8), v(1)), + Arguments.of(v(17), UInt32.valueOf(8), v(2)), + Arguments.of(v(1024), UInt32.valueOf(8), v(128)), + Arguments.of(v(1026), UInt32.valueOf(8), v(128))); + } + + @Test + void shouldThrowForDivideUInt32ByZero() { + Throwable exception = assertThrows(ArithmeticException.class, () -> v(5).divide(UInt32.ZERO)); + assertEquals("divide by zero", exception.getMessage()); + } + + @ParameterizedTest + @MethodSource("divideLongProvider") + void divideLong(Value v1, int v2, Value expected) { + assertValueEquals(expected, v1.divide(v2)); + } + + private static Stream<Arguments> divideLongProvider() { + return Stream.of( + Arguments.of(v(0), 2, v(0)), + Arguments.of(v(1), 2, v(0)), + Arguments.of(v(2), 2, v(1)), + Arguments.of(v(3), 2, v(1)), + Arguments.of(v(4), 2, v(2)), + Arguments.of(v(2), 8, v(0)), + Arguments.of(v(7), 8, v(0)), + Arguments.of(v(8), 8, v(1)), + Arguments.of(v(9), 8, v(1)), + Arguments.of(v(17), 8, v(2)), + Arguments.of(v(1024), 8, v(128)), + Arguments.of(v(1026), 8, v(128))); + } + + @Test + void shouldThrowForDivideLongByZero() { + Throwable exception = assertThrows(ArithmeticException.class, () -> v(5).divide(0)); + assertEquals("divide by zero", exception.getMessage()); + } + + @Test + void shouldThrowForDivideLongByNegative() { + Throwable exception = assertThrows(ArithmeticException.class, () -> v(5).divide(-5)); + assertEquals("divide unsigned by negative", exception.getMessage()); + } + + @ParameterizedTest + @MethodSource("powUInt32Provider") + void powUInt32(Value v1, UInt32 v2, Value expected) { + assertValueEquals(expected, v1.pow(v2)); + } + + private static Stream<Arguments> powUInt32Provider() { + return Stream.of( + Arguments.of(v(0), UInt32.valueOf(2), v(0)), + Arguments.of(v(2), UInt32.valueOf(2), v(4)), + Arguments.of(v(2), UInt32.valueOf(8), v(256)), + Arguments.of(v(3), UInt32.valueOf(3), v(27))); + } + + @ParameterizedTest + @MethodSource("powLongProvider") + void powLong(Value v1, int v2, Value expected) { + assertValueEquals(expected, v1.pow(v2)); + } + + private static Stream<Arguments> powLongProvider() { + return Stream.of( + Arguments.of(v(0), 2, v(0)), + Arguments.of(v(2), 2, v(4)), + Arguments.of(v(2), 8, v(256)), + Arguments.of(v(3), 3, v(27))); + } + + @ParameterizedTest + @MethodSource("modUInt32Provider") + void modUInt32(Value v1, UInt32 v2, Value expected) { + assertValueEquals(expected, v1.mod(v2)); + } + + private static Stream<Arguments> modUInt32Provider() { + return Stream.of( + Arguments.of(v(0), UInt32.valueOf(2), v(0)), + Arguments.of(v(1), UInt32.valueOf(2), v(1)), + Arguments.of(v(2), UInt32.valueOf(2), v(0)), + Arguments.of(v(3), UInt32.valueOf(2), v(1)), + Arguments.of(v(0), UInt32.valueOf(8), v(0)), + Arguments.of(v(1), UInt32.valueOf(8), v(1)), + Arguments.of(v(2), UInt32.valueOf(8), v(2)), + Arguments.of(v(3), UInt32.valueOf(8), v(3)), + Arguments.of(v(7), UInt32.valueOf(8), v(7)), + Arguments.of(v(8), UInt32.valueOf(8), v(0)), + Arguments.of(v(9), UInt32.valueOf(8), v(1)), + Arguments.of(v(1024), UInt32.valueOf(8), v(0)), + Arguments.of(v(1026), UInt32.valueOf(8), v(2))); + } + + @Test + void shouldThrowForModUInt32ByZero() { + Throwable exception = assertThrows(ArithmeticException.class, () -> v(5).mod(UInt32.ZERO)); + assertEquals("mod by zero", exception.getMessage()); + } + + @ParameterizedTest + @MethodSource("modLongProvider") + void modLong(Value v1, int v2, Value expected) { + assertValueEquals(expected, v1.mod(v2)); + } + + private static Stream<Arguments> modLongProvider() { + return Stream.of( + Arguments.of(v(0), 2, v(0)), + Arguments.of(v(1), 2, v(1)), + Arguments.of(v(2), 2, v(0)), + Arguments.of(v(3), 2, v(1)), + Arguments.of(v(0), 8, v(0)), + Arguments.of(v(1), 8, v(1)), + Arguments.of(v(2), 8, v(2)), + Arguments.of(v(3), 8, v(3)), + Arguments.of(v(7), 8, v(7)), + Arguments.of(v(8), 8, v(0)), + Arguments.of(v(9), 8, v(1)), + Arguments.of(v(1024), 8, v(0)), + Arguments.of(v(1026), 8, v(2))); + } + + @Test + void shouldThrowForModLongByZero() { + Throwable exception = assertThrows(ArithmeticException.class, () -> v(5).mod(0)); + assertEquals("mod by zero", exception.getMessage()); + } + + @Test + void shouldThrowForModLongByNegative() { + Throwable exception = assertThrows(ArithmeticException.class, () -> v(5).mod(-5)); + assertEquals("mod by negative", exception.getMessage()); + } + + @ParameterizedTest + @MethodSource("compareToProvider") + void compareTo(Value v1, Value v2, int expected) { + assertEquals(expected, v1.compareTo(v2)); + } + + private static Stream<Arguments> compareToProvider() { + return Stream.of( + Arguments.of(v(5), v(5), 0), + Arguments.of(v(5), v(3), 1), + Arguments.of(v(5), v(6), -1), + Arguments.of(hv("0x00000000"), hv("0x00000000"), 0), + Arguments.of(hv("0xFFFFFFFF"), hv("0xFFFFFFFF"), 0), + Arguments.of(hv("0x0000FFFF"), hv("0x0000FFFF"), 0), + Arguments.of(hv("0xFFFFFFFF"), hv("0x00000000"), 1), + Arguments.of(hv("0x00000000"), hv("0xFFFFFFFF"), -1), + Arguments.of(hv("0x0001FFFF"), hv("0x0000FFFF"), 1), + Arguments.of(hv("0x0000FFFE"), hv("0x0000FFFF"), -1)); + } + + @ParameterizedTest + @MethodSource("toBytesProvider") + void toBytesTest(Value value, Bytes expected) { + assertEquals(expected, value.toBytes()); + } + + private static Stream<Arguments> toBytesProvider() { + return Stream.of( + Arguments.of(hv("0x00"), Bytes.fromHexString("0x00000000")), + Arguments.of(hv("0x01000000"), Bytes.fromHexString("0x01000000"))); + } + + @ParameterizedTest + @MethodSource("toMinimalBytesProvider") + void toMinimalBytesTest(Value value, Bytes expected) { + assertEquals(expected, value.toMinimalBytes()); + } + + private static Stream<Arguments> toMinimalBytesProvider() { + return Stream + .of(Arguments.of(hv("0x00"), Bytes.EMPTY), Arguments.of(hv("0x01000000"), Bytes.fromHexString("0x01000000"))); + } + + @ParameterizedTest + @MethodSource("numberOfLeadingZerosProvider") + void numberOfLeadingZeros(Value value, int expected) { + assertEquals(expected, value.numberOfLeadingZeros()); + } + + private static Stream<Arguments> numberOfLeadingZerosProvider() { + return Stream.of( + Arguments.of(hv("0x00"), 32), + Arguments.of(hv("0x01"), 31), + Arguments.of(hv("0x02"), 30), + Arguments.of(hv("0x03"), 30), + Arguments.of(hv("0x0F"), 28), + Arguments.of(hv("0x8F"), 24)); + } + + @ParameterizedTest + @MethodSource("bitLengthProvider") + void bitLength(Value value, int expected) { + assertEquals(expected, value.bitLength()); + } + + private static Stream<Arguments> bitLengthProvider() { + return Stream.of( + Arguments.of(hv("0x00"), 0), + Arguments.of(hv("0x01"), 1), + Arguments.of(hv("0x02"), 2), + Arguments.of(hv("0x03"), 2), + Arguments.of(hv("0x0F"), 4), + Arguments.of(hv("0x8F"), 8)); + } + + @ParameterizedTest + @MethodSource("addExactProvider") + void addExact(Value value, Value operand) { + assertThrows(ArithmeticException.class, () -> value.addExact(operand)); + } + + private static Stream<Arguments> addExactProvider() { + return Stream.of(Arguments.of(Value.MAX_VALUE, v(1)), Arguments.of(Value.MAX_VALUE, Value.MAX_VALUE)); + } + + @ParameterizedTest + @MethodSource("addExactLongProvider") + void addExactLong(Value value, int operand) { + assertThrows(ArithmeticException.class, () -> value.addExact(operand)); + } + + private static Stream<Arguments> addExactLongProvider() { + return Stream + .of(Arguments.of(Value.MAX_VALUE, 3), Arguments.of(Value.MAX_VALUE, Integer.MAX_VALUE), Arguments.of(v(0), -1)); + } + + @ParameterizedTest + @MethodSource("subtractExactProvider") + void subtractExact(Value value, Value operand) { + assertThrows(ArithmeticException.class, () -> value.subtractExact(operand)); + } + + private static Stream<Arguments> subtractExactProvider() { + return Stream.of(Arguments.of(v(0), v(1)), Arguments.of(v(0), Value.MAX_VALUE)); + } + + @ParameterizedTest + @MethodSource("subtractExactLongProvider") + void subtractExactLong(Value value, int operand) { + assertThrows(ArithmeticException.class, () -> value.subtractExact(operand)); + } + + private static Stream<Arguments> subtractExactLongProvider() { + return Stream.of(Arguments.of(v(0), 1), Arguments.of(v(0), Integer.MAX_VALUE), Arguments.of(Value.MAX_VALUE, -1)); + } + + private void assertValueEquals(Value expected, Value actual) { + String msg = String.format("Expected %s but got %s", expected.toHexString(), actual.toHexString()); + assertEquals(expected, actual, msg); + } +} diff --git a/units/src/test/java/org/apache/tuweni/units/bigints/UInt32Test.java b/units/src/test/java/org/apache/tuweni/units/bigints/UInt32Test.java new file mode 100644 index 0000000..b9d38f4 --- /dev/null +++ b/units/src/test/java/org/apache/tuweni/units/bigints/UInt32Test.java @@ -0,0 +1,746 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE + * file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file + * to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package org.apache.tuweni.units.bigints; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.apache.tuweni.bytes.Bytes; + +import java.math.BigInteger; +import java.util.stream.Stream; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class UInt32Test { + + private static UInt32 v(int v) { + return UInt32.valueOf(v); + } + + private static UInt32 hv(String s) { + return UInt32.fromHexString(s); + } + + @Test + void valueOfInt() { + assertThrows(IllegalArgumentException.class, () -> UInt32.valueOf(-1)); + assertThrows(IllegalArgumentException.class, () -> UInt32.valueOf(Integer.MIN_VALUE)); + assertThrows(IllegalArgumentException.class, () -> UInt32.valueOf(~0)); + } + + @Test + void valueOfBigInteger() { + assertThrows(IllegalArgumentException.class, () -> UInt32.valueOf(BigInteger.valueOf(-1))); + assertThrows(IllegalArgumentException.class, () -> UInt32.valueOf(BigInteger.valueOf(2).pow(32))); + } + + @ParameterizedTest + @MethodSource("addProvider") + void add(UInt32 v1, UInt32 v2, UInt32 expected) { + assertValueEquals(expected, v1.add(v2)); + } + + private static Stream<Arguments> addProvider() { + return Stream.of( + Arguments.of(v(1), v(0), v(1)), + Arguments.of(v(5), v(0), v(5)), + Arguments.of(v(0), v(1), v(1)), + Arguments.of(v(0), v(100), v(100)), + Arguments.of(v(2), v(2), v(4)), + Arguments.of(v(100), v(90), v(190)), + Arguments.of(UInt32.MAX_VALUE, v(1), v(0)), + Arguments.of(UInt32.MAX_VALUE, v(2), v(1)), + Arguments.of(hv("0xFFFFFFF0"), v(1), hv("0xFFFFFFF1")), + Arguments.of(hv("0xFFFFFFFE"), v(1), UInt32.MAX_VALUE)); + } + + @ParameterizedTest + @MethodSource("addLongProvider") + void addLong(UInt32 v1, int v2, UInt32 expected) { + assertValueEquals(expected, v1.add(v2)); + } + + private static Stream<Arguments> addLongProvider() { + return Stream.of( + Arguments.of(v(1), 0, v(1)), + Arguments.of(v(5), 0, v(5)), + Arguments.of(v(0), 1, v(1)), + Arguments.of(v(0), 100, v(100)), + Arguments.of(v(2), 2, v(4)), + Arguments.of(v(100), 90, v(190)), + Arguments.of(UInt32.MAX_VALUE, 1, v(0)), + Arguments.of(UInt32.MAX_VALUE, 2, v(1)), + Arguments.of(hv("0xFFFFFFF0"), 1, hv("0xFFFFFFF1")), + Arguments.of(hv("0xFFFFFFFE"), 1, UInt32.MAX_VALUE), + Arguments.of(v(10), -5, v(5)), + Arguments.of(v(0), -1, UInt32.MAX_VALUE)); + } + + @ParameterizedTest + @MethodSource("addModProvider") + void addMod(UInt32 v1, UInt32 v2, UInt32 m, UInt32 expected) { + assertValueEquals(expected, v1.addMod(v2, m)); + } + + private static Stream<Arguments> addModProvider() { + return Stream.of( + Arguments.of(v(0), v(1), UInt32.valueOf(2), v(1)), + Arguments.of(v(1), v(1), UInt32.valueOf(2), v(0)), + Arguments.of(UInt32.MAX_VALUE.subtract(2), v(1), UInt32.MAX_VALUE, UInt32.MAX_VALUE.subtract(1)), + Arguments.of(UInt32.MAX_VALUE.subtract(1), v(1), UInt32.MAX_VALUE, v(0)), + Arguments.of(v(2), v(1), UInt32.valueOf(2), v(1)), + Arguments.of(v(3), v(2), UInt32.valueOf(6), v(5)), + Arguments.of(v(3), v(4), UInt32.valueOf(2), v(1))); + } + + @Test + void shouldThrowForAddModOfZero() { + Throwable exception = assertThrows(ArithmeticException.class, () -> v(0).addMod(v(1), UInt32.ZERO)); + assertEquals("addMod with zero modulus", exception.getMessage()); + } + + @ParameterizedTest + @MethodSource("addModUInt32UInt32Provider") + void addModUInt32UInt32(UInt32 v1, UInt32 v2, UInt32 m, UInt32 expected) { + assertValueEquals(expected, v1.addMod(v2, m)); + } + + private static Stream<Arguments> addModUInt32UInt32Provider() { + return Stream.of( + Arguments.of(v(0), UInt32.ONE, UInt32.valueOf(2), v(1)), + Arguments.of(v(1), UInt32.ONE, UInt32.valueOf(2), v(0)), + Arguments.of(UInt32.MAX_VALUE.subtract(2), UInt32.ONE, UInt32.MAX_VALUE, UInt32.MAX_VALUE.subtract(1)), + Arguments.of(UInt32.MAX_VALUE.subtract(1), UInt32.ONE, UInt32.MAX_VALUE, v(0)), + Arguments.of(v(2), UInt32.ONE, UInt32.valueOf(2), v(1)), + Arguments.of(v(3), UInt32.valueOf(2), UInt32.valueOf(6), v(5)), + Arguments.of(v(3), UInt32.valueOf(4), UInt32.valueOf(2), v(1))); + } + + @Test + void shouldThrowForAddModLongUInt32OfZero() { + Throwable exception = assertThrows(ArithmeticException.class, () -> v(0).addMod(1, UInt32.ZERO)); + assertEquals("addMod with zero modulus", exception.getMessage()); + } + + @ParameterizedTest + @MethodSource("addModLongUInt32Provider") + void addModLongUInt32(UInt32 v1, long v2, UInt32 m, UInt32 expected) { + assertValueEquals(expected, v1.addMod(v2, m)); + } + + private static Stream<Arguments> addModLongUInt32Provider() { + return Stream.of( + Arguments.of(v(0), 1, UInt32.valueOf(2), v(1)), + Arguments.of(v(1), 1, UInt32.valueOf(2), v(0)), + Arguments.of(UInt32.MAX_VALUE.subtract(2), 1, UInt32.MAX_VALUE, UInt32.MAX_VALUE.subtract(1)), + Arguments.of(UInt32.MAX_VALUE.subtract(1), 1, UInt32.MAX_VALUE, v(0)), + Arguments.of(v(2), 1, UInt32.valueOf(2), v(1)), + Arguments.of(v(2), -1, UInt32.valueOf(2), v(1)), + Arguments.of(v(1), -7, UInt32.valueOf(5), v(4))); + } + + @Test + void shouldThrowForAddModUInt32UInt32OfZero() { + Throwable exception = assertThrows(ArithmeticException.class, () -> v(0).addMod(UInt32.ONE, UInt32.ZERO)); + assertEquals("addMod with zero modulus", exception.getMessage()); + } + + @ParameterizedTest + @MethodSource("addModLongLongProvider") + void addModLongLong(UInt32 v1, long v2, long m, UInt32 expected) { + assertValueEquals(expected, v1.addMod(v2, m)); + } + + private static Stream<Arguments> addModLongLongProvider() { + return Stream.of(Arguments.of(v(0), 1, 2, v(1)), Arguments.of(v(1), 1, 2, v(0)), Arguments.of(v(2), 1, 2, v(1))); + } + + @Test + void shouldThrowForAddModLongLongOfZero() { + Throwable exception = assertThrows(ArithmeticException.class, () -> v(0).addMod(1, 0)); + assertEquals("addMod with zero modulus", exception.getMessage()); + } + + @Test + void shouldThrowForAddModLongLongOfNegative() { + Throwable exception = assertThrows(ArithmeticException.class, () -> v(0).addMod(1, -5)); + assertEquals("addMod unsigned with negative modulus", exception.getMessage()); + } + + @ParameterizedTest + @MethodSource("subtractProvider") + void subtract(UInt32 v1, UInt32 v2, UInt32 expected) { + assertValueEquals(expected, v1.subtract(v2)); + } + + private static Stream<Arguments> subtractProvider() { + return Stream.of( + Arguments.of(v(1), v(0), v(1)), + Arguments.of(v(5), v(0), v(5)), + Arguments.of(v(2), v(1), v(1)), + Arguments.of(v(100), v(100), v(0)), + Arguments.of(v(0), v(1), UInt32.MAX_VALUE), + Arguments.of(v(1), v(2), UInt32.MAX_VALUE), + Arguments.of(UInt32.MAX_VALUE, v(1), hv("0xFFFFFFFE"))); + } + + @ParameterizedTest + @MethodSource("subtractLongProvider") + void subtractLong(UInt32 v1, int v2, UInt32 expected) { + assertValueEquals(expected, v1.subtract(v2)); + } + + private static Stream<Arguments> subtractLongProvider() { + return Stream.of( + Arguments.of(v(1), 0, v(1)), + Arguments.of(v(5), 0, v(5)), + Arguments.of(v(2), 1, v(1)), + Arguments.of(v(100), 100, v(0)), + Arguments.of(v(0), 1, UInt32.MAX_VALUE), + Arguments.of(v(1), 2, UInt32.MAX_VALUE), + Arguments.of(UInt32.MAX_VALUE, 1, hv("0xFFFFFFFE")), + Arguments.of(v(0), -1, v(1)), + Arguments.of(v(0), -100, v(100)), + Arguments.of(v(2), -2, v(4))); + } + + @ParameterizedTest + @MethodSource("multiplyProvider") + void multiply(UInt32 v1, UInt32 v2, UInt32 expected) { + assertValueEquals(expected, v1.multiply(v2)); + } + + private static Stream<Arguments> multiplyProvider() { + return Stream.of( + Arguments.of(v(0), v(2), v(0)), + Arguments.of(v(1), v(2), v(2)), + Arguments.of(v(2), v(2), v(4)), + Arguments.of(v(3), v(2), v(6)), + Arguments.of(v(4), v(2), v(8)), + Arguments.of(v(10), v(18), v(180)), + Arguments.of(v(2), v(8), v(16)), + Arguments.of(v(7), v(8), v(56)), + Arguments.of(v(8), v(8), v(64)), + Arguments.of(v(17), v(8), v(136)), + Arguments.of(v(22), v(0), v(0))); + } + + @ParameterizedTest + @MethodSource("multiplyLongProvider") + void multiplyLong(UInt32 v1, int v2, UInt32 expected) { + assertValueEquals(expected, v1.multiply(v2)); + } + + private static Stream<Arguments> multiplyLongProvider() { + return Stream.of( + Arguments.of(v(0), 2, v(0)), + Arguments.of(v(1), 2, v(2)), + Arguments.of(v(2), 2, v(4)), + Arguments.of(v(3), 2, v(6)), + Arguments.of(v(4), 2, v(8)), + Arguments.of(v(10), 18, v(180)), + Arguments.of(v(2), 8, v(16)), + Arguments.of(v(7), 8, v(56)), + Arguments.of(v(8), 8, v(64)), + Arguments.of(v(17), 8, v(136)), + Arguments.of(v(22), 0, v(0)), + Arguments.of(hv("0x0FFFFFFF"), 2, hv("0x1FFFFFFE")), + Arguments.of(hv("0xFFFFFFFF"), 2, hv("0xFFFFFFFE"))); + } + + @Test + void shouldThrowForMultiplyLongOfNegative() { + Throwable exception = assertThrows(ArithmeticException.class, () -> v(2).multiply(-5)); + assertEquals("multiply unsigned by negative", exception.getMessage()); + } + + @ParameterizedTest + @MethodSource("multiplyModProvider") + void multiplyMod(UInt32 v1, UInt32 v2, UInt32 m, UInt32 expected) { + assertValueEquals(expected, v1.multiplyMod(v2, m)); + } + + private static Stream<Arguments> multiplyModProvider() { + return Stream.of( + Arguments.of(v(0), v(5), UInt32.valueOf(2), v(0)), + Arguments.of(v(2), v(3), UInt32.valueOf(7), v(6)), + Arguments.of(v(2), v(3), UInt32.valueOf(6), v(0)), + Arguments.of(v(2), v(0), UInt32.valueOf(6), v(0)), + Arguments.of(hv("0x0FFFFFFE"), v(2), UInt32.MAX_VALUE, hv("0x1FFFFFFC"))); + } + + @Test + void shouldThrowForMultiplyModOfModZero() { + Throwable exception = assertThrows(ArithmeticException.class, () -> v(0).multiplyMod(v(1), UInt32.ZERO)); + assertEquals("multiplyMod with zero modulus", exception.getMessage()); + } + + @ParameterizedTest + @MethodSource("multiplyModLongUInt32Provider") + void multiplyModLongUInt32(UInt32 v1, int v2, UInt32 m, UInt32 expected) { + assertValueEquals(expected, v1.multiplyMod(v2, m)); + } + + private static Stream<Arguments> multiplyModLongUInt32Provider() { + return Stream.of( + Arguments.of(v(0), 5, UInt32.valueOf(2), v(0)), + Arguments.of(v(2), 3, UInt32.valueOf(7), v(6)), + Arguments.of(v(2), 3, UInt32.valueOf(6), v(0)), + Arguments.of(v(2), 0, UInt32.valueOf(6), v(0)), + Arguments.of(hv("0x0FFFFFFE"), 2, UInt32.MAX_VALUE, hv("0x1FFFFFFC"))); + } + + @Test + void shouldThrowForMultiplyModLongUInt32OfModZero() { + Throwable exception = assertThrows(ArithmeticException.class, () -> v(5).multiplyMod(1, UInt32.ZERO)); + assertEquals("multiplyMod with zero modulus", exception.getMessage()); + } + + @Test + void shouldThrowForMultiplyModLongUInt32OfNegative() { + Throwable exception = assertThrows(ArithmeticException.class, () -> v(3).multiplyMod(-1, UInt32.valueOf(2))); + assertEquals("multiplyMod unsigned by negative", exception.getMessage()); + } + + @ParameterizedTest + @MethodSource("multiplyModLongLongProvider") + void multiplyModLongLong(UInt32 v1, int v2, int m, UInt32 expected) { + assertValueEquals(expected, v1.multiplyMod(v2, m)); + } + + private static Stream<Arguments> multiplyModLongLongProvider() { + return Stream.of( + Arguments.of(v(0), 5, 2, v(0)), + Arguments.of(v(2), 3, 7, v(6)), + Arguments.of(v(2), 3, 6, v(0)), + Arguments.of(v(2), 0, 6, v(0)), + Arguments.of(hv("0x0FFFFFFE"), 2, Integer.MAX_VALUE, hv("0x1FFFFFFC"))); + } + + @Test + void shouldThrowForMultiplyModLongLongOfModZero() { + Throwable exception = assertThrows(ArithmeticException.class, () -> v(5).multiplyMod(1, 0)); + assertEquals("multiplyMod with zero modulus", exception.getMessage()); + } + + @Test + void shouldThrowForMultiplyModLongLongOfModNegative() { + Throwable exception = assertThrows(ArithmeticException.class, () -> v(2).multiplyMod(5, -7)); + assertEquals("multiplyMod unsigned with negative modulus", exception.getMessage()); + } + + @Test + void shouldThrowForMultiplyModLongLongOfNegative() { + Throwable exception = assertThrows(ArithmeticException.class, () -> v(3).multiplyMod(-1, 2)); + assertEquals("multiplyMod unsigned by negative", exception.getMessage()); + } + + @ParameterizedTest + @MethodSource("divideProvider") + void divide(UInt32 v1, UInt32 v2, UInt32 expected) { + assertValueEquals(expected, v1.divide(v2)); + } + + private static Stream<Arguments> divideProvider() { + return Stream.of( + Arguments.of(v(0), v(2), v(0)), + Arguments.of(v(1), v(2), v(0)), + Arguments.of(v(2), v(2), v(1)), + Arguments.of(v(3), v(2), v(1)), + Arguments.of(v(4), v(2), v(2)), + Arguments.of(v(2), v(8), v(0)), + Arguments.of(v(7), v(8), v(0)), + Arguments.of(v(8), v(8), v(1)), + Arguments.of(v(9), v(8), v(1)), + Arguments.of(v(17), v(8), v(2)), + Arguments.of(v(1024), v(8), v(128)), + Arguments.of(v(1026), v(8), v(128))); + } + + @Test + void shouldThrowForDivideByZero() { + Throwable exception = assertThrows(ArithmeticException.class, () -> v(5).divide(v(0))); + assertEquals("divide by zero", exception.getMessage()); + } + + @ParameterizedTest + @MethodSource("divideLongProvider") + void divideLong(UInt32 v1, int v2, UInt32 expected) { + assertValueEquals(expected, v1.divide(v2)); + } + + private static Stream<Arguments> divideLongProvider() { + return Stream.of( + Arguments.of(v(0), 2, v(0)), + Arguments.of(v(1), 2, v(0)), + Arguments.of(v(2), 2, v(1)), + Arguments.of(v(3), 2, v(1)), + Arguments.of(v(4), 2, v(2)), + Arguments.of(v(2), 8, v(0)), + Arguments.of(v(7), 8, v(0)), + Arguments.of(v(8), 8, v(1)), + Arguments.of(v(9), 8, v(1)), + Arguments.of(v(17), 8, v(2)), + Arguments.of(v(1024), 8, v(128)), + Arguments.of(v(1026), 8, v(128))); + } + + @Test + void shouldThrowForDivideLongByZero() { + Throwable exception = assertThrows(ArithmeticException.class, () -> v(5).divide(0)); + assertEquals("divide by zero", exception.getMessage()); + } + + @Test + void shouldThrowForDivideLongByNegative() { + Throwable exception = assertThrows(ArithmeticException.class, () -> v(5).divide(-5)); + assertEquals("divide unsigned by negative", exception.getMessage()); + } + + @ParameterizedTest + @MethodSource("powUInt32Provider") + void powUInt32(UInt32 v1, UInt32 v2, UInt32 expected) { + assertValueEquals(expected, v1.pow(v2)); + } + + private static Stream<Arguments> powUInt32Provider() { + return Stream.of( + Arguments.of(v(0), UInt32.valueOf(2), v(0)), + Arguments.of(v(2), UInt32.valueOf(2), v(4)), + Arguments.of(v(2), UInt32.valueOf(8), v(256)), + Arguments.of(v(3), UInt32.valueOf(3), v(27)), + Arguments.of(hv("0xFFF0F0F0"), UInt32.valueOf(3), hv("0x19A2F000"))); + } + + @ParameterizedTest + @MethodSource("powLongProvider") + void powLong(UInt32 v1, long v2, UInt32 expected) { + assertValueEquals(expected, v1.pow(v2)); + } + + private static Stream<Arguments> powLongProvider() { + return Stream.of( + Arguments.of(v(0), 2, v(0)), + Arguments.of(v(2), 2, v(4)), + Arguments.of(v(2), 8, v(256)), + Arguments.of(v(3), 3, v(27)), + Arguments.of(hv("0xFFF0F0F0"), 3, hv("0x19A2F000"))); + } + + @ParameterizedTest + @MethodSource("modLongProvider") + void modLong(UInt32 v1, int v2, UInt32 expected) { + assertValueEquals(expected, v1.mod(v2)); + } + + private static Stream<Arguments> modLongProvider() { + return Stream.of( + Arguments.of(v(0), 2, v(0)), + Arguments.of(v(1), 2, v(1)), + Arguments.of(v(2), 2, v(0)), + Arguments.of(v(3), 2, v(1)), + Arguments.of(v(0), 8, v(0)), + Arguments.of(v(1), 8, v(1)), + Arguments.of(v(2), 8, v(2)), + Arguments.of(v(3), 8, v(3)), + Arguments.of(v(7), 8, v(7)), + Arguments.of(v(8), 8, v(0)), + Arguments.of(v(9), 8, v(1)), + Arguments.of(v(1024), 8, v(0)), + Arguments.of(v(1026), 8, v(2))); + } + + @Test + void shouldThrowForModLongByZero() { + Throwable exception = assertThrows(ArithmeticException.class, () -> v(5).mod(0)); + assertEquals("mod by zero", exception.getMessage()); + } + + @Test + void shouldThrowForModLongByNegative() { + Throwable exception = assertThrows(ArithmeticException.class, () -> v(5).mod(-5)); + assertEquals("mod by negative", exception.getMessage()); + } + + @ParameterizedTest + @MethodSource("andProvider") + void and(UInt32 v1, UInt32 v2, UInt32 expected) { + assertValueEquals(expected, v1.and(v2)); + } + + private static Stream<Arguments> andProvider() { + return Stream.of( + Arguments.of(hv("0x0000FFFF"), hv("0xFFFF0000"), hv("0x00000000")), + Arguments.of(hv("0x0000FFFF"), hv("0xFFFFFF00"), hv("0x0000FF00"))); + } + + @ParameterizedTest + @MethodSource("orProvider") + void or(UInt32 v1, UInt32 v2, UInt32 expected) { + assertValueEquals(expected, v1.or(v2)); + } + + private static Stream<Arguments> orProvider() { + return Stream.of( + Arguments.of(hv("0x0000FFFF"), hv("0xFFFF0000"), hv("0xFFFFFFFF")), + Arguments.of(hv("0x0000FFFF"), hv("0xFFFF0000"), hv("0xFFFFFFFF")), + Arguments.of(hv("0x000000FF"), hv("0xFFFF0000"), hv("0xFFFF00FF"))); + } + + @ParameterizedTest + @MethodSource("xorProvider") + void xor(UInt32 v1, UInt32 v2, UInt32 expected) { + assertValueEquals(expected, v1.xor(v2)); + } + + private static Stream<Arguments> xorProvider() { + return Stream.of( + Arguments.of(hv("0xFFFFFFFF"), hv("0xFFFFFFFF"), hv("0x00000000")), + Arguments.of(hv("0x0000FFFF"), hv("0xFFFF0000"), hv("0xFFFFFFFF")), + Arguments.of(hv("0x0000FFFF"), hv("0xFFFFFF00"), hv("0xFFFF00FF"))); + } + + @ParameterizedTest + @MethodSource("notProvider") + void not(UInt32 value, UInt32 expected) { + assertValueEquals(expected, value.not()); + } + + private static Stream<Arguments> notProvider() { + return Stream.of( + Arguments.of(hv("0xFFFFFFFF"), hv("0x00000000")), + Arguments.of(hv("0x00000000"), hv("0xFFFFFFFF")), + Arguments.of(hv("0x0000FFFF"), hv("0xFFFF0000"))); + } + + @ParameterizedTest + @MethodSource("shiftLeftProvider") + void shiftLeft(UInt32 value, int distance, UInt32 expected) { + assertValueEquals(expected, value.shiftLeft(distance)); + } + + private static Stream<Arguments> shiftLeftProvider() { + return Stream.of( + Arguments.of(hv("0x01"), 1, hv("0x02")), + Arguments.of(hv("0x01"), 2, hv("0x04")), + Arguments.of(hv("0x01"), 8, hv("0x0100")), + Arguments.of(hv("0x01"), 9, hv("0x0200")), + Arguments.of(hv("0x01"), 16, hv("0x10000")), + Arguments.of(hv("0x00FF00"), 4, hv("0x0FF000")), + Arguments.of(hv("0x00FF00"), 8, hv("0xFF0000")), + Arguments.of(hv("0x00FF00"), 1, hv("0x01FE00")), + Arguments.of(hv("0x00000001"), 16, hv("0x00010000")), + Arguments.of(hv("0x00000001"), 15, hv("0x00008000")), + Arguments.of(hv("0xFFFFFFFF"), 23, hv("0xFF800000")), + Arguments.of(hv("0x0000FFFF"), 18, hv("0xFFFC0000"))); + } + + @ParameterizedTest + @MethodSource("shiftRightProvider") + void shiftRight(UInt32 value, int distance, UInt32 expected) { + assertValueEquals(expected, value.shiftRight(distance)); + } + + private static Stream<Arguments> shiftRightProvider() { + return Stream.of( + Arguments.of(hv("0x01"), 1, hv("0x00")), + Arguments.of(hv("0x10"), 1, hv("0x08")), + Arguments.of(hv("0x10"), 2, hv("0x04")), + Arguments.of(hv("0x10"), 8, hv("0x00")), + Arguments.of(hv("0x1000"), 4, hv("0x0100")), + Arguments.of(hv("0x1000"), 5, hv("0x0080")), + Arguments.of(hv("0x1000"), 8, hv("0x0010")), + Arguments.of(hv("0x1000"), 9, hv("0x0008")), + Arguments.of(hv("0x1000"), 16, hv("0x0000")), + Arguments.of(hv("0x00FF00"), 4, hv("0x000FF0")), + Arguments.of(hv("0x00FF00"), 8, hv("0x0000FF")), + Arguments.of(hv("0x00FF00"), 1, hv("0x007F80")), + Arguments.of(hv("0x100000"), 16, hv("0x000010")), + Arguments.of(hv("0x100000"), 15, hv("0x000020")), + Arguments.of(hv("0xFFFFFFFF"), 23, hv("0x000001FF")), + Arguments.of(hv("0xFFFFFFFF"), 202, hv("0x00000000"))); + } + + @ParameterizedTest + @MethodSource("intValueProvider") + void intValue(UInt32 value, int expected) { + assertEquals(expected, value.intValue()); + } + + private static Stream<Arguments> intValueProvider() { + return Stream.of( + Arguments.of(hv("0x"), 0), + Arguments.of(hv("0x00"), 0), + Arguments.of(hv("0x00000000"), 0), + Arguments.of(hv("0x01"), 1), + Arguments.of(hv("0x0001"), 1), + Arguments.of(hv("0x000001"), 1), + Arguments.of(hv("0x00000001"), 1), + Arguments.of(hv("0x0100"), 256), + Arguments.of(hv("0x000100"), 256), + Arguments.of(hv("0x00000100"), 256)); + } + + @ParameterizedTest + @MethodSource("longValueProvider") + void longValue(UInt32 value, long expected) { + assertEquals(expected, value.toLong()); + } + + private static Stream<Arguments> longValueProvider() { + return Stream.of( + Arguments.of(hv("0x"), 0L), + Arguments.of(hv("0x00"), 0L), + Arguments.of(hv("0x00000000"), 0L), + Arguments.of(hv("0x01"), 1L), + Arguments.of(hv("0x0001"), 1L), + Arguments.of(hv("0x000001"), 1L), + Arguments.of(hv("0x0100"), 256L), + Arguments.of(hv("0x000100"), 256L), + Arguments.of(hv("0x00000100"), 256L), + Arguments.of(hv("0xFFFFFFFF"), (1L << 32) - 1)); + } + + @ParameterizedTest + @MethodSource("compareToProvider") + void compareTo(UInt32 v1, UInt32 v2, int expected) { + assertEquals(expected, v1.compareTo(v2)); + } + + private static Stream<Arguments> compareToProvider() { + return Stream.of( + Arguments.of(v(5), v(5), 0), + Arguments.of(v(5), v(3), 1), + Arguments.of(v(5), v(6), -1), + Arguments.of(hv("0x00000000"), hv("0x00000000"), 0), + Arguments.of(hv("0xFFFFFFFF"), hv("0xFFFFFFFF"), 0), + Arguments.of(hv("0x0000FFFF"), hv("0x0000FFFF"), 0), + Arguments.of(hv("0xFFFFFFFF"), hv("0x00000000"), 1), + Arguments.of(hv("0x00000000"), hv("0xFFFFFFFF"), -1), + Arguments.of(hv("0x0001FFFF"), hv("0x0000FFFF"), 1), + Arguments.of(hv("0x0000FFFE"), hv("0x0000FFFF"), -1)); + } + + @ParameterizedTest + @MethodSource("toBytesProvider") + void toBytesTest(UInt32 value, Bytes expected) { + assertEquals(expected, value.toBytes()); + } + + private static Stream<Arguments> toBytesProvider() { + return Stream.of( + Arguments.of(hv("0x00"), Bytes.fromHexString("0x00000000")), + Arguments.of(hv("0x01000000"), Bytes.fromHexString("0x01000000")), + Arguments.of(hv("0xf10000ab"), Bytes.fromHexString("0xF10000AB"))); + } + + @ParameterizedTest + @MethodSource("toMinimalBytesProvider") + void toMinimalBytesTest(UInt32 value, Bytes expected) { + assertEquals(expected, value.toMinimalBytes()); + } + + private static Stream<Arguments> toMinimalBytesProvider() { + return Stream.of( + Arguments.of(hv("0x00"), Bytes.EMPTY), + Arguments.of(hv("0x01000000"), Bytes.fromHexString("0x01000000")), + Arguments.of(hv("0xf10000ab"), Bytes.fromHexString("0xf10000ab")), + Arguments.of(hv("0x01000000"), Bytes.fromHexString("0x01000000"))); + } + + @ParameterizedTest + @MethodSource("numberOfLeadingZerosProvider") + void numberOfLeadingZeros(UInt32 value, int expected) { + assertEquals(expected, value.numberOfLeadingZeros()); + } + + private static Stream<Arguments> numberOfLeadingZerosProvider() { + return Stream.of( + Arguments.of(hv("0x00"), 32), + Arguments.of(hv("0x01"), 31), + Arguments.of(hv("0x02"), 30), + Arguments.of(hv("0x03"), 30), + Arguments.of(hv("0x0F"), 28), + Arguments.of(hv("0x8F"), 24), + Arguments.of(hv("0x1000000"), 7)); + } + + @ParameterizedTest + @MethodSource("bitLengthProvider") + void bitLength(UInt32 value, int expected) { + assertEquals(expected, value.bitLength()); + } + + private static Stream<Arguments> bitLengthProvider() { + return Stream.of( + Arguments.of(hv("0x00"), 0), + Arguments.of(hv("0x01"), 1), + Arguments.of(hv("0x02"), 2), + Arguments.of(hv("0x03"), 2), + Arguments.of(hv("0x0F"), 4), + Arguments.of(hv("0x8F"), 8), + Arguments.of(hv("0x10000000"), 29)); + } + + @ParameterizedTest + @MethodSource("addExactProvider") + void addExact(UInt32 value, UInt32 operand) { + assertThrows(ArithmeticException.class, () -> value.addExact(operand)); + } + + private static Stream<Arguments> addExactProvider() { + return Stream.of(Arguments.of(UInt32.MAX_VALUE, v(1)), Arguments.of(UInt32.MAX_VALUE, UInt32.MAX_VALUE)); + } + + @ParameterizedTest + @MethodSource("addExactLongProvider") + void addExactLong(UInt32 value, int operand) { + assertThrows(ArithmeticException.class, () -> value.addExact(operand)); + } + + private static Stream<Arguments> addExactLongProvider() { + return Stream.of( + Arguments.of(UInt32.MAX_VALUE, 3), + Arguments.of(UInt32.MAX_VALUE, Integer.MAX_VALUE), + Arguments.of(v(0), -1)); + } + + @ParameterizedTest + @MethodSource("subtractExactProvider") + void subtractExact(UInt32 value, UInt32 operand) { + assertThrows(ArithmeticException.class, () -> value.subtractExact(operand)); + } + + private static Stream<Arguments> subtractExactProvider() { + return Stream.of(Arguments.of(v(0), v(1)), Arguments.of(v(0), UInt32.MAX_VALUE)); + } + + @ParameterizedTest + @MethodSource("subtractExactLongProvider") + void subtractExactLong(UInt32 value, int operand) { + assertThrows(ArithmeticException.class, () -> value.subtractExact(operand)); + } + + private static Stream<Arguments> subtractExactLongProvider() { + return Stream.of(Arguments.of(v(0), 1), Arguments.of(v(0), Integer.MAX_VALUE), Arguments.of(UInt32.MAX_VALUE, -1)); + } + + private void assertValueEquals(UInt32 expected, UInt32 actual) { + String msg = String.format("Expected %s but got %s", expected.toHexString(), actual.toHexString()); + assertEquals(expected, actual, msg); + } +} --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
