#include <stdio.h>
#include <stdlib.h>
#include <math.h>
/******************************************************************************
Branch Prediction Simulator
(c) M. Lutfi, 2009
 ******************************************************************************/
#define ROW_MAJOR_ADDR(A, r, c, n)  ((A) + (r)*(n) + (c))
typedef enum {
    not_taken = 0,
    taken
} branch_t;
typedef enum {
    strongly_not_taken  = 0,
    weakly_not_taken,
    weakly_taken,
    strongly_taken
} branch_state_t;
typedef struct {
    unsigned int    correctPredictionCount;
    branch_state_t  state;
    unsigned int    n_iter;
} lbp_t;
enum {
    BRANCH_A = 0,
    BRANCH_B,
    BRANCH_C,
    BRANCH_D,
    N_OF_BRANCHES
};
#define NBITS_OF_GBH    3    // number of bits in Global History register
lbp_t lbp_table[NBITS_OF_GBH][N_OF_BRANCHES];
unsigned char    gbh=0;  // global branch history register (3-bit only)
branch_t    last_outcome = not_taken;
char* get_branch_state_str(branch_t b)
{
    if (b == not_taken)
    return "not_taken";
    else
    return "taken";
}
void update_gbh(void)
{
#if 0
    gbh = (gbh<<1) | last_outcome;
    gbh &= ((1<    printf("last_outcome = %s\n", get_branch_state_str(last_outcome));
    printf("gbh = %0X\n", gbh);
#endif
}
/* Local Branch Prediction FSM */
void lbp_update(lbp_t *lbp, branch_t outcome)
{
   switch (lbp->state) {
    case strongly_not_taken:
        if (outcome == taken) {
        lbp->state = weakly_not_taken;
        } else {
        lbp->correctPredictionCount++;
        }
        break;
    case weakly_not_taken:
        if (outcome == taken) {
        lbp->state = weakly_taken;
        } else {
        lbp->state = strongly_not_taken;
        }
        break;
    case weakly_taken:
        if (outcome == taken) {
        lbp->state = strongly_taken;
        } else
        lbp->state = weakly_not_taken;
        break;
    case strongly_taken:
        if (outcome == taken) {
        lbp->correctPredictionCount++;
        } else {
        // the outcome is NT
        lbp->state = weakly_taken;
        }
        break;
    }
    lbp->n_iter++;
}
int fun(double *p, int n)
{
    int r = 0;
    int c = 0;
    double *pA;
    while (r <= n-1) {
    last_outcome = not_taken;
    update_gbh();
    lbp_update(&lbp_table[gbh][BRANCH_A], not_taken);
    while (c <= n-1) {
        last_outcome = not_taken;
        update_gbh();
        pA = ROW_MAJOR_ADDR(p, r, c, n);
        if (r < c) {
        lbp_update(&lbp_table[gbh][BRANCH_C], not_taken);
            *pA = 2* (*pA) + 1;
        last_outcome = not_taken;
        } else {
        lbp_update(&lbp_table[gbh][BRANCH_C], taken); 
        last_outcome = taken;
        }
        if (r > c) {
        lbp_update(&lbp_table[gbh][BRANCH_D], not_taken);
        *pA = 2* (*pA) - 1;
        last_outcome = not_taken;
        } else {
        lbp_update(&lbp_table[gbh][BRANCH_D], taken);
        last_outcome = taken;
        }
        ++c;
    }
    lbp_update(&lbp_table[gbh][BRANCH_B], taken);
    last_outcome = taken;
    update_gbh();
    ++r;
    }
    lbp_update(&lbp_table[gbh][BRANCH_A], taken);
    last_outcome = taken;
    update_gbh();
    return 0;
}
void print_A(double *p, int n)
{
    int i,j;
    double *pA;
   
    for (i=0; i<n; i++) {
    for(j=0; j<n; j++) {
        pA = ROW_MAJOR_ADDR(p, i, j, n);
        if (!pA) return;
        printf("%d: &A[%d][%d] = %0X,\t", __LINE__, i, j, pA);
        printf("%d: A[%d][%d] = %lf\n", __LINE__, i, j, *pA);
    }
    }
}
void init_gbh(void)
{
    int i,j;
    lbp_t *lbpP;
   
    gbh = 0;
    for(i=0; i<(NBITS_OF_GBH<<3); i++) {
    lbpP = (lbp_t*)&lbp_table[i];
    for(j=0; j<N_OF_BRANCHES; j++) {
        lbpP[j].correctPredictionCount = 0;
        lbpP[j].state = weakly_not_taken;
        lbpP[j].n_iter = 0;
    }
    }
}
int main(void)
{
    #define N 50
    double A[N][N];
    int i,j;
    init_gbh();  
    printf("sizeof(double) = %d\n", sizeof(double));
    printf("%d: &A = %0X\n", __LINE__, &A[0][0]);
    for(i=0; i<N; i++) {
    for(j=0; j<N; j++) {
        A[i][j] = ROW_MAJOR_ADDR(1, i, j, N);
    }
    }
    fun(&A[0][0], N);
    printf("gbh = %0X\n", gbh);
    for(i=0; i<N_OF_BRANCHES; i++) {
    printf("lbp[%d] iteration = %d\n", i, lbp_table[gbh][i].n_iter);
    printf("lbp[%d] correct prediction count = %d\n",
        i, lbp_table[gbh][i].correctPredictionCount);
    printf("Correct Prediction Rate = %4.2lf%\n\n",
      ((double)(lbp_table[gbh][i].correctPredictionCount)/(double)lbp_table[gbh][i].n_iter) *100.0);
    }
}