/*
 * Various machine-independent optimizations
 * for summing elements of a vector
 *
 * See also the related code for CS:App
 * code/opt/vec.c and code/opt/combine.c
 */
#include <stdio.h>

/* type of integer vectors */
typedef struct {
  int len;			/* length */
  int *data;			/* raw array */
} vec;

/*
 * vnew(n) -- allocate and return pointer to new vector of length n
 * returns NULL if allocation fails
 */
vec *vnew(int len) {
  vec *result = (vec *)malloc(sizeof(vec*)); /* allocate vec struct */
  if (!result)
    return NULL;		/* return NULL on failure */
  result->len = len;		/* set len field */
  if (len > 0) {
    int *data = (int *)calloc(len, sizeof(int)); /* allocate raw array */
    if (!data) {
      free ((void *) result);	/* free vec struct */
      return NULL;		/* return NULL on failure */
    }
    result->data = data;	/* set data array field */
  }
  else
    result->data = NULL;	/* set data array field to NULL if len <= 0 */
  return result;
}

/*
 * vget(p, i, dest) -- store ith element of vector pointed to by p in dest
 * return 0 if i is out of boundse, 1 otherwise
 */
int vget(vec *p, int i, int *dest) {
  if (i < 0 || i >= p->len)
    return 0;
  *dest = p->data[i];
  return 1;
}

/*
 * vget(p, i, n) -- store n in ith element of vector
 * return 0 if i is out of boundse, 1 otherwise
 */
int vset(vec *p, int i, int n) {
  if (i < 0 || i >= p->len)
    return 0;
  p->data[i] = n;
  return 1;
}

/*
 * vlen(p) -- length of vector pointed to by p
 */
int vlen(vec *p) {
  return p->len;
}

/*
 * vdata(p) -- raw data vectore pointed to by p
 */
int *vdata(vec* p) {
  return p->data;
}

/*
 * sumX(p, dest) -- sum elements of vector and write to dest
 *
 * Various implementations differ in efficiency
 * and sometimes in behavior if p and dest are not disjoint (aliased)
 */

void sum1(vec *p, int *dest) {
  int i;
  int val;
  *dest = 0;
  for (i = 0; i < vlen(p); i++) { /* vlen calculated each time */
    vget(p, i, &val);		/* must succeed, returning 1 */
    *dest += val;
  }
}

void sum2(vec *p, int *dest) {
  int i;
  int val;
  int length = vlen(p);		/* calculation of vlen hoisted out of loop */
  *dest = 0;
  for (i = 0; i < length; i++) {
    vget(p, i, &val);
    *dest += val;		/* must succeed, returning 1 */
  }
}

void sum3(vec *p, int *dest) {
  int i;
  int length = vlen(p);
  int *data = vdata(p);		/* eliminated bounds check on vector access */
  *dest = 0;
  for (i = 0; i < length; i++) {
    *dest += data[i];		/* eliminated function call */
  }
}

void sum4(vec *p, int *dest) {
  int i;
  int length = vlen(p);
  int *data = vdata(p);
  int x = 0;
  for (i = 0; i < length; i++) {
    x += data[i];		/* hoisted memory reference out of loop  */
  }
  *dest = x;
}
