import java.util.*;
import java.math.*;
//=========================
// Custom Timeout Exception
//=========================
class TimeoutException extends Exception {
public TimeoutException() {
super();
}
public TimeoutException(String message) {
super(message);
}
}
//=====================================
// Utility that Adds, Counts, and Times
//=====================================
class Util {
public static Integer timeLimitMs = 500;
public static Long count = 0L;
private static Long t0 = 0L;
public static BigInteger add(BigInteger a, BigInteger b)
throws TimeoutException {
count++;
if (count % 10000 == 0) {
checkTime();
}
return a.add(b);
}
public static void start() {
count = 0L;
t0 = System.nanoTime();
}
public static Double stop() {
return elapsedMs();
}
private static Double elapsedMs() {
return (System.nanoTime() - t0)/1000000d;
}
private static void checkTime()
throws TimeoutException {
if (elapsedMs() > timeLimitMs) {
throw new TimeoutException(timeLimitMs + " ms limit exceeded.");
}
}
}
//==========================
// Generic Abstract Function
//==========================
abstract class Func<In, Out> {
public abstract Out call(Func<In, Out> self, In x)
throws TimeoutException;
public Out call(In x)
throws TimeoutException {
return this.call(this, x);
}
}
//==========================
// Generic Memoized Function
//==========================
class MemoizedFunc<In, Out> extends Func<In, Out> {
private Map<In, Out> cache = new HashMap<In, Out>();
private Func<In, Out> otherFunc;
public MemoizedFunc(Func<In, Out> func) {
this.otherFunc = func;
}
public Out call(Func<In, Out> self, In x)
throws TimeoutException {
if (!cache.containsKey(x)) {
cache.put(x, otherFunc.call(self, x));
}
return cache.get(x);
}
}
//============================
// Abstract Fibonacci Function
//============================
abstract class Fib extends Func<Integer, BigInteger> {
}
//============================
// Memoized Fibonacci Function
//============================
abstract class MemoizedFib extends MemoizedFunc<Integer, BigInteger> {
MemoizedFib(Fib func) {
super(func);
}
}
//===========================
// Amnesic Fibonacci Function
//===========================
class AmnesicFib extends Fib {
public BigInteger call(Func<Integer, BigInteger> self, Integer i)
throws TimeoutException {
BigInteger NEG1 = BigInteger.valueOf(-1);
if (i == 0) { // return first Fibonacci number
return BigInteger.valueOf(0);
}
else if (i == 1) { // return second Fibonacci number
return BigInteger.valueOf(1);
}
else if (i > 0) { // return ith Fibonacci number
return Util.add(
self.call(self, i-2),
self.call(self, i-1));
}
else { // return ith "negafibonacci" number
return Util.add(
self.call(self, i+2),
self.call(self, i+1).multiply(NEG1));
}
}
}
//===========================
// Eidetic Fibonacci Function
//===========================
class EideticFib extends MemoizedFib {
EideticFib() {
super(new AmnesicFib());
}
}
//===========================
// Grinder Fibonacci Function
//===========================
class GrinderFib extends Fib {
public BigInteger call(Func<Integer, BigInteger> self, Integer i)
throws TimeoutException {
//----------------------------------------
// 1 multiplier => Fibonacci number
// -1 multiplier => "negafibonacci" number
// 0 is the "bipolafibonacci" number
//----------------------------------------
Integer multiplier = i < 0 ? -1 : 1;
BigInteger MULTIPLIER = BigInteger.valueOf(multiplier);
Integer magnitude = multiplier * i;
BigInteger f2 = BigInteger.valueOf(-1);
BigInteger f1 = BigInteger.valueOf(1);
BigInteger f0 = BigInteger.valueOf(0);
for (Integer j = 0; j < magnitude; j++) {
f2 = f1;
f1 = f0;
f0 = Util.add(f2, (f1.multiply(MULTIPLIER)));
}
return f0;
}
}
//===========================
// Unitary Fibonacci Function
//===========================
class UnitaryFib extends MemoizedFib {
UnitaryFib() {
super(new GrinderFib());
}
}
//===========================
// Lacunar Fibonacci Function
//===========================
class LacunarFib extends Fib {
public BigInteger call(Func<Integer, BigInteger> self, Integer i)
throws TimeoutException {
// HACK
final MathContext mcL = new MathContext(128, RoundingMode.HALF_UP);
final MathContext mcM = new MathContext(32, RoundingMode.HALF_UP);
final MathContext mcS = new MathContext(2, RoundingMode.HALF_UP);
final BigDecimal ONE = BigDecimal.valueOf(1);
final BigDecimal NEG1 = BigDecimal.valueOf(-1);
final BigDecimal TWO = BigDecimal.valueOf(2);
final BigDecimal SQRT5 = BigDecimal.valueOf(Math.sqrt(5));
final BigDecimal PHI = (ONE.add(SQRT5)).divide(TWO, mcL);
final BigDecimal PHIHAT = NEG1.divide(PHI, mcL);
final BigDecimal PHIPOWI = PHI.pow(i, mcL);
final BigDecimal PHIHATPOWI = PHIHAT.pow(i, mcL);
final BigDecimal DIFF = PHIPOWI.subtract(PHIHATPOWI);
// HACK
final BigDecimal FIB = DIFF.abs().doubleValue() < 10 ?
DIFF.divide(SQRT5, mcS) : DIFF.divide(SQRT5, mcM);
final BigInteger BIGFIB = FIB.toBigInteger();
return BIGFIB;
}
}
//=======
// Report
//=======
public class BigFibs {
private final static int STACK_OVERFLOW = 1;
private final static int DATATYPE_OVERFLOW = 2;
private final static int TIMEOUT = 3;
private final static String[] PRTF = { "%5s", "%55s", "%12d", "%12.3f" };
private final static String[] PRTH = { "%5s", "%55s", "%12s", "%12s" };
private static void printData
(Func<Integer, BigInteger> F, String[] seqNums, int i0) {
String nm = F.getClass().getName();
Integer n;
BigInteger Fn = null; // nth Fibonacci number
Double dt = 0d; // delta (change in) time in ms
Double sumTm = 0d; // sum of times
Long sumCt = 0L; // sum of counts (number of operations)
Integer err; // error code
//---------------------------------------
// print one data row per sequence number
//---------------------------------------
for (Integer i = i0; i < seqNums.length; i++) {
err = 0;
n = Integer.parseInt(seqNums[i]);
Util.start();
try {
Fn = F.call(n);
}
catch(StackOverflowError soe) {
Fn = null;
err = STACK_OVERFLOW;
}
catch(TimeoutException toe) {
Fn = null;
err = TIMEOUT;
}
catch(Exception e) {
Fn = null;
err = DATATYPE_OVERFLOW;
}
dt = Util.stop();
sumTm += dt;
sumCt += Util.count;
printDataRow(n, Fn, Util.count, dt, nm, err);
}
//----------------------------------------------------
// if more than one sequence number, print summary row
//----------------------------------------------------
if (seqNums.length - i0 > 1) {
printDataRow(null, null, sumCt, sumTm, "TOTALS", 0);
System.out.println();
}
}
private static void printHeaderRow() {
System.out.println("BigFibs: " + (new Date()).toString());
System.out.println();
System.out.printf(PRTH[0], "i");
System.out.printf(PRTH[1], "Fib(i)");
System.out.printf(PRTH[2], "operations");
System.out.printf(PRTH[3], "time (ms)");
System.out.println();
System.out.printf(PRTH[0], "-----");
System.out.printf(PRTH[1], "------------------------------------------------------");
System.out.printf(PRTH[2], "-----------");
System.out.printf(PRTH[3], "-----------");
System.out.println();
}
private static void printDataRow
(Integer n, BigInteger Fn, Long ct, Double dt, String name, Integer err) {
if (n != null) {
System.out.printf(PRTF[0], n.toString());
}
else {
System.out.printf(PRTF[0], "");
}
if (Fn == null && err == 0) {
System.out.printf(PRTF[1], "");
}
else {
switch(err) {
case 0:
System.out.printf(PRTF[1], Fn);
break;
case STACK_OVERFLOW:
System.out.printf(PRTF[1], "STACK_OVERFLOW");
break;
case DATATYPE_OVERFLOW:
System.out.printf(PRTF[1], "DATATYPE_OVERFLOW");
break;
case TIMEOUT:
System.out.printf(PRTF[1], "TIMEOUT");
break;
default:
System.out.printf(PRTF[1], "UNEXPECTED_ERROR");
break;
}
}
System.out.printf(PRTF[2], ct);
System.out.printf(PRTF[3], dt);
System.out.print(' ');
System.out.print(name);
System.out.println();
}
public static void unoReport
(Func<Integer, BigInteger> fib, String[] args, int i0) {
printHeaderRow();
printData(fib, args, i0);
if (args.length == 1) {
System.out.println();
}
}
public static void comboReport(String[] args, int i0) {
printHeaderRow();
printData(new AmnesicFib(), args, i0);
printData(new EideticFib(), args, i0);
printData(new GrinderFib(), args, i0);
printData(new UnitaryFib(), args, i0);
printData(new LacunarFib(), args, i0);
if (args.length == 1) {
System.out.println();
}
}
public static void main(String[] args) {
if (args.length == 0) {
return;
}
switch(args[0].toLowerCase()) {
case "amnesic":
unoReport(new AmnesicFib(), args, 1);
break;
case "eidetic":
unoReport(new EideticFib(), args, 1);
break;
case "grinder":
unoReport(new GrinderFib(), args, 1);
break;
case "unitary":
unoReport(new UnitaryFib(), args, 1);
break;
case "lacunar":
unoReport(new LacunarFib(), args, 1);
break;
default:
comboReport(args, 0);
break;
}
}
}