Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#7430 : Implement MultiFactorial methods #7433

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -790,6 +790,33 @@ public void testNullPointers() {
tester.testAllPublicStaticMethods(BigIntegerMath.class);
}

public void testMultifactorialBasic() {
assertEquals(BigInteger.valueOf(105), BigIntegerMath.multiFactorial(7, 2)); // 7 * 5 * 3 * 1
assertEquals(BigInteger.valueOf(28), BigIntegerMath.multiFactorial(7, 3)); // 7 * 4 * 1
assertEquals(BigInteger.ONE, BigIntegerMath.multiFactorial(0, 1)); // Base case
assertEquals(BigInteger.ONE, BigIntegerMath.multiFactorial(1, 1)); // 1!
}

public void testMultifactorialWithIncrementOne() {
assertEquals(BigInteger.valueOf(120), BigIntegerMath.multiFactorial(5, 1)); // 5 * 4 * 3 * 2 * 1
}

public void testMultifactorialNegativeIncrement() {
Exception exception = assertThrows(IllegalArgumentException.class, () -> {
BigIntegerMath.multiFactorial(5, -1);
});
assertEquals("Step value k must be a positive integer.", exception.getMessage());
}

public void testMultifactorialWithLargeNumber() {
assertEquals(new BigInteger("3628800"), BigIntegerMath.multiFactorial(10, 1));
}

public void testMultifactorialWithLargeInput() {
BigInteger result = BigIntegerMath.multiFactorial(100, 10);
assertTrue(result.compareTo(BigInteger.ZERO) > 0); // Result should be positive
}

@GwtIncompatible // String.format
private static void failFormat(String template, Object... args) {
fail(String.format(template, args));
Expand Down
27 changes: 27 additions & 0 deletions android/guava-tests/test/com/google/common/math/IntMathTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -771,6 +771,33 @@ public void testIsPrime() {
}
}

public void testMultifactorialBasic() {
assertEquals(105, IntMath.multiFactorial(7, 2)); // 7 * 5 * 3 * 1
assertEquals(28, IntMath.multiFactorial(7, 3)); // 7 * 4 * 1
assertEquals(1, IntMath.multiFactorial(0, 1)); // Base case
assertEquals(1, IntMath.multiFactorial(1, 1)); // 1!
}

public void testMultifactorialWithIncrementOne() {
assertEquals(120, IntMath.multiFactorial(5, 1)); // 5 * 4 * 3 * 2 * 1
}

public void testMultifactorialNegativeIncrement() {
Exception exception = assertThrows(IllegalArgumentException.class, () -> {
IntMath.multiFactorial(5, -1);
});
assertEquals("Step value k must be a positive integer.", exception.getMessage());
}

public void testMultifactorialWithLargeNumber() {
assertEquals(3628800, IntMath.multiFactorial(10, 1));
}

public void testMultifactorialWithLargeInput() {
int result = IntMath.multiFactorial(100, 10);
assertTrue(result > 0); // Result should be positive
}

private static int force32(int value) {
// GWT doesn't consistently overflow values to make them 32-bit, so we need to force it.
return value & 0xffffffff;
Expand Down
27 changes: 27 additions & 0 deletions android/guava-tests/test/com/google/common/math/LongMathTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -1025,6 +1025,33 @@ public void testRoundToDoubleAgainstBigIntegerUnnecessary() {
}
}

public void testMultifactorialBasic() {
assertEquals(105l, LongMath.multiFactorial(7, 2)); // 7 * 5 * 3 * 1
assertEquals(28l, LongMath.multiFactorial(7, 3)); // 7 * 4 * 1
assertEquals(1l, LongMath.multiFactorial(0, 1)); // Base case
assertEquals(1l, LongMath.multiFactorial(1, 1)); // 1!
}

public void testMultifactorialWithIncrementOne() {
assertEquals(120l, LongMath.multiFactorial(5, 1)); // 5 * 4 * 3 * 2 * 1
}

public void testMultifactorialNegativeIncrement() {
Exception exception = assertThrows(IllegalArgumentException.class, () -> {
LongMath.multiFactorial(5, -1);
});
assertEquals("Step value k must be a positive integer.", exception.getMessage());
}

public void testMultifactorialWithLargeNumber() {
assertEquals(3628800l, LongMath.multiFactorial(10, 1));
}

public void testMultifactorialWithLargeInput() {
long result = LongMath.multiFactorial(100, 10);
assertTrue(result > 0l); // Result should be positive
}

private static void failFormat(String template, Object... args) {
assertWithMessage(template, args).fail();
}
Expand Down
28 changes: 28 additions & 0 deletions guava-tests/test/com/google/common/math/BigIntegerMathTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import static java.math.RoundingMode.UP;
import static java.math.RoundingMode.values;
import static java.util.Arrays.asList;
import static org.junit.Assert.assertThrows;

import com.google.common.annotations.GwtCompatible;
import com.google.common.annotations.GwtIncompatible;
Expand Down Expand Up @@ -790,6 +791,33 @@ public void testNullPointers() {
tester.testAllPublicStaticMethods(BigIntegerMath.class);
}

public void testMultifactorialBasic() {
assertEquals(BigInteger.valueOf(105), BigIntegerMath.multiFactorial(7, 2)); // 7 * 5 * 3 * 1
assertEquals(BigInteger.valueOf(28), BigIntegerMath.multiFactorial(7, 3)); // 7 * 4 * 1
assertEquals(BigInteger.ONE, BigIntegerMath.multiFactorial(0, 1)); // Base case
assertEquals(BigInteger.ONE, BigIntegerMath.multiFactorial(1, 1)); // 1!
}

public void testMultifactorialWithIncrementOne() {
assertEquals(BigInteger.valueOf(120), BigIntegerMath.multiFactorial(5, 1)); // 5 * 4 * 3 * 2 * 1
}

public void testMultifactorialNegativeIncrement() {
Exception exception = assertThrows(IllegalArgumentException.class, () -> {
BigIntegerMath.multiFactorial(5, -1);
});
assertEquals("Step value k must be a positive integer.", exception.getMessage());
}

public void testMultifactorialWithLargeNumber() {
assertEquals(new BigInteger("3628800"), BigIntegerMath.multiFactorial(10, 1));
}

public void testMultifactorialWithLargeInput() {
BigInteger result = BigIntegerMath.multiFactorial(100, 10);
assertTrue(result.compareTo(BigInteger.ZERO) > 0); // Result should be positive
}

@GwtIncompatible // String.format
private static void failFormat(String template, Object... args) {
fail(String.format(template, args));
Expand Down
28 changes: 28 additions & 0 deletions guava-tests/test/com/google/common/math/IntMathTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import static java.math.BigInteger.valueOf;
import static java.math.RoundingMode.FLOOR;
import static java.math.RoundingMode.UNNECESSARY;
import static org.junit.Assert.assertThrows;

import com.google.common.annotations.GwtCompatible;
import com.google.common.annotations.GwtIncompatible;
Expand Down Expand Up @@ -771,6 +772,33 @@ public void testIsPrime() {
}
}

public void testMultifactorialBasic() {
assertEquals(105, IntMath.multiFactorial(7, 2)); // 7 * 5 * 3 * 1
assertEquals(28, IntMath.multiFactorial(7, 3)); // 7 * 4 * 1
assertEquals(1, IntMath.multiFactorial(0, 1)); // Base case
assertEquals(1, IntMath.multiFactorial(1, 1)); // 1!
}

public void testMultifactorialWithIncrementOne() {
assertEquals(120, IntMath.multiFactorial(5, 1)); // 5 * 4 * 3 * 2 * 1
}

public void testMultifactorialNegativeIncrement() {
Exception exception = assertThrows(IllegalArgumentException.class, () -> {
IntMath.multiFactorial(5, -1);
});
assertEquals("Step value k must be a positive integer.", exception.getMessage());
}

public void testMultifactorialWithLargeNumber() {
assertEquals(3628800, IntMath.multiFactorial(10, 1));
}

public void testMultifactorialWithLargeInput() {
int result = IntMath.multiFactorial(100, 10);
assertTrue(result > 0); // Result should be positive
}

private static int force32(int value) {
// GWT doesn't consistently overflow values to make them 32-bit, so we need to force it.
return value & 0xffffffff;
Expand Down
28 changes: 28 additions & 0 deletions guava-tests/test/com/google/common/math/LongMathTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import static java.math.BigInteger.valueOf;
import static java.math.RoundingMode.FLOOR;
import static java.math.RoundingMode.UNNECESSARY;
import static org.junit.Assert.assertThrows;

import com.google.common.annotations.GwtCompatible;
import com.google.common.annotations.GwtIncompatible;
Expand Down Expand Up @@ -1025,6 +1026,33 @@ public void testRoundToDoubleAgainstBigIntegerUnnecessary() {
}
}

public void testMultifactorialBasic() {
assertEquals(105l, LongMath.multiFactorial(7, 2)); // 7 * 5 * 3 * 1
assertEquals(28l, LongMath.multiFactorial(7, 3)); // 7 * 4 * 1
assertEquals(1l, LongMath.multiFactorial(0, 1)); // Base case
assertEquals(1l, LongMath.multiFactorial(1, 1)); // 1!
}

public void testMultifactorialWithIncrementOne() {
assertEquals(120l, LongMath.multiFactorial(5, 1)); // 5 * 4 * 3 * 2 * 1
}

public void testMultifactorialNegativeIncrement() {
Exception exception = assertThrows(IllegalArgumentException.class, () -> {
LongMath.multiFactorial(5, -1);
});
assertEquals("Step value k must be a positive integer.", exception.getMessage());
}

public void testMultifactorialWithLargeNumber() {
assertEquals(3628800l, LongMath.multiFactorial(10, 1));
}

public void testMultifactorialWithLargeInput() {
long result = LongMath.multiFactorial(100, 10);
assertTrue(result > 0l); // Result should be positive
}

private static void failFormat(String template, Object... args) {
assertWithMessage(template, args).fail();
}
Expand Down
24 changes: 24 additions & 0 deletions guava/src/com/google/common/math/BigIntegerMath.java
Original file line number Diff line number Diff line change
Expand Up @@ -521,5 +521,29 @@ static boolean fitsInLong(BigInteger x) {
return x.bitLength() <= Long.SIZE - 1;
}

/**
* Computes the multifactorial of a given non-negative integer {@code n}
* with a specified step {@code k}. The multifactorial is defined as the
* product of all integers from {@code n} down to 1, decrementing by {@code k}.
*
* @param n the non-negative integer for which to compute the multifactorial
* @param k the step value (must be a positive integer)
* @return the computed multifactorial of {@code n} with step {@code k}
* @throws IllegalArgumentException if {@code k} is less than or equal to 0
*/
public static BigInteger multiFactorial(int n, int k) {
if (k <= 0) {
throw new IllegalArgumentException("Step value k must be a positive integer.");
}
if (n <= 0) {
return BigInteger.ONE; // Base case for n <= 0
}
BigInteger result = BigInteger.ONE;
for (int i = n; i > 0; i -= k) {
result = result.multiply(BigInteger.valueOf(i));
}
return result;
}

private BigIntegerMath() {}
}
24 changes: 24 additions & 0 deletions guava/src/com/google/common/math/IntMath.java
Original file line number Diff line number Diff line change
Expand Up @@ -722,5 +722,29 @@ public static boolean isPrime(int n) {
return LongMath.isPrime(n);
}

/**
* Computes the multifactorial of a given non-negative integer {@code n}
* with a specified step {@code k}. The multifactorial is defined as the
* product of all integers from {@code n} down to 1, decrementing by {@code k}.
*
* @param n the non-negative integer for which to compute the multifactorial
* @param k the step value (must be a positive integer)
* @return the computed multifactorial of {@code n} with step {@code k}
* @throws IllegalArgumentException if {@code k} is less than or equal to 0
*/
public static int multiFactorial(int n, int k) {
if (k <= 0) {
throw new IllegalArgumentException("Step value k must be a positive integer.");
}
if (n <= 0) {
return 1; // Base case for n <= 0
}
int result = 1;
for (int i = n; i > 0; i -= k) {
result *= i;
}
return result;
}

private IntMath() {}
}
25 changes: 24 additions & 1 deletion guava/src/com/google/common/math/LongMath.java
Original file line number Diff line number Diff line change
Expand Up @@ -1346,5 +1346,28 @@ public static double roundToDouble(long x, RoundingMode mode) {
throw new AssertionError("impossible");
}

private LongMath() {}
/**
* Computes the multifactorial of a given non-negative integer {@code n}
* with a specified step {@code k}. The multifactorial is defined as the
* product of all integers from {@code n} down to 1, decrementing by {@code k}.
*
* @param n the non-negative integer for which to compute the multifactorial
* @param k the step value (must be a positive integer)
* @return the computed multifactorial of {@code n} with step {@code k}
* @throws IllegalArgumentException if {@code k} is less than or equal to 0
*/
public static long multiFactorial(int n, int k) {
if (k <= 0) {
throw new IllegalArgumentException("Step value k must be a positive integer.");
}
if (n <= 0) {
return 1; // Base case for n <= 0
}
long result = 1;
for (int i = n; i > 0; i -= k) {
result *= i;
}
return result;
}

}