Skip to content

Instantly share code, notes, and snippets.

@kitsook
Last active June 18, 2017 17:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kitsook/6ce2113df79d6a83b0f9442805bf40b8 to your computer and use it in GitHub Desktop.
Save kitsook/6ce2113df79d6a83b0f9442805bf40b8 to your computer and use it in GitHub Desktop.
TriFib - A Java stream approach on testing triangular Fibonacci numbers
package net.clarenceho.TriFib;
import java.util.stream.Stream;
import java.math.BigInteger;
public class FibonacciNum {
/**
* Stream of Fibonacci number in BigInteger
* @return stream of Fibonacci number
*/
public static Stream<BigInteger> stream() {
return Stream
.iterate(
new BigInteger[] { BigInteger.ONE, BigInteger.ONE },
p -> new BigInteger[] { p[1], p[0].add(p[1]) })
.map(p -> p[0]);
}
}
package net.clarenceho.TriFib.test;
import static org.junit.Assert.*;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.FixMethodOrder;
import org.junit.runners.MethodSorters;
import static org.hamcrest.CoreMatchers.*;
import net.clarenceho.TriFib.FibonacciNum;
import net.clarenceho.TriFib.TriangularNum;
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class Test {
// how many Fibonacci number to test?
private static final int TEST_LIMIT = 70;
// Expect numbers that are both triangular and Fibonacci
private static final BigInteger[] EXPECTED = {
BigInteger.ONE, new BigInteger("3"), new BigInteger("21"), new BigInteger("55") };
@org.junit.Test
public void test01CompareFirst30Tri() {
Object[] tri = TriangularNum.stream().limit(30).map(i -> i.intValue()).toArray();
int[] expected = {
1, 3, 6, 10, 15, 21, 28, 36, 45, 55,
66, 78, 91, 105, 120, 136, 153, 171, 190, 210,
231, 253, 276, 300, 325, 351, 378, 406, 435, 465 };
assertThat(tri, is(expected));
}
@org.junit.Test
public void test02CompareFirst25Fib() {
Object[] fib = FibonacciNum.stream().limit(25).map(i -> i.intValue()).toArray();
int[] expected = {
1, 1, 2, 3, 5, 8, 13, 21, 34, 55,
89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765,
10946, 17711, 28657, 46368, 75025 };
assertThat(fib, is(expected));
}
@org.junit.Test
public void test99SlowVerify() {
Iterator<BigInteger> fib = FibonacciNum.stream().limit(TEST_LIMIT).iterator();
Iterator<BigInteger> tri = TriangularNum.stream().iterator();
BigInteger t = tri.next();
List<BigInteger> result = new ArrayList<BigInteger>();
while (fib.hasNext()) {
BigInteger f = fib.next();
while (t.compareTo(f) <= 0) {
if (t.equals(f)) {
result.add(t);
}
t = tri.next();
}
}
assertThat(result.toArray(), is(EXPECTED));
}
@org.junit.Test
public void test99FastVerify() {
List<BigInteger> result = FibonacciNum
.stream()
.limit(TEST_LIMIT)
.parallel()
.filter(f -> TriangularNum.isTriangular(f))
.distinct()
.sorted()
.collect(Collectors.toList());
assertThat(result.toArray(), is(EXPECTED));
}
}
package net.clarenceho.TriFib;
import java.util.stream.Stream;
import java.math.BigInteger;
public class TriangularNum {
private static final BigInteger TWO = new BigInteger("2");
private static final BigInteger EIGHT = new BigInteger("8");
/**
* Stream of triangular numbers in BigInteger
* @return stream of triangular numbers
*/
public static Stream<BigInteger> stream() {
return Stream
.iterate(
BigInteger.ONE,
i -> i.add(BigInteger.ONE))
.map(i -> i.multiply(i.add(BigInteger.ONE)).divide(TWO));
}
/**
* Test if a given number is a triangular number.
* A integer x is triangular iff 8x + 1 is a perfect square.
*
* @param x a BigInteger to be tested
* @return true if x is a triangular number. false otherwise
*/
public static boolean isTriangular(BigInteger x) {
BigInteger test = x.multiply(EIGHT).add(BigInteger.ONE);
BigInteger testSqrt = sqrt(test);
return test.equals(testSqrt.multiply(testSqrt));
}
/*
* Calculate square root of an BigInteger.
* Copied from https://stackoverflow.com/questions/2685524/check-if-biginteger-is-not-a-perfect-square
*/
private static BigInteger sqrt(BigInteger n) {
if (n.signum() >= 0) {
final int bitLength = n.bitLength();
BigInteger root = BigInteger.ONE.shiftLeft(bitLength / 2);
while (!isSqrt(n, root)) {
root = root.add(n.divide(root)).divide(TWO);
}
return root;
} else {
throw new ArithmeticException("square root of negative number");
}
}
private static boolean isSqrt(BigInteger n, BigInteger root) {
final BigInteger lowerBound = root.pow(2);
final BigInteger upperBound = root.add(BigInteger.ONE).pow(2);
return lowerBound.compareTo(n) <= 0
&& n.compareTo(upperBound) < 0;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment