
org.plumelib.util.MathPlume Maven / Gradle / Ivy
package org.plumelib.util;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import org.checkerframework.checker.index.qual.IndexFor;
import org.checkerframework.checker.index.qual.LessThan;
import org.checkerframework.checker.index.qual.NonNegative;
import org.checkerframework.checker.index.qual.PolyUpperBound;
import org.checkerframework.checker.lock.qual.GuardSatisfied;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.checker.signedness.qual.Unsigned;
import org.checkerframework.common.value.qual.ArrayLen;
import org.checkerframework.common.value.qual.MinLen;
import org.checkerframework.common.value.qual.StaticallyExecutable;
import org.checkerframework.dataflow.qual.Pure;
import org.checkerframework.dataflow.qual.SideEffectFree;
/** Mathematical utilities. */
public final class MathPlume {
/** This class is a collection of methods; it does not represent anything. */
private MathPlume() {
throw new Error("do not instantiate");
}
///
/// Function versions of Java operators
///
/**
* Negates its argument.
*
* @param a value to negate
* @return negative of a
*/
@Pure
@StaticallyExecutable
public static int negate(int a) {
return -a;
}
/**
* Negates its argument.
*
* @param a value to negate
* @return negative of a
*/
@Pure
@StaticallyExecutable
public static long negate(long a) {
return -a;
}
/**
* Negates its argument.
*
* @param a value to negate
* @return negative of a
*/
@Pure
@StaticallyExecutable
public static double negate(double a) {
return -a;
}
/**
* Returns ~a, the bitwise complement of its argument.
*
* @param a value to bitwise-complement
* @return ~a, the bitwise complement of a
*/
@Pure
@StaticallyExecutable
public static int bitwiseComplement(int a) {
return ~a;
}
/**
* Returns ~a, the bitwise complement of its argument.
*
* @param a value to bitwise-complement
* @return ~a, the bitwise complement of a
*/
@Pure
@StaticallyExecutable
public static long bitwiseComplement(long a) {
return ~a;
}
/**
* Multiplies its arguments.
*
* @param x first multiplicand
* @param y second multiplicand
* @return x * y
*/
@Pure
@StaticallyExecutable
public static int mul(int x, int y) {
return x * y;
}
/**
* Multiplies its arguments.
*
* @param x first multiplicand
* @param y second multiplicand
* @return x * y
*/
@Pure
@StaticallyExecutable
public static long mul(long x, long y) {
return x * y;
}
/**
* Multiplies its arguments.
*
* @param x first multiplicand
* @param y second multiplicand
* @return x * y
*/
@Pure
@StaticallyExecutable
public static double mul(double x, double y) {
return x * y;
}
/**
* Divides its arguments.
*
* @param x dividend
* @param y divisor
* @return x / y
*/
@Pure
@StaticallyExecutable
public static int div(int x, int y) {
return x / y;
}
/**
* Divides its arguments.
*
* @param x dividend
* @param y divisor
* @return x / y
*/
@Pure
@StaticallyExecutable
public static long div(long x, long y) {
return x / y;
}
/**
* Divides its arguments.
*
* @param x dividend
* @param y divisor
* @return x / y
*/
@Pure
@StaticallyExecutable
public static double div(double x, double y) {
return x / y;
}
/**
* Returns x % y, the modulus operation applied to its arguments.
*
* @param x valued to be modded
* @param y modulus
* @return x % y
*/
@Pure
@StaticallyExecutable
public static int mod(int x, int y) {
return x % y;
}
/**
* Returns x % y, the modulus operation applied to its arguments.
*
* @param x valued to be modded
* @param y modulus
* @return x % y
*/
@Pure
@StaticallyExecutable
public static long mod(long x, long y) {
return x % y;
}
/**
* Returns x << y, the left-shift operation applied to its arguments.
*
* @param x valued to be left-shifted
* @param y magnitude of the left-shift
* @return x << y
*/
@Pure
@StaticallyExecutable
public static int lshift(int x, int y) {
return x << y;
}
/**
* Returns x << y, the left-shift operation applied to its arguments.
*
* @param x valued to be left-shifted
* @param y magnitude of the left-shift
* @return x << y
*/
@Pure
@StaticallyExecutable
public static long lshift(long x, long y) {
return x << y;
}
/**
* Returns x >> y, the signed right-shift operation applied to its arguments.
*
* @param x valued to be right-shifted
* @param y magnitude of the right-shift
* @return x >> y
*/
@Pure
@StaticallyExecutable
public static int rshiftSigned(int x, int y) {
return x >> y;
}
/**
* Returns x >> y, the signed right-shift operation applied to its arguments.
*
* @param x valued to be right-shifted
* @param y magnitude of the right-shift
* @return x >> y
*/
@Pure
@StaticallyExecutable
public static long rshiftSigned(long x, long y) {
return x >> y;
}
/**
* Returns x >>> y, the unsigned right-shift operation applied to its arguments.
*
* @param x valued to be right-shifted
* @param y magnitude of the right-shift
* @return x >>> y
*/
@Pure
@StaticallyExecutable
public static @Unsigned int rshiftUnsigned(@Unsigned int x, int y) {
return x >>> y;
}
/**
* Returns x >>> y, the unsigned right-shift operation applied to its arguments.
*
* @param x valued to be right-shifted
* @param y magnitude of the right-shift
* @return x >>> y
*/
@Pure
@StaticallyExecutable
public static @Unsigned long rshiftUnsigned(@Unsigned long x, long y) {
return x >>> y;
}
/**
* Returns x & y, the bitwise and of its arguments.
*
* @param x first operand
* @param y second operand
* @return x & y
*/
@Pure
@StaticallyExecutable
public static int bitwiseAnd(int x, int y) {
return x & y;
}
/**
* Returns x & y, the bitwise and of its arguments.
*
* @param x first operand
* @param y second operand
* @return x & y
*/
@Pure
@StaticallyExecutable
public static long bitwiseAnd(long x, long y) {
return x & y;
}
/**
* Returns the logical and of its arguments. The result is always 0 or 1.
*
* @param x first operand
* @param y second operand
* @return the logical and of x and y; the result is always 0 or 1
*/
@Pure
@StaticallyExecutable
public static int logicalAnd(int x, int y) {
return ((x != 0) && (y != 0)) ? 1 : 0;
}
/**
* Returns the logical and of its arguments. The result is always 0 or 1.
*
* @param x first operand
* @param y second operand
* @return the logical and of x and y; the result is always 0 or 1
*/
@Pure
@StaticallyExecutable
public static long logicalAnd(long x, long y) {
return ((x != 0) && (y != 0)) ? 1 : 0;
}
/**
* Returns x ^ y, the bitwise xor of its arguments.
*
* @param x first operand
* @param y second operand
* @return x ^ y
*/
@Pure
@StaticallyExecutable
public static int bitwiseXor(int x, int y) {
return x ^ y;
}
/**
* Returns x ^ y, the bitwise xor of its arguments.
*
* @param x first operand
* @param y second operand
* @return x ^ y
*/
@Pure
@StaticallyExecutable
public static long bitwiseXor(long x, long y) {
return x ^ y;
}
/**
* Returns the logical xor of its arguments. The result is always 0 or 1.
*
* @param x first operand
* @param y second operand
* @return the logical xor of x and y; the result is always 0 or 1
*/
@Pure
@StaticallyExecutable
public static int logicalXor(int x, int y) {
return ((x != 0) ^ (y != 0)) ? 1 : 0;
}
/**
* Returns the logical xor of its arguments. The result is always 0 or 1.
*
* @param x first operand
* @param y second operand
* @return the logical xor of x and y; the result is always 0 or 1
*/
@Pure
@StaticallyExecutable
public static long logicalXor(long x, long y) {
return ((x != 0) ^ (y != 0)) ? 1 : 0;
}
/**
* Returns x | y, the bitwise or of its arguments.
*
* @param x first operand
* @param y second operand
* @return x | y
*/
@Pure
@StaticallyExecutable
public static int bitwiseOr(int x, int y) {
return x | y;
}
/**
* Returns x | y, the bitwise or of its arguments.
*
* @param x first operand
* @param y second operand
* @return x | y
*/
@Pure
@StaticallyExecutable
public static long bitwiseOr(long x, long y) {
return x | y;
}
/**
* Returns the logical or of its arguments. The result is always 0 or 1.
*
* @param x first operand
* @param y second operand
* @return the logical or of x and y; the result is always 0 or 1
*/
@Pure
@StaticallyExecutable
public static int logicalOr(int x, int y) {
return ((x != 0) || (y != 0)) ? 1 : 0;
}
/**
* Returns the logical or of its arguments. The result is always 0 or 1.
*
* @param x first operand
* @param y second operand
* @return the logical or of x and y; the result is always 0 or 1
*/
@Pure
@StaticallyExecutable
public static long logicalOr(long x, long y) {
return ((x != 0) || (y != 0)) ? 1 : 0;
}
///
/// sign
///
/**
* Returns the sign of its argument. The result is always -1, 0, or 1.
*
* @param a value to have its sign taken
* @return the sign of a: -1, 0, or 1
*/
@Pure
@StaticallyExecutable
public static int sign(int a) {
if (a == 0) {
return 0;
} else if (a > 0) {
return 1;
} else {
return -1;
}
}
///
/// exponentiation
///
/**
* Returns of value of the first argument raised to the power of the second argument. The
* arguments are integers.
*
* @param base the base
* @param expt the exponent
* @return base to the expt power
* @see Math#pow(double, double)
*/
@Pure
@StaticallyExecutable
public static int pow(int base, int expt) throws ArithmeticException {
return powFast(base, expt);
}
/**
* Returns of value of the first argument raised to the power of the second argument.
*
* @param base the base
* @param expt the exponent
* @return base to the expt power
* @see Math#pow(double, double)
*/
@Pure
@StaticallyExecutable
public static long pow(long base, long expt) throws ArithmeticException {
return powFast(base, expt);
}
/**
* Returns of value of the first argument raised to the power of the second argument. Uses a fast
* algorithm.
*
* @param base the base
* @param expt the exponent
* @return base to the expt power
* @see Math#pow(double, double)
*/
@Pure
@StaticallyExecutable
private static int powFast(int base, int expt) throws ArithmeticException {
if (expt < 0) {
throw new ArithmeticException("Negative exponent passed to pow");
}
int thisSquarePow = base;
int result = 1;
while (expt > 0) {
if ((expt & 1) != 0) {
result *= thisSquarePow;
}
expt >>= 1;
thisSquarePow *= thisSquarePow;
}
return result;
}
/**
* Returns the first argument raised to the power of the second argument. Uses a fast algorithm.
*
* @param base the base
* @param expt the exponent
* @return base to the expt power
* @see Math#pow(double, double)
*/
@Pure
@StaticallyExecutable
private static long powFast(long base, long expt) throws ArithmeticException {
if (expt < 0) {
throw new ArithmeticException("Negative exponent passed to pow");
}
long thisSquarePow = base;
long result = 1;
while (expt > 0) {
if ((expt & 1) != 0) {
result *= thisSquarePow;
}
expt >>= 1;
thisSquarePow *= thisSquarePow;
}
return result;
}
// /**
// * Returns the first argument raised to the power of the second argument. Uses a slow
// * algorithm.
// *
// * @param base the base
// * @param expt the exponent
// * @return base to the expt power
// * @see Math#pow(double, double)
// */
// @Pure
// @StaticallyExecutable
// private static int powSlow(int base, int expt) throws ArithmeticException {
// if (expt < 0) {
// throw new ArithmeticException("Negative exponent passed to pow");
// }
//
// int result = 1;
// for (int i = 0; i < expt; i++) {
// result *= base;
// }
// return result;
// }
///
/// gcd
///
/**
* Returns the greatest common divisor of the two arguments.
*
* @param a first operand
* @param b second operand
* @return greatest common divisor of a and b
*/
@Pure
@StaticallyExecutable
public static int gcd(int a, int b) {
// Euclid's method
if (b == 0) {
return Math.abs(a);
}
a = Math.abs(a);
b = Math.abs(b);
while (b != 0) {
int tmp = b;
b = a % b;
a = tmp;
}
return a;
}
/**
* Returns the greatest common divisor of the elements of int array a.
*
* @param a array of operands
* @return greatest common divisor of the elements of a
*/
@Pure
@StaticallyExecutable
public static int gcd(int[] a) {
if (a.length == 0) {
return 0;
}
int result = a[0];
for (int i = 1; i < a.length; i++) {
result = gcd(a[i], result);
if ((result == 1) || (result == 0)) {
return result;
}
}
return result;
}
/**
* Returns the gcd (greatest common divisor) of the differences between the elements of int array
* a.
*
* @param a array of operands
* @return greatest common divisor of the differences between the elements of a
*/
@Pure
@StaticallyExecutable
public static int gcdDifferences(int[] a) {
if (a.length < 2) {
return 0;
}
int result = a[1] - a[0];
for (int i = 2; i < a.length; i++) {
result = gcd(a[i] - a[i - 1], result);
if ((result == 1) || (result == 0)) {
return result;
}
}
return result;
}
/// gcd -- version for manipulating long (rather than int) values
/**
* Returns the greatest common divisor of the two arguments.
*
* @param a first operand
* @param b second operand
* @return greatest common divisor of a and b
*/
@Pure
@StaticallyExecutable
public static long gcd(long a, long b) {
// Euclid's method
if (b == 0) {
return Math.abs(a);
}
a = Math.abs(a);
b = Math.abs(b);
while (b != 0) {
long tmp = b;
b = a % b;
a = tmp;
}
return a;
}
/**
* Returns the greatest common divisor of the elements of long array a.
*
* @param a array of operands
* @return greatest common divisor of the elements of a
*/
@Pure
@StaticallyExecutable
public static long gcd(long[] a) {
if (a.length == 0) {
return 0;
}
long result = a[0];
for (int i = 1; i < a.length; i++) {
result = gcd(a[i], result);
if ((result == 1) || (result == 0)) {
return result;
}
}
return result;
}
/**
* Returns the gcd (greatest common divisor) of the differences between the elements of long array
* a.
*
* @param a array of operands
* @return greatest common divisor of the differences between the elements of a
*/
@Pure
@StaticallyExecutable
public static long gcdDifferences(long[] a) {
if (a.length < 2) {
return 0;
}
long result = a[1] - a[0];
for (int i = 2; i < a.length; i++) {
result = gcd(a[i] - a[i - 1], result);
if ((result == 1) || (result == 0)) {
return result;
}
}
return result;
}
/**
* Returns the greatest common divisor of the two arguments.
*
* @param a first operand
* @param b second operand
* @return greatest common divisor of a and b
*/
@Pure
@StaticallyExecutable
public static double gcd(double a, double b) {
if (a == Double.POSITIVE_INFINITY
|| a == Double.NEGATIVE_INFINITY
|| Double.isNaN(a)
|| b == Double.POSITIVE_INFINITY
|| b == Double.NEGATIVE_INFINITY
|| Double.isNaN(b)) {
return Double.NaN;
}
// Euclid's method
if (b == 0) {
return Math.abs(a);
}
a = Math.abs(a);
b = Math.abs(b);
while (b != 0) {
double tmp = b;
b = a % b;
a = tmp;
}
return a;
}
/**
* Returns the greatest common divisor of the elements of double array a.
*
* @param a array of operands
* @return greatest common divisor of the elements of a
*/
@Pure
@StaticallyExecutable
public static double gcd(double[] a) {
if (a.length == 0) {
return 0;
}
double result = a[0];
for (int i = 1; i < a.length; i++) {
result = gcd(a[i], result);
if ((result == 1) || (result == 0)) {
return result;
}
}
return result;
}
/**
* Returns the gcd (greatest common divisor) of the differences between the elements of double
* array a.
*
* @param a array of operands
* @return greatest common divisor of the differences between the elements of a
*/
@Pure
@StaticallyExecutable
public static double gcdDifferences(double[] a) {
if (a.length < 2) {
return 0;
}
double result = a[1] - a[0];
for (int i = 2; i < a.length; i++) {
result = gcd(a[i] - a[i - 1], result);
if ((result == 1) || (result == 0)) {
return result;
}
}
return result;
}
///
/// Modulus
///
/**
* Returns z such that {@code (z == x mod y) && (0 <= z < abs(y))}. This should really be named
* {@code modNonnegative} rather than {@code modPositive}.
*
* @param x value to be modded
* @param y modulus
* @return x % y, where the result is constrained to be non-negative
* @deprecated use {@link #modNonnegative(int, int)}
*/
@Deprecated // 2020-02-20
// @InlineMe(replacement = "MathPlume.modNonnegative(x, y)", imports =
// "org.plumelib.util.MathPlume")
@Pure
@StaticallyExecutable
public static @NonNegative @LessThan("#2") @PolyUpperBound int modPositive(
int x, @PolyUpperBound int y) {
return modNonnegative(x, y);
}
/**
* Returns z such that {@code (z == x mod y) && (0 <= z < abs(y))}.
*
* @param x value to be modded
* @param y modulus
* @return x % y, where the result is constrained to be non-negative
*/
@SuppressWarnings({
"lessthan:return",
"lowerbound:return",
"index:return"
}) // result is non-negative because either y is positive (-> x % y is non-negative)
// or |y| is added to x % y, which is also non-negative
@Pure
@StaticallyExecutable
public static @NonNegative @LessThan("#2") @PolyUpperBound int modNonnegative(
int x, @PolyUpperBound int y) {
int result = x % y;
if (result < 0) {
result += Math.abs(y);
}
return result;
}
/**
* Returns an array of two integers (r,m) such that each number in NUMS is equal to r (mod m). The
* largest possible modulus is used, and the trivial constraint that all integers are equal to 0
* mod 1 is not returned (null is returned instead). Also, return null if the array is less than 3
* elements long.
*
* @param nums array of operands
* @return an array of two integers (r,m) such that each number in NUMS is equal to r (mod m), or
* null if no such exists or the iterator contains fewer than 3 elements
*/
@SuppressWarnings("value:statically.executable.not.pure") // results are .equals() but not ==
@SideEffectFree
@StaticallyExecutable
public static int @Nullable @ArrayLen(2) [] modulus(int[] nums) {
if (nums.length < 3) {
return null;
}
int modulus = Math.abs(gcdDifferences(nums));
if ((modulus == 0) || (modulus == 1)) {
return null;
}
int remainder = nums[0] % modulus;
if (remainder < 0) {
remainder += modulus;
}
return new int[] {remainder, modulus};
}
/**
* The iterator produces Integer values. This can be more efficient than modulus(int[]) if the
* int[] doesn't already exist, because this does not necessarily examine every value produced by
* its iterator.
*
* @param itor iterator of operands; modified by this method
* @return an array of two integers (r,m) such that each number in itor is equal to r (mod m), or
* null if no such exists or the iterator contains fewer than 3 elements
* @see #modulus(int[])
*/
public static int @Nullable @ArrayLen(2) [] modulusInt(Iterator itor) {
if (!itor.hasNext()) {
return null;
}
int avalue = itor.next().intValue();
if (!itor.hasNext()) {
return null;
}
int modulus = Math.abs(avalue - itor.next().intValue());
if (modulus == 1) {
return null;
}
int count = 2;
while (itor.hasNext()) {
int i = itor.next().intValue();
if (i == avalue) {
continue;
}
modulus = MathPlume.gcd(modulus, Math.abs(avalue - i));
count++;
if (modulus == 1) {
return null;
}
}
if (count < 3) {
return null;
}
return new int[] {MathPlume.modNonnegative(avalue, modulus), modulus};
}
/**
* Returns an array of two integers (r,m) such that each number in NUMS is equal to r (mod m). The
* largest possible modulus is used, and the trivial constraint that all integers are equal to 0
* mod 1 is not returned (null is returned instead).
*
* This "Strict" version requires its input to be sorted, and no element may be missing.
*
*
This "Strict" version differs from the regular modulus by requiring that the argument be
* dense: that is, every pair of numbers in the argument array is separated by exactly the
* modulus.
*
*
The endpoints can be treated in two different ways: Either exactly like other numbers in the
* input, or they can merely be checked for the condition without the strict density requirement.
*
* @param nums array of operands
* @param nonstrictEnds whether endpoints are NOT subject to the strict density requirement
* @return an array of two integers (r,m) such that each number in NUMS is equal to r (mod m), or
* null if no such exists or the array contains fewer than 3 elements
*/
@SuppressWarnings("value:statically.executable.not.pure") // results are .equals() but not ==
@SideEffectFree
@StaticallyExecutable
public static int @Nullable @ArrayLen(2) [] modulusStrict(int[] nums, boolean nonstrictEnds) {
if (nums.length < 3) {
return null;
}
int firstIndex = 0;
int lastIndex = nums.length - 1;
int firstNonstrict = 0; // arbitrary initial value
int lastNonstrict = 0; // arbitrary initial value
if (nonstrictEnds) {
firstNonstrict = nums[firstIndex];
firstIndex++;
lastNonstrict = nums[lastIndex];
lastIndex--;
}
if (lastIndex - firstIndex < 2) {
return null;
}
int modulus = nums[firstIndex + 1] - nums[firstIndex];
if (modulus == 1) {
return null;
}
for (int i = firstIndex + 2; i <= lastIndex; i++) {
if (nums[i] - nums[i - 1] != modulus) {
return null;
}
}
int r = MathPlume.modNonnegative(nums[firstIndex], modulus);
if (nonstrictEnds) {
if ((r != modNonnegative(firstNonstrict, modulus))
|| (r != modNonnegative(lastNonstrict, modulus))) {
return null;
}
}
return new int[] {r, modulus};
}
/**
* The iterator produces Integer values. This can be more efficient than modulus(int[]) if the
* int[] doesn't already exist, because this does not necessarily examine every value produced by
* its iterator.
*
*
For documentation, see {@link #modulusStrict(int[], boolean)}.
*
* @param itor iterator of operands; modified by this method
* @param nonstrictEnds whether endpoints are NOT subject to the strict density requirement
* @return an array of two integers (r,m) such that each number in NUMS is equal to r (mod m), or
* null if no such exists or the iterator contains fewer than 3 elements
* @see #modulusStrict(int[], boolean)
*/
public static int @Nullable @ArrayLen(2) [] modulusStrictInt(
Iterator itor, boolean nonstrictEnds) {
if (!itor.hasNext()) {
return null;
}
int firstNonstrict = 0; // arbitrary initial value
int lastNonstrict = 0; // arbitrary initial value
if (nonstrictEnds) {
firstNonstrict = itor.next().intValue();
if (!itor.hasNext()) {
return null;
}
}
int prev = itor.next().intValue();
if (!itor.hasNext()) {
return null;
}
int next = itor.next().intValue();
int modulus = next - prev;
if (modulus == 1) {
return null;
}
int count = 2;
while (itor.hasNext()) {
prev = next;
next = itor.next().intValue();
if (nonstrictEnds && !itor.hasNext()) {
lastNonstrict = next;
break;
}
if (next - prev != modulus) {
return null;
}
count++;
}
if (count < 3) {
return null;
}
int r = MathPlume.modNonnegative(next, modulus);
if (nonstrictEnds) {
if ((r != modNonnegative(firstNonstrict, modulus))
|| (r != modNonnegative(lastNonstrict, modulus))) {
return null;
}
}
return new int[] {r, modulus};
}
/// modulus for long (as opposed to int) values
/**
* Returns z such that {@code (z == x mod y) && (0 <= z < abs(y))}. This should really be named
* {@code modNonnegative} rather than {@code modPositive}.
*
* @param x value to be modded
* @param y modulus
* @return x % y, where the result is constrained to be non-negative
* @deprecated use {@link #modNonnegative(long, long)}
*/
@Deprecated // 2020-02-20
// @InlineMe(replacement = "MathPlume.modNonnegative(x, y)", imports =
// "org.plumelib.util.MathPlume")
@Pure
@StaticallyExecutable
public static @NonNegative @LessThan("#2") @PolyUpperBound long modPositive(
long x, @PolyUpperBound long y) {
return modNonnegative(x, y);
}
/**
* Returns z such that {@code (z == x mod y) && (0 <= z < abs(y))}.
*
* @param x value to be modded
* @param y modulus
* @return x % y, where the result is constrained to be non-negative
*/
@SuppressWarnings({
"lessthan:return",
"lowerbound:return",
"index:return"
}) // result is non-negative because either y is positive (-> x % y is non-negative) or
// |y| is added to x % y, which is also non-negative
@Pure
@StaticallyExecutable
public static @NonNegative @LessThan("#2") @PolyUpperBound long modNonnegative(
long x, @PolyUpperBound long y) {
long result = x % y;
if (result < 0) {
result += Math.abs(y);
}
return result;
}
/**
* Returns an array of two integers (r,m) such that each number in NUMS is equal to r (mod m). The
* largest possible modulus is used, and the trivial constraint that all integers are equal to 0
* mod 1 is not returned (null is returned instead). Also, return null if the array is less than 3
* elements long.
*
* @param nums array of operands
* @return an array of two integers (r,m) such that each number in NUMS is equal to r (mod m), or
* null if no such exists or the iterator contains fewer than 3 elements
*/
@SuppressWarnings("value:statically.executable.not.pure") // results are .equals() but not ==
@SideEffectFree
@StaticallyExecutable
public static long @Nullable @ArrayLen(2) [] modulus(long[] nums) {
if (nums.length < 3) {
return null;
}
long modulus = Math.abs(gcdDifferences(nums));
if ((modulus == 0) || (modulus == 1)) {
return null;
}
long remainder = nums[0] % modulus;
if (remainder < 0) {
remainder += modulus;
}
return new long[] {remainder, modulus};
}
/**
* The iterator produces Long values. This can be more efficient than modulus(long[]) if the
* long[] doesn't already exist, because this does not necessarily examine every value produced by
* its iterator.
*
* @param itor iterator of operands; modified by this method
* @return an array of two integers (r,m) such that each number in itor is equal to r (mod m), or
* null if no such exists or the iterator contains fewer than 3 elements
* @see #modulus(long[])
*/
public static long @Nullable @ArrayLen(2) [] modulusLong(Iterator itor) {
if (!itor.hasNext()) {
return null;
}
long avalue = itor.next().longValue();
if (!itor.hasNext()) {
return null;
}
long modulus = Math.abs(avalue - itor.next().longValue());
if (modulus == 1) {
return null;
}
int count = 2;
while (itor.hasNext()) {
long i = itor.next().longValue();
if (i == avalue) {
continue;
}
modulus = MathPlume.gcd(modulus, Math.abs(avalue - i));
count++;
if (modulus == 1) {
return null;
}
}
if (count < 3) {
return null;
}
return new long[] {MathPlume.modNonnegative(avalue, modulus), modulus};
}
/**
* Returns an array of two integers (r,m) such that each number in NUMS is equal to r (mod m). The
* largest possible modulus is used, and the trivial constraint that all integers are equal to 0
* mod 1 is not returned (null is returned instead).
*
* This "Strict" version requires its input to be sorted, and no element may be missing.
*
*
This "Strict" version differs from the regular modulus by requiring that the argument be
* dense: that is, every pair of numbers in the argument array is separated by exactly the
* modulus.
*
*
The endpoints can be treated in two different ways: Either exactly like other numbers in the
* input, or they can merely be checked for the condition without the strict density requirement.
*
* @param nums array of operands
* @param nonstrictEnds whether endpoints are NOT subject to the strict density requirement
* @return an array of two integers (r,m) such that each number in NUMS is equal to r (mod m), or
* null if no such exists or the array contains fewer than 3 elements
*/
@SuppressWarnings("value:statically.executable.not.pure") // results are .equals() but not ==
@SideEffectFree
@StaticallyExecutable
public static long @Nullable @ArrayLen(2) [] modulusStrict(long[] nums, boolean nonstrictEnds) {
if (nums.length < 3) {
return null;
}
int firstIndex = 0;
int lastIndex = nums.length - 1;
long firstNonstrict = 0; // arbitrary initial value
long lastNonstrict = 0; // arbitrary initial value
if (nonstrictEnds) {
firstNonstrict = nums[firstIndex];
firstIndex++;
lastNonstrict = nums[lastIndex];
lastIndex--;
}
if (lastIndex - firstIndex < 2) {
return null;
}
long modulus = nums[firstIndex + 1] - nums[firstIndex];
if (modulus == 1) {
return null;
}
for (int i = firstIndex + 2; i <= lastIndex; i++) {
if (nums[i] - nums[i - 1] != modulus) {
return null;
}
}
long r = modNonnegative(nums[firstIndex], modulus);
if (nonstrictEnds) {
if ((r != modNonnegative(firstNonstrict, modulus))
|| (r != modNonnegative(lastNonstrict, modulus))) {
return null;
}
}
return new long[] {r, modulus};
}
/**
* The iterator produces Long values. This can be more efficient than modulus(long[]) if the
* long[] doesn't already exist, because this does not necessarily examine every value produced by
* its iterator.
*
*
For documentation, see {@link #modulusStrict(long[], boolean)}.
*
* @param itor iterator of operands; modified by this method
* @param nonstrictEnds whether endpoints are NOT subject to the strict density requirement
* @return an array of two integers (r,m) such that each number in NUMS is equal to r (mod m), or
* null if no such exists or the iterator contains fewer than 3 elements
* @see #modulusStrict(int[], boolean)
*/
public static long @Nullable @ArrayLen(2) [] modulusStrictLong(
Iterator itor, boolean nonstrictEnds) {
if (!itor.hasNext()) {
return null;
}
long firstNonstrict = 0; // arbitrary initial value
long lastNonstrict = 0; // arbitrary initial value
if (nonstrictEnds) {
firstNonstrict = itor.next().longValue();
if (!itor.hasNext()) {
return null;
}
}
long prev = itor.next().longValue();
if (!itor.hasNext()) {
return null;
}
long next = itor.next().longValue();
long modulus = next - prev;
if (modulus == 1 || modulus == 0) {
return null;
}
int count = 2;
while (itor.hasNext()) {
prev = next;
next = itor.next().longValue();
if (nonstrictEnds && !itor.hasNext()) {
lastNonstrict = next;
break;
}
if (next - prev != modulus) {
return null;
}
count++;
}
if (count < 3) {
if (nonstrictEnds) {
if (count == 2) {
return modulusLong(
Arrays.stream(new Long[] {firstNonstrict, prev - modulus, prev, next}).iterator());
} else if (count == 1) {
return modulusLong(Arrays.stream(new Long[] {firstNonstrict, prev, next}).iterator());
}
}
return null;
}
long r = MathPlume.modNonnegative(next, modulus);
if (nonstrictEnds) {
if ((r != modNonnegative(firstNonstrict, modulus))
|| (r != modNonnegative(lastNonstrict, modulus))) {
return null;
}
}
return new long[] {r, modulus};
}
///
/// Non-Modulus
///
/**
* Returns an array containing all the numbers not in its argument array (which must be
* non-empty) but in the argument's range; that is, bigger than its argument's minimum value and
* smaller than its argument's maximum value. The result contains no duplicates and is in order.
*
* @param nums numbers to be excluded; length > 0; may contain duplicates
* @return the set: [min(nums)..max(nums)] - nums
*/
@SuppressWarnings({"allcheckers:purity", "lock"})
@Pure
@StaticallyExecutable
public static int[] missingNumbers(int @MinLen(1) [] nums) {
// avoid modifying parameter
nums = nums.clone();
Arrays.sort(nums);
int min = nums[0];
int max = nums[nums.length - 1];
int sizeEstimate = max - min + 1 - nums.length;
List resultList = new ArrayList<>(sizeEstimate < 1 ? 1 : sizeEstimate);
int val = min;
for (int i = 0; i < nums.length; i++) {
while (val < nums[i]) {
resultList.add(val);
val++;
}
if (val == nums[i]) {
val++;
}
}
int[] resultArray = new int[resultList.size()];
for (int i = 0; i < resultArray.length; i++) {
resultArray[i] = resultList.get(i).intValue();
}
return resultArray;
}
/**
* This iterator returns all the numbers *not* in its argument array (which must be non-empty) but
* in the argument's range; that is, bigger than its argument's minimum value and smaller than its
* argument's maximum value. The result contains no duplicates and is in order. If boolean addEnds
* is set, then the bracketing endpoints are also returned; otherwise, all returned values are
* between the minimum and maximum of the original values.
*/
static final class MissingNumbersIteratorInt implements Iterator {
// Exactly one of nums and numsItor is non-null.
/** The numbers not to include in the iterator. */
int @MonotonicNonNull @MinLen(1) [] nums;
/** The numbers not to include in the iterator. */
@MonotonicNonNull Iterator numsItor;
/** The current element of the numbers not to include in the iterator. */
int currentNonmissing;
/** The next element to be returned by the iterator. */
int currentMissing;
/** Used only if nums != null, in which case it is an index into nums. */
@IndexFor("nums") int currentIndex;
/**
* If true, include the value just before the minimum excluded element and the value just after
* the maximum excluded element.
*/
boolean addEnds;
/**
* An iterator over all the numbers not in the argument array, but within its range.
*
* @param nums a non-empty array
* @param addEnds if true, include the bracketing endpoints
*/
MissingNumbersIteratorInt(int @MinLen(1) [] nums, boolean addEnds) {
this.addEnds = addEnds;
{ // avoid modifying parameter
int[] numsCopy = new int[nums.length];
System.arraycopy(nums, 0, numsCopy, 0, nums.length);
nums = numsCopy;
}
Arrays.sort(nums);
this.nums = nums;
currentIndex = 0;
currentNonmissing = nums[currentIndex];
if (addEnds) {
currentMissing = currentNonmissing - 1;
} else {
currentMissing = currentNonmissing;
}
}
/**
* An iterator over all the numbers not in the argument iterator, but within its range.
*
* @param numsItor a non-empty iterator; it must return integers in sorted order
* @param addEnds if true, include the bracketing endpoints
*/
MissingNumbersIteratorInt(Iterator numsItor, boolean addEnds) {
this.addEnds = addEnds;
if (!numsItor.hasNext()) {
throw new Error("No elements in numsItor");
}
currentNonmissing = numsItor.next().intValue();
if (addEnds) {
currentMissing = currentNonmissing - 1;
} else {
currentMissing = currentNonmissing;
}
this.numsItor = numsItor;
@IndexFor("nums") int unused = Integer.MIN_VALUE;
currentIndex = unused;
}
@SuppressWarnings({
"allcheckers:purity", // benevolent side effects
"lock:method.guarantee.violated"
})
@Override
public boolean hasNext(@GuardSatisfied MissingNumbersIteratorInt this) {
if (currentMissing < currentNonmissing) {
return true;
}
// This loop ("while" instead of "if") permits duplicates in nums.
while (currentMissing == currentNonmissing) {
if (nums != null) {
@SuppressWarnings(
"index:assignment" // This breaks the invariant, but it's checked right below and the
// function exits.
)
@IndexFor("nums") int currentIndexTmp = currentIndex + 1;
currentIndex = currentIndexTmp;
if (currentIndex >= nums.length) {
if (addEnds) {
currentMissing++;
return true;
} else {
return false;
}
}
currentNonmissing = nums[currentIndex];
} else if (numsItor != null) {
if (!numsItor.hasNext()) {
if (addEnds) {
currentMissing++;
return true;
} else {
return false;
}
}
// prevNonmissing is for testing only
int prevNonmissing = currentNonmissing;
currentNonmissing = numsItor.next().intValue();
if (!(prevNonmissing < currentNonmissing)) {
throw new Error(
"Non-sorted Iterator supplied to MissingNumbersIteratorInt: prevNonmissing = "
+ prevNonmissing
+ ", currentNonmissing = "
+ currentNonmissing);
}
} else {
throw new Error("Can't happen");
}
currentMissing++;
return hasNext();
}
if (addEnds) {
return (currentMissing == currentNonmissing + 1);
} else {
throw new Error("Can't happen: " + currentMissing + " " + currentNonmissing);
}
}
@Override
public Integer next(@GuardSatisfied MissingNumbersIteratorInt this) {
if (!hasNext()) {
throw new NoSuchElementException();
}
Integer result = currentMissing;
currentMissing++;
return result;
}
@Override
public void remove(@GuardSatisfied MissingNumbersIteratorInt this) {
throw new UnsupportedOperationException();
}
}
/**
* Returns a tuple of (r,m) where no number in NUMS is equal to r (mod m) but all missing numbers
* in their range are. Returns null if the input array has 0 length.
*
* @param nums the list of operands
* @return a (remainder, modulus) pair that fails to match elements of nums
*/
@SuppressWarnings({"allcheckers:purity", "lock"})
@Pure
@StaticallyExecutable
public static int @Nullable @ArrayLen(2) [] nonmodulusStrict(int[] nums) {
// This implementation is particularly inefficient; find a better way to
// compute this. Perhaps obtain the new modulus numbers incrementally
// instead of all at once.
if (nums.length == 0) {
return null;
}
int range = ArraysPlume.elementRange(nums);
if (range > 65536) {
return null;
}
return nonmodulusStrictIntInternal(new MissingNumbersIteratorInt(nums, true));
}
/**
* Helper for {@link #nonmodulusStrict(int[])}.
*
* @param missing the missing integers; modified by this method
* @return value to be returned by {@link #nonmodulusStrict(int[])}: a tuple of (r,m) where all
* numbers in {@code missing} are equal to r (mod m)
*/
private static int @Nullable @ArrayLen(2) [] nonmodulusStrictIntInternal(
Iterator missing) {
// Must not use regular modulus: that can produce errors, eg
// nonmodulusStrict({1,2,3,5,6,7,9,11}) => {0,2}. Thus, use
// modulusStrict.
CollectionsPlume.RemoveFirstAndLastIterator missingNums =
new CollectionsPlume.RemoveFirstAndLastIterator(missing);
int[] result = modulusStrictInt(missingNums, false);
if (result == null) {
return result;
}
if (!checkFirstAndLastNonmodulus(result, missingNums)) {
return null;
}
return result;
}
/**
* Return true if the first and last elements are not equal to r (mod m).
*
* @param rm a tuple of (r,m)
* @param rfali a sequence of numbers, plus a first and last element outside their range. This
* iterator has already been iterated all the way to its end.
* @return true if the first and last elements are not equal to r (mod m)
*/
private static boolean checkFirstAndLastNonmodulus(
int @ArrayLen(2) [] rm, CollectionsPlume.RemoveFirstAndLastIterator rfali) {
int r = rm[0];
int m = rm[1];
int first = rfali.getFirst().intValue();
int last = rfali.getLast().intValue();
return ((r != modNonnegative(first, m)) && (r != modNonnegative(last, m)));
}
/**
* Returns a tuple of (r,m) where no number in NUMS is equal to r (mod m) but all missing numbers
* in their range are.
*
* @param nums the list of operands
* @return a (remainder, modulus) pair that fails to match elements of nums
*/
public static int @Nullable @ArrayLen(2) [] nonmodulusStrictInt(Iterator nums) {
return nonmodulusStrictIntInternal(new MissingNumbersIteratorInt(nums, true));
}
// Old, slightly less efficient implementation that uses the version of
// missingNumbers that returns an array instead of an Iterator.
// /**
// * Returns a tuple of (r,m) where no number in NUMS is equal to r (mod
// * m) but all missing numbers in their range are.
// */
// public static int @Nullable @ArrayLen(2) [] nonmodulusStrict(int[] nums) {
// // This implementation is particularly inefficient; find a better way to
// // compute this. Perhaps obtain the new modulus numbers incrementally
// // instead of all at once.
// if (nums.length == 0) {
// return null;
// }
// int range = ArraysPlume.elementRange(nums);
// if (range > 65536) {
// return null;
// }
// return modulus(missingNumbers(nums));
// }
/**
* Returns a tuple of (r,m) where no number in NUMS is equal to r (mod m) but for every number in
* NUMS, at least one is equal to every non-r remainder. The modulus is chosen as small as
* possible, but no greater than half the range of the input numbers (else null is returned).
*
* @param nums the list of operands
* @return a (remainder, modulus) pair that fails to match elements of nums
*/
// This seems to give too many false positives (or maybe my probability
// model was wrong); use nonmodulusStrict instead.
@SuppressWarnings("allcheckers:purity")
@Pure
@StaticallyExecutable
public static int @Nullable @ArrayLen(2) [] nonmodulusNonstrict(int[] nums) {
if (nums.length < 4) {
return null;
}
int maxModulus = Math.min(nums.length / 2, ArraysPlume.elementRange(nums) / 2);
// System.out.println("nums.length=" + nums.length + ", range=" +
// ArraysPlume.elementRange(nums) + ", maxModulus=" + maxModulus);
// no real sense checking 2, as commonModulus would have found it, but
// include it to make this function stand on its own
for (int m = 2; m <= maxModulus; m++) {
// System.out.println("Trying m=" + m);
boolean[] hasModulus = new boolean[m]; // initialized to false?
int numNonmodulus = m;
for (int i = 0; i < nums.length; i++) {
@IndexFor("hasModulus") int rem = modNonnegative(nums[i], m);
if (!hasModulus[rem]) {
hasModulus[rem] = true;
numNonmodulus--;
// System.out.println("rem=" + rem + " for " + nums[i] + "; numNonmodulus=" +
// numNonmodulus);
if (numNonmodulus == 0) {
// Quit as soon as we see every remainder instead of processing
// each element of the input list.
break;
}
}
}
// System.out.println("For m=" + m + ", numNonmodulus=" + numNonmodulus);
if (numNonmodulus == 1) {
return new int[] {ArraysPlume.indexOf(hasModulus, false), m};
}
}
return null;
}
/// non-modulus for long (as opposed to int) values
/**
* Returns an array containing all the numbers not in its argument array (which must be
* non-empty) but in the argument's range; that is, bigger than its argument's minimum value and
* smaller than its argument's maximum value. The result contains no duplicates and is in order.
*
* @param nums numbers to be excluded; length > 0; may contain duplicates
* @return the set: [min(nums)..max(nums)] - nums
*/
@SuppressWarnings({"allcheckers:purity", "lock"})
@Pure
@StaticallyExecutable
public static long[] missingNumbers(long @MinLen(1) [] nums) {
// avoid modifying parameter
nums = nums.clone();
Arrays.sort(nums);
long min = nums[0];
long max = nums[nums.length - 1];
int sizeEstimate = ((int) (max - min + 1 - nums.length));
List resultList = new ArrayList<>(sizeEstimate < 1 ? 1 : sizeEstimate);
long val = min;
for (int i = 0; i < nums.length; i++) {
while (val < nums[i]) {
resultList.add(val);
val++;
}
if (val == nums[i]) {
val++;
}
}
long[] resultArray = new long[resultList.size()];
for (int i = 0; i < resultArray.length; i++) {
resultArray[i] = resultList.get(i).longValue();
}
return resultArray;
}
/**
* This iterator returns all the numbers *not* in its argument array (which must be non-empty) but
* in the argument's range; that is, bigger than its argument's minimum value and smaller than its
* argument's maximum value. The result contains no duplicates and is in order. If boolean addEnds
* is set, then the bracketing endpoints are also returned; otherwise, all returned values are
* between the minimum and maximum of the original values.
*/
static final class MissingNumbersIteratorLong implements Iterator {
// Exactly one of nums and numsItor is non-null.
/** The numbers not to include in the iterator. */
long @MonotonicNonNull @MinLen(1) [] nums;
/** The numbers not to include in the iterator. */
@MonotonicNonNull Iterator numsItor;
/** The current element of the numbers not to include in the iterator. */
long currentNonmissing;
/** The next element to be returned by the iterator. */
long currentMissing;
/** Used only if nums != null, in which case it is an index into nums. */
@IndexFor("nums") int currentIndex;
/**
* If true, include the value just before the minimum excluded element and the value just after
* the maximum excluded element.
*/
boolean addEnds;
/**
* An iterator over all the numbers not in its original argument array, but within its
* range.
*
* @param nums a non-empty array
* @param addEnds if true, include the bracketing endpoints
*/
MissingNumbersIteratorLong(long @MinLen(1) [] nums, boolean addEnds) {
this.addEnds = addEnds;
{ // avoid modifying parameter
long[] numsCopy = new long[nums.length];
System.arraycopy(nums, 0, numsCopy, 0, nums.length);
nums = numsCopy;
}
Arrays.sort(nums);
this.nums = nums;
currentIndex = 0;
currentNonmissing = nums[currentIndex];
if (addEnds) {
currentMissing = currentNonmissing - 1;
} else {
currentMissing = currentNonmissing;
}
}
/**
* An iterator over all the numbers not in its argument iterator, but within its range.
*
* @param numsItor a non-empty array; must return longs in sorted order
* @param addEnds if true, include the bracketing endpoints
*/
MissingNumbersIteratorLong(Iterator numsItor, boolean addEnds) {
this.addEnds = addEnds;
if (!numsItor.hasNext()) {
throw new Error("No elements in numsItor");
}
currentNonmissing = numsItor.next().longValue();
if (addEnds) {
currentMissing = currentNonmissing - 1;
} else {
currentMissing = currentNonmissing;
}
this.numsItor = numsItor;
@IndexFor("nums") int unused = Integer.MIN_VALUE;
currentIndex = unused;
}
@SuppressWarnings({
"allcheckers:purity", // benevolent side effects
"lock:method.guarantee.violated"
})
@Override
public boolean hasNext(@GuardSatisfied MissingNumbersIteratorLong this) {
if (currentMissing < currentNonmissing) {
return true;
}
// This loop ("while" instead of "if") permits duplicates in nums.
while (currentMissing == currentNonmissing) {
if (nums != null) {
@SuppressWarnings(
"index:assignment" // This breaks the invariant, but it's checked right below and the
// function exits.
)
@IndexFor("nums") int currentIndexTmp = currentIndex + 1;
currentIndex = currentIndexTmp;
if (currentIndex >= nums.length) {
if (addEnds) {
currentMissing++;
return true;
} else {
return false;
}
}
currentNonmissing = nums[currentIndex];
} else if (numsItor != null) {
if (!numsItor.hasNext()) {
if (addEnds) {
currentMissing++;
return true;
} else {
return false;
}
}
// prevNonmissing is for testing only
long prevNonmissing = currentNonmissing;
currentNonmissing = numsItor.next().longValue();
if (!(prevNonmissing < currentNonmissing)) {
throw new Error(
"Non-sorted Iterator supplied to MissingNumbersIteratorLong: prevNonmissing = "
+ prevNonmissing
+ ", currentNonmissing = "
+ currentNonmissing);
}
} else {
throw new Error("Can't happen");
}
currentMissing++;
return hasNext();
}
if (addEnds) {
return (currentMissing == currentNonmissing + 1);
} else {
throw new Error("Can't happen: " + currentMissing + " " + currentNonmissing);
}
}
@Override
public Long next(@GuardSatisfied MissingNumbersIteratorLong this) {
if (!hasNext()) {
throw new NoSuchElementException();
}
Long result = currentMissing;
currentMissing++;
return result;
}
@Override
public void remove(@GuardSatisfied MissingNumbersIteratorLong this) {
throw new UnsupportedOperationException();
}
}
/**
* Returns a tuple of (r,m) where no number in NUMS is equal to r (mod m) but all missing numbers
* in their range are. Returns null if the input array has 0 length.
*
* @param nums the list of operands
* @return a (remainder, modulus) pair that fails to match elements of nums
*/
@SuppressWarnings({"allcheckers:purity", "lock"})
@Pure
@StaticallyExecutable
public static long @Nullable @ArrayLen(2) [] nonmodulusStrict(long[] nums) {
// This implementation is particularly inefficient; find a better way to
// compute this. Perhaps obtain the new modulus numbers incrementally
// instead of all at once.
if (nums.length == 0) {
return null;
}
long range = ArraysPlume.elementRange(nums);
if (range > 65536) {
return null;
}
return nonmodulusStrictLongInternal(new MissingNumbersIteratorLong(nums, true));
}
/**
* Helper for {@link #nonmodulusStrict(long[])}.
*
* @param missing the missing integers; modified by this method
* @return value to be returned by {@link #nonmodulusStrict(long[])}
*/
private static long @Nullable @ArrayLen(2) [] nonmodulusStrictLongInternal(
Iterator missing) {
// Must not use regular modulus: that can produce errors, eg
// nonmodulusStrict({1,2,3,5,6,7,9,11}) => {0,2}. Thus, use
// modulusStrict.
CollectionsPlume.RemoveFirstAndLastIterator missingNums =
new CollectionsPlume.RemoveFirstAndLastIterator(missing);
long[] result = modulusStrictLong(missingNums, false);
if (result == null) {
return result;
}
if (!checkFirstAndLastNonmodulus(result, missingNums)) {
return null;
}
return result;
}
/**
* Return true if the first and last elements are equal to r (mod m).
*
* @param rm an array containing two elements
* @param rfali a sequence of numbers, plus a first and last element outside their range. This
* iterator has already been iterated all the way to its end.
* @return true if the first and last elements are equal to r (mod m)
*/
@Pure
private static boolean checkFirstAndLastNonmodulus(
long @ArrayLen(2) [] rm, CollectionsPlume.RemoveFirstAndLastIterator rfali) {
long r = rm[0];
long m = rm[1];
long first = rfali.getFirst().longValue();
long last = rfali.getLast().longValue();
return ((r != modNonnegative(first, m)) && (r != modNonnegative(last, m)));
}
/**
* Returns a tuple of (r,m) where no number in NUMS is equal to r (mod m) but all missing numbers
* in their range are.
*
* @param nums the list of operands
* @return a (remainder, modulus) pair that fails to match elements of nums
*/
public static long @Nullable @ArrayLen(2) [] nonmodulusStrictLong(Iterator nums) {
return nonmodulusStrictLongInternal(new MissingNumbersIteratorLong(nums, true));
}
// Old, slightly less efficient implementation that uses the version of
// missingNumbers that returns an array instead of an Iterator.
// /**
// * Returns a tuple of (r,m) where no number in NUMS is equal to r (mod
// * m) but all missing numbers in their range are.
// */
// public static long @Nullable @ArrayLen(2) [] nonmodulusStrict(long[] nums) {
// // This implementation is particularly inefficient; find a better way to
// // compute this. Perhaps obtain the new modulus numbers incrementally
// // instead of all at once.
// if (nums.length == 0) {
// return null;
// }
// long range = ArraysPlume.elementRange(nums);
// if (range > 65536) {
// return null;
// }
// return modulus(missingNumbers(nums));
// }
/**
* Returns a tuple of (r,m) where no number in NUMS is equal to r (mod m) but for every number in
* NUMS, at least one is equal to every non-r remainder. The modulus is chosen as small as
* possible, but no greater than half the range of the input numbers (else null is returned).
*
* @param nums the list of operands
* @return a (remainder, modulus) pair that fails to match elements of nums
*/
// This seems to give too many false positives (or maybe my probability
// model was wrong); use nonmodulusStrict instead.
@SuppressWarnings("allcheckers:purity")
@Pure
@StaticallyExecutable
public static long @Nullable @ArrayLen(2) [] nonmodulusNonstrict(long[] nums) {
if (nums.length < 4) {
return null;
}
int maxModulus = (int) Math.min(nums.length / 2, ArraysPlume.elementRange(nums) / 2);
// System.out.println("nums.length=" + nums.length + ", range=" +
// ArraysPlume.elementRange(nums) + ", maxModulus=" + maxModulus);
// no real sense checking 2, as commonModulus would have found it, but
// include it to make this function stand on its own
for (int m = 2; m <= maxModulus; m++) {
// System.out.println("Trying m=" + m);
boolean[] hasModulus = new boolean[m]; // initialized to false?
int numNonmodulus = m;
for (int i = 0; i < nums.length; i++) {
@IndexFor("hasModulus") int rem = (int) modNonnegative(nums[i], m);
if (!hasModulus[rem]) {
hasModulus[rem] = true;
numNonmodulus--;
// System.out.println("rem=" + rem + " for " + nums[i] + "; numNonmodulus=" +
// numNonmodulus);
if (numNonmodulus == 0) {
// Quit as soon as we see every remainder instead of processing
// each element of the input list.
break;
}
}
}
// System.out.println("For m=" + m + ", numNonmodulus=" + numNonmodulus);
if (numNonmodulus == 1) {
return new long[] {ArraysPlume.indexOf(hasModulus, false), m};
}
}
return null;
}
}