From b6f45b188974f26347646e207f8cf9e157be4625 Mon Sep 17 00:00:00 2001 From: Paul Sandoz Date: Wed, 4 Nov 2015 16:44:38 +0100 Subject: [PATCH] 8033148: Lexicographic comparators for arrays Reviewed-by: jrose, chegar, bchristi, mduigou --- .../share/classes/java/lang/Byte.java | 16 + .../share/classes/java/lang/Short.java | 16 + .../share/classes/java/util/Arrays.java | 3719 ++++++++++++++++- .../java/util/Arrays/ArraysEqCmpTest.java | 1083 +++++ 4 files changed, 4823 insertions(+), 11 deletions(-) create mode 100644 jdk/test/java/util/Arrays/ArraysEqCmpTest.java diff --git a/jdk/src/java.base/share/classes/java/lang/Byte.java b/jdk/src/java.base/share/classes/java/lang/Byte.java index 0ae3903ca7e..51f687cb389 100644 --- a/jdk/src/java.base/share/classes/java/lang/Byte.java +++ b/jdk/src/java.base/share/classes/java/lang/Byte.java @@ -462,6 +462,22 @@ public final class Byte extends Number implements Comparable { return x - y; } + /** + * Compares two {@code byte} values numerically treating the values + * as unsigned. + * + * @param x the first {@code byte} to compare + * @param y the second {@code byte} to compare + * @return the value {@code 0} if {@code x == y}; a value less + * than {@code 0} if {@code x < y} as unsigned values; and + * a value greater than {@code 0} if {@code x > y} as + * unsigned values + * @since 9 + */ + public static int compareUnsigned(byte x, byte y) { + return Byte.toUnsignedInt(x) - Byte.toUnsignedInt(y); + } + /** * Converts the argument to an {@code int} by an unsigned * conversion. In an unsigned conversion to an {@code int}, the diff --git a/jdk/src/java.base/share/classes/java/lang/Short.java b/jdk/src/java.base/share/classes/java/lang/Short.java index 6cc4d17d35a..9fa79f3d8c2 100644 --- a/jdk/src/java.base/share/classes/java/lang/Short.java +++ b/jdk/src/java.base/share/classes/java/lang/Short.java @@ -467,6 +467,22 @@ public final class Short extends Number implements Comparable { return x - y; } + /** + * Compares two {@code short} values numerically treating the values + * as unsigned. + * + * @param x the first {@code short} to compare + * @param y the second {@code short} to compare + * @return the value {@code 0} if {@code x == y}; a value less + * than {@code 0} if {@code x < y} as unsigned values; and + * a value greater than {@code 0} if {@code x > y} as + * unsigned values + * @since 9 + */ + public static int compareUnsigned(short x, short y) { + return Short.toUnsignedInt(x) - Short.toUnsignedInt(y); + } + /** * The number of bits used to represent a {@code short} value in two's * complement binary form. diff --git a/jdk/src/java.base/share/classes/java/util/Arrays.java b/jdk/src/java.base/share/classes/java/util/Arrays.java index 35962987cc8..f18180ac84d 100644 --- a/jdk/src/java.base/share/classes/java/util/Arrays.java +++ b/jdk/src/java.base/share/classes/java/util/Arrays.java @@ -25,6 +25,8 @@ package java.util; +import jdk.internal.HotSpotIntrinsicCandidate; + import java.lang.reflect.Array; import java.util.concurrent.ForkJoinPool; import java.util.function.BinaryOperator; @@ -42,7 +44,6 @@ import java.util.stream.IntStream; import java.util.stream.LongStream; import java.util.stream.Stream; import java.util.stream.StreamSupport; -import jdk.internal.HotSpotIntrinsicCandidate; /** * This class contains various methods for manipulating arrays (such as @@ -2585,6 +2586,55 @@ public class Arrays { return true; } + /** + * Returns true if the two specified arrays of longs, over the specified + * ranges, are equal to one another. + * + *

Two arrays are considered equal if the number of elements covered by + * each range is the same, and all corresponding pairs of elements over the + * specified ranges in the two arrays are equal. In other words, two arrays + * are equal if they contain, over the specified ranges, the same elements + * in the same order. + * + * @param a the first array to be tested for equality + * @param aFromIndex the index (inclusive) of the first element in the + * first array to be tested + * @param aToIndex the index (exclusive) of the last element in the + * first array to be tested + * @param b the second array to be tested fro equality + * @param bFromIndex the index (inclusive) of the first element in the + * second array to be tested + * @param bToIndex the index (exclusive) of the last element in the + * second array to be tested + * @return {@code true} if the two arrays, over the specified ranges, are + * equal + * @throws IllegalArgumentException + * if {@code aFromIndex > aToIndex} or + * if {@code bFromIndex > bToIndex} + * @throws ArrayIndexOutOfBoundsException + * if {@code aFromIndex < 0 or aToIndex > a.length} or + * if {@code bFromIndex < 0 or bToIndex > b.length} + * @throws NullPointerException + * if either array is {@code null} + * @since 9 + */ + public static boolean equals(long[] a, int aFromIndex, int aToIndex, + long[] b, int bFromIndex, int bToIndex) { + rangeCheck(a.length, aFromIndex, aToIndex); + rangeCheck(b.length, bFromIndex, bToIndex); + + int aLength = aToIndex - aFromIndex; + int bLength = bToIndex - bFromIndex; + if (aLength != bLength) + return false; + + for (int i = 0; i < aLength; i++) + if (a[aFromIndex++] != b[bFromIndex++]) + return false; + + return true; + } + /** * Returns {@code true} if the two specified arrays of ints are * equal to one another. Two arrays are considered equal if both @@ -2614,6 +2664,55 @@ public class Arrays { return true; } + /** + * Returns true if the two specified arrays of ints, over the specified + * ranges, are equal to one another. + * + *

Two arrays are considered equal if the number of elements covered by + * each range is the same, and all corresponding pairs of elements over the + * specified ranges in the two arrays are equal. In other words, two arrays + * are equal if they contain, over the specified ranges, the same elements + * in the same order. + * + * @param a the first array to be tested for equality + * @param aFromIndex the index (inclusive) of the first element in the + * first array to be tested + * @param aToIndex the index (exclusive) of the last element in the + * first array to be tested + * @param b the second array to be tested fro equality + * @param bFromIndex the index (inclusive) of the first element in the + * second array to be tested + * @param bToIndex the index (exclusive) of the last element in the + * second array to be tested + * @return {@code true} if the two arrays, over the specified ranges, are + * equal + * @throws IllegalArgumentException + * if {@code aFromIndex > aToIndex} or + * if {@code bFromIndex > bToIndex} + * @throws ArrayIndexOutOfBoundsException + * if {@code aFromIndex < 0 or aToIndex > a.length} or + * if {@code bFromIndex < 0 or bToIndex > b.length} + * @throws NullPointerException + * if either array is {@code null} + * @since 9 + */ + public static boolean equals(int[] a, int aFromIndex, int aToIndex, + int[] b, int bFromIndex, int bToIndex) { + rangeCheck(a.length, aFromIndex, aToIndex); + rangeCheck(b.length, bFromIndex, bToIndex); + + int aLength = aToIndex - aFromIndex; + int bLength = bToIndex - bFromIndex; + if (aLength != bLength) + return false; + + for (int i = 0; i < aLength; i++) + if (a[aFromIndex++] != b[bFromIndex++]) + return false; + + return true; + } + /** * Returns {@code true} if the two specified arrays of shorts are * equal to one another. Two arrays are considered equal if both @@ -2643,6 +2742,55 @@ public class Arrays { return true; } + /** + * Returns true if the two specified arrays of shorts, over the specified + * ranges, are equal to one another. + * + *

Two arrays are considered equal if the number of elements covered by + * each range is the same, and all corresponding pairs of elements over the + * specified ranges in the two arrays are equal. In other words, two arrays + * are equal if they contain, over the specified ranges, the same elements + * in the same order. + * + * @param a the first array to be tested for equality + * @param aFromIndex the index (inclusive) of the first element in the + * first array to be tested + * @param aToIndex the index (exclusive) of the last element in the + * first array to be tested + * @param b the second array to be tested fro equality + * @param bFromIndex the index (inclusive) of the first element in the + * second array to be tested + * @param bToIndex the index (exclusive) of the last element in the + * second array to be tested + * @return {@code true} if the two arrays, over the specified ranges, are + * equal + * @throws IllegalArgumentException + * if {@code aFromIndex > aToIndex} or + * if {@code bFromIndex > bToIndex} + * @throws ArrayIndexOutOfBoundsException + * if {@code aFromIndex < 0 or aToIndex > a.length} or + * if {@code bFromIndex < 0 or bToIndex > b.length} + * @throws NullPointerException + * if either array is {@code null} + * @since 9 + */ + public static boolean equals(short[] a, int aFromIndex, int aToIndex, + short[] b, int bFromIndex, int bToIndex) { + rangeCheck(a.length, aFromIndex, aToIndex); + rangeCheck(b.length, bFromIndex, bToIndex); + + int aLength = aToIndex - aFromIndex; + int bLength = bToIndex - bFromIndex; + if (aLength != bLength) + return false; + + for (int i = 0; i < aLength; i++) + if (a[aFromIndex++] != b[bFromIndex++]) + return false; + + return true; + } + /** * Returns {@code true} if the two specified arrays of chars are * equal to one another. Two arrays are considered equal if both @@ -2673,6 +2821,55 @@ public class Arrays { return true; } + /** + * Returns true if the two specified arrays of chars, over the specified + * ranges, are equal to one another. + * + *

Two arrays are considered equal if the number of elements covered by + * each range is the same, and all corresponding pairs of elements over the + * specified ranges in the two arrays are equal. In other words, two arrays + * are equal if they contain, over the specified ranges, the same elements + * in the same order. + * + * @param a the first array to be tested for equality + * @param aFromIndex the index (inclusive) of the first element in the + * first array to be tested + * @param aToIndex the index (exclusive) of the last element in the + * first array to be tested + * @param b the second array to be tested fro equality + * @param bFromIndex the index (inclusive) of the first element in the + * second array to be tested + * @param bToIndex the index (exclusive) of the last element in the + * second array to be tested + * @return {@code true} if the two arrays, over the specified ranges, are + * equal + * @throws IllegalArgumentException + * if {@code aFromIndex > aToIndex} or + * if {@code bFromIndex > bToIndex} + * @throws ArrayIndexOutOfBoundsException + * if {@code aFromIndex < 0 or aToIndex > a.length} or + * if {@code bFromIndex < 0 or bToIndex > b.length} + * @throws NullPointerException + * if either array is {@code null} + * @since 9 + */ + public static boolean equals(char[] a, int aFromIndex, int aToIndex, + char[] b, int bFromIndex, int bToIndex) { + rangeCheck(a.length, aFromIndex, aToIndex); + rangeCheck(b.length, bFromIndex, bToIndex); + + int aLength = aToIndex - aFromIndex; + int bLength = bToIndex - bFromIndex; + if (aLength != bLength) + return false; + + for (int i = 0; i < aLength; i++) + if (a[aFromIndex++] != b[bFromIndex++]) + return false; + + return true; + } + /** * Returns {@code true} if the two specified arrays of bytes are * equal to one another. Two arrays are considered equal if both @@ -2702,6 +2899,55 @@ public class Arrays { return true; } + /** + * Returns true if the two specified arrays of bytes, over the specified + * ranges, are equal to one another. + * + *

Two arrays are considered equal if the number of elements covered by + * each range is the same, and all corresponding pairs of elements over the + * specified ranges in the two arrays are equal. In other words, two arrays + * are equal if they contain, over the specified ranges, the same elements + * in the same order. + * + * @param a the first array to be tested for equality + * @param aFromIndex the index (inclusive) of the first element in the + * first array to be tested + * @param aToIndex the index (exclusive) of the last element in the + * first array to be tested + * @param b the second array to be tested fro equality + * @param bFromIndex the index (inclusive) of the first element in the + * second array to be tested + * @param bToIndex the index (exclusive) of the last element in the + * second array to be tested + * @return {@code true} if the two arrays, over the specified ranges, are + * equal + * @throws IllegalArgumentException + * if {@code aFromIndex > aToIndex} or + * if {@code bFromIndex > bToIndex} + * @throws ArrayIndexOutOfBoundsException + * if {@code aFromIndex < 0 or aToIndex > a.length} or + * if {@code bFromIndex < 0 or bToIndex > b.length} + * @throws NullPointerException + * if either array is {@code null} + * @since 9 + */ + public static boolean equals(byte[] a, int aFromIndex, int aToIndex, + byte[] b, int bFromIndex, int bToIndex) { + rangeCheck(a.length, aFromIndex, aToIndex); + rangeCheck(b.length, bFromIndex, bToIndex); + + int aLength = aToIndex - aFromIndex; + int bLength = bToIndex - bFromIndex; + if (aLength != bLength) + return false; + + for (int i = 0; i < aLength; i++) + if (a[aFromIndex++] != b[bFromIndex++]) + return false; + + return true; + } + /** * Returns {@code true} if the two specified arrays of booleans are * equal to one another. Two arrays are considered equal if both @@ -2731,6 +2977,55 @@ public class Arrays { return true; } + /** + * Returns true if the two specified arrays of booleans, over the specified + * ranges, are equal to one another. + * + *

Two arrays are considered equal if the number of elements covered by + * each range is the same, and all corresponding pairs of elements over the + * specified ranges in the two arrays are equal. In other words, two arrays + * are equal if they contain, over the specified ranges, the same elements + * in the same order. + * + * @param a the first array to be tested for equality + * @param aFromIndex the index (inclusive) of the first element in the + * first array to be tested + * @param aToIndex the index (exclusive) of the last element in the + * first array to be tested + * @param b the second array to be tested fro equality + * @param bFromIndex the index (inclusive) of the first element in the + * second array to be tested + * @param bToIndex the index (exclusive) of the last element in the + * second array to be tested + * @return {@code true} if the two arrays, over the specified ranges, are + * equal + * @throws IllegalArgumentException + * if {@code aFromIndex > aToIndex} or + * if {@code bFromIndex > bToIndex} + * @throws ArrayIndexOutOfBoundsException + * if {@code aFromIndex < 0 or aToIndex > a.length} or + * if {@code bFromIndex < 0 or bToIndex > b.length} + * @throws NullPointerException + * if either array is {@code null} + * @since 9 + */ + public static boolean equals(boolean[] a, int aFromIndex, int aToIndex, + boolean[] b, int bFromIndex, int bToIndex) { + rangeCheck(a.length, aFromIndex, aToIndex); + rangeCheck(b.length, bFromIndex, bToIndex); + + int aLength = aToIndex - aFromIndex; + int bLength = bToIndex - bFromIndex; + if (aLength != bLength) + return false; + + for (int i = 0; i < aLength; i++) + if (a[aFromIndex++] != b[bFromIndex++]) + return false; + + return true; + } + /** * Returns {@code true} if the two specified arrays of doubles are * equal to one another. Two arrays are considered equal if both @@ -2759,9 +3054,70 @@ public class Arrays { if (a2.length != length) return false; - for (int i=0; iequal to one another. + * + *

Two arrays are considered equal if the number of elements covered by + * each range is the same, and all corresponding pairs of elements over the + * specified ranges in the two arrays are equal. In other words, two arrays + * are equal if they contain, over the specified ranges, the same elements + * in the same order. + * + *

Two doubles {@code d1} and {@code d2} are considered equal if: + *

    {@code new Double(d1).equals(new Double(d2))}
+ * (Unlike the {@code ==} operator, this method considers + * {@code NaN} equals to itself, and 0.0d unequal to -0.0d.) + * + * @param a the first array to be tested for equality + * @param aFromIndex the index (inclusive) of the first element in the + * first array to be tested + * @param aToIndex the index (exclusive) of the last element in the + * first array to be tested + * @param b the second array to be tested fro equality + * @param bFromIndex the index (inclusive) of the first element in the + * second array to be tested + * @param bToIndex the index (exclusive) of the last element in the + * second array to be tested + * @return {@code true} if the two arrays, over the specified ranges, are + * equal + * @throws IllegalArgumentException + * if {@code aFromIndex > aToIndex} or + * if {@code bFromIndex > bToIndex} + * @throws ArrayIndexOutOfBoundsException + * if {@code aFromIndex < 0 or aToIndex > a.length} or + * if {@code bFromIndex < 0 or bToIndex > b.length} + * @throws NullPointerException + * if either array is {@code null} + * @see Double#equals(Object) + * @since 9 + */ + public static boolean equals(double[] a, int aFromIndex, int aToIndex, + double[] b, int bFromIndex, int bToIndex) { + rangeCheck(a.length, aFromIndex, aToIndex); + rangeCheck(b.length, bFromIndex, bToIndex); + + int aLength = aToIndex - aFromIndex; + int bLength = bToIndex - bFromIndex; + if (aLength != bLength) + return false; + + for (int i = 0; i < aLength; i++) { + Double va = a[aFromIndex++], vb = b[bFromIndex++]; + if (Double.doubleToRawLongBits(va) != Double.doubleToRawLongBits(vb)) + if (!Double.isNaN(va) || !Double.isNaN(vb)) + return false; + } return true; } @@ -2794,9 +3150,70 @@ public class Arrays { if (a2.length != length) return false; - for (int i=0; iequal to one another. + * + *

Two arrays are considered equal if the number of elements covered by + * each range is the same, and all corresponding pairs of elements over the + * specified ranges in the two arrays are equal. In other words, two arrays + * are equal if they contain, over the specified ranges, the same elements + * in the same order. + * + *

Two floats {@code f1} and {@code f2} are considered equal if: + *

    {@code new Float(f1).equals(new Float(f2))}
+ * (Unlike the {@code ==} operator, this method considers + * {@code NaN} equals to itself, and 0.0f unequal to -0.0f.) + * + * @param a the first array to be tested for equality + * @param aFromIndex the index (inclusive) of the first element in the + * first array to be tested + * @param aToIndex the index (exclusive) of the last element in the + * first array to be tested + * @param b the second array to be tested fro equality + * @param bFromIndex the index (inclusive) of the first element in the + * second array to be tested + * @param bToIndex the index (exclusive) of the last element in the + * second array to be tested + * @return {@code true} if the two arrays, over the specified ranges, are + * equal + * @throws IllegalArgumentException + * if {@code aFromIndex > aToIndex} or + * if {@code bFromIndex > bToIndex} + * @throws ArrayIndexOutOfBoundsException + * if {@code aFromIndex < 0 or aToIndex > a.length} or + * if {@code bFromIndex < 0 or bToIndex > b.length} + * @throws NullPointerException + * if either array is {@code null} + * @see Float#equals(Object) + * @since 9 + */ + public static boolean equals(float[] a, int aFromIndex, int aToIndex, + float[] b, int bFromIndex, int bToIndex) { + rangeCheck(a.length, aFromIndex, aToIndex); + rangeCheck(b.length, bFromIndex, bToIndex); + + int aLength = aToIndex - aFromIndex; + int bLength = bToIndex - bFromIndex; + if (aLength != bLength) + return false; + + for (int i = 0; i < aLength; i++) { + float va = a[aFromIndex++], vb = b[bFromIndex++]; + if (Float.floatToRawIntBits(va) != Float.floatToRawIntBits(vb)) + if (!Float.isNaN(va) || !Float.isNaN(vb)) + return false; + } return true; } @@ -2827,9 +3244,60 @@ public class Arrays { return false; for (int i=0; iequal to one another. + * + *

Two arrays are considered equal if the number of elements covered by + * each range is the same, and all corresponding pairs of elements over the + * specified ranges in the two arrays are equal. In other words, two arrays + * are equal if they contain, over the specified ranges, the same elements + * in the same order. + * + *

Two objects {@code e1} and {@code e2} are considered equal if + * {@code Objects.equals(e1, e2)}. + * + * @param a the first array to be tested for equality + * @param aFromIndex the index (inclusive) of the first element in the + * first array to be tested + * @param aToIndex the index (exclusive) of the last element in the + * first array to be tested + * @param b the second array to be tested fro equality + * @param bFromIndex the index (inclusive) of the first element in the + * second array to be tested + * @param bToIndex the index (exclusive) of the last element in the + * second array to be tested + * @return {@code true} if the two arrays, over the specified ranges, are + * equal + * @throws IllegalArgumentException + * if {@code aFromIndex > aToIndex} or + * if {@code bFromIndex > bToIndex} + * @throws ArrayIndexOutOfBoundsException + * if {@code aFromIndex < 0 or aToIndex > a.length} or + * if {@code bFromIndex < 0 or bToIndex > b.length} + * @throws NullPointerException + * if either array is {@code null} + * @since 9 + */ + public static boolean equals(Object[] a, int aFromIndex, int aToIndex, + Object[] b, int bFromIndex, int bToIndex) { + rangeCheck(a.length, aFromIndex, aToIndex); + rangeCheck(b.length, bFromIndex, bToIndex); + + int aLength = aToIndex - aFromIndex; + int bLength = bToIndex - bFromIndex; + if (aLength != bLength) + return false; + + for (int i = 0; i < aLength; i++) { + if (!Objects.equals(a[aFromIndex++], b[bFromIndex++])) return false; } @@ -5185,4 +5653,3233 @@ public class Arrays { public static DoubleStream stream(double[] array, int startInclusive, int endExclusive) { return StreamSupport.doubleStream(spliterator(array, startInclusive, endExclusive), false); } -} + + + // Comparison methods + + // Compare boolean + + /** + * Compares two {@code boolean} arrays lexicographically. + * + *

If the two arrays share a common prefix then the lexicographic + * comparison is the result of comparing two elements, as if by + * {@link Boolean#compare(boolean, boolean)}, at an index within the + * respective arrays that is the prefix length. + * Otherwise, one array is a proper prefix of the other and, lexicographic + * comparison is the result of comparing the two array lengths. + * (See {@link #mismatch(boolean[], boolean[])} for the definition of a + * common and proper prefix.) + * + *

A {@code null} array reference is considered lexicographically less + * than a non-{@code null} array reference. Two {@code null} array + * references are considered equal. + * + *

The comparison is consistent with {@link #equals(boolean[], boolean[]) equals}, + * more specifically the following holds for arrays {@code a} and {@code b}: + *

{@code
+     *     Arrays.equals(a, b) == (Arrays.compare(a, b) == 0)
+     * }
+ * + * @apiNote + *

This method behaves as if (for non-{@code null} array references): + *

{@code
+     *     int i = Arrays.mismatch(a, b);
+     *     if (i >= 0 && i < Math.min(a.length, b.length))
+     *         return Boolean.compare(a[i], b[i]);
+     *     return a.length - b.length;
+     * }
+ * + * @param a the first array to compare + * @param b the second array to compare + * @return the value {@code 0} if the first and second array are equal and + * contain the same elements in the same order; + * a value less than {@code 0} if the first array is + * lexicographically less than the second array; and + * a value greater than {@code 0} if the first array is + * lexicographically greater than the second array + * @since 9 + */ + public static int compare(boolean[] a, boolean[] b) { + if (a == b) + return 0; + if (a == null || b == null) + return a == null ? -1 : 1; + + int length = Math.min(a.length, b.length); + for (int i = 0; i < length; i++) { + if (a[i] != b[i]) return Boolean.compare(a[i], b[i]); + } + + return a.length - b.length; + } + + /** + * Compares two {@code boolean} arrays lexicographically over the specified + * ranges. + * + *

If the two arrays, over the specified ranges, share a common prefix + * then the lexicographic comparison is the result of comparing two + * elements, as if by {@link Boolean#compare(boolean, boolean)}, at a + * relative index within the respective arrays that is the length of the + * prefix. + * Otherwise, one array is a proper prefix of the other and, lexicographic + * comparison is the result of comparing the two range lengths. + * (See {@link #mismatch(boolean[], int, int, boolean[], int, int)} for the + * definition of a common and proper prefix.) + * + *

The comparison is consistent with + * {@link #equals(boolean[], int, int, boolean[], int, int) equals}, more + * specifically the following holds for arrays {@code a} and {@code b} with + * specified ranges [{@code aFromIndex}, {@code atoIndex}) and + * [{@code bFromIndex}, {@code btoIndex}) respectively: + *

{@code
+     *     Arrays.equals(a, aFromIndex, aToIndex, b, bFromIndex, bToIndex) ==
+     *         (Arrays.compare(a, aFromIndex, aToIndex, b, bFromIndex, bToIndex) == 0)
+     * }
+ * + * @apiNote + *

This method behaves as if: + *

{@code
+     *     int i = Arrays.mismatch(a, aFromIndex, aToIndex,
+     *                             b, bFromIndex, bToIndex);
+     *     if (i >= 0 && i < Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex))
+     *         return Boolean.compare(a[aFromIndex + i], b[bFromIndex + i]);
+     *     return (aToIndex - aFromIndex) - (bToIndex - bFromIndex);
+     * }
+ * + * @param a the first array to compare + * @param aFromIndex the index (inclusive) of the first element in the + * first array to be compared + * @param aToIndex the index (exclusive) of the last element in the + * first array to be compared + * @param b the second array to compare + * @param bFromIndex the index (inclusive) of the first element in the + * second array to be compared + * @param bToIndex the index (exclusive) of the last element in the + * second array to be compared + * @return the value {@code 0} if, over the specified ranges, the first and + * second array are equal and contain the same elements in the same + * order; + * a value less than {@code 0} if, over the specified ranges, the + * first array is lexicographically less than the second array; and + * a value greater than {@code 0} if, over the specified ranges, the + * first array is lexicographically greater than the second array + * @throws IllegalArgumentException + * if {@code aFromIndex > aToIndex} or + * if {@code bFromIndex > bToIndex} + * @throws ArrayIndexOutOfBoundsException + * if {@code aFromIndex < 0 or aToIndex > a.length} or + * if {@code bFromIndex < 0 or bToIndex > b.length} + * @throws NullPointerException + * if either array is {@code null} + * @since 9 + */ + public static int compare(boolean[] a, int aFromIndex, int aToIndex, + boolean[] b, int bFromIndex, int bToIndex) { + rangeCheck(a.length, aFromIndex, aToIndex); + rangeCheck(b.length, bFromIndex, bToIndex); + + int aLength = aToIndex - aFromIndex; + int bLength = bToIndex - bFromIndex; + int length = Math.min(aLength, bLength); + for (int i = 0; i < length; i++) { + boolean va = a[aFromIndex++]; + boolean vb = b[bFromIndex++]; + if (va != vb) return Boolean.compare(va, vb); + } + + return aLength - bLength; + } + + // Compare byte + + /** + * Compares two {@code byte} arrays lexicographically. + * + *

If the two arrays share a common prefix then the lexicographic + * comparison is the result of comparing two elements, as if by + * {@link Byte#compare(byte, byte)}, at an index within the respective + * arrays that is the prefix length. + * Otherwise, one array is a proper prefix of the other and, lexicographic + * comparison is the result of comparing the two array lengths. + * (See {@link #mismatch(byte[], byte[])} for the definition of a common and + * proper prefix.) + * + *

A {@code null} array reference is considered lexicographically less + * than a non-{@code null} array reference. Two {@code null} array + * references are considered equal. + * + *

The comparison is consistent with {@link #equals(byte[], byte[]) equals}, + * more specifically the following holds for arrays {@code a} and {@code b}: + *

{@code
+     *     Arrays.equals(a, b) == (Arrays.compare(a, b) == 0)
+     * }
+ * + * @apiNote + *

This method behaves as if (for non-{@code null} array references): + *

{@code
+     *     int i = Arrays.mismatch(a, b);
+     *     if (i >= 0 && i < Math.min(a.length, b.length))
+     *         return Byte.compare(a[i], b[i]);
+     *     return a.length - b.length;
+     * }
+ * + * @param a the first array to compare + * @param b the second array to compare + * @return the value {@code 0} if the first and second array are equal and + * contain the same elements in the same order; + * a value less than {@code 0} if the first array is + * lexicographically less than the second array; and + * a value greater than {@code 0} if the first array is + * lexicographically greater than the second array + * @since 9 + */ + public static int compare(byte[] a, byte[] b) { + if (a == b) + return 0; + if (a == null || b == null) + return a == null ? -1 : 1; + + int length = Math.min(a.length, b.length); + for (int i = 0; i < length; i++) { + if (a[i] != b[i]) return Byte.compare(a[i], b[i]); + } + + return a.length - b.length; + } + + /** + * Compares two {@code byte} arrays lexicographically over the specified + * ranges. + * + *

If the two arrays, over the specified ranges, share a common prefix + * then the lexicographic comparison is the result of comparing two + * elements, as if by {@link Byte#compare(byte, byte)}, at a relative index + * within the respective arrays that is the length of the prefix. + * Otherwise, one array is a proper prefix of the other and, lexicographic + * comparison is the result of comparing the two range lengths. + * (See {@link #mismatch(byte[], int, int, byte[], int, int)} for the + * definition of a common and proper prefix.) + * + *

The comparison is consistent with + * {@link #equals(byte[], int, int, byte[], int, int) equals}, more + * specifically the following holds for arrays {@code a} and {@code b} with + * specified ranges [{@code aFromIndex}, {@code atoIndex}) and + * [{@code bFromIndex}, {@code btoIndex}) respectively: + *

{@code
+     *     Arrays.equals(a, aFromIndex, aToIndex, b, bFromIndex, bToIndex) ==
+     *         (Arrays.compare(a, aFromIndex, aToIndex, b, bFromIndex, bToIndex) == 0)
+     * }
+ * + * @apiNote + *

This method behaves as if: + *

{@code
+     *     int i = Arrays.mismatch(a, aFromIndex, aToIndex,
+     *                             b, bFromIndex, bToIndex);
+     *     if (i >= 0 && i < Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex))
+     *         return Byte.compare(a[aFromIndex + i], b[bFromIndex + i]);
+     *     return (aToIndex - aFromIndex) - (bToIndex - bFromIndex);
+     * }
+ * + * @param a the first array to compare + * @param aFromIndex the index (inclusive) of the first element in the + * first array to be compared + * @param aToIndex the index (exclusive) of the last element in the + * first array to be compared + * @param b the second array to compare + * @param bFromIndex the index (inclusive) of the first element in the + * second array to be compared + * @param bToIndex the index (exclusive) of the last element in the + * second array to be compared + * @return the value {@code 0} if, over the specified ranges, the first and + * second array are equal and contain the same elements in the same + * order; + * a value less than {@code 0} if, over the specified ranges, the + * first array is lexicographically less than the second array; and + * a value greater than {@code 0} if, over the specified ranges, the + * first array is lexicographically greater than the second array + * @throws IllegalArgumentException + * if {@code aFromIndex > aToIndex} or + * if {@code bFromIndex > bToIndex} + * @throws ArrayIndexOutOfBoundsException + * if {@code aFromIndex < 0 or aToIndex > a.length} or + * if {@code bFromIndex < 0 or bToIndex > b.length} + * @throws NullPointerException + * if either array is {@code null} + * @since 9 + */ + public static int compare(byte[] a, int aFromIndex, int aToIndex, + byte[] b, int bFromIndex, int bToIndex) { + rangeCheck(a.length, aFromIndex, aToIndex); + rangeCheck(b.length, bFromIndex, bToIndex); + + int aLength = aToIndex - aFromIndex; + int bLength = bToIndex - bFromIndex; + int length = Math.min(aLength, bLength); + for (int i = 0; i < length; i++) { + byte va = a[aFromIndex++]; + byte vb = b[bFromIndex++]; + if (va != vb) return Byte.compare(va, vb); + } + + return aLength - bLength; + } + + /** + * Compares two {@code byte} arrays lexicographically, numerically treating + * elements as unsigned. + * + *

If the two arrays share a common prefix then the lexicographic + * comparison is the result of comparing two elements, as if by + * {@link Byte#compareUnsigned(byte, byte)}, at an index within the + * respective arrays that is the prefix length. + * Otherwise, one array is a proper prefix of the other and, lexicographic + * comparison is the result of comparing the two array lengths. + * (See {@link #mismatch(byte[], byte[])} for the definition of a common + * and proper prefix.) + * + *

A {@code null} array reference is considered lexicographically less + * than a non-{@code null} array reference. Two {@code null} array + * references are considered equal. + * + * @apiNote + *

This method behaves as if (for non-{@code null} array references): + *

{@code
+     *     int i = Arrays.mismatch(a, b);
+     *     if (i >= 0 && i < Math.min(a.length, b.length))
+     *         return Byte.compareUnsigned(a[i], b[i]);
+     *     return a.length - b.length;
+     * }
+ * + * @param a the first array to compare + * @param b the second array to compare + * @return the value {@code 0} if the first and second array are + * equal and contain the same elements in the same order; + * a value less than {@code 0} if the first array is + * lexicographically less than the second array; and + * a value greater than {@code 0} if the first array is + * lexicographically greater than the second array + * @since 9 + */ + public static int compareUnsigned(byte[] a, byte[] b) { + if (a == b) + return 0; + if (a == null || b == null) + return a == null ? -1 : 1; + + int length = Math.min(a.length, b.length); + for (int i = 0; i < length; i++) { + if (a[i] != b[i]) return Byte.compareUnsigned(a[i], b[i]); + } + + return a.length - b.length; + } + + + /** + * Compares two {@code byte} arrays lexicographically over the specified + * ranges, numerically treating elements as unsigned. + * + *

If the two arrays, over the specified ranges, share a common prefix + * then the lexicographic comparison is the result of comparing two + * elements, as if by {@link Byte#compareUnsigned(byte, byte)}, at a + * relative index within the respective arrays that is the length of the + * prefix. + * Otherwise, one array is a proper prefix of the other and, lexicographic + * comparison is the result of comparing the two range lengths. + * (See {@link #mismatch(byte[], int, int, byte[], int, int)} for the + * definition of a common and proper prefix.) + * + * @apiNote + *

This method behaves as if: + *

{@code
+     *     int i = Arrays.mismatch(a, aFromIndex, aToIndex,
+     *                             b, bFromIndex, bToIndex);
+     *     if (i >= 0 && i < Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex))
+     *         return Byte.compareUnsigned(a[aFromIndex + i], b[bFromIndex + i]);
+     *     return (aToIndex - aFromIndex) - (bToIndex - bFromIndex);
+     * }
+ * + * @param a the first array to compare + * @param aFromIndex the index (inclusive) of the first element in the + * first array to be compared + * @param aToIndex the index (exclusive) of the last element in the + * first array to be compared + * @param b the second array to compare + * @param bFromIndex the index (inclusive) of the first element in the + * second array to be compared + * @param bToIndex the index (exclusive) of the last element in the + * second array to be compared + * @return the value {@code 0} if, over the specified ranges, the first and + * second array are equal and contain the same elements in the same + * order; + * a value less than {@code 0} if, over the specified ranges, the + * first array is lexicographically less than the second array; and + * a value greater than {@code 0} if, over the specified ranges, the + * first array is lexicographically greater than the second array + * @throws IllegalArgumentException + * if {@code aFromIndex > aToIndex} or + * if {@code bFromIndex > bToIndex} + * @throws ArrayIndexOutOfBoundsException + * if {@code aFromIndex < 0 or aToIndex > a.length} or + * if {@code bFromIndex < 0 or bToIndex > b.length} + * @throws NullPointerException + * if either array is null + * @since 9 + */ + public static int compareUnsigned(byte[] a, int aFromIndex, int aToIndex, + byte[] b, int bFromIndex, int bToIndex) { + rangeCheck(a.length, aFromIndex, aToIndex); + rangeCheck(b.length, bFromIndex, bToIndex); + + int aLength = aToIndex - aFromIndex; + int bLength = bToIndex - bFromIndex; + int length = Math.min(aLength, bLength); + for (int i = 0; i < length; i++) { + byte va = a[aFromIndex++]; + byte vb = b[bFromIndex++]; + if (va != vb) return Byte.compareUnsigned(va, vb); + } + + return aLength - bLength; + } + + // Compare short + + /** + * Compares two {@code short} arrays lexicographically. + * + *

If the two arrays share a common prefix then the lexicographic + * comparison is the result of comparing two elements, as if by + * {@link Short#compare(short, short)}, at an index within the respective + * arrays that is the prefix length. + * Otherwise, one array is a proper prefix of the other and, lexicographic + * comparison is the result of comparing the two array lengths. + * (See {@link #mismatch(short[], short[])} for the definition of a common + * and proper prefix.) + * + *

A {@code null} array reference is considered lexicographically less + * than a non-{@code null} array reference. Two {@code null} array + * references are considered equal. + * + *

The comparison is consistent with {@link #equals(short[], short[]) equals}, + * more specifically the following holds for arrays {@code a} and {@code b}: + *

{@code
+     *     Arrays.equals(a, b) == (Arrays.compare(a, b) == 0)
+     * }
+ * + * @apiNote + *

This method behaves as if (for non-{@code null} array references): + *

{@code
+     *     int i = Arrays.mismatch(a, b);
+     *     if (i >= 0 && i < Math.min(a.length, b.length))
+     *         return Short.compare(a[i], b[i]);
+     *     return a.length - b.length;
+     * }
+ * + * @param a the first array to compare + * @param b the second array to compare + * @return the value {@code 0} if the first and second array are equal and + * contain the same elements in the same order; + * a value less than {@code 0} if the first array is + * lexicographically less than the second array; and + * a value greater than {@code 0} if the first array is + * lexicographically greater than the second array + * @since 9 + */ + public static int compare(short[] a, short[] b) { + if (a == b) + return 0; + if (a == null || b == null) + return a == null ? -1 : 1; + + int length = Math.min(a.length, b.length); + for (int i = 0; i < length; i++) { + if (a[i] != b[i]) return Short.compare(a[i], b[i]); + } + + return a.length - b.length; + } + + /** + * Compares two {@code short} arrays lexicographically over the specified + * ranges. + * + *

If the two arrays, over the specified ranges, share a common prefix + * then the lexicographic comparison is the result of comparing two + * elements, as if by {@link Short#compare(short, short)}, at a relative + * index within the respective arrays that is the length of the prefix. + * Otherwise, one array is a proper prefix of the other and, lexicographic + * comparison is the result of comparing the two range lengths. + * (See {@link #mismatch(short[], int, int, short[], int, int)} for the + * definition of a common and proper prefix.) + * + *

The comparison is consistent with + * {@link #equals(short[], int, int, short[], int, int) equals}, more + * specifically the following holds for arrays {@code a} and {@code b} with + * specified ranges [{@code aFromIndex}, {@code atoIndex}) and + * [{@code bFromIndex}, {@code btoIndex}) respectively: + *

{@code
+     *     Arrays.equals(a, aFromIndex, aToIndex, b, bFromIndex, bToIndex) ==
+     *         (Arrays.compare(a, aFromIndex, aToIndex, b, bFromIndex, bToIndex) == 0)
+     * }
+ * + * @apiNote + *

This method behaves as if: + *

{@code
+     *     int i = Arrays.mismatch(a, aFromIndex, aToIndex,
+     *                             b, bFromIndex, bToIndex);
+     *     if (i >= 0 && i < Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex))
+     *         return Short.compare(a[aFromIndex + i], b[bFromIndex + i]);
+     *     return (aToIndex - aFromIndex) - (bToIndex - bFromIndex);
+     * }
+ * + * @param a the first array to compare + * @param aFromIndex the index (inclusive) of the first element in the + * first array to be compared + * @param aToIndex the index (exclusive) of the last element in the + * first array to be compared + * @param b the second array to compare + * @param bFromIndex the index (inclusive) of the first element in the + * second array to be compared + * @param bToIndex the index (exclusive) of the last element in the + * second array to be compared + * @return the value {@code 0} if, over the specified ranges, the first and + * second array are equal and contain the same elements in the same + * order; + * a value less than {@code 0} if, over the specified ranges, the + * first array is lexicographically less than the second array; and + * a value greater than {@code 0} if, over the specified ranges, the + * first array is lexicographically greater than the second array + * @throws IllegalArgumentException + * if {@code aFromIndex > aToIndex} or + * if {@code bFromIndex > bToIndex} + * @throws ArrayIndexOutOfBoundsException + * if {@code aFromIndex < 0 or aToIndex > a.length} or + * if {@code bFromIndex < 0 or bToIndex > b.length} + * @throws NullPointerException + * if either array is {@code null} + * @since 9 + */ + public static int compare(short[] a, int aFromIndex, int aToIndex, + short[] b, int bFromIndex, int bToIndex) { + rangeCheck(a.length, aFromIndex, aToIndex); + rangeCheck(b.length, bFromIndex, bToIndex); + + int aLength = aToIndex - aFromIndex; + int bLength = bToIndex - bFromIndex; + int length = Math.min(aLength, bLength); + for (int i = 0; i < length; i++) { + short va = a[aFromIndex++]; + short vb = b[bFromIndex++]; + if (va != vb) return Short.compare(va, vb); + } + + return aLength - bLength; + } + + /** + * Compares two {@code short} arrays lexicographically, numerically treating + * elements as unsigned. + * + *

If the two arrays share a common prefix then the lexicographic + * comparison is the result of comparing two elements, as if by + * {@link Short#compareUnsigned(short, short)}, at an index within the + * respective arrays that is the prefix length. + * Otherwise, one array is a proper prefix of the other and, lexicographic + * comparison is the result of comparing the two array lengths. + * (See {@link #mismatch(short[], short[])} for the definition of a common + * and proper prefix.) + * + *

A {@code null} array reference is considered lexicographically less + * than a non-{@code null} array reference. Two {@code null} array + * references are considered equal. + * + * @apiNote + *

This method behaves as if (for non-{@code null} array references): + *

{@code
+     *     int i = Arrays.mismatch(a, b);
+     *     if (i >= 0 && i < Math.min(a.length, b.length))
+     *         return Short.compareUnsigned(a[i], b[i]);
+     *     return a.length - b.length;
+     * }
+ * + * @param a the first array to compare + * @param b the second array to compare + * @return the value {@code 0} if the first and second array are + * equal and contain the same elements in the same order; + * a value less than {@code 0} if the first array is + * lexicographically less than the second array; and + * a value greater than {@code 0} if the first array is + * lexicographically greater than the second array + * @since 9 + */ + public static int compareUnsigned(short[] a, short[] b) { + if (a == b) + return 0; + if (a == null || b == null) + return a == null ? -1 : 1; + + int length = Math.min(a.length, b.length); + for (int i = 0; i < length; i++) { + if (a[i] != b[i]) return Short.compareUnsigned(a[i], b[i]); + } + + return a.length - b.length; + } + + /** + * Compares two {@code short} arrays lexicographically over the specified + * ranges, numerically treating elements as unsigned. + * + *

If the two arrays, over the specified ranges, share a common prefix + * then the lexicographic comparison is the result of comparing two + * elements, as if by {@link Short#compareUnsigned(short, short)}, at a + * relative index within the respective arrays that is the length of the + * prefix. + * Otherwise, one array is a proper prefix of the other and, lexicographic + * comparison is the result of comparing the two range lengths. + * (See {@link #mismatch(short[], int, int, short[], int, int)} for the + * definition of a common and proper prefix.) + * + * @apiNote + *

This method behaves as if: + *

{@code
+     *     int i = Arrays.mismatch(a, aFromIndex, aToIndex,
+     *                             b, bFromIndex, bToIndex);
+     *     if (i >= 0 && i < Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex))
+     *         return Short.compareUnsigned(a[aFromIndex + i], b[bFromIndex + i]);
+     *     return (aToIndex - aFromIndex) - (bToIndex - bFromIndex);
+     * }
+ * + * @param a the first array to compare + * @param aFromIndex the index (inclusive) of the first element in the + * first array to be compared + * @param aToIndex the index (exclusive) of the last element in the + * first array to be compared + * @param b the second array to compare + * @param bFromIndex the index (inclusive) of the first element in the + * second array to be compared + * @param bToIndex the index (exclusive) of the last element in the + * second array to be compared + * @return the value {@code 0} if, over the specified ranges, the first and + * second array are equal and contain the same elements in the same + * order; + * a value less than {@code 0} if, over the specified ranges, the + * first array is lexicographically less than the second array; and + * a value greater than {@code 0} if, over the specified ranges, the + * first array is lexicographically greater than the second array + * @throws IllegalArgumentException + * if {@code aFromIndex > aToIndex} or + * if {@code bFromIndex > bToIndex} + * @throws ArrayIndexOutOfBoundsException + * if {@code aFromIndex < 0 or aToIndex > a.length} or + * if {@code bFromIndex < 0 or bToIndex > b.length} + * @throws NullPointerException + * if either array is null + * @since 9 + */ + public static int compareUnsigned(short[] a, int aFromIndex, int aToIndex, + short[] b, int bFromIndex, int bToIndex) { + rangeCheck(a.length, aFromIndex, aToIndex); + rangeCheck(b.length, bFromIndex, bToIndex); + + int aLength = aToIndex - aFromIndex; + int bLength = bToIndex - bFromIndex; + int length = Math.min(aLength, bLength); + for (int i = 0; i < length; i++) { + short va = a[aFromIndex++]; + short vb = b[bFromIndex++]; + if (va != vb) return Short.compareUnsigned(va, vb); + } + + return aLength - bLength; + } + + // Compare char + + /** + * Compares two {@code char} arrays lexicographically. + * + *

If the two arrays share a common prefix then the lexicographic + * comparison is the result of comparing two elements, as if by + * {@link Character#compare(char, char)}, at an index within the respective + * arrays that is the prefix length. + * Otherwise, one array is a proper prefix of the other and, lexicographic + * comparison is the result of comparing the two array lengths. + * (See {@link #mismatch(char[], char[])} for the definition of a common and + * proper prefix.) + * + *

A {@code null} array reference is considered lexicographically less + * than a non-{@code null} array reference. Two {@code null} array + * references are considered equal. + * + *

The comparison is consistent with {@link #equals(char[], char[]) equals}, + * more specifically the following holds for arrays {@code a} and {@code b}: + *

{@code
+     *     Arrays.equals(a, b) == (Arrays.compare(a, b) == 0)
+     * }
+ * + * @apiNote + *

This method behaves as if (for non-{@code null} array references): + *

{@code
+     *     int i = Arrays.mismatch(a, b);
+     *     if (i >= 0 && i < Math.min(a.length, b.length))
+     *         return Character.compare(a[i], b[i]);
+     *     return a.length - b.length;
+     * }
+ * + * @param a the first array to compare + * @param b the second array to compare + * @return the value {@code 0} if the first and second array are equal and + * contain the same elements in the same order; + * a value less than {@code 0} if the first array is + * lexicographically less than the second array; and + * a value greater than {@code 0} if the first array is + * lexicographically greater than the second array + * @since 9 + */ + public static int compare(char[] a, char[] b) { + if (a == b) + return 0; + if (a == null || b == null) + return a == null ? -1 : 1; + + int length = Math.min(a.length, b.length); + for (int i = 0; i < length; i++) { + if (a[i] != b[i]) return Character.compare(a[i], b[i]); + } + + return a.length - b.length; + } + + /** + * Compares two {@code char} arrays lexicographically over the specified + * ranges. + * + *

If the two arrays, over the specified ranges, share a common prefix + * then the lexicographic comparison is the result of comparing two + * elements, as if by {@link Character#compare(char, char)}, at a relative + * index within the respective arrays that is the length of the prefix. + * Otherwise, one array is a proper prefix of the other and, lexicographic + * comparison is the result of comparing the two range lengths. + * (See {@link #mismatch(char[], int, int, char[], int, int)} for the + * definition of a common and proper prefix.) + * + *

The comparison is consistent with + * {@link #equals(char[], int, int, char[], int, int) equals}, more + * specifically the following holds for arrays {@code a} and {@code b} with + * specified ranges [{@code aFromIndex}, {@code atoIndex}) and + * [{@code bFromIndex}, {@code btoIndex}) respectively: + *

{@code
+     *     Arrays.equals(a, aFromIndex, aToIndex, b, bFromIndex, bToIndex) ==
+     *         (Arrays.compare(a, aFromIndex, aToIndex, b, bFromIndex, bToIndex) == 0)
+     * }
+ * + * @apiNote + *

This method behaves as if: + *

{@code
+     *     int i = Arrays.mismatch(a, aFromIndex, aToIndex,
+     *                             b, bFromIndex, bToIndex);
+     *     if (i >= 0 && i < Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex))
+     *         return Character.compare(a[aFromIndex + i], b[bFromIndex + i]);
+     *     return (aToIndex - aFromIndex) - (bToIndex - bFromIndex);
+     * }
+ * + * @param a the first array to compare + * @param aFromIndex the index (inclusive) of the first element in the + * first array to be compared + * @param aToIndex the index (exclusive) of the last element in the + * first array to be compared + * @param b the second array to compare + * @param bFromIndex the index (inclusive) of the first element in the + * second array to be compared + * @param bToIndex the index (exclusive) of the last element in the + * second array to be compared + * @return the value {@code 0} if, over the specified ranges, the first and + * second array are equal and contain the same elements in the same + * order; + * a value less than {@code 0} if, over the specified ranges, the + * first array is lexicographically less than the second array; and + * a value greater than {@code 0} if, over the specified ranges, the + * first array is lexicographically greater than the second array + * @throws IllegalArgumentException + * if {@code aFromIndex > aToIndex} or + * if {@code bFromIndex > bToIndex} + * @throws ArrayIndexOutOfBoundsException + * if {@code aFromIndex < 0 or aToIndex > a.length} or + * if {@code bFromIndex < 0 or bToIndex > b.length} + * @throws NullPointerException + * if either array is {@code null} + * @since 9 + */ + public static int compare(char[] a, int aFromIndex, int aToIndex, + char[] b, int bFromIndex, int bToIndex) { + rangeCheck(a.length, aFromIndex, aToIndex); + rangeCheck(b.length, bFromIndex, bToIndex); + + int aLength = aToIndex - aFromIndex; + int bLength = bToIndex - bFromIndex; + int length = Math.min(aLength, bLength); + for (int i = 0; i < length; i++) { + char va = a[aFromIndex++]; + char vb = b[bFromIndex++]; + if (va != vb) return Character.compare(va, vb); + } + + return aLength - bLength; + } + + // Compare int + + /** + * Compares two {@code int} arrays lexicographically. + * + *

If the two arrays share a common prefix then the lexicographic + * comparison is the result of comparing two elements, as if by + * {@link Integer#compare(int, int)}, at an index within the respective + * arrays that is the prefix length. + * Otherwise, one array is a proper prefix of the other and, lexicographic + * comparison is the result of comparing the two array lengths. + * (See {@link #mismatch(int[], int[])} for the definition of a common and + * proper prefix.) + * + *

A {@code null} array reference is considered lexicographically less + * than a non-{@code null} array reference. Two {@code null} array + * references are considered equal. + * + *

The comparison is consistent with {@link #equals(int[], int[]) equals}, + * more specifically the following holds for arrays {@code a} and {@code b}: + *

{@code
+     *     Arrays.equals(a, b) == (Arrays.compare(a, b) == 0)
+     * }
+ * + * @apiNote + *

This method behaves as if (for non-{@code null} array references): + *

{@code
+     *     int i = Arrays.mismatch(a, b);
+     *     if (i >= 0 && i < Math.min(a.length, b.length))
+     *         return Integer.compare(a[i], b[i]);
+     *     return a.length - b.length;
+     * }
+ * + * @param a the first array to compare + * @param b the second array to compare + * @return the value {@code 0} if the first and second array are equal and + * contain the same elements in the same order; + * a value less than {@code 0} if the first array is + * lexicographically less than the second array; and + * a value greater than {@code 0} if the first array is + * lexicographically greater than the second array + * @since 9 + */ + public static int compare(int[] a, int[] b) { + if (a == b) + return 0; + if (a == null || b == null) + return a == null ? -1 : 1; + + int length = Math.min(a.length, b.length); + for (int i = 0; i < length; i++) { + if (a[i] != b[i]) return Integer.compare(a[i], b[i]); + } + + return a.length - b.length; + } + + /** + * Compares two {@code int} arrays lexicographically over the specified + * ranges. + * + *

If the two arrays, over the specified ranges, share a common prefix + * then the lexicographic comparison is the result of comparing two + * elements, as if by {@link Integer#compare(int, int)}, at a relative index + * within the respective arrays that is the length of the prefix. + * Otherwise, one array is a proper prefix of the other and, lexicographic + * comparison is the result of comparing the two range lengths. + * (See {@link #mismatch(int[], int, int, int[], int, int)} for the + * definition of a common and proper prefix.) + * + *

The comparison is consistent with + * {@link #equals(int[], int, int, int[], int, int) equals}, more + * specifically the following holds for arrays {@code a} and {@code b} with + * specified ranges [{@code aFromIndex}, {@code atoIndex}) and + * [{@code bFromIndex}, {@code btoIndex}) respectively: + *

{@code
+     *     Arrays.equals(a, aFromIndex, aToIndex, b, bFromIndex, bToIndex) ==
+     *         (Arrays.compare(a, aFromIndex, aToIndex, b, bFromIndex, bToIndex) == 0)
+     * }
+ * + * @apiNote + *

This method behaves as if: + *

{@code
+     *     int i = Arrays.mismatch(a, aFromIndex, aToIndex,
+     *                             b, bFromIndex, bToIndex);
+     *     if (i >= 0 && i < Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex))
+     *         return Integer.compare(a[aFromIndex + i], b[bFromIndex + i]);
+     *     return (aToIndex - aFromIndex) - (bToIndex - bFromIndex);
+     * }
+ * + * @param a the first array to compare + * @param aFromIndex the index (inclusive) of the first element in the + * first array to be compared + * @param aToIndex the index (exclusive) of the last element in the + * first array to be compared + * @param b the second array to compare + * @param bFromIndex the index (inclusive) of the first element in the + * second array to be compared + * @param bToIndex the index (exclusive) of the last element in the + * second array to be compared + * @return the value {@code 0} if, over the specified ranges, the first and + * second array are equal and contain the same elements in the same + * order; + * a value less than {@code 0} if, over the specified ranges, the + * first array is lexicographically less than the second array; and + * a value greater than {@code 0} if, over the specified ranges, the + * first array is lexicographically greater than the second array + * @throws IllegalArgumentException + * if {@code aFromIndex > aToIndex} or + * if {@code bFromIndex > bToIndex} + * @throws ArrayIndexOutOfBoundsException + * if {@code aFromIndex < 0 or aToIndex > a.length} or + * if {@code bFromIndex < 0 or bToIndex > b.length} + * @throws NullPointerException + * if either array is {@code null} + * @since 9 + */ + public static int compare(int[] a, int aFromIndex, int aToIndex, + int[] b, int bFromIndex, int bToIndex) { + rangeCheck(a.length, aFromIndex, aToIndex); + rangeCheck(b.length, bFromIndex, bToIndex); + + int aLength = aToIndex - aFromIndex; + int bLength = bToIndex - bFromIndex; + int length = Math.min(aLength, bLength); + for (int i = 0; i < length; i++) { + int va = a[aFromIndex++]; + int vb = b[bFromIndex++]; + if (va != vb) return Integer.compare(va, vb); + } + + return aLength - bLength; + } + + /** + * Compares two {@code int} arrays lexicographically, numerically treating + * elements as unsigned. + * + *

If the two arrays share a common prefix then the lexicographic + * comparison is the result of comparing two elements, as if by + * {@link Integer#compareUnsigned(int, int)}, at an index within the + * respective arrays that is the prefix length. + * Otherwise, one array is a proper prefix of the other and, lexicographic + * comparison is the result of comparing the two array lengths. + * (See {@link #mismatch(int[], int[])} for the definition of a common + * and proper prefix.) + * + *

A {@code null} array reference is considered lexicographically less + * than a non-{@code null} array reference. Two {@code null} array + * references are considered equal. + * + * @apiNote + *

This method behaves as if (for non-{@code null} array references): + *

{@code
+     *     int i = Arrays.mismatch(a, b);
+     *     if (i >= 0 && i < Math.min(a.length, b.length))
+     *         return Integer.compareUnsigned(a[i], b[i]);
+     *     return a.length - b.length;
+     * }
+ * + * @param a the first array to compare + * @param b the second array to compare + * @return the value {@code 0} if the first and second array are + * equal and contain the same elements in the same order; + * a value less than {@code 0} if the first array is + * lexicographically less than the second array; and + * a value greater than {@code 0} if the first array is + * lexicographically greater than the second array + * @since 9 + */ + public static int compareUnsigned(int[] a, int[] b) { + if (a == b) + return 0; + if (a == null || b == null) + return a == null ? -1 : 1; + + int length = Math.min(a.length, b.length); + for (int i = 0; i < length; i++) { + if (a[i] != b[i]) return Integer.compareUnsigned(a[i], b[i]); + } + + return a.length - b.length; + } + + /** + * Compares two {@code int} arrays lexicographically over the specified + * ranges, numerically treating elements as unsigned. + * + *

If the two arrays, over the specified ranges, share a common prefix + * then the lexicographic comparison is the result of comparing two + * elements, as if by {@link Integer#compareUnsigned(int, int)}, at a + * relative index within the respective arrays that is the length of the + * prefix. + * Otherwise, one array is a proper prefix of the other and, lexicographic + * comparison is the result of comparing the two range lengths. + * (See {@link #mismatch(int[], int, int, int[], int, int)} for the + * definition of a common and proper prefix.) + * + * @apiNote + *

This method behaves as if: + *

{@code
+     *     int i = Arrays.mismatch(a, aFromIndex, aToIndex,
+     *                             b, bFromIndex, bToIndex);
+     *     if (i >= 0 && i < Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex))
+     *         return Integer.compareUnsigned(a[aFromIndex + i], b[bFromIndex + i]);
+     *     return (aToIndex - aFromIndex) - (bToIndex - bFromIndex);
+     * }
+ * + * @param a the first array to compare + * @param aFromIndex the index (inclusive) of the first element in the + * first array to be compared + * @param aToIndex the index (exclusive) of the last element in the + * first array to be compared + * @param b the second array to compare + * @param bFromIndex the index (inclusive) of the first element in the + * second array to be compared + * @param bToIndex the index (exclusive) of the last element in the + * second array to be compared + * @return the value {@code 0} if, over the specified ranges, the first and + * second array are equal and contain the same elements in the same + * order; + * a value less than {@code 0} if, over the specified ranges, the + * first array is lexicographically less than the second array; and + * a value greater than {@code 0} if, over the specified ranges, the + * first array is lexicographically greater than the second array + * @throws IllegalArgumentException + * if {@code aFromIndex > aToIndex} or + * if {@code bFromIndex > bToIndex} + * @throws ArrayIndexOutOfBoundsException + * if {@code aFromIndex < 0 or aToIndex > a.length} or + * if {@code bFromIndex < 0 or bToIndex > b.length} + * @throws NullPointerException + * if either array is null + * @since 9 + */ + public static int compareUnsigned(int[] a, int aFromIndex, int aToIndex, + int[] b, int bFromIndex, int bToIndex) { + rangeCheck(a.length, aFromIndex, aToIndex); + rangeCheck(b.length, bFromIndex, bToIndex); + + int aLength = aToIndex - aFromIndex; + int bLength = bToIndex - bFromIndex; + int length = Math.min(aLength, bLength); + for (int i = 0; i < length; i++) { + int va = a[aFromIndex++]; + int vb = b[bFromIndex++]; + if (va != vb) return Integer.compareUnsigned(va, vb); + } + + return aLength - bLength; + } + + // Compare long + + /** + * Compares two {@code long} arrays lexicographically. + * + *

If the two arrays share a common prefix then the lexicographic + * comparison is the result of comparing two elements, as if by + * {@link Long#compare(long, long)}, at an index within the respective + * arrays that is the prefix length. + * Otherwise, one array is a proper prefix of the other and, lexicographic + * comparison is the result of comparing the two array lengths. + * (See {@link #mismatch(long[], long[])} for the definition of a common and + * proper prefix.) + * + *

A {@code null} array reference is considered lexicographically less + * than a non-{@code null} array reference. Two {@code null} array + * references are considered equal. + * + *

The comparison is consistent with {@link #equals(long[], long[]) equals}, + * more specifically the following holds for arrays {@code a} and {@code b}: + *

{@code
+     *     Arrays.equals(a, b) == (Arrays.compare(a, b) == 0)
+     * }
+ * + * @apiNote + *

This method behaves as if (for non-{@code null} array references): + *

{@code
+     *     int i = Arrays.mismatch(a, b);
+     *     if (i >= 0 && i < Math.min(a.length, b.length))
+     *         return Long.compare(a[i], b[i]);
+     *     return a.length - b.length;
+     * }
+ * + * @param a the first array to compare + * @param b the second array to compare + * @return the value {@code 0} if the first and second array are equal and + * contain the same elements in the same order; + * a value less than {@code 0} if the first array is + * lexicographically less than the second array; and + * a value greater than {@code 0} if the first array is + * lexicographically greater than the second array + * @since 9 + */ + public static int compare(long[] a, long[] b) { + if (a == b) + return 0; + if (a == null || b == null) + return a == null ? -1 : 1; + + int length = Math.min(a.length, b.length); + for (int i = 0; i < length; i++) { + if (a[i] != b[i]) return Long.compare(a[i], b[i]); + } + + return a.length - b.length; + } + + /** + * Compares two {@code long} arrays lexicographically over the specified + * ranges. + * + *

If the two arrays, over the specified ranges, share a common prefix + * then the lexicographic comparison is the result of comparing two + * elements, as if by {@link Long#compare(long, long)}, at a relative index + * within the respective arrays that is the length of the prefix. + * Otherwise, one array is a proper prefix of the other and, lexicographic + * comparison is the result of comparing the two range lengths. + * (See {@link #mismatch(long[], int, int, long[], int, int)} for the + * definition of a common and proper prefix.) + * + *

The comparison is consistent with + * {@link #equals(long[], int, int, long[], int, int) equals}, more + * specifically the following holds for arrays {@code a} and {@code b} with + * specified ranges [{@code aFromIndex}, {@code atoIndex}) and + * [{@code bFromIndex}, {@code btoIndex}) respectively: + *

{@code
+     *     Arrays.equals(a, aFromIndex, aToIndex, b, bFromIndex, bToIndex) ==
+     *         (Arrays.compare(a, aFromIndex, aToIndex, b, bFromIndex, bToIndex) == 0)
+     * }
+ * + * @apiNote + *

This method behaves as if: + *

{@code
+     *     int i = Arrays.mismatch(a, aFromIndex, aToIndex,
+     *                             b, bFromIndex, bToIndex);
+     *     if (i >= 0 && i < Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex))
+     *         return Long.compare(a[aFromIndex + i], b[bFromIndex + i]);
+     *     return (aToIndex - aFromIndex) - (bToIndex - bFromIndex);
+     * }
+ * + * @param a the first array to compare + * @param aFromIndex the index (inclusive) of the first element in the + * first array to be compared + * @param aToIndex the index (exclusive) of the last element in the + * first array to be compared + * @param b the second array to compare + * @param bFromIndex the index (inclusive) of the first element in the + * second array to be compared + * @param bToIndex the index (exclusive) of the last element in the + * second array to be compared + * @return the value {@code 0} if, over the specified ranges, the first and + * second array are equal and contain the same elements in the same + * order; + * a value less than {@code 0} if, over the specified ranges, the + * first array is lexicographically less than the second array; and + * a value greater than {@code 0} if, over the specified ranges, the + * first array is lexicographically greater than the second array + * @throws IllegalArgumentException + * if {@code aFromIndex > aToIndex} or + * if {@code bFromIndex > bToIndex} + * @throws ArrayIndexOutOfBoundsException + * if {@code aFromIndex < 0 or aToIndex > a.length} or + * if {@code bFromIndex < 0 or bToIndex > b.length} + * @throws NullPointerException + * if either array is {@code null} + * @since 9 + */ + public static int compare(long[] a, int aFromIndex, int aToIndex, + long[] b, int bFromIndex, int bToIndex) { + rangeCheck(a.length, aFromIndex, aToIndex); + rangeCheck(b.length, bFromIndex, bToIndex); + + int aLength = aToIndex - aFromIndex; + int bLength = bToIndex - bFromIndex; + int length = Math.min(aLength, bLength); + for (int i = 0; i < length; i++) { + long va = a[aFromIndex++]; + long vb = b[bFromIndex++]; + if (va != vb) return Long.compare(va, vb); + } + + return aLength - bLength; + } + + /** + * Compares two {@code long} arrays lexicographically, numerically treating + * elements as unsigned. + * + *

If the two arrays share a common prefix then the lexicographic + * comparison is the result of comparing two elements, as if by + * {@link Long#compareUnsigned(long, long)}, at an index within the + * respective arrays that is the prefix length. + * Otherwise, one array is a proper prefix of the other and, lexicographic + * comparison is the result of comparing the two array lengths. + * (See {@link #mismatch(long[], long[])} for the definition of a common + * and proper prefix.) + * + *

A {@code null} array reference is considered lexicographically less + * than a non-{@code null} array reference. Two {@code null} array + * references are considered equal. + * + * @apiNote + *

This method behaves as if (for non-{@code null} array references): + *

{@code
+     *     int i = Arrays.mismatch(a, b);
+     *     if (i >= 0 && i < Math.min(a.length, b.length))
+     *         return Long.compareUnsigned(a[i], b[i]);
+     *     return a.length - b.length;
+     * }
+ * + * @param a the first array to compare + * @param b the second array to compare + * @return the value {@code 0} if the first and second array are + * equal and contain the same elements in the same order; + * a value less than {@code 0} if the first array is + * lexicographically less than the second array; and + * a value greater than {@code 0} if the first array is + * lexicographically greater than the second array + * @since 9 + */ + public static int compareUnsigned(long[] a, long[] b) { + if (a == b) + return 0; + if (a == null || b == null) + return a == null ? -1 : 1; + + int length = Math.min(a.length, b.length); + for (int i = 0; i < length; i++) { + if (a[i] != b[i]) return Long.compareUnsigned(a[i], b[i]); + } + + return a.length - b.length; + } + + /** + * Compares two {@code long} arrays lexicographically over the specified + * ranges, numerically treating elements as unsigned. + * + *

If the two arrays, over the specified ranges, share a common prefix + * then the lexicographic comparison is the result of comparing two + * elements, as if by {@link Long#compareUnsigned(long, long)}, at a + * relative index within the respective arrays that is the length of the + * prefix. + * Otherwise, one array is a proper prefix of the other and, lexicographic + * comparison is the result of comparing the two range lengths. + * (See {@link #mismatch(long[], int, int, long[], int, int)} for the + * definition of a common and proper prefix.) + * + * @apiNote + *

This method behaves as if: + *

{@code
+     *     int i = Arrays.mismatch(a, aFromIndex, aToIndex,
+     *                             b, bFromIndex, bToIndex);
+     *     if (i >= 0 && i < Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex))
+     *         return Long.compareUnsigned(a[aFromIndex + i], b[bFromIndex + i]);
+     *     return (aToIndex - aFromIndex) - (bToIndex - bFromIndex);
+     * }
+ * + * @param a the first array to compare + * @param aFromIndex the index (inclusive) of the first element in the + * first array to be compared + * @param aToIndex the index (exclusive) of the last element in the + * first array to be compared + * @param b the second array to compare + * @param bFromIndex the index (inclusive) of the first element in the + * second array to be compared + * @param bToIndex the index (exclusive) of the last element in the + * second array to be compared + * @return the value {@code 0} if, over the specified ranges, the first and + * second array are equal and contain the same elements in the same + * order; + * a value less than {@code 0} if, over the specified ranges, the + * first array is lexicographically less than the second array; and + * a value greater than {@code 0} if, over the specified ranges, the + * first array is lexicographically greater than the second array + * @throws IllegalArgumentException + * if {@code aFromIndex > aToIndex} or + * if {@code bFromIndex > bToIndex} + * @throws ArrayIndexOutOfBoundsException + * if {@code aFromIndex < 0 or aToIndex > a.length} or + * if {@code bFromIndex < 0 or bToIndex > b.length} + * @throws NullPointerException + * if either array is null + * @since 9 + */ + public static int compareUnsigned(long[] a, int aFromIndex, int aToIndex, + long[] b, int bFromIndex, int bToIndex) { + rangeCheck(a.length, aFromIndex, aToIndex); + rangeCheck(b.length, bFromIndex, bToIndex); + + int aLength = aToIndex - aFromIndex; + int bLength = bToIndex - bFromIndex; + int length = Math.min(aLength, bLength); + for (int i = 0; i < length; i++) { + long va = a[aFromIndex++]; + long vb = b[bFromIndex++]; + if (va != vb) return Long.compareUnsigned(va, vb); + } + + return aLength - bLength; + } + + // Compare float + + /** + * Compares two {@code float} arrays lexicographically. + * + *

If the two arrays share a common prefix then the lexicographic + * comparison is the result of comparing two elements, as if by + * {@link Float#compare(float, float)}, at an index within the respective + * arrays that is the prefix length. + * Otherwise, one array is a proper prefix of the other and, lexicographic + * comparison is the result of comparing the two array lengths. + * (See {@link #mismatch(float[], float[])} for the definition of a common + * and proper prefix.) + * + *

A {@code null} array reference is considered lexicographically less + * than a non-{@code null} array reference. Two {@code null} array + * references are considered equal. + * + *

The comparison is consistent with {@link #equals(float[], float[]) equals}, + * more specifically the following holds for arrays {@code a} and {@code b}: + *

{@code
+     *     Arrays.equals(a, b) == (Arrays.compare(a, b) == 0)
+     * }
+ * + * @apiNote + *

This method behaves as if (for non-{@code null} array references): + *

{@code
+     *     int i = Arrays.mismatch(a, b);
+     *     if (i >= 0 && i < Math.min(a.length, b.length))
+     *         return Float.compare(a[i], b[i]);
+     *     return a.length - b.length;
+     * }
+ * + * @param a the first array to compare + * @param b the second array to compare + * @return the value {@code 0} if the first and second array are equal and + * contain the same elements in the same order; + * a value less than {@code 0} if the first array is + * lexicographically less than the second array; and + * a value greater than {@code 0} if the first array is + * lexicographically greater than the second array + * @since 9 + */ + public static int compare(float[] a, float[] b) { + if (a == b) + return 0; + if (a == null || b == null) + return a == null ? -1 : 1; + + int length = Math.min(a.length, b.length); + for (int i = 0; i < length; i++) { + float va = a[i], vb = b[i]; + if (Float.floatToRawIntBits(va) != Float.floatToRawIntBits(vb)) { + int c = Float.compare(va, vb); + if (c != 0) return c; + } + } + + return a.length - b.length; + } + + /** + * Compares two {@code float} arrays lexicographically over the specified + * ranges. + * + *

If the two arrays, over the specified ranges, share a common prefix + * then the lexicographic comparison is the result of comparing two + * elements, as if by {@link Float#compare(float, float)}, at a relative + * index within the respective arrays that is the length of the prefix. + * Otherwise, one array is a proper prefix of the other and, lexicographic + * comparison is the result of comparing the two range lengths. + * (See {@link #mismatch(float[], int, int, float[], int, int)} for the + * definition of a common and proper prefix.) + * + *

The comparison is consistent with + * {@link #equals(float[], int, int, float[], int, int) equals}, more + * specifically the following holds for arrays {@code a} and {@code b} with + * specified ranges [{@code aFromIndex}, {@code atoIndex}) and + * [{@code bFromIndex}, {@code btoIndex}) respectively: + *

{@code
+     *     Arrays.equals(a, aFromIndex, aToIndex, b, bFromIndex, bToIndex) ==
+     *         (Arrays.compare(a, aFromIndex, aToIndex, b, bFromIndex, bToIndex) == 0)
+     * }
+ * + * @apiNote + *

This method behaves as if: + *

{@code
+     *     int i = Arrays.mismatch(a, aFromIndex, aToIndex,
+     *                             b, bFromIndex, bToIndex);
+     *     if (i >= 0 && i < Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex))
+     *         return Float.compare(a[aFromIndex + i], b[bFromIndex + i]);
+     *     return (aToIndex - aFromIndex) - (bToIndex - bFromIndex);
+     * }
+ * + * @param a the first array to compare + * @param aFromIndex the index (inclusive) of the first element in the + * first array to be compared + * @param aToIndex the index (exclusive) of the last element in the + * first array to be compared + * @param b the second array to compare + * @param bFromIndex the index (inclusive) of the first element in the + * second array to be compared + * @param bToIndex the index (exclusive) of the last element in the + * second array to be compared + * @return the value {@code 0} if, over the specified ranges, the first and + * second array are equal and contain the same elements in the same + * order; + * a value less than {@code 0} if, over the specified ranges, the + * first array is lexicographically less than the second array; and + * a value greater than {@code 0} if, over the specified ranges, the + * first array is lexicographically greater than the second array + * @throws IllegalArgumentException + * if {@code aFromIndex > aToIndex} or + * if {@code bFromIndex > bToIndex} + * @throws ArrayIndexOutOfBoundsException + * if {@code aFromIndex < 0 or aToIndex > a.length} or + * if {@code bFromIndex < 0 or bToIndex > b.length} + * @throws NullPointerException + * if either array is {@code null} + * @since 9 + */ + public static int compare(float[] a, int aFromIndex, int aToIndex, + float[] b, int bFromIndex, int bToIndex) { + rangeCheck(a.length, aFromIndex, aToIndex); + rangeCheck(b.length, bFromIndex, bToIndex); + + int aLength = aToIndex - aFromIndex; + int bLength = bToIndex - bFromIndex; + int length = Math.min(aLength, bLength); + for (int i = 0; i < length; i++) { + float va = a[aFromIndex++], vb = b[bFromIndex++]; + if (Float.floatToRawIntBits(va) != Float.floatToRawIntBits(vb)) { + int c = Float.compare(va, vb); + if (c != 0) return c; + } + } + + return aLength - bLength; + } + + // Compare double + + /** + * Compares two {@code double} arrays lexicographically. + * + *

If the two arrays share a common prefix then the lexicographic + * comparison is the result of comparing two elements, as if by + * {@link Double#compare(double, double)}, at an index within the respective + * arrays that is the prefix length. + * Otherwise, one array is a proper prefix of the other and, lexicographic + * comparison is the result of comparing the two array lengths. + * (See {@link #mismatch(double[], double[])} for the definition of a common + * and proper prefix.) + * + *

A {@code null} array reference is considered lexicographically less + * than a non-{@code null} array reference. Two {@code null} array + * references are considered equal. + * + *

The comparison is consistent with {@link #equals(double[], double[]) equals}, + * more specifically the following holds for arrays {@code a} and {@code b}: + *

{@code
+     *     Arrays.equals(a, b) == (Arrays.compare(a, b) == 0)
+     * }
+ * + * @apiNote + *

This method behaves as if (for non-{@code null} array references): + *

{@code
+     *     int i = Arrays.mismatch(a, b);
+     *     if (i >= 0 && i < Math.min(a.length, b.length))
+     *         return Double.compare(a[i], b[i]);
+     *     return a.length - b.length;
+     * }
+ * + * @param a the first array to compare + * @param b the second array to compare + * @return the value {@code 0} if the first and second array are equal and + * contain the same elements in the same order; + * a value less than {@code 0} if the first array is + * lexicographically less than the second array; and + * a value greater than {@code 0} if the first array is + * lexicographically greater than the second array + * @since 9 + */ + public static int compare(double[] a, double[] b) { + if (a == b) + return 0; + if (a == null || b == null) + return a == null ? -1 : 1; + + int length = Math.min(a.length, b.length); + for (int i = 0; i < length; i++) { + double va = a[i], vb = b[i]; + if (Double.doubleToRawLongBits(va) != Double.doubleToRawLongBits(vb)) { + int c = Double.compare(va, vb); + if (c != 0) return c; + } + } + + return a.length - b.length; + } + + /** + * Compares two {@code double} arrays lexicographically over the specified + * ranges. + * + *

If the two arrays, over the specified ranges, share a common prefix + * then the lexicographic comparison is the result of comparing two + * elements, as if by {@link Double#compare(double, double)}, at a relative + * index within the respective arrays that is the length of the prefix. + * Otherwise, one array is a proper prefix of the other and, lexicographic + * comparison is the result of comparing the two range lengths. + * (See {@link #mismatch(double[], int, int, double[], int, int)} for the + * definition of a common and proper prefix.) + * + *

The comparison is consistent with + * {@link #equals(double[], int, int, double[], int, int) equals}, more + * specifically the following holds for arrays {@code a} and {@code b} with + * specified ranges [{@code aFromIndex}, {@code atoIndex}) and + * [{@code bFromIndex}, {@code btoIndex}) respectively: + *

{@code
+     *     Arrays.equals(a, aFromIndex, aToIndex, b, bFromIndex, bToIndex) ==
+     *         (Arrays.compare(a, aFromIndex, aToIndex, b, bFromIndex, bToIndex) == 0)
+     * }
+ * + * @apiNote + *

This method behaves as if: + *

{@code
+     *     int i = Arrays.mismatch(a, aFromIndex, aToIndex,
+     *                             b, bFromIndex, bToIndex);
+     *     if (i >= 0 && i < Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex))
+     *         return Double.compare(a[aFromIndex + i], b[bFromIndex + i]);
+     *     return (aToIndex - aFromIndex) - (bToIndex - bFromIndex);
+     * }
+ * + * @param a the first array to compare + * @param aFromIndex the index (inclusive) of the first element in the + * first array to be compared + * @param aToIndex the index (exclusive) of the last element in the + * first array to be compared + * @param b the second array to compare + * @param bFromIndex the index (inclusive) of the first element in the + * second array to be compared + * @param bToIndex the index (exclusive) of the last element in the + * second array to be compared + * @return the value {@code 0} if, over the specified ranges, the first and + * second array are equal and contain the same elements in the same + * order; + * a value less than {@code 0} if, over the specified ranges, the + * first array is lexicographically less than the second array; and + * a value greater than {@code 0} if, over the specified ranges, the + * first array is lexicographically greater than the second array + * @throws IllegalArgumentException + * if {@code aFromIndex > aToIndex} or + * if {@code bFromIndex > bToIndex} + * @throws ArrayIndexOutOfBoundsException + * if {@code aFromIndex < 0 or aToIndex > a.length} or + * if {@code bFromIndex < 0 or bToIndex > b.length} + * @throws NullPointerException + * if either array is {@code null} + * @since 9 + */ + public static int compare(double[] a, int aFromIndex, int aToIndex, + double[] b, int bFromIndex, int bToIndex) { + rangeCheck(a.length, aFromIndex, aToIndex); + rangeCheck(b.length, bFromIndex, bToIndex); + + int aLength = aToIndex - aFromIndex; + int bLength = bToIndex - bFromIndex; + int length = Math.min(aLength, bLength); + for (int i = 0; i < length; i++) { + double va = a[aFromIndex++], vb = b[bFromIndex++]; + if (Double.doubleToRawLongBits(va) != Double.doubleToRawLongBits(vb)) { + int c = Double.compare(va, vb); + if (c != 0) return c; + } + } + + return aLength - bLength; + } + + // Compare objects + + /** + * Compares two {@code Object} arrays, within comparable elements, + * lexicographically. + * + *

If the two arrays share a common prefix then the lexicographic + * comparison is the result of comparing two elements of type {@code T} at + * an index {@code i} within the respective arrays that is the prefix + * length, as if by: + *

{@code
+     *     Comparator.nullsFirst(Comparator.naturalOrder()).
+     *         compare(a[i], b[i])
+     * }
+ * Otherwise, one array is a proper prefix of the other and, lexicographic + * comparison is the result of comparing the two array lengths. + * (See {@link #mismatch(Object[], Object[])} for the definition of a common + * and proper prefix.) + * + *

A {@code null} array reference is considered lexicographically less + * than a non-{@code null} array reference. Two {@code null} array + * references are considered equal. + * A {@code null} array element is considered lexicographically than a + * non-{@code null} array element. Two {@code null} array elements are + * considered equal. + * + *

The comparison is consistent with {@link #equals(Object[], Object[]) equals}, + * more specifically the following holds for arrays {@code a} and {@code b}: + *

{@code
+     *     Arrays.equals(a, b) == (Arrays.compare(a, b) == 0)
+     * }
+ * + * @apiNote + *

This method behaves as if (for non-{@code null} array references + * and elements): + *

{@code
+     *     int i = Arrays.mismatch(a, b);
+     *     if (i >= 0 && i < Math.min(a.length, b.length))
+     *         return a[i].compareTo(b[i]);
+     *     return a.length - b.length;
+     * }
+ * + * @param a the first array to compare + * @param b the second array to compare + * @param the type of comparable array elements + * @return the value {@code 0} if the first and second array are equal and + * contain the same elements in the same order; + * a value less than {@code 0} if the first array is + * lexicographically less than the second array; and + * a value greater than {@code 0} if the first array is + * lexicographically greater than the second array + * @since 9 + */ + public static > int compare(T[] a, T[] b) { + if (a == b) + return 0; + // A null array is less than a non-null array + if (a == null || b == null) + return a == null ? -1 : 1; + + int length = Math.min(a.length, b.length); + for (int i = 0; i < length; i++) { + T oa = a[i]; + T ob = b[i]; + if (oa != ob) { + // A null element is less than a non-null element + if (oa == null || ob == null) + return oa == null ? -1 : 1; + int v = oa.compareTo(ob); + if (v != 0) { + return v; + } + } + } + + return a.length - b.length; + } + + /** + * Compares two {@code Object} arrays lexicographically over the specified + * ranges. + * + *

If the two arrays, over the specified ranges, share a common prefix + * then the lexicographic comparison is the result of comparing two + * elements of type {@code T} at a relative index {@code i} within the + * respective arrays that is the prefix length, as if by: + *

{@code
+     *     Comparator.nullsFirst(Comparator.naturalOrder()).
+     *         compare(a[aFromIndex + i, b[bFromIndex + i])
+     * }
+ * Otherwise, one array is a proper prefix of the other and, lexicographic + * comparison is the result of comparing the two range lengths. + * (See {@link #mismatch(Object[], int, int, Object[], int, int)} for the + * definition of a common and proper prefix.) + * + *

The comparison is consistent with + * {@link #equals(Object[], int, int, Object[], int, int) equals}, more + * specifically the following holds for arrays {@code a} and {@code b} with + * specified ranges [{@code aFromIndex}, {@code atoIndex}) and + * [{@code bFromIndex}, {@code btoIndex}) respectively: + *

{@code
+     *     Arrays.equals(a, aFromIndex, aToIndex, b, bFromIndex, bToIndex) ==
+     *         (Arrays.compare(a, aFromIndex, aToIndex, b, bFromIndex, bToIndex) == 0)
+     * }
+ * + * @apiNote + *

This method behaves as if (for non-{@code null} array elements): + *

{@code
+     *     int i = Arrays.mismatch(a, aFromIndex, aToIndex,
+     *                             b, bFromIndex, bToIndex);
+     *     if (i >= 0 && i < Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex))
+     *         return a[aFromIndex + i].compareTo(b[bFromIndex + i]);
+     *     return (aToIndex - aFromIndex) - (bToIndex - bFromIndex);
+     * }
+ * + * @param a the first array to compare + * @param aFromIndex the index (inclusive) of the first element in the + * first array to be compared + * @param aToIndex the index (exclusive) of the last element in the + * first array to be compared + * @param b the second array to compare + * @param bFromIndex the index (inclusive) of the first element in the + * second array to be compared + * @param bToIndex the index (exclusive) of the last element in the + * second array to be compared + * @param the type of comparable array elements + * @return the value {@code 0} if, over the specified ranges, the first and + * second array are equal and contain the same elements in the same + * order; + * a value less than {@code 0} if, over the specified ranges, the + * first array is lexicographically less than the second array; and + * a value greater than {@code 0} if, over the specified ranges, the + * first array is lexicographically greater than the second array + * @throws IllegalArgumentException + * if {@code aFromIndex > aToIndex} or + * if {@code bFromIndex > bToIndex} + * @throws ArrayIndexOutOfBoundsException + * if {@code aFromIndex < 0 or aToIndex > a.length} or + * if {@code bFromIndex < 0 or bToIndex > b.length} + * @throws NullPointerException + * if either array is {@code null} + * @since 9 + */ + public static > int compare( + T[] a, int aFromIndex, int aToIndex, + T[] b, int bFromIndex, int bToIndex) { + rangeCheck(a.length, aFromIndex, aToIndex); + rangeCheck(b.length, bFromIndex, bToIndex); + + int aLength = aToIndex - aFromIndex; + int bLength = bToIndex - bFromIndex; + int length = Math.min(aLength, bLength); + for (int i = 0; i < length; i++) { + T oa = a[aFromIndex++]; + T ob = b[bFromIndex++]; + if (oa != ob) { + if (oa == null || ob == null) + return oa == null ? -1 : 1; + int v = oa.compareTo(ob); + if (v != 0) { + return v; + } + } + } + + return aLength - bLength; + } + + /** + * Compares two {@code Object} arrays lexicographically using a specified + * comparator. + * + *

If the two arrays share a common prefix then the lexicographic + * comparison is the result of comparing with the specified comparator two + * elements at an index within the respective arrays that is the prefix + * length. + * Otherwise, one array is a proper prefix of the other and, lexicographic + * comparison is the result of comparing the two array lengths. + * (See {@link #mismatch(Object[], Object[])} for the definition of a common + * and proper prefix.) + * + *

A {@code null} array reference is considered lexicographically less + * than a non-{@code null} array reference. Two {@code null} array + * references are considered equal. + * + * @apiNote + *

This method behaves as if (for non-{@code null} array references): + *

{@code
+     *     int i = Arrays.mismatch(a, b, cmp);
+     *     if (i >= 0 && i < Math.min(a.length, b.length))
+     *         return cmp.compare(a[i], b[i]);
+     *     return a.length - b.length;
+     * }
+ * + * @param a the first array to compare + * @param b the second array to compare + * @param cmp the comparator to compare array elements + * @param the type of array elements + * @return the value {@code 0} if the first and second array are equal and + * contain the same elements in the same order; + * a value less than {@code 0} if the first array is + * lexicographically less than the second array; and + * a value greater than {@code 0} if the first array is + * lexicographically greater than the second array + * @throws NullPointerException if the comparator is {@code null} + * @since 9 + */ + public static int compare(T[] a, T[] b, + Comparator cmp) { + Objects.requireNonNull(cmp); + if (a == b) + return 0; + if (a == null || b == null) + return a == null ? -1 : 1; + + int length = Math.min(a.length, b.length); + for (int i = 0; i < length; i++) { + T oa = a[i]; + T ob = b[i]; + if (oa != ob) { + // Null-value comparison is deferred to the comparator + int v = cmp.compare(oa, ob); + if (v != 0) { + return v; + } + } + } + + return a.length - b.length; + } + + /** + * Compares two {@code Object} arrays lexicographically over the specified + * ranges. + * + *

If the two arrays, over the specified ranges, share a common prefix + * then the lexicographic comparison is the result of comparing with the + * specified comparator two elements at a relative index within the + * respective arrays that is the prefix length. + * Otherwise, one array is a proper prefix of the other and, lexicographic + * comparison is the result of comparing the two range lengths. + * (See {@link #mismatch(Object[], int, int, Object[], int, int)} for the + * definition of a common and proper prefix.) + * + * @apiNote + *

This method behaves as if (for non-{@code null} array elements): + *

{@code
+     *     int i = Arrays.mismatch(a, aFromIndex, aToIndex,
+     *                             b, bFromIndex, bToIndex, cmp);
+     *     if (i >= 0 && i < Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex))
+     *         return cmp.compare(a[aFromIndex + i], b[bFromIndex + i]);
+     *     return (aToIndex - aFromIndex) - (bToIndex - bFromIndex);
+     * }
+ * + * @param a the first array to compare + * @param aFromIndex the index (inclusive) of the first element in the + * first array to be compared + * @param aToIndex the index (exclusive) of the last element in the + * first array to be compared + * @param b the second array to compare + * @param bFromIndex the index (inclusive) of the first element in the + * second array to be compared + * @param bToIndex the index (exclusive) of the last element in the + * second array to be compared + * @param cmp the comparator to compare array elements + * @param the type of array elements + * @return the value {@code 0} if, over the specified ranges, the first and + * second array are equal and contain the same elements in the same + * order; + * a value less than {@code 0} if, over the specified ranges, the + * first array is lexicographically less than the second array; and + * a value greater than {@code 0} if, over the specified ranges, the + * first array is lexicographically greater than the second array + * @throws IllegalArgumentException + * if {@code aFromIndex > aToIndex} or + * if {@code bFromIndex > bToIndex} + * @throws ArrayIndexOutOfBoundsException + * if {@code aFromIndex < 0 or aToIndex > a.length} or + * if {@code bFromIndex < 0 or bToIndex > b.length} + * @throws NullPointerException + * if either array or the comparator is {@code null} + * @since 9 + */ + public static int compare( + T[] a, int aFromIndex, int aToIndex, + T[] b, int bFromIndex, int bToIndex, + Comparator cmp) { + Objects.requireNonNull(cmp); + rangeCheck(a.length, aFromIndex, aToIndex); + rangeCheck(b.length, bFromIndex, bToIndex); + + int aLength = aToIndex - aFromIndex; + int bLength = bToIndex - bFromIndex; + int length = Math.min(aLength, bLength); + for (int i = 0; i < length; i++) { + T oa = a[aFromIndex++]; + T ob = b[bFromIndex++]; + if (oa != ob) { + // Null-value comparison is deferred to the comparator + int v = cmp.compare(oa, ob); + if (v != 0) { + return v; + } + } + } + + return aLength - bLength; + } + + + // Mismatch methods + + // Mismatch boolean + + /** + * Finds and returns the index of the first mismatch between two + * {@code boolean} arrays, otherwise return -1 if no mismatch is found. The + * index will be in the range of 0 (inclusive) up to the length (inclusive) + * of the smaller array. + * + *

If the two arrays share a common prefix then the returned index is the + * length of the common prefix and it follows that there is a mismatch + * between the two elements at that index within the respective arrays. + * If one array is a proper prefix of the other then the returned index is + * the length of the smaller array and it follows that the index is only + * valid for the larger array. + * Otherwise, there is no mismatch. + * + *

Two non-{@code null} arrays, {@code a} and {@code b}, share a common + * prefix of length {@code pl} if the following expression is true: + *

{@code
+     *     pl >= 0 &&
+     *     pl < Math.min(a.length, b.length) &&
+     *     Arrays.equals(a, 0, pl, b, 0, pl) &&
+     *     a[pl] != b[pl]
+     * }
+ * Note that a common prefix length of {@code 0} indicates that the first + * elements from each array mismatch. + * + *

Two non-{@code null} arrays, {@code a} and {@code b}, share a proper + * prefix if the following expression is true: + *

{@code
+     *     a.length != b.length &&
+     *     Arrays.equals(a, 0, Math.min(a.length, b.length),
+     *                   b, 0, Math.min(a.length, b.length))
+     * }
+ * + * @param a the first array to be tested for a mismatch + * @param b the second array to be tested for a mismatch + * @return the index of the first mismatch between the two arrays, + * otherwise {@code -1}. + * @throws NullPointerException + * if either array is {@code null} + * @since 9 + */ + public static int mismatch(boolean[] a, boolean[] b) { + int length = Math.min(a.length, b.length); // Check null array refs + if (a == b) + return -1; + + for (int i = 0; i < length; i++) { + if (a[i] != b[i]) return i; + } + + return a.length != b.length ? length : -1; + } + + /** + * Finds and returns the relative index of the first mismatch between two + * {@code boolean} arrays over the specified ranges, otherwise return -1 if + * no mismatch is found. The index will be in the range of 0 (inclusive) up + * to the length (inclusive) of the smaller range. + * + *

If the two arrays, over the specified ranges, share a common prefix + * then the returned relative index is the length of the common prefix and + * it follows that there is a mismatch between the two elements at that + * relative index within the respective arrays. + * If one array is a proper prefix of the other, over the specified ranges, + * then the returned relative index is the length of the smaller range and + * it follows that the relative index is only valid for the array with the + * larger range. + * Otherwise, there is no mismatch. + * + *

Two non-{@code null} arrays, {@code a} and {@code b} with specified + * ranges [{@code aFromIndex}, {@code atoIndex}) and + * [{@code bFromIndex}, {@code btoIndex}) respectively, share a common + * prefix of length {@code pl} if the following expression is true: + *

{@code
+     *     pl >= 0 &&
+     *     pl < Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex) &&
+     *     Arrays.equals(a, aFromIndex, aFromIndex + pl, b, bFromIndex, bFromIndex + pl) &&
+     *     a[aFromIndex + pl] != b[bFromIndex + pl]
+     * }
+ * Note that a common prefix length of {@code 0} indicates that the first + * elements from each array mismatch. + * + *

Two non-{@code null} arrays, {@code a} and {@code b} with specified + * ranges [{@code aFromIndex}, {@code atoIndex}) and + * [{@code bFromIndex}, {@code btoIndex}) respectively, share a proper + * if the following expression is true: + *

{@code
+     *     (aToIndex - aFromIndex) != (bToIndex - bFromIndex) &&
+     *     Arrays.equals(a, 0, Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex),
+     *                   b, 0, Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex))
+     * }
+ * + * @param a the first array to be tested for a mismatch + * @param aFromIndex the index (inclusive) of the first element in the + * first array to be tested + * @param aToIndex the index (exclusive) of the last element in the + * first array to be tested + * @param b the second array to be tested for a mismatch + * @param bFromIndex the index (inclusive) of the first element in the + * second array to be tested + * @param bToIndex the index (exclusive) of the last element in the + * second array to be tested + * @return the relative index of the first mismatch between the two arrays + * over the specified ranges, otherwise {@code -1}. + * @throws IllegalArgumentException + * if {@code aFromIndex > aToIndex} or + * if {@code bFromIndex > bToIndex} + * @throws ArrayIndexOutOfBoundsException + * if {@code aFromIndex < 0 or aToIndex > a.length} or + * if {@code bFromIndex < 0 or bToIndex > b.length} + * @throws NullPointerException + * if either array is {@code null} + * @since 9 + */ + public static int mismatch(boolean[] a, int aFromIndex, int aToIndex, + boolean[] b, int bFromIndex, int bToIndex) { + rangeCheck(a.length, aFromIndex, aToIndex); + rangeCheck(b.length, bFromIndex, bToIndex); + + int aLength = aToIndex - aFromIndex; + int bLength = bToIndex - bFromIndex; + int length = Math.min(aLength, bLength); + for (int i = 0; i < length; i++) { + if (a[aFromIndex++] != b[bFromIndex++]) return i; + } + + return aLength != bLength ? length : -1; + } + + // Mismatch byte + + /** + * Finds and returns the index of the first mismatch between two {@code byte} + * arrays, otherwise return -1 if no mismatch is found. The index will be + * in the range of 0 (inclusive) up to the length (inclusive) of the smaller + * array. + * + *

If the two arrays share a common prefix then the returned index is the + * length of the common prefix and it follows that there is a mismatch + * between the two elements at that index within the respective arrays. + * If one array is a proper prefix of the other then the returned index is + * the length of the smaller array and it follows that the index is only + * valid for the larger array. + * Otherwise, there is no mismatch. + * + *

Two non-{@code null} arrays, {@code a} and {@code b}, share a common + * prefix of length {@code pl} if the following expression is true: + *

{@code
+     *     pl >= 0 &&
+     *     pl < Math.min(a.length, b.length) &&
+     *     Arrays.equals(a, 0, pl, b, 0, pl) &&
+     *     a[pl] != b[pl]
+     * }
+ * Note that a common prefix length of {@code 0} indicates that the first + * elements from each array mismatch. + * + *

Two non-{@code null} arrays, {@code a} and {@code b}, share a proper + * prefix if the following expression is true: + *

{@code
+     *     a.length != b.length &&
+     *     Arrays.equals(a, 0, Math.min(a.length, b.length),
+     *                   b, 0, Math.min(a.length, b.length))
+     * }
+ * + * @param a the first array to be tested for a mismatch + * @param b the second array to be tested for a mismatch + * @return the index of the first mismatch between the two arrays, + * otherwise {@code -1}. + * @throws NullPointerException + * if either array is {@code null} + * @since 9 + */ + public static int mismatch(byte[] a, byte[] b) { + int length = Math.min(a.length, b.length); // Check null array refs + if (a == b) + return -1; + + for (int i = 0; i < length; i++) { + if (a[i] != b[i]) return i; + } + + return a.length != b.length ? length : -1; + } + + /** + * Finds and returns the relative index of the first mismatch between two + * {@code byte} arrays over the specified ranges, otherwise return -1 if no + * mismatch is found. The index will be in the range of 0 (inclusive) up to + * the length (inclusive) of the smaller range. + * + *

If the two arrays, over the specified ranges, share a common prefix + * then the returned relative index is the length of the common prefix and + * it follows that there is a mismatch between the two elements at that + * relative index within the respective arrays. + * If one array is a proper prefix of the other, over the specified ranges, + * then the returned relative index is the length of the smaller range and + * it follows that the relative index is only valid for the array with the + * larger range. + * Otherwise, there is no mismatch. + * + *

Two non-{@code null} arrays, {@code a} and {@code b} with specified + * ranges [{@code aFromIndex}, {@code atoIndex}) and + * [{@code bFromIndex}, {@code btoIndex}) respectively, share a common + * prefix of length {@code pl} if the following expression is true: + *

{@code
+     *     pl >= 0 &&
+     *     pl < Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex) &&
+     *     Arrays.equals(a, aFromIndex, aFromIndex + pl, b, bFromIndex, bFromIndex + pl) &&
+     *     a[aFromIndex + pl] != b[bFromIndex + pl]
+     * }
+ * Note that a common prefix length of {@code 0} indicates that the first + * elements from each array mismatch. + * + *

Two non-{@code null} arrays, {@code a} and {@code b} with specified + * ranges [{@code aFromIndex}, {@code atoIndex}) and + * [{@code bFromIndex}, {@code btoIndex}) respectively, share a proper + * if the following expression is true: + *

{@code
+     *     (aToIndex - aFromIndex) != (bToIndex - bFromIndex) &&
+     *     Arrays.equals(a, 0, Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex),
+     *                   b, 0, Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex))
+     * }
+ * + * @param a the first array to be tested for a mismatch + * @param aFromIndex the index (inclusive) of the first element in the + * first array to be tested + * @param aToIndex the index (exclusive) of the last element in the + * first array to be tested + * @param b the second array to be tested for a mismatch + * @param bFromIndex the index (inclusive) of the first element in the + * second array to be tested + * @param bToIndex the index (exclusive) of the last element in the + * second array to be tested + * @return the relative index of the first mismatch between the two arrays + * over the specified ranges, otherwise {@code -1}. + * @throws IllegalArgumentException + * if {@code aFromIndex > aToIndex} or + * if {@code bFromIndex > bToIndex} + * @throws ArrayIndexOutOfBoundsException + * if {@code aFromIndex < 0 or aToIndex > a.length} or + * if {@code bFromIndex < 0 or bToIndex > b.length} + * @throws NullPointerException + * if either array is {@code null} + * @since 9 + */ + public static int mismatch(byte[] a, int aFromIndex, int aToIndex, + byte[] b, int bFromIndex, int bToIndex) { + rangeCheck(a.length, aFromIndex, aToIndex); + rangeCheck(b.length, bFromIndex, bToIndex); + + int aLength = aToIndex - aFromIndex; + int bLength = bToIndex - bFromIndex; + int length = Math.min(aLength, bLength); + for (int i = 0; i < length; i++) { + if (a[aFromIndex++] != b[bFromIndex++]) return i; + } + + return aLength != bLength ? length : -1; + } + + // Mismatch char + + /** + * Finds and returns the index of the first mismatch between two {@code char} + * arrays, otherwise return -1 if no mismatch is found. The index will be + * in the range of 0 (inclusive) up to the length (inclusive) of the smaller + * array. + * + *

If the two arrays share a common prefix then the returned index is the + * length of the common prefix and it follows that there is a mismatch + * between the two elements at that index within the respective arrays. + * If one array is a proper prefix of the other then the returned index is + * the length of the smaller array and it follows that the index is only + * valid for the larger array. + * Otherwise, there is no mismatch. + * + *

Two non-{@code null} arrays, {@code a} and {@code b}, share a common + * prefix of length {@code pl} if the following expression is true: + *

{@code
+     *     pl >= 0 &&
+     *     pl < Math.min(a.length, b.length) &&
+     *     Arrays.equals(a, 0, pl, b, 0, pl) &&
+     *     a[pl] != b[pl]
+     * }
+ * Note that a common prefix length of {@code 0} indicates that the first + * elements from each array mismatch. + * + *

Two non-{@code null} arrays, {@code a} and {@code b}, share a proper + * prefix if the following expression is true: + *

{@code
+     *     a.length != b.length &&
+     *     Arrays.equals(a, 0, Math.min(a.length, b.length),
+     *                   b, 0, Math.min(a.length, b.length))
+     * }
+ * + * @param a the first array to be tested for a mismatch + * @param b the second array to be tested for a mismatch + * @return the index of the first mismatch between the two arrays, + * otherwise {@code -1}. + * @throws NullPointerException + * if either array is {@code null} + * @since 9 + */ + public static int mismatch(char[] a, char[] b) { + int length = Math.min(a.length, b.length); // Check null array refs + if (a == b) + return -1; + + for (int i = 0; i < length; i++) { + if (a[i] != b[i]) return i; + } + + return a.length != b.length ? length : -1; + } + + /** + * Finds and returns the relative index of the first mismatch between two + * {@code char} arrays over the specified ranges, otherwise return -1 if no + * mismatch is found. The index will be in the range of 0 (inclusive) up to + * the length (inclusive) of the smaller range. + * + *

If the two arrays, over the specified ranges, share a common prefix + * then the returned relative index is the length of the common prefix and + * it follows that there is a mismatch between the two elements at that + * relative index within the respective arrays. + * If one array is a proper prefix of the other, over the specified ranges, + * then the returned relative index is the length of the smaller range and + * it follows that the relative index is only valid for the array with the + * larger range. + * Otherwise, there is no mismatch. + * + *

Two non-{@code null} arrays, {@code a} and {@code b} with specified + * ranges [{@code aFromIndex}, {@code atoIndex}) and + * [{@code bFromIndex}, {@code btoIndex}) respectively, share a common + * prefix of length {@code pl} if the following expression is true: + *

{@code
+     *     pl >= 0 &&
+     *     pl < Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex) &&
+     *     Arrays.equals(a, aFromIndex, aFromIndex + pl, b, bFromIndex, bFromIndex + pl) &&
+     *     a[aFromIndex + pl] != b[bFromIndex + pl]
+     * }
+ * Note that a common prefix length of {@code 0} indicates that the first + * elements from each array mismatch. + * + *

Two non-{@code null} arrays, {@code a} and {@code b} with specified + * ranges [{@code aFromIndex}, {@code atoIndex}) and + * [{@code bFromIndex}, {@code btoIndex}) respectively, share a proper + * if the following expression is true: + *

{@code
+     *     (aToIndex - aFromIndex) != (bToIndex - bFromIndex) &&
+     *     Arrays.equals(a, 0, Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex),
+     *                   b, 0, Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex))
+     * }
+ * + * @param a the first array to be tested for a mismatch + * @param aFromIndex the index (inclusive) of the first element in the + * first array to be tested + * @param aToIndex the index (exclusive) of the last element in the + * first array to be tested + * @param b the second array to be tested for a mismatch + * @param bFromIndex the index (inclusive) of the first element in the + * second array to be tested + * @param bToIndex the index (exclusive) of the last element in the + * second array to be tested + * @return the relative index of the first mismatch between the two arrays + * over the specified ranges, otherwise {@code -1}. + * @throws IllegalArgumentException + * if {@code aFromIndex > aToIndex} or + * if {@code bFromIndex > bToIndex} + * @throws ArrayIndexOutOfBoundsException + * if {@code aFromIndex < 0 or aToIndex > a.length} or + * if {@code bFromIndex < 0 or bToIndex > b.length} + * @throws NullPointerException + * if either array is {@code null} + * @since 9 + */ + public static int mismatch(char[] a, int aFromIndex, int aToIndex, + char[] b, int bFromIndex, int bToIndex) { + rangeCheck(a.length, aFromIndex, aToIndex); + rangeCheck(b.length, bFromIndex, bToIndex); + + int aLength = aToIndex - aFromIndex; + int bLength = bToIndex - bFromIndex; + int length = Math.min(aLength, bLength); + for (int i = 0; i < length; i++) { + if (a[aFromIndex++] != b[bFromIndex++]) return i; + } + + return aLength != bLength ? length : -1; + } + + // Mismatch short + + /** + * Finds and returns the index of the first mismatch between two {@code short} + * arrays, otherwise return -1 if no mismatch is found. The index will be + * in the range of 0 (inclusive) up to the length (inclusive) of the smaller + * array. + * + *

If the two arrays share a common prefix then the returned index is the + * length of the common prefix and it follows that there is a mismatch + * between the two elements at that index within the respective arrays. + * If one array is a proper prefix of the other then the returned index is + * the length of the smaller array and it follows that the index is only + * valid for the larger array. + * Otherwise, there is no mismatch. + * + *

Two non-{@code null} arrays, {@code a} and {@code b}, share a common + * prefix of length {@code pl} if the following expression is true: + *

{@code
+     *     pl >= 0 &&
+     *     pl < Math.min(a.length, b.length) &&
+     *     Arrays.equals(a, 0, pl, b, 0, pl) &&
+     *     a[pl] != b[pl]
+     * }
+ * Note that a common prefix length of {@code 0} indicates that the first + * elements from each array mismatch. + * + *

Two non-{@code null} arrays, {@code a} and {@code b}, share a proper + * prefix if the following expression is true: + *

{@code
+     *     a.length != b.length &&
+     *     Arrays.equals(a, 0, Math.min(a.length, b.length),
+     *                   b, 0, Math.min(a.length, b.length))
+     * }
+ * + * @param a the first array to be tested for a mismatch + * @param b the second array to be tested for a mismatch + * @return the index of the first mismatch between the two arrays, + * otherwise {@code -1}. + * @throws NullPointerException + * if either array is {@code null} + * @since 9 + */ + public static int mismatch(short[] a, short[] b) { + int length = Math.min(a.length, b.length); // Check null array refs + if (a == b) + return -1; + + for (int i = 0; i < length; i++) { + if (a[i] != b[i]) return i; + } + + return a.length != b.length ? length : -1; + } + + /** + * Finds and returns the relative index of the first mismatch between two + * {@code short} arrays over the specified ranges, otherwise return -1 if no + * mismatch is found. The index will be in the range of 0 (inclusive) up to + * the length (inclusive) of the smaller range. + * + *

If the two arrays, over the specified ranges, share a common prefix + * then the returned relative index is the length of the common prefix and + * it follows that there is a mismatch between the two elements at that + * relative index within the respective arrays. + * If one array is a proper prefix of the other, over the specified ranges, + * then the returned relative index is the length of the smaller range and + * it follows that the relative index is only valid for the array with the + * larger range. + * Otherwise, there is no mismatch. + * + *

Two non-{@code null} arrays, {@code a} and {@code b} with specified + * ranges [{@code aFromIndex}, {@code atoIndex}) and + * [{@code bFromIndex}, {@code btoIndex}) respectively, share a common + * prefix of length {@code pl} if the following expression is true: + *

{@code
+     *     pl >= 0 &&
+     *     pl < Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex) &&
+     *     Arrays.equals(a, aFromIndex, aFromIndex + pl, b, bFromIndex, bFromIndex + pl) &&
+     *     a[aFromIndex + pl] != b[bFromIndex + pl]
+     * }
+ * Note that a common prefix length of {@code 0} indicates that the first + * elements from each array mismatch. + * + *

Two non-{@code null} arrays, {@code a} and {@code b} with specified + * ranges [{@code aFromIndex}, {@code atoIndex}) and + * [{@code bFromIndex}, {@code btoIndex}) respectively, share a proper + * if the following expression is true: + *

{@code
+     *     (aToIndex - aFromIndex) != (bToIndex - bFromIndex) &&
+     *     Arrays.equals(a, 0, Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex),
+     *                   b, 0, Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex))
+     * }
+ * + * @param a the first array to be tested for a mismatch + * @param aFromIndex the index (inclusive) of the first element in the + * first array to be tested + * @param aToIndex the index (exclusive) of the last element in the + * first array to be tested + * @param b the second array to be tested for a mismatch + * @param bFromIndex the index (inclusive) of the first element in the + * second array to be tested + * @param bToIndex the index (exclusive) of the last element in the + * second array to be tested + * @return the relative index of the first mismatch between the two arrays + * over the specified ranges, otherwise {@code -1}. + * @throws IllegalArgumentException + * if {@code aFromIndex > aToIndex} or + * if {@code bFromIndex > bToIndex} + * @throws ArrayIndexOutOfBoundsException + * if {@code aFromIndex < 0 or aToIndex > a.length} or + * if {@code bFromIndex < 0 or bToIndex > b.length} + * @throws NullPointerException + * if either array is {@code null} + * @since 9 + */ + public static int mismatch(short[] a, int aFromIndex, int aToIndex, + short[] b, int bFromIndex, int bToIndex) { + rangeCheck(a.length, aFromIndex, aToIndex); + rangeCheck(b.length, bFromIndex, bToIndex); + + int aLength = aToIndex - aFromIndex; + int bLength = bToIndex - bFromIndex; + int length = Math.min(aLength, bLength); + for (int i = 0; i < length; i++) { + if (a[aFromIndex++] != b[bFromIndex++]) return i; + } + + return aLength != bLength ? length : -1; + } + + // Mismatch int + + /** + * Finds and returns the index of the first mismatch between two {@code int} + * arrays, otherwise return -1 if no mismatch is found. The index will be + * in the range of 0 (inclusive) up to the length (inclusive) of the smaller + * array. + * + *

If the two arrays share a common prefix then the returned index is the + * length of the common prefix and it follows that there is a mismatch + * between the two elements at that index within the respective arrays. + * If one array is a proper prefix of the other then the returned index is + * the length of the smaller array and it follows that the index is only + * valid for the larger array. + * Otherwise, there is no mismatch. + * + *

Two non-{@code null} arrays, {@code a} and {@code b}, share a common + * prefix of length {@code pl} if the following expression is true: + *

{@code
+     *     pl >= 0 &&
+     *     pl < Math.min(a.length, b.length) &&
+     *     Arrays.equals(a, 0, pl, b, 0, pl) &&
+     *     a[pl] != b[pl]
+     * }
+ * Note that a common prefix length of {@code 0} indicates that the first + * elements from each array mismatch. + * + *

Two non-{@code null} arrays, {@code a} and {@code b}, share a proper + * prefix if the following expression is true: + *

{@code
+     *     a.length != b.length &&
+     *     Arrays.equals(a, 0, Math.min(a.length, b.length),
+     *                   b, 0, Math.min(a.length, b.length))
+     * }
+ * + * @param a the first array to be tested for a mismatch + * @param b the second array to be tested for a mismatch + * @return the index of the first mismatch between the two arrays, + * otherwise {@code -1}. + * @throws NullPointerException + * if either array is {@code null} + * @since 9 + */ + public static int mismatch(int[] a, int[] b) { + int length = Math.min(a.length, b.length); // Check null array refs + if (a == b) + return -1; + + for (int i = 0; i < length; i++) { + if (a[i] != b[i]) return i; + } + + return a.length != b.length ? length : -1; + } + + /** + * Finds and returns the relative index of the first mismatch between two + * {@code int} arrays over the specified ranges, otherwise return -1 if no + * mismatch is found. The index will be in the range of 0 (inclusive) up to + * the length (inclusive) of the smaller range. + * + *

If the two arrays, over the specified ranges, share a common prefix + * then the returned relative index is the length of the common prefix and + * it follows that there is a mismatch between the two elements at that + * relative index within the respective arrays. + * If one array is a proper prefix of the other, over the specified ranges, + * then the returned relative index is the length of the smaller range and + * it follows that the relative index is only valid for the array with the + * larger range. + * Otherwise, there is no mismatch. + * + *

Two non-{@code null} arrays, {@code a} and {@code b} with specified + * ranges [{@code aFromIndex}, {@code atoIndex}) and + * [{@code bFromIndex}, {@code btoIndex}) respectively, share a common + * prefix of length {@code pl} if the following expression is true: + *

{@code
+     *     pl >= 0 &&
+     *     pl < Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex) &&
+     *     Arrays.equals(a, aFromIndex, aFromIndex + pl, b, bFromIndex, bFromIndex + pl) &&
+     *     a[aFromIndex + pl] != b[bFromIndex + pl]
+     * }
+ * Note that a common prefix length of {@code 0} indicates that the first + * elements from each array mismatch. + * + *

Two non-{@code null} arrays, {@code a} and {@code b} with specified + * ranges [{@code aFromIndex}, {@code atoIndex}) and + * [{@code bFromIndex}, {@code btoIndex}) respectively, share a proper + * if the following expression is true: + *

{@code
+     *     (aToIndex - aFromIndex) != (bToIndex - bFromIndex) &&
+     *     Arrays.equals(a, 0, Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex),
+     *                   b, 0, Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex))
+     * }
+ * + * @param a the first array to be tested for a mismatch + * @param aFromIndex the index (inclusive) of the first element in the + * first array to be tested + * @param aToIndex the index (exclusive) of the last element in the + * first array to be tested + * @param b the second array to be tested for a mismatch + * @param bFromIndex the index (inclusive) of the first element in the + * second array to be tested + * @param bToIndex the index (exclusive) of the last element in the + * second array to be tested + * @return the relative index of the first mismatch between the two arrays + * over the specified ranges, otherwise {@code -1}. + * @throws IllegalArgumentException + * if {@code aFromIndex > aToIndex} or + * if {@code bFromIndex > bToIndex} + * @throws ArrayIndexOutOfBoundsException + * if {@code aFromIndex < 0 or aToIndex > a.length} or + * if {@code bFromIndex < 0 or bToIndex > b.length} + * @throws NullPointerException + * if either array is {@code null} + * @since 9 + */ + public static int mismatch(int[] a, int aFromIndex, int aToIndex, + int[] b, int bFromIndex, int bToIndex) { + rangeCheck(a.length, aFromIndex, aToIndex); + rangeCheck(b.length, bFromIndex, bToIndex); + + int aLength = aToIndex - aFromIndex; + int bLength = bToIndex - bFromIndex; + int length = Math.min(aLength, bLength); + for (int i = 0; i < length; i++) { + if (a[aFromIndex++] != b[bFromIndex++]) return i; + } + + return aLength != bLength ? length : -1; + } + + // Mismatch long + + /** + * Finds and returns the index of the first mismatch between two {@code long} + * arrays, otherwise return -1 if no mismatch is found. The index will be + * in the range of 0 (inclusive) up to the length (inclusive) of the smaller + * array. + * + *

If the two arrays share a common prefix then the returned index is the + * length of the common prefix and it follows that there is a mismatch + * between the two elements at that index within the respective arrays. + * If one array is a proper prefix of the other then the returned index is + * the length of the smaller array and it follows that the index is only + * valid for the larger array. + * Otherwise, there is no mismatch. + * + *

Two non-{@code null} arrays, {@code a} and {@code b}, share a common + * prefix of length {@code pl} if the following expression is true: + *

{@code
+     *     pl >= 0 &&
+     *     pl < Math.min(a.length, b.length) &&
+     *     Arrays.equals(a, 0, pl, b, 0, pl) &&
+     *     a[pl] != b[pl]
+     * }
+ * Note that a common prefix length of {@code 0} indicates that the first + * elements from each array mismatch. + * + *

Two non-{@code null} arrays, {@code a} and {@code b}, share a proper + * prefix if the following expression is true: + *

{@code
+     *     a.length != b.length &&
+     *     Arrays.equals(a, 0, Math.min(a.length, b.length),
+     *                   b, 0, Math.min(a.length, b.length))
+     * }
+ * + * @param a the first array to be tested for a mismatch + * @param b the second array to be tested for a mismatch + * @return the index of the first mismatch between the two arrays, + * otherwise {@code -1}. + * @throws NullPointerException + * if either array is {@code null} + * @since 9 + */ + public static int mismatch(long[] a, long[] b) { + int length = Math.min(a.length, b.length); // Check null array refs + if (a == b) + return -1; + + for (int i = 0; i < length; i++) { + if (a[i] != b[i]) return i; + } + + return a.length != b.length ? length : -1; + } + + /** + * Finds and returns the relative index of the first mismatch between two + * {@code long} arrays over the specified ranges, otherwise return -1 if no + * mismatch is found. The index will be in the range of 0 (inclusive) up to + * the length (inclusive) of the smaller range. + * + *

If the two arrays, over the specified ranges, share a common prefix + * then the returned relative index is the length of the common prefix and + * it follows that there is a mismatch between the two elements at that + * relative index within the respective arrays. + * If one array is a proper prefix of the other, over the specified ranges, + * then the returned relative index is the length of the smaller range and + * it follows that the relative index is only valid for the array with the + * larger range. + * Otherwise, there is no mismatch. + * + *

Two non-{@code null} arrays, {@code a} and {@code b} with specified + * ranges [{@code aFromIndex}, {@code atoIndex}) and + * [{@code bFromIndex}, {@code btoIndex}) respectively, share a common + * prefix of length {@code pl} if the following expression is true: + *

{@code
+     *     pl >= 0 &&
+     *     pl < Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex) &&
+     *     Arrays.equals(a, aFromIndex, aFromIndex + pl, b, bFromIndex, bFromIndex + pl) &&
+     *     a[aFromIndex + pl] != b[bFromIndex + pl]
+     * }
+ * Note that a common prefix length of {@code 0} indicates that the first + * elements from each array mismatch. + * + *

Two non-{@code null} arrays, {@code a} and {@code b} with specified + * ranges [{@code aFromIndex}, {@code atoIndex}) and + * [{@code bFromIndex}, {@code btoIndex}) respectively, share a proper + * if the following expression is true: + *

{@code
+     *     (aToIndex - aFromIndex) != (bToIndex - bFromIndex) &&
+     *     Arrays.equals(a, 0, Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex),
+     *                   b, 0, Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex))
+     * }
+ * + * @param a the first array to be tested for a mismatch + * @param aFromIndex the index (inclusive) of the first element in the + * first array to be tested + * @param aToIndex the index (exclusive) of the last element in the + * first array to be tested + * @param b the second array to be tested for a mismatch + * @param bFromIndex the index (inclusive) of the first element in the + * second array to be tested + * @param bToIndex the index (exclusive) of the last element in the + * second array to be tested + * @return the relative index of the first mismatch between the two arrays + * over the specified ranges, otherwise {@code -1}. + * @throws IllegalArgumentException + * if {@code aFromIndex > aToIndex} or + * if {@code bFromIndex > bToIndex} + * @throws ArrayIndexOutOfBoundsException + * if {@code aFromIndex < 0 or aToIndex > a.length} or + * if {@code bFromIndex < 0 or bToIndex > b.length} + * @throws NullPointerException + * if either array is {@code null} + * @since 9 + */ + public static int mismatch(long[] a, int aFromIndex, int aToIndex, + long[] b, int bFromIndex, int bToIndex) { + rangeCheck(a.length, aFromIndex, aToIndex); + rangeCheck(b.length, bFromIndex, bToIndex); + + int aLength = aToIndex - aFromIndex; + int bLength = bToIndex - bFromIndex; + int length = Math.min(aLength, bLength); + for (int i = 0; i < length; i++) { + if (a[aFromIndex++] != b[bFromIndex++]) return i; + } + + return aLength != bLength ? length : -1; + } + + // Mismatch float + + /** + * Finds and returns the index of the first mismatch between two {@code float} + * arrays, otherwise return -1 if no mismatch is found. The index will be + * in the range of 0 (inclusive) up to the length (inclusive) of the smaller + * array. + * + *

If the two arrays share a common prefix then the returned index is the + * length of the common prefix and it follows that there is a mismatch + * between the two elements at that index within the respective arrays. + * If one array is a proper prefix of the other then the returned index is + * the length of the smaller array and it follows that the index is only + * valid for the larger array. + * Otherwise, there is no mismatch. + * + *

Two non-{@code null} arrays, {@code a} and {@code b}, share a common + * prefix of length {@code pl} if the following expression is true: + *

{@code
+     *     pl >= 0 &&
+     *     pl < Math.min(a.length, b.length) &&
+     *     Arrays.equals(a, 0, pl, b, 0, pl) &&
+     *     Float.compare(a[pl], b[pl]) != 0
+     * }
+ * Note that a common prefix length of {@code 0} indicates that the first + * elements from each array mismatch. + * + *

Two non-{@code null} arrays, {@code a} and {@code b}, share a proper + * prefix if the following expression is true: + *

{@code
+     *     a.length != b.length &&
+     *     Arrays.equals(a, 0, Math.min(a.length, b.length),
+     *                   b, 0, Math.min(a.length, b.length))
+     * }
+ * + * @param a the first array to be tested for a mismatch + * @param b the second array to be tested for a mismatch + * @return the index of the first mismatch between the two arrays, + * otherwise {@code -1}. + * @throws NullPointerException + * if either array is {@code null} + * @since 9 + */ + public static int mismatch(float[] a, float[] b) { + int length = Math.min(a.length, b.length); // Check null array refs + if (a == b) + return -1; + + for (int i = 0; i < length; i++) { + float va = a[i], vb = b[i]; + if (Float.floatToRawIntBits(va) != Float.floatToRawIntBits(vb)) + if (!Float.isNaN(va) || !Float.isNaN(vb)) + return i; + } + + return a.length != b.length ? length : -1; + } + + /** + * Finds and returns the relative index of the first mismatch between two + * {@code float} arrays over the specified ranges, otherwise return -1 if no + * mismatch is found. The index will be in the range of 0 (inclusive) up to + * the length (inclusive) of the smaller range. + * + *

If the two arrays, over the specified ranges, share a common prefix + * then the returned relative index is the length of the common prefix and + * it follows that there is a mismatch between the two elements at that + * relative index within the respective arrays. + * If one array is a proper prefix of the other, over the specified ranges, + * then the returned relative index is the length of the smaller range and + * it follows that the relative index is only valid for the array with the + * larger range. + * Otherwise, there is no mismatch. + * + *

Two non-{@code null} arrays, {@code a} and {@code b} with specified + * ranges [{@code aFromIndex}, {@code atoIndex}) and + * [{@code bFromIndex}, {@code btoIndex}) respectively, share a common + * prefix of length {@code pl} if the following expression is true: + *

{@code
+     *     pl >= 0 &&
+     *     pl < Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex) &&
+     *     Arrays.equals(a, aFromIndex, aFromIndex + pl, b, bFromIndex, bFromIndex + pl) &&
+     *     Float.compare(a[aFromIndex + pl], b[bFromIndex + pl]) != 0
+     * }
+ * Note that a common prefix length of {@code 0} indicates that the first + * elements from each array mismatch. + * + *

Two non-{@code null} arrays, {@code a} and {@code b} with specified + * ranges [{@code aFromIndex}, {@code atoIndex}) and + * [{@code bFromIndex}, {@code btoIndex}) respectively, share a proper + * if the following expression is true: + *

{@code
+     *     (aToIndex - aFromIndex) != (bToIndex - bFromIndex) &&
+     *     Arrays.equals(a, 0, Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex),
+     *                   b, 0, Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex))
+     * }
+ * + * @param a the first array to be tested for a mismatch + * @param aFromIndex the index (inclusive) of the first element in the + * first array to be tested + * @param aToIndex the index (exclusive) of the last element in the + * first array to be tested + * @param b the second array to be tested for a mismatch + * @param bFromIndex the index (inclusive) of the first element in the + * second array to be tested + * @param bToIndex the index (exclusive) of the last element in the + * second array to be tested + * @return the relative index of the first mismatch between the two arrays + * over the specified ranges, otherwise {@code -1}. + * @throws IllegalArgumentException + * if {@code aFromIndex > aToIndex} or + * if {@code bFromIndex > bToIndex} + * @throws ArrayIndexOutOfBoundsException + * if {@code aFromIndex < 0 or aToIndex > a.length} or + * if {@code bFromIndex < 0 or bToIndex > b.length} + * @throws NullPointerException + * if either array is {@code null} + * @since 9 + */ + public static int mismatch(float[] a, int aFromIndex, int aToIndex, + float[] b, int bFromIndex, int bToIndex) { + rangeCheck(a.length, aFromIndex, aToIndex); + rangeCheck(b.length, bFromIndex, bToIndex); + + int aLength = aToIndex - aFromIndex; + int bLength = bToIndex - bFromIndex; + int length = Math.min(aLength, bLength); + for (int i = 0; i < length; i++) { + float va = a[aFromIndex++], vb = b[bFromIndex++]; + if (Float.floatToRawIntBits(va) != Float.floatToRawIntBits(vb)) + if (!Float.isNaN(va) || !Float.isNaN(vb)) + return i; + } + + return aLength != bLength ? length : -1; + } + + // Mismatch double + + /** + * Finds and returns the index of the first mismatch between two + * {@code double} arrays, otherwise return -1 if no mismatch is found. The + * index will be in the range of 0 (inclusive) up to the length (inclusive) + * of the smaller array. + * + *

If the two arrays share a common prefix then the returned index is the + * length of the common prefix and it follows that there is a mismatch + * between the two elements at that index within the respective arrays. + * If one array is a proper prefix of the other then the returned index is + * the length of the smaller array and it follows that the index is only + * valid for the larger array. + * Otherwise, there is no mismatch. + * + *

Two non-{@code null} arrays, {@code a} and {@code b}, share a common + * prefix of length {@code pl} if the following expression is true: + *

{@code
+     *     pl >= 0 &&
+     *     pl < Math.min(a.length, b.length) &&
+     *     Arrays.equals(a, 0, pl, b, 0, pl) &&
+     *     Double.compare(a[pl], b[pl]) != 0
+     * }
+ * Note that a common prefix length of {@code 0} indicates that the first + * elements from each array mismatch. + * + *

Two non-{@code null} arrays, {@code a} and {@code b}, share a proper + * prefix if the following expression is true: + *

{@code
+     *     a.length != b.length &&
+     *     Arrays.equals(a, 0, Math.min(a.length, b.length),
+     *                   b, 0, Math.min(a.length, b.length))
+     * }
+ * + * @param a the first array to be tested for a mismatch + * @param b the second array to be tested for a mismatch + * @return the index of the first mismatch between the two arrays, + * otherwise {@code -1}. + * @throws NullPointerException + * if either array is {@code null} + * @since 9 + */ + public static int mismatch(double[] a, double[] b) { + int length = Math.min(a.length, b.length); // Check null array refs + if (a == b) + return -1; + + for (int i = 0; i < length; i++) { + double va = a[i], vb = b[i]; + if (Double.doubleToRawLongBits(va) != Double.doubleToRawLongBits(vb)) + if (!Double.isNaN(va) || !Double.isNaN(vb)) + return i; + } + + return a.length != b.length ? length : -1; + } + + /** + * Finds and returns the relative index of the first mismatch between two + * {@code double} arrays over the specified ranges, otherwise return -1 if + * no mismatch is found. The index will be in the range of 0 (inclusive) up + * to the length (inclusive) of the smaller range. + * + *

If the two arrays, over the specified ranges, share a common prefix + * then the returned relative index is the length of the common prefix and + * it follows that there is a mismatch between the two elements at that + * relative index within the respective arrays. + * If one array is a proper prefix of the other, over the specified ranges, + * then the returned relative index is the length of the smaller range and + * it follows that the relative index is only valid for the array with the + * larger range. + * Otherwise, there is no mismatch. + * + *

Two non-{@code null} arrays, {@code a} and {@code b} with specified + * ranges [{@code aFromIndex}, {@code atoIndex}) and + * [{@code bFromIndex}, {@code btoIndex}) respectively, share a common + * prefix of length {@code pl} if the following expression is true: + *

{@code
+     *     pl >= 0 &&
+     *     pl < Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex) &&
+     *     Arrays.equals(a, aFromIndex, aFromIndex + pl, b, bFromIndex, bFromIndex + pl) &&
+     *     Double.compare(a[aFromIndex + pl], b[bFromIndex + pl]) != 0
+     * }
+ * Note that a common prefix length of {@code 0} indicates that the first + * elements from each array mismatch. + * + *

Two non-{@code null} arrays, {@code a} and {@code b} with specified + * ranges [{@code aFromIndex}, {@code atoIndex}) and + * [{@code bFromIndex}, {@code btoIndex}) respectively, share a proper + * if the following expression is true: + *

{@code
+     *     (aToIndex - aFromIndex) != (bToIndex - bFromIndex) &&
+     *     Arrays.equals(a, 0, Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex),
+     *                   b, 0, Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex))
+     * }
+ * + * @param a the first array to be tested for a mismatch + * @param aFromIndex the index (inclusive) of the first element in the + * first array to be tested + * @param aToIndex the index (exclusive) of the last element in the + * first array to be tested + * @param b the second array to be tested for a mismatch + * @param bFromIndex the index (inclusive) of the first element in the + * second array to be tested + * @param bToIndex the index (exclusive) of the last element in the + * second array to be tested + * @return the relative index of the first mismatch between the two arrays + * over the specified ranges, otherwise {@code -1}. + * @throws IllegalArgumentException + * if {@code aFromIndex > aToIndex} or + * if {@code bFromIndex > bToIndex} + * @throws ArrayIndexOutOfBoundsException + * if {@code aFromIndex < 0 or aToIndex > a.length} or + * if {@code bFromIndex < 0 or bToIndex > b.length} + * @throws NullPointerException + * if either array is {@code null} + * @since 9 + */ + public static int mismatch(double[] a, int aFromIndex, int aToIndex, + double[] b, int bFromIndex, int bToIndex) { + rangeCheck(a.length, aFromIndex, aToIndex); + rangeCheck(b.length, bFromIndex, bToIndex); + + int aLength = aToIndex - aFromIndex; + int bLength = bToIndex - bFromIndex; + int length = Math.min(aLength, bLength); + for (int i = 0; i < length; i++) { + double va = a[aFromIndex++], vb = b[bFromIndex++]; + if (Double.doubleToRawLongBits(va) != Double.doubleToRawLongBits(vb)) + if (!Double.isNaN(va) || !Double.isNaN(vb)) + return i; + } + + return aLength != bLength ? length : -1; + } + + // Mismatch objects + + /** + * Finds and returns the index of the first mismatch between two + * {@code Object} arrays, otherwise return -1 if no mismatch is found. The + * index will be in the range of 0 (inclusive) up to the length (inclusive) + * of the smaller array. + * + *

If the two arrays share a common prefix then the returned index is the + * length of the common prefix and it follows that there is a mismatch + * between the two elements at that index within the respective arrays. + * If one array is a proper prefix of the other then the returned index is + * the length of the smaller array and it follows that the index is only + * valid for the larger array. + * Otherwise, there is no mismatch. + * + *

Two non-{@code null} arrays, {@code a} and {@code b}, share a common + * prefix of length {@code pl} if the following expression is true: + *

{@code
+     *     pl >= 0 &&
+     *     pl < Math.min(a.length, b.length) &&
+     *     Arrays.equals(a, 0, pl, b, 0, pl) &&
+     *     !Objects.equals(a[pl], b[pl])
+     * }
+ * Note that a common prefix length of {@code 0} indicates that the first + * elements from each array mismatch. + * + *

Two non-{@code null} arrays, {@code a} and {@code b}, share a proper + * prefix if the following expression is true: + *

{@code
+     *     a.length != b.length &&
+     *     Arrays.equals(a, 0, Math.min(a.length, b.length),
+     *                   b, 0, Math.min(a.length, b.length))
+     * }
+ * + * @param a the first array to be tested for a mismatch + * @param b the second array to be tested for a mismatch + * @return the index of the first mismatch between the two arrays, + * otherwise {@code -1}. + * @throws NullPointerException + * if either array is {@code null} + * @since 9 + */ + public static int mismatch(Object[] a, Object[] b) { + int length = Math.min(a.length, b.length); // Check null array refs + if (a == b) + return -1; + + for (int i = 0; i < length; i++) { + if (!Objects.equals(a[i], b[i])) + return i; + } + + return a.length != b.length ? length : -1; + } + + /** + * Finds and returns the relative index of the first mismatch between two + * {@code Object} arrays over the specified ranges, otherwise return -1 if + * no mismatch is found. The index will be in the range of 0 (inclusive) up + * to the length (inclusive) of the smaller range. + * + *

If the two arrays, over the specified ranges, share a common prefix + * then the returned relative index is the length of the common prefix and + * it follows that there is a mismatch between the two elements at that + * relative index within the respective arrays. + * If one array is a proper prefix of the other, over the specified ranges, + * then the returned relative index is the length of the smaller range and + * it follows that the relative index is only valid for the array with the + * larger range. + * Otherwise, there is no mismatch. + * + *

Two non-{@code null} arrays, {@code a} and {@code b} with specified + * ranges [{@code aFromIndex}, {@code atoIndex}) and + * [{@code bFromIndex}, {@code btoIndex}) respectively, share a common + * prefix of length {@code pl} if the following expression is true: + *

{@code
+     *     pl >= 0 &&
+     *     pl < Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex) &&
+     *     Arrays.equals(a, aFromIndex, aFromIndex + pl, b, bFromIndex, bFromIndex + pl) &&
+     *     !Objects.equals(a[aFromIndex + pl], b[bFromIndex + pl])
+     * }
+ * Note that a common prefix length of {@code 0} indicates that the first + * elements from each array mismatch. + * + *

Two non-{@code null} arrays, {@code a} and {@code b} with specified + * ranges [{@code aFromIndex}, {@code atoIndex}) and + * [{@code bFromIndex}, {@code btoIndex}) respectively, share a proper + * if the following expression is true: + *

{@code
+     *     (aToIndex - aFromIndex) != (bToIndex - bFromIndex) &&
+     *     Arrays.equals(a, 0, Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex),
+     *                   b, 0, Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex))
+     * }
+ * + * @param a the first array to be tested for a mismatch + * @param aFromIndex the index (inclusive) of the first element in the + * first array to be tested + * @param aToIndex the index (exclusive) of the last element in the + * first array to be tested + * @param b the second array to be tested for a mismatch + * @param bFromIndex the index (inclusive) of the first element in the + * second array to be tested + * @param bToIndex the index (exclusive) of the last element in the + * second array to be tested + * @return the relative index of the first mismatch between the two arrays + * over the specified ranges, otherwise {@code -1}. + * @throws IllegalArgumentException + * if {@code aFromIndex > aToIndex} or + * if {@code bFromIndex > bToIndex} + * @throws ArrayIndexOutOfBoundsException + * if {@code aFromIndex < 0 or aToIndex > a.length} or + * if {@code bFromIndex < 0 or bToIndex > b.length} + * @throws NullPointerException + * if either array is {@code null} + * @since 9 + */ + public static int mismatch( + Object[] a, int aFromIndex, int aToIndex, + Object[] b, int bFromIndex, int bToIndex) { + rangeCheck(a.length, aFromIndex, aToIndex); + rangeCheck(b.length, bFromIndex, bToIndex); + + int aLength = aToIndex - aFromIndex; + int bLength = bToIndex - bFromIndex; + int length = Math.min(aLength, bLength); + for (int i = 0; i < length; i++) { + if (!Objects.equals(a[aFromIndex++], b[bFromIndex++])) + return i; + } + + return aLength != bLength ? length : -1; + } + + /** + * Finds and returns the index of the first mismatch between two + * {@code Object} arrays, otherwise return -1 if no mismatch is found. + * The index will be in the range of 0 (inclusive) up to the length + * (inclusive) of the smaller array. + * + *

The specified comparator is used to determine if two array elements + * from the each array are not equal. + * + *

If the two arrays share a common prefix then the returned index is the + * length of the common prefix and it follows that there is a mismatch + * between the two elements at that index within the respective arrays. + * If one array is a proper prefix of the other then the returned index is + * the length of the smaller array and it follows that the index is only + * valid for the larger array. + * Otherwise, there is no mismatch. + * + *

Two non-{@code null} arrays, {@code a} and {@code b}, share a common + * prefix of length {@code pl} if the following expression is true: + *

{@code
+     *     pl >= 0 &&
+     *     pl < Math.min(a.length, b.length) &&
+     *     IntStream.range(0, pl).
+     *         map(i -> cmp.compare(a[i], b[i])).
+     *         allMatch(c -> c == 0) &&
+     *     cmp.compare(a[pl], b[pl]) != 0
+     * }
+ * Note that a common prefix length of {@code 0} indicates that the first + * elements from each array mismatch. + * + *

Two non-{@code null} arrays, {@code a} and {@code b}, share a proper + * prefix if the following expression is true: + *

{@code
+     *     a.length != b.length &&
+     *     IntStream.range(0, Math.min(a.length, b.length)).
+     *         map(i -> cmp.compare(a[i], b[i])).
+     *         allMatch(c -> c == 0) &&
+     * }
+ * + * @param a the first array to be tested for a mismatch + * @param b the second array to be tested for a mismatch + * @param cmp the comparator to compare array elements + * @param the type of array elements + * @return the index of the first mismatch between the two arrays, + * otherwise {@code -1}. + * @throws NullPointerException + * if either array or the comparator is {@code null} + * @since 9 + */ + public static int mismatch(T[] a, T[] b, Comparator cmp) { + Objects.requireNonNull(cmp); + int length = Math.min(a.length, b.length); // Check null array refs + if (a == b) + return -1; + + for (int i = 0; i < length; i++) { + T oa = a[i]; + T ob = b[i]; + if (oa != ob) { + // Null-value comparison is deferred to the comparator + int v = cmp.compare(oa, ob); + if (v != 0) { + return i; + } + } + } + + return a.length != b.length ? length : -1; + } + + /** + * Finds and returns the relative index of the first mismatch between two + * {@code Object} arrays over the specified ranges, otherwise return -1 if + * no mismatch is found. The index will be in the range of 0 (inclusive) up + * to the length (inclusive) of the smaller range. + * + *

If the two arrays, over the specified ranges, share a common prefix + * then the returned relative index is the length of the common prefix and + * it follows that there is a mismatch between the two elements at that + * relative index within the respective arrays. + * If one array is a proper prefix of the other, over the specified ranges, + * then the returned relative index is the length of the smaller range and + * it follows that the relative index is only valid for the array with the + * larger range. + * Otherwise, there is no mismatch. + * + *

Two non-{@code null} arrays, {@code a} and {@code b} with specified + * ranges [{@code aFromIndex}, {@code atoIndex}) and + * [{@code bFromIndex}, {@code btoIndex}) respectively, share a common + * prefix of length {@code pl} if the following expression is true: + *

{@code
+     *     pl >= 0 &&
+     *     pl < Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex) &&
+     *     IntStream.range(0, pl).
+     *         map(i -> cmp.compare(a[aFromIndex + i], b[bFromIndex + i])).
+     *         allMatch(c -> c == 0) &&
+     *     cmp.compare(a[aFromIndex + pl], b[bFromIndex + pl]) != 0
+     * }
+ * Note that a common prefix length of {@code 0} indicates that the first + * elements from each array mismatch. + * + *

Two non-{@code null} arrays, {@code a} and {@code b} with specified + * ranges [{@code aFromIndex}, {@code atoIndex}) and + * [{@code bFromIndex}, {@code btoIndex}) respectively, share a proper + * if the following expression is true: + *

{@code
+     *     (aToIndex - aFromIndex) != (bToIndex - bFromIndex) &&
+     *     IntStream.range(0, Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex)).
+     *         map(i -> cmp.compare(a[aFromIndex + i], b[bFromIndex + i])).
+     *         allMatch(c -> c == 0)
+     * }
+ * + * @param a the first array to be tested for a mismatch + * @param aFromIndex the index (inclusive) of the first element in the + * first array to be tested + * @param aToIndex the index (exclusive) of the last element in the + * first array to be tested + * @param b the second array to be tested for a mismatch + * @param bFromIndex the index (inclusive) of the first element in the + * second array to be tested + * @param bToIndex the index (exclusive) of the last element in the + * second array to be tested + * @param cmp the comparator to compare array elements + * @param the type of array elements + * @return the relative index of the first mismatch between the two arrays + * over the specified ranges, otherwise {@code -1}. + * @throws IllegalArgumentException + * if {@code aFromIndex > aToIndex} or + * if {@code bFromIndex > bToIndex} + * @throws ArrayIndexOutOfBoundsException + * if {@code aFromIndex < 0 or aToIndex > a.length} or + * if {@code bFromIndex < 0 or bToIndex > b.length} + * @throws NullPointerException + * if either array or the comparator is {@code null} + * @since 9 + */ + public static int mismatch( + T[] a, int aFromIndex, int aToIndex, + T[] b, int bFromIndex, int bToIndex, + Comparator cmp) { + Objects.requireNonNull(cmp); + rangeCheck(a.length, aFromIndex, aToIndex); + rangeCheck(b.length, bFromIndex, bToIndex); + + int aLength = aToIndex - aFromIndex; + int bLength = bToIndex - bFromIndex; + int length = Math.min(aLength, bLength); + for (int i = 0; i < length; i++) { + T oa = a[aFromIndex++]; + T ob = b[bFromIndex++]; + if (oa != ob) { + // Null-value comparison is deferred to the comparator + int v = cmp.compare(oa, ob); + if (v != 0) { + return i; + } + } + } + + return aLength != bLength ? length : -1; + } +} \ No newline at end of file diff --git a/jdk/test/java/util/Arrays/ArraysEqCmpTest.java b/jdk/test/java/util/Arrays/ArraysEqCmpTest.java new file mode 100644 index 00000000000..590b71668b4 --- /dev/null +++ b/jdk/test/java/util/Arrays/ArraysEqCmpTest.java @@ -0,0 +1,1083 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8033148 + * @summary tests for array equals and compare + * @run testng ArraysEqCmpTest +*/ + +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.reflect.Array; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.BiFunction; +import java.util.function.LongFunction; +import java.util.stream.IntStream; + +public class ArraysEqCmpTest { + + // Maximum width in bits + static final int MAX_WIDTH = 512; + + static final Map typeToWidth; + + static { + typeToWidth = new HashMap<>(); + typeToWidth.put(boolean.class, Byte.SIZE); + typeToWidth.put(byte.class, Byte.SIZE); + typeToWidth.put(short.class, Short.SIZE); + typeToWidth.put(char.class, Character.SIZE); + typeToWidth.put(int.class, Integer.SIZE); + typeToWidth.put(long.class, Long.SIZE); + typeToWidth.put(float.class, Float.SIZE); + typeToWidth.put(double.class, Double.SIZE); + typeToWidth.put(Object.class, Integer.SIZE); // @@@ 32 or 64? + } + + static int arraySizeFor(Class type) { + type = type.isPrimitive() ? type : Object.class; + return 4 * MAX_WIDTH / typeToWidth.get(type); + } + + static abstract class ArrayType { + final Class arrayType; + final Class componentType; + final boolean unsigned; + + final MethodHandle cpy; + + final MethodHandle eq; + final MethodHandle eqr; + final MethodHandle cmp; + final MethodHandle cmpr; + final MethodHandle mm; + final MethodHandle mmr; + + final MethodHandle getter; + + final MethodHandle toString; + + public ArrayType(Class arrayType) { + this(arrayType, false); + } + + public ArrayType(Class arrayType, boolean unsigned) { + this.arrayType = arrayType; + this.componentType = arrayType.getComponentType(); + this.unsigned = unsigned; + + try { + MethodHandles.Lookup l = MethodHandles.lookup(); + + getter = MethodHandles.arrayElementGetter(arrayType); + + if (componentType.isPrimitive()) { + cpy = l.findStatic(Arrays.class, "copyOfRange", + MethodType.methodType(arrayType, arrayType, int.class, int.class)); + + MethodType eqt = MethodType.methodType( + boolean.class, arrayType, arrayType); + MethodType eqrt = MethodType.methodType( + boolean.class, arrayType, int.class, int.class, arrayType, int.class, int.class); + + eq = l.findStatic(Arrays.class, "equals", eqt); + eqr = l.findStatic(Arrays.class, "equals", eqrt); + + String compareName = unsigned ? "compareUnsigned" : "compare"; + cmp = l.findStatic(Arrays.class, compareName, + eqt.changeReturnType(int.class)); + cmpr = l.findStatic(Arrays.class, compareName, + eqrt.changeReturnType(int.class)); + + mm = l.findStatic(Arrays.class, "mismatch", + eqt.changeReturnType(int.class)); + mmr = l.findStatic(Arrays.class, "mismatch", + eqrt.changeReturnType(int.class)); + + toString = l.findStatic(Arrays.class, "toString", + MethodType.methodType(String.class, arrayType)); + } + else { + cpy = l.findStatic(Arrays.class, "copyOfRange", + MethodType.methodType(Object[].class, Object[].class, int.class, int.class)); + + MethodType eqt = MethodType.methodType( + boolean.class, Object[].class, Object[].class); + MethodType eqrt = MethodType.methodType( + boolean.class, Object[].class, int.class, int.class, Object[].class, int.class, int.class); + + eq = l.findStatic(Arrays.class, "equals", eqt); + eqr = l.findStatic(Arrays.class, "equals", eqrt); + + MethodType cmpt = MethodType.methodType( + int.class, Comparable[].class, Comparable[].class); + MethodType cmprt = MethodType.methodType( + int.class, Comparable[].class, int.class, int.class, Comparable[].class, int.class, int.class); + + cmp = l.findStatic(Arrays.class, "compare", cmpt); + cmpr = l.findStatic(Arrays.class, "compare", cmprt); + + mm = l.findStatic(Arrays.class, "mismatch", + eqt.changeReturnType(int.class)); + mmr = l.findStatic(Arrays.class, "mismatch", + eqrt.changeReturnType(int.class)); + + toString = l.findStatic(Arrays.class, "toString", + MethodType.methodType(String.class, Object[].class)); + } + + } + catch (Exception e) { + throw new Error(e); + } + } + + @Override + public String toString() { + String s = arrayType.getCanonicalName(); + return unsigned ? "unsigned " + s : s; + } + + Object construct(int length) { + return Array.newInstance(componentType, length); + } + + Object copyOf(Object a) { + return copyOf(a, 0, Array.getLength(a)); + } + + Object copyOf(Object a, int from, int to) { + try { + return (Object) cpy.invoke(a, from, to); + } + catch (RuntimeException | Error e) { + throw e; + } + catch (Throwable t) { + throw new Error(t); + } + } + + Object get(Object a, int i) { + try { + return (Object) getter.invoke(a, i); + } + catch (RuntimeException | Error e) { + throw e; + } + catch (Throwable t) { + throw new Error(t); + } + } + + abstract void set(Object a, int i, Object v); + + boolean equals(Object a, Object b) { + try { + return (boolean) eq.invoke(a, b); + } + catch (RuntimeException | Error e) { + throw e; + } + catch (Throwable t) { + throw new Error(t); + } + } + + boolean equals(Object a, int aFromIndex, int aToIndex, + Object b, int bFromIndex, int bToIndex) { + try { + return (boolean) eqr.invoke(a, aFromIndex, aToIndex, b, bFromIndex, bToIndex); + } + catch (RuntimeException | Error e) { + throw e; + } + catch (Throwable t) { + throw new Error(t); + } + } + + int compare(Object a, Object b) { + try { + return (int) cmp.invoke(a, b); + } + catch (RuntimeException | Error e) { + throw e; + } + catch (Throwable t) { + throw new Error(t); + } + } + + int compare(Object a, int aFromIndex, int aToIndex, + Object b, int bFromIndex, int bToIndex) { + try { + return (int) cmpr.invoke(a, aFromIndex, aToIndex, b, bFromIndex, bToIndex); + } + catch (RuntimeException | Error e) { + throw e; + } + catch (Throwable t) { + throw new Error(t); + } + } + + int mismatch(Object a, Object b) { + try { + return (int) mm.invoke(a, b); + } + catch (RuntimeException | Error e) { + throw e; + } + catch (Throwable t) { + throw new Error(t); + } + } + + int mismatch(Object a, int aFromIndex, int aToIndex, + Object b, int bFromIndex, int bToIndex) { + try { + return (int) mmr.invoke(a, aFromIndex, aToIndex, b, bFromIndex, bToIndex); + } + catch (RuntimeException | Error e) { + throw e; + } + catch (Throwable t) { + throw new Error(t); + } + } + + String toString(Object a) { + try { + return (String) toString.invoke(a); + } + catch (RuntimeException | Error e) { + throw e; + } + catch (Throwable t) { + throw new Error(t); + } + } + + static class BoxedIntegers extends ArrayType { + public BoxedIntegers() { + super(Integer[].class); + } + + @Override + void set(Object a, int i, Object v) { + // Ensure unique reference + ((Integer[]) a)[i] = v != null ? new Integer((Integer) v) : null; + } + } + + static class BoxedIntegersWithReverseComparator extends BoxedIntegers { + final Comparator c = (a, b) -> { + // Nulls sort after non-nulls + if (a == null || b == null) + return a == null ? b == null ? 0 : 1 : -1; + + return Integer.compare(b, a); + }; + + final MethodHandle cmpc; + final MethodHandle cmpcr; + final MethodHandle mismatchc; + final MethodHandle mismatchcr; + + public BoxedIntegersWithReverseComparator() { + try { + MethodHandles.Lookup l = MethodHandles.lookup(); + + MethodType cmpt = MethodType.methodType( + int.class, Object[].class, Object[].class, Comparator.class); + MethodType cmprt = MethodType.methodType( + int.class, Object[].class, int.class, int.class, + Object[].class, int.class, int.class, Comparator.class); + + cmpc = l.findStatic(Arrays.class, "compare", cmpt); + cmpcr = l.findStatic(Arrays.class, "compare", cmprt); + mismatchc = l.findStatic(Arrays.class, "mismatch", cmpt); + mismatchcr = l.findStatic(Arrays.class, "mismatch", cmprt); + } + catch (Exception e) { + throw new Error(e); + } + } + + @Override + int compare(Object a, Object b) { + try { + return (int) cmpc.invoke(a, b, c); + } + catch (RuntimeException | Error e) { + throw e; + } + catch (Throwable t) { + throw new Error(t); + } + } + + @Override + int compare(Object a, int aFromIndex, int aToIndex, + Object b, int bFromIndex, int bToIndex) { + try { + return (int) cmpcr.invoke(a, aFromIndex, aToIndex, b, bFromIndex, bToIndex, c); + } + catch (RuntimeException | Error e) { + throw e; + } + catch (Throwable t) { + throw new Error(t); + } + } + + @Override + int mismatch(Object a, Object b) { + try { + return (int) mismatchc.invoke(a, b, c); + } + catch (RuntimeException | Error e) { + throw e; + } + catch (Throwable t) { + throw new Error(t); + } + } + + @Override + int mismatch(Object a, int aFromIndex, int aToIndex, + Object b, int bFromIndex, int bToIndex) { + try { + return (int) mismatchcr.invoke(a, aFromIndex, aToIndex, b, bFromIndex, bToIndex, c); + } + catch (RuntimeException | Error e) { + throw e; + } + catch (Throwable t) { + throw new Error(t); + } + } + + @Override + public String toString() { + return arrayType.getCanonicalName() + " with Comparator"; + } + } + + static class Booleans extends ArrayType { + public Booleans() { + super(boolean[].class); + } + + @Override + void set(Object a, int i, Object v) { + boolean pv; + if (v instanceof Boolean) { + pv = (Boolean) v; + } + else if (v instanceof Integer) { + pv = ((Integer) v) >= 0; + } + else throw new IllegalStateException(); + + ((boolean[]) a)[i] = pv; + } + } + + static class Bytes extends ArrayType { + public Bytes(boolean unsigned) { + super(byte[].class, unsigned); + } + + @Override + void set(Object a, int i, Object v) { + byte pv; + if (v instanceof Byte) { + pv = (Byte) v; + } + else if (v instanceof Integer) { + pv = ((Integer) v).byteValue(); + } + else throw new IllegalStateException(); + + ((byte[]) a)[i] = pv; + } + } + + static class Characters extends ArrayType { + public Characters() { + super(char[].class); + } + + @Override + void set(Object a, int i, Object v) { + char pv; + if (v instanceof Character) { + pv = (Character) v; + } + else if (v instanceof Integer) { + pv = (char) ((Integer) v).intValue(); + } + else throw new IllegalStateException(); + + ((char[]) a)[i] = pv; + } + } + + static class Shorts extends ArrayType { + public Shorts(boolean unsigned) { + super(short[].class, unsigned); + } + + @Override + void set(Object a, int i, Object v) { + short pv; + if (v instanceof Short) { + pv = (Short) v; + } + else if (v instanceof Integer) { + pv = ((Integer) v).shortValue(); + } + else throw new IllegalStateException(); + + ((short[]) a)[i] = pv; + } + } + + static class Integers extends ArrayType { + public Integers(boolean unsigned) { + super(int[].class, unsigned); + } + + @Override + void set(Object a, int i, Object v) { + int pv; + if (v instanceof Integer) { + pv = ((Integer) v).shortValue(); + } + else throw new IllegalStateException(); + + ((int[]) a)[i] = pv; + } + } + + static class Longs extends ArrayType { + public Longs(boolean unsigned) { + super(long[].class, unsigned); + } + + @Override + void set(Object a, int i, Object v) { + long pv; + if (v instanceof Long) { + pv = (Long) v; + } + else if (v instanceof Integer) { + pv = ((Integer) v).longValue(); + } + else throw new IllegalStateException(); + + ((long[]) a)[i] = pv; + } + } + + static class Floats extends ArrayType { + public Floats() { + super(float[].class); + } + + @Override + void set(Object a, int i, Object v) { + float pv; + if (v instanceof Float) { + pv = (Float) v; + } + else if (v instanceof Integer) { + pv = ((Integer) v).floatValue(); + } + else throw new IllegalStateException(); + + ((float[]) a)[i] = pv; + } + } + + static class Doubles extends ArrayType { + public Doubles() { + super(double[].class); + } + + @Override + void set(Object a, int i, Object v) { + double pv; + if (v instanceof Double) { + pv = (Double) v; + } + else if (v instanceof Integer) { + pv = ((Integer) v).doubleValue(); + } + else throw new IllegalStateException(); + + ((double[]) a)[i] = pv; + } + } + } + + static Object[][] arrayTypes; + + @DataProvider + public static Object[][] arrayTypesProvider() { + if (arrayTypes == null) { + arrayTypes = new Object[][]{ + new Object[]{new ArrayType.BoxedIntegers()}, + new Object[]{new ArrayType.BoxedIntegersWithReverseComparator()}, + new Object[]{new ArrayType.Booleans()}, + new Object[]{new ArrayType.Bytes(false)}, + new Object[]{new ArrayType.Bytes(true)}, + new Object[]{new ArrayType.Characters()}, + new Object[]{new ArrayType.Shorts(false)}, + new Object[]{new ArrayType.Shorts(true)}, + new Object[]{new ArrayType.Integers(false)}, + new Object[]{new ArrayType.Integers(true)}, + new Object[]{new ArrayType.Longs(false)}, + new Object[]{new ArrayType.Longs(true)}, + new Object[]{new ArrayType.Floats()}, + new Object[]{new ArrayType.Doubles()}, + }; + } + return arrayTypes; + } + + static Object[][] floatArrayTypes; + + @DataProvider + public static Object[][] floatArrayTypesProvider() { + if (floatArrayTypes == null) { + LongFunction bTof = rb -> Float.intBitsToFloat((int) rb); + LongFunction bToD = Double::longBitsToDouble; + + floatArrayTypes = new Object[][]{ + new Object[]{new ArrayType.Floats(), 0x7fc00000L, 0x7f800001L, bTof}, + new Object[]{new ArrayType.Doubles(), 0x7ff8000000000000L, 0x7ff0000000000001L, bToD}, + }; + } + return floatArrayTypes; + } + + static Object[][] objectArrayTypes; + + @DataProvider + public static Object[][] objectArrayTypesProvider() { + if (objectArrayTypes == null) { + LongFunction bTof = rb -> Float.intBitsToFloat((int) rb); + LongFunction bToD = Double::longBitsToDouble; + + objectArrayTypes = new Object[][]{ + new Object[]{new ArrayType.BoxedIntegers()}, + new Object[]{new ArrayType.BoxedIntegersWithReverseComparator()}, + }; + } + return objectArrayTypes; + } + + + static Object[][] signedUnsignedArrayTypes; + + @DataProvider + public static Object[][] signedUnsignedArrayTypes() { + if (signedUnsignedArrayTypes == null) { + signedUnsignedArrayTypes = new Object[][]{ + new Object[]{new ArrayType.Bytes(false), new ArrayType.Bytes(true)}, + new Object[]{new ArrayType.Shorts(false), new ArrayType.Shorts(true)}, + new Object[]{new ArrayType.Integers(false), new ArrayType.Integers(true)}, + new Object[]{new ArrayType.Longs(false), new ArrayType.Longs(true)}, + }; + } + return signedUnsignedArrayTypes; + } + + // Equality and comparison tests + + @Test(dataProvider = "arrayTypesProvider") + public void testArray(ArrayType arrayType) { + BiFunction, Integer, Object> constructor = (at, s) -> { + Object a = at.construct(s); + for (int x = 0; x < s; x++) { + at.set(a, x, x % 8); + } + return a; + }; + + BiFunction, Object, Object> cloner = (at, a) -> + constructor.apply(at, Array.getLength(a)); + + testArrayType(arrayType, constructor, cloner); + } + + @Test(dataProvider = "floatArrayTypesProvider") + public void testPrimitiveFloatArray( + ArrayType arrayType, + long canonicalNanRawBits, long nonCanonicalNanRawBits, + LongFunction bitsToFloat) { + Object canonicalNan = bitsToFloat.apply(canonicalNanRawBits); + // If conversion is a signalling NaN it may be subject to conversion to a + // quiet NaN on some processors, even if a copy is performed + // The tests assume that if conversion occurs it does not convert to the + // canonical NaN + Object nonCanonicalNan = bitsToFloat.apply(nonCanonicalNanRawBits); + + BiFunction, Integer, Object> canonicalNaNs = (at, s) -> { + Object a = at.construct(s); + for (int x = 0; x < s; x++) { + at.set(a, x, canonicalNan); + } + return a; + }; + + BiFunction, Object, Object> nonCanonicalNaNs = (at, a) -> { + int s = Array.getLength(a); + Object ac = at.construct(s); + for (int x = 0; x < s; x++) { + at.set(ac, x, nonCanonicalNan); + } + return ac; + }; + + BiFunction, Object, Object> halfNonCanonicalNaNs = (at, a) -> { + int s = Array.getLength(a); + Object ac = at.construct(s); + for (int x = 0; x < s / 2; x++) { + at.set(ac, x, nonCanonicalNan); + } + for (int x = s / 2; x < s; x++) { + at.set(ac, x, 1); + } + return ac; + }; + + testArrayType(arrayType, canonicalNaNs, nonCanonicalNaNs); + testArrayType(arrayType, canonicalNaNs, halfNonCanonicalNaNs); + } + + @Test(dataProvider = "objectArrayTypesProvider") + public void testNullElementsInObjectArray(ArrayType arrayType) { + BiFunction, Object, Object> cloner = ArrayType::copyOf; + + // All nulls + testArrayType(arrayType, + (at, s) -> { + Object a = at.construct(s); + for (int x = 0; x < s; x++) { + at.set(a, x, null); + } + return a; + }, + cloner); + + + // Some nulls + testArrayType(arrayType, + (at, s) -> { + Object a = at.construct(s); + for (int x = 0; x < s; x++) { + int v = x % 8; + at.set(a, x, v == 0 ? null : v); + } + return a; + }, + cloner); + + Integer[] a = new Integer[]{null, 0}; + Integer[] b = new Integer[]{0, 0}; + Assert.assertTrue(Arrays.compare(a, b) < 0); + Assert.assertTrue(Arrays.compare(b, a) > 0); + } + + @Test(dataProvider = "objectArrayTypesProvider") + public void testSameRefElementsInObjectArray(ArrayType arrayType) { + BiFunction, Object, Object> cloner = ArrayType::copyOf; + + // One ref + Integer one = 1; + testArrayType(arrayType, + (at, s) -> { + Integer[] a = (Integer[]) at.construct(s); + for (int x = 0; x < s; x++) { + a[x] = one; + } + return a; + }, + cloner); + + // All ref + testArrayType(arrayType, + (at, s) -> { + Integer[] a = (Integer[]) at.construct(s); + for (int x = 0; x < s; x++) { + a[x] = Integer.valueOf(s); + } + return a; + }, + cloner); + + // Some same ref + testArrayType(arrayType, + (at, s) -> { + Integer[] a = (Integer[]) at.construct(s); + for (int x = 0; x < s; x++) { + int v = x % 8; + a[x] = v == 1 ? one : new Integer(v); + } + return a; + }, + cloner); + } + + @Test(dataProvider = "signedUnsignedArrayTypes") + public void testSignedUnsignedArray(ArrayType sat, ArrayType uat) { + BiFunction, Integer, Object> constructor = (at, s) -> { + Object a = at.construct(s); + for (int x = 0; x < s; x++) { + at.set(a, x, 1); + } + return a; + }; + + int n = arraySizeFor(sat.componentType); + + for (int s : ranges(0, n)) { + Object a = constructor.apply(sat, s); + + for (int aFrom : ranges(0, s)) { + for (int aTo : ranges(aFrom, s)) { + int aLength = aTo - aFrom; + + if (aLength > 0) { + for (int i = aFrom; i < aTo; i++) { + Object ac = sat.copyOf(a); + // Create common prefix with a length of i - aFrom + sat.set(ac, i, -1); + + int sc = sat.compare(ac, aFrom, aTo, a, aFrom, aTo); + int uc = uat.compare(ac, aFrom, aTo, a, aFrom, aTo); + + Assert.assertTrue(sc < 0); + Assert.assertTrue(uc > 0); + } + } + } + } + } + } + + void testArrayType(ArrayType at, + BiFunction, Integer, Object> constructor, + BiFunction, Object, Object> cloner) { + int n = arraySizeFor(at.componentType); + + for (int s : ranges(0, n)) { + Object a = constructor.apply(at, s); + Object b = cloner.apply(at, a); + + for (int aFrom : ranges(0, s)) { + for (int aTo : ranges(aFrom, s)) { + int aLength = aTo - aFrom; + + for (int bFrom : ranges(0, s)) { + for (int bTo : ranges(bFrom, s)) { + int bLength = bTo - bFrom; + + Object anr = at.copyOf(a, aFrom, aTo); + Object bnr = at.copyOf(b, bFrom, bTo); + + boolean eq = isEqual(at, a, aFrom, aTo, b, bFrom, bTo); + Assert.assertEquals(at.equals(a, aFrom, aTo, b, bFrom, bTo), eq); + Assert.assertEquals(at.equals(b, bFrom, bTo, a, aFrom, aTo), eq); + Assert.assertEquals(at.equals(anr, bnr), eq); + Assert.assertEquals(at.equals(bnr, anr), eq); + if (eq) { + Assert.assertEquals(at.compare(a, aFrom, aTo, b, bFrom, bTo), 0); + Assert.assertEquals(at.compare(b, bFrom, bTo, a, aFrom, aTo), 0); + Assert.assertEquals(at.compare(anr, bnr), 0); + Assert.assertEquals(at.compare(bnr, anr), 0); + + Assert.assertEquals(at.mismatch(a, aFrom, aTo, b, bFrom, bTo), -1); + Assert.assertEquals(at.mismatch(b, bFrom, bTo, a, aFrom, aTo), -1); + Assert.assertEquals(at.mismatch(anr, bnr), -1); + Assert.assertEquals(at.mismatch(bnr, anr), -1); + } + else { + int aCb = at.compare(a, aFrom, aTo, b, bFrom, bTo); + int bCa = at.compare(b, bFrom, bTo, a, aFrom, aTo); + int v = Integer.signum(aCb) * Integer.signum(bCa); + Assert.assertTrue(v == -1); + + int anrCbnr = at.compare(anr, bnr); + int bnrCanr = at.compare(bnr, anr); + Assert.assertEquals(anrCbnr, aCb); + Assert.assertEquals(bnrCanr, bCa); + + + int aMb = at.mismatch(a, aFrom, aTo, b, bFrom, bTo); + int bMa = at.mismatch(b, bFrom, bTo, a, aFrom, aTo); + int anrMbnr = at.mismatch(anr, bnr); + int bnrManr = at.mismatch(bnr, anr); + + Assert.assertNotEquals(aMb, -1); + Assert.assertEquals(aMb, bMa); + Assert.assertNotEquals(anrMbnr, -1); + Assert.assertEquals(anrMbnr, bnrManr); + Assert.assertEquals(aMb, anrMbnr); + Assert.assertEquals(bMa, bnrManr); + + // Common or proper prefix + Assert.assertTrue(at.equals(a, aFrom, aFrom + aMb, b, bFrom, bFrom + aMb)); + if (aMb < Math.min(aLength, bLength)) { + // Common prefix + Assert.assertFalse(isEqual(at, a, aFrom + aMb, b, bFrom + aMb)); + } + } + } + } + + if (aLength > 0) { + for (int i = aFrom; i < aTo; i++) { + Object ac = at.copyOf(a); + // Create common prefix with a length of i - aFrom + at.set(ac, i, -1); + + Object acnr = at.copyOf(ac, aFrom, aTo); + Object anr = at.copyOf(a, aFrom, aTo); + + Assert.assertFalse(at.equals(ac, aFrom, aTo, a, aFrom, aTo)); + Assert.assertFalse(at.equals(acnr, anr)); + + int acCa = at.compare(ac, aFrom, aTo, a, aFrom, aTo); + int aCac = at.compare(a, aFrom, aTo, ac, aFrom, aTo); + int v = Integer.signum(acCa) * Integer.signum(aCac); + Assert.assertTrue(v == -1); + + int acnrCanr = at.compare(acnr, anr); + int anrCacnr = at.compare(anr, acnr); + Assert.assertEquals(acnrCanr, acCa); + Assert.assertEquals(anrCacnr, aCac); + + + int acMa = at.mismatch(ac, aFrom, aTo, a, aFrom, aTo); + int aMac = at.mismatch(a, aFrom, aTo, ac, aFrom, aTo); + Assert.assertEquals(acMa, aMac); + Assert.assertEquals(acMa, i - aFrom); + + int acnrManr = at.mismatch(acnr, anr); + int anrMacnr = at.mismatch(anr, acnr); + Assert.assertEquals(acnrManr, anrMacnr); + Assert.assertEquals(acnrManr, i - aFrom); + } + } + } + } + } + } + + static boolean isEqual(ArrayType at, Object a, int aFromIndex, int aToIndex, + Object b, int bFromIndex, int bToIndex) { + int aLength = aToIndex - aFromIndex; + int bLength = bToIndex - bFromIndex; + if (aLength != bLength) + return false; + + for (int i = 0; i < aLength; i++) { + Object av = at.get(a, aFromIndex++); + Object bv = at.get(b, bFromIndex++); + if (!Objects.equals(av, bv)) return false; + } + + return true; + } + + static boolean isEqual(ArrayType at, Object a, int aFrom, Object b, int bFrom) { + Object av = at.get(a, aFrom); + Object bv = at.get(b, bFrom); + + return Objects.equals(av, bv); + } + + static int[] ranges(int from, int to) { + int width = to - from; + switch (width) { + case 0: + return new int[]{}; + case 1: + return new int[]{from, to}; + case 2: + return new int[]{from, from + 1, to}; + case 3: + return new int[]{from, from + 1, from + 2, to}; + default: + return IntStream.of(from, from + 1, from + 2, to / 2 - 1, to / 2, to / 2 + 1, to - 2, to - 1, to) + .filter(i -> i >= from && i <= to) + .distinct().toArray(); + } + } + + + // Null array reference tests + + @Test(dataProvider = "arrayTypesProvider") + public void testNullArrayRefs(ArrayType arrayType) { + Object n = null; + Object a = arrayType.construct(0); + + Assert.assertTrue(arrayType.equals(n, n)); + Assert.assertFalse(arrayType.equals(n, a)); + Assert.assertFalse(arrayType.equals(a, n)); + + Assert.assertEquals(arrayType.compare(n, n), 0); + Assert.assertTrue(arrayType.compare(n, a) < 0); + Assert.assertTrue(arrayType.compare(a, n) > 0); + } + + + // Exception throwing tests + + @Test(dataProvider = "arrayTypesProvider") + public void testNPEs(ArrayType arrayType) { + Object[] values = new Object[]{null, arrayType.construct(0)}; + + for (Object o1 : values) { + for (Object o2 : values) { + if (o1 != null && o2 != null) + continue; + + testNPE(() -> arrayType.equals(o1, 0, 0, o2, 0, 0)); + testNPE(() -> arrayType.compare(o1, 0, 0, o2, 0, 0)); + testNPE(() -> arrayType.mismatch(o1, o2)); + testNPE(() -> arrayType.mismatch(o1, 0, 0, o2, 0, 0)); + } + } + } + + @Test + public void testObjectNPEs() { + String[][] values = new String[][]{null, new String[0]}; + Comparator c = String::compareTo; + Comparator[] cs = new Comparator[]{null, c}; + + for (String[] o1 : values) { + for (String[] o2 : values) { + for (Comparator o3 : cs) { + if (o1 != null && o2 != null && o3 != null) + continue; + + if (o3 == null) { + testNPE(() -> Arrays.compare(o1, o2, o3)); + testNPE(() -> Arrays.mismatch(o1, o2, o3)); + } + + testNPE(() -> Arrays.compare(o1, 0, 0, o2, 0, 0, o3)); + testNPE(() -> Arrays.mismatch(o1, 0, 0, o2, 0, 0, o3)); + } + } + } + } + + @Test(dataProvider = "arrayTypesProvider") + public void testIAEs(ArrayType arrayType) { + List values = Arrays.asList(0, 1); + + for (int s : values) { + Object a = arrayType.construct(s); + + for (int o1 : values) { + for (int o2 : values) { + if (o1 <= o2) continue; + + testIAE(() -> arrayType.equals(a, o1, 0, a, o2, 0)); + testIAE(() -> arrayType.compare(a, o1, 0, a, o2, 0)); + testIAE(() -> arrayType.mismatch(a, o1, 0, a, o2, 0)); + } + } + } + } + + @Test(dataProvider = "arrayTypesProvider") + public void testAIOBEs(ArrayType arrayType) { + List froms = Arrays.asList(-1, 0); + + for (int s : Arrays.asList(0, 1)) { + List tos = Arrays.asList(s, s + 1); + Object a = arrayType.construct(s); + + for (int aFrom : froms) { + for (int aTo : tos) { + for (int bFrom : froms) { + for (int bTo : tos) { + if (aFrom >= 0 && aTo <= s && + bFrom >= 0 && bTo <= s) continue; + + testAIOBE(() -> arrayType.equals(a, aFrom, aTo, a, bFrom, bTo)); + testAIOBE(() -> arrayType.compare(a, aFrom, aTo, a, bFrom, bTo)); + testAIOBE(() -> arrayType.mismatch(a, aFrom, aTo, a, bFrom, bTo)); + } + } + } + } + } + } + + static void testNPE(Runnable r) { + testThrowable(r, NullPointerException.class); + } + + static void testIAE(Runnable r) { + testThrowable(r, IllegalArgumentException.class); + } + + static void testAIOBE(Runnable r) { + testThrowable(r, ArrayIndexOutOfBoundsException.class); + } + + static void testThrowable(Runnable r, Class expected) { + Throwable caught = null; + try { + r.run(); + } + catch (Throwable t) { + caught = t; + } + Assert.assertNotNull(caught); + Assert.assertTrue(expected.isInstance(caught)); + } +} \ No newline at end of file