// Written by J Dean Brock (2017) for CSCI 235

#include <stdio.h>
#include <stdlib.h>

// Mostly for fun -- using gdb, print expressions like these before running
//    print (void *)xxxNums
//    print (void *)&xxxNums[2]
//    print (void *)(xxxNums+2)
char charNums[10] = { 0, 0, 1, -1, 4, -4, 1, -1, 6, -6} ;
short shortNums[10] = { 0, 0, 1, -1, 4, -4, 1, -1, 6, -6} ;
int intNums[10] = { 0, 0, 1, -1, 4, -4, 1, -1, 6, -6} ;
long longNums[10] = { 0, 0, 1, -1, 4, -4, 1, -1, 6, -6} ;

int sumNumsSimple(int *, int) ;
int sumNumsPointer(int *, int) ;
int sumNumsPlusPlus(int *, int) ;
int sumNumsUnroll(int *, int) ;
int sumNumsConfused(int *, int) ;

int main(int argc, char** argv) {
    (void) printf("Simple    returns %2d\n", sumNumsSimple(intNums, 10)) ;
    (void) printf("Pointer   returns %2d\n", sumNumsPointer(intNums, 1000000)) ;
    (void) printf("PlusPlus  returns %2d\n", sumNumsPlusPlus(intNums, 10)) ;
    (void) printf("Unroll    returns %2d\n", sumNumsUnroll(intNums, 10)) ;
    (void) printf("Confused  returns %2d\n", sumNumsConfused(intNums, 10)) ;
    return (EXIT_SUCCESS);
}

// The straightforward sum program
int sumNumsSimple(int V[], int n) {
    int sum = 0 ;
    for (int i=0; i<n; ++i) {
        sum = sum + V[i] ;
    }
    return sum ;
}

// A pointer-based sum program.
// It eliminates the cost of the computing the offset of the i'th element
int sumNumsPointer(int V[], int n) {
    int sum = 0 ;
    int *posV = V ;
    const int *endV = &V[n] ;
    for (; posV<endV; ++posV) {
        sum = sum + *posV ;
    }
    return sum ;
}

// Another pointer-based program
// It uses *posV++ as an "optimization"
//   *++posV     is WRONG     -- skips the 0'th item
//   (*posV)++   is WRONG     -- Adds n to V[0]
//   *(posV++)   is *posV++   -- by C precidence rules

int sumNumsPlusPlus(int V[], int n) {
    int sum = 0 ;
    int *posV = V ;
    const int *endV = &V[n] ;
    for (; posV<endV; sum += *posV++) ;
    return sum ;
}

// The program uses an unrolled loop.
// It halves the overhead of testing and incrementing the loop.
// Optimizations like this are preformed by modern compilers.
int sumNumsUnroll(int V[], int n) {
    if (n<0) {
        return 0 ;
    }
    int sum = 0 ;
    int i = 0 ;
    if (n & 1) {         // Test if n is odd.... More next week
        i = 1 ;
        sum = V[0] ;
    }
    for (; i<n; i += 2) {
        sum = sum + V[i] ;
        sum = sum + V[i+1] ;
    }
    return sum ;
}

// This program does too much.
// It increments a pointers and tests n>0 rather than i<n.
int sumNumsConfused(int V[], int n) {
    if (n<=0) {
        return 0 ;
    }
    int sum = 0 ;
    int *posV = V ;
    if (n & 1) {         // Test if n is odd.... More next week
        --n ;
        sum = *posV ;
        ++posV ;
    }
    for (; n > 0; posV += 2) {
        sum += *posV ;
        sum += *(posV+1) ;   // Assumes instruction support for a fixed offset
        n -= 2 ;
    }
    return sum ;    
}
