Last active
June 18, 2017 17:04
-
-
Save kitsook/6ce2113df79d6a83b0f9442805bf40b8 to your computer and use it in GitHub Desktop.
TriFib - A Java stream approach on testing triangular Fibonacci numbers
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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]); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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