import java.util.*;
//=========================
// 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 Long add(Long a, Long b)
throws TimeoutException {
count++;
if (count % 10000 == 0) {
checkTime();
}
return Math.addExact(a, 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, Long> {
}
//============================
// Memoized Fibonacci Function
//============================
class MemoizedFib extends MemoizedFunc<Integer, Long> {
MemoizedFib(Fib func) {
super(func);
}
}
//===========================
// Amnesic Fibonacci Function
//===========================
class AmnesicFib extends Fib {
public Long call(Func<Integer, Long> self, Integer i)
throws TimeoutException {
if (i == 0) { // return first Fibonacci number
return 0L;
}
else if (i == 1) { // return second Fibonacci number
return 1L;
}
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),
-1 * self.call(self, i+1));
}
}
}
//===========================
// Eidetic Fibonacci Function
//===========================
class EideticFib extends MemoizedFib {
EideticFib() {
super(new AmnesicFib());
}
}
//===========================
// Grinder Fibonacci Function
//===========================
class GrinderFib extends Fib {
public Long call(Func<Integer, Long> self, Integer i)
throws TimeoutException {
//----------------------------------------
// 1 multiplier => Fibonacci number
// -1 multiplier => "negafibonacci" number
// 0 is the "bipolafibonacci" number
//----------------------------------------
Integer multiplier = i < 0 ? -1 : 1;
Integer magnitude = multiplier * i;
Long f2 = -1L;
Long f1 = 1L;
Long f0 = 0L;
for (Integer j = 0; j < magnitude; j++) {
f2 = f1;
f1 = f0;
f0 = Util.add(f2, (multiplier * f1));
}
return f0;
}
}
//===========================
// Unitary Fibonacci Function
//===========================
class UnitaryFib extends MemoizedFib {
UnitaryFib() {
super(new GrinderFib());
}
}
//===========================
// Lacunar Fibonacci Function
//===========================
class LacunarFib extends Fib {
public Long call(Func<Integer, Long> self, Integer i)
throws TimeoutException {
Double sqrt5 = Math.sqrt(5);
Double phi = (1 + sqrt5)/2;
Double phiHat = -1/phi;
Double phiPowI = Math.pow(phi, i);
Double phiHatPowI = Math.pow(phiHat, i);
Double fib = (phiPowI - phiHatPowI)/sqrt5;
return Math.round(fib);
}
}
//=======
// Report
//=======
public class FunkyFibs {
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, Long> F, String[] seqNums, int i0) {
String nm = F.getClass().getName();
Integer n;
Long 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("FunkyFibs: " + (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, Long 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, Long> 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;
}
}
}