/* Fibonacci, demonstrating dynamic programming
 * 15-122 Principle of Imperative Computation, Fall 2010
 * Frank Pfenning
 */

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include "contracts.h"

int fib0(int n) {
  REQUIRES(n >= 0);
  if (n == 0) return 0;
  else if (n == 1) return 1;
  else return fib0(n-1) + fib0(n-2);
}

/* fib0 is ok in C0, where it will return the
 * nth Fibonacci number mod 2^32.  In C, the
 * result of overflow is undefined, so this program
 * is buggy!
 *
 * Instead, we would like to signal an overflow.
 */

int safe_plus(int x, int y) {
  if (   (x > 0 && y > INT_MAX-x)
      || (x < 0 && y < INT_MIN-x) ) {
    fprintf(stderr, "integer overflow\n");
    abort();
  } else {
    return x+y;
  }
}

int fib1(int n) {
  REQUIRES(n >= 0);
  if (n == 0) return 0;
  else if (n == 1) return 1;
  else return safe_plus(fib1(n-1),fib1(n-2));
}

/* fib1 is correct, but it it is very slow (in fact, exponential
 * in n).  We can use dynamic programming to improve its running
 * time.  First, top-down dynamic programming.  The main function
 * is recursive and passes an array that is updated with the
 * computed results.  This version is O(n).
 */

int fib2_rec(int n, int* A) {
  REQUIRES(n >= 0);
  if (n == 0) return 0;
  else if (n == 1) return 1;
  else if (A[n] > 0) return A[n];
  else {
    int result = safe_plus(fib2_rec(n-1,A), fib2_rec(n-2,A));
    A[n] = result;		/* store A[n] == fib(n)  */
    return result;
  }
}

/* fib2 allocates the table to hold the results which is
 * updated in place.
 */
int fib2(int n) {
  REQUIRES(n >= 0);
  int* A = calloc(n+1, sizeof(int));
  if (A == NULL) { fprintf(stderr, "allocation failed\n"); abort(); }
  /* calloc initializes the array with 0s */
  return fib2_rec(n, A);
}

/* Next, bottom-up dynamic programming.  We build a table
 * and then just look up the answer.  Generally speaking, it is
 * more difficult to design bottom-up dynamic programming
 * algorithms than top-down, but they can be more efficient
 */
int fib3(int n) {
  int i;
  int* A = calloc(n+1, sizeof(int));
  if (A == NULL) { fprintf(stderr, "allocation failed\n"); abort(); }
  A[0] = 0; A[1] = 1;
  for (i = 2; i <= n; i++) {
    /* loop invariant: 2 <= i && i <= n+1; */
    /* loop invariant: A[i] = fib(i) for i in [0,i) */
    A[i] = safe_plus(A[i-1], A[i-2]);
  }
  ASSERT(i == n+1);
  return A[n];
}

/* Even more elegant: after we have used A[i] to
 * compute A[i+2] we no longer need it, so instead of
 * a full array, we can restrict outselves to two
 * values.  This is borderline dynamic programming.
 */
int fib4(int n) {
  REQUIRES(n >= 0);
  int i; int A0, A1, A2;
  A0 = 0; A1 = 1;
  if (n == 0) return A0;
  else if (n == 1) return A1;
  else for (i = 2; i <= n; i++) {
      /* loop invariant: A1 = fib(i-1), A0 = fib(i-2) */
      A2 = safe_plus(A1, A0);
      A0 = A1;
      A1 = A2;
    }
  ASSERT(i == n+1);
  return A1;
}

int main() {
  printf("fib0(22) = %d\n", fib0(22));
  printf("fib1(22) = %d\n", fib1(22));
  printf("fib2(36) = %d\n", fib2(36));
  printf("fib3(36) = %d\n", fib3(36));
  printf("fib4(36) = %d\n", fib3(36));
  /* next line should overflow with 32 bits */
  // printf("fib2(48) = %d\n", fib2(48));
  return 0;
}
