#include "interface.h"  // Do NOT edit interface .h
#include "sample_prefetcher.h"

SamplePrefetcher *pref1;   // This is the L1 prefetcher
SamplePrefetcher *pref2;   // Thss is the L2 prefetcher

NNLPrefetcher *pref7;

//
//  Function to initialize the prefetchers.  DO NOT change the prototype of this
//  function.  You can change the body of the function by calling your necessary
//  initialization functions.
//


// ahmad begin

// GLOBAL FUNCTIONS BEGIN HERE.
template <class T, class U>
extern void PRINT_DELTAS_P(const T & coll, const U & now)
{
#if 0
  bool first=true;
  typename T::const_iterator pos;
  typename T::const_iterator prev;
  //  cout<<"ALL:"<<endl;
  for( pos=coll.begin() ; pos!=coll.end() ; pos++ )
  {
    if(first==false)
    {
      if(a==*pos)
        ;//cout<<(signed long long int)(*pos-*prev)<<"+ ";
      else
        ;//cout<<(signed long long int)(*pos-*prev)<<" ";
    }
    else
    {
      first=false;
    }
    prev=pos;
  }
  ;//cout<<"L2 ONLY:"<<endl;
  first=true;
  for( pos=coll.begin() ; pos!=coll.end() ; pos++ )
  {
    if(first==false)
    {
      if(a==*pos)
        ;//cout<<(signed long long int)(*pos-*prev)<<"+ ";
      else
        ;//cout<<(signed long long int)(*pos-*prev)<<" ";
    }
    else
    {
      first=false;
    }
    prev=pos;
  }
#endif
}

template <class T, class V>
extern void GetN(V & output, T & region, typename T::iterator i, int n)
{
  CacheAddr_t a;
  if(n<0)
  {
    a=*i;
    do
    {
      i--;
      output.push_front(a-*i);
      a=*i;
      n++;
    }
    while(i!=region.begin() && n<0);
  }
  else
  {
    assert(0);
  }
}
template <class T, class U>
void FindStride(T & region, U i, map<int, int> & strideMap)
{
  // Start from i-1 and see if it extends beyond...
  typename T::reverse_iterator ri(i);
  typename T::iterator j=i;
  AddressList currentStrides;
  int size=0;
  CacheAddr_t a;
  CacheAddr_t diff;
  a=*i;

  while(j!=region.begin())
  {
    j--;
    size++;
  }
  if(size==1)
  {
    return;
  }

  for( int current=1 ; current<=size/2 ; current++ )
  {
    // Try with stride=current.. report the findings in the strideMap.
    currentStrides.push_back(a-*ri);
    a=*ri;
    ri++;

    // At this point we have currentStrides set.
    typename T::reverse_iterator rev(i);
    typename T::reverse_iterator old;
    AddressList::iterator strideIt=currentStrides.begin();
    int counter=0;
    int loopCount=0;
    while(counter<current)
    {
      rev++;
      counter++;
    }

    old=rev;
    old--;
    while(rev!=region.rend())
    {
      diff=*old-*rev;
///      ;//cout<<"Diff: "<<diff<<endl;
      if(diff!=*strideIt)
      {
        break;
      }
      strideIt++;
      old++;
      rev++;
      loopCount++;
      if(strideIt==currentStrides.end())
      {
        strideIt=currentStrides.begin();
      }
    }
    strideMap[current]=loopCount;
#if 0
    ;//cout<<"current: "<<current<<" loopCount: "<<loopCount<<endl;
    ;//cout<<"Strides: "<<endl;
    for( AddressList::reverse_iterator rit=currentStrides.rbegin() ; rit!=currentStrides.rend() ; rit++ )
    {
      ;//cout<<*rit<<" ";
    }
    ;//cout<<endl;
#endif
  }
}
template <class T, class U>
extern void GET_NNL(T & output, U & input, PrefetchData_t * d, CacheAddr_t threshold)
{
  assert(input.size()!=0);
  assert(output.size()==0);
  for( typename T::iterator i=input.begin() ; i!=input.end() ; i++ )
  {
    CacheAddr_t diff;
    if(i->DataAddr>d->DataAddr)
      diff=i->DataAddr-d->DataAddr;
    else
      diff=d->DataAddr-i->DataAddr;
    if(LINE_NUMBER(diff)<threshold)
      output.push_back(*i);
  }
  assert(output.size());
}

template <class T, class U>
extern void GET_NN(T & output, U & input, CacheAddr_t current, CacheAddr_t threshold)
{
  assert(input.size()!=0);
  assert(output.size()==0);
  for( typename T::iterator i=input.begin() ; i!=input.end() ; i++ )
  {
    CacheAddr_t diff;
    if(*i>current)
      diff=*i-current;
    else
      diff=current-*i;
///    ;//cout<<"Diff: "<<diff<<endl;
    if(diff<threshold)
      output.push_back(*i);
  }
  assert(output.size());
}

template <class T>
extern void PRINT_ABS(const T& coll)
{
  typename T::const_iterator pos;
  for( pos=coll.begin() ; pos!=coll.end() ; pos++ )
  {
    ;//cout<<(signed long long int)*pos<<" ";
  }
}

template <class T, class U>
extern void GET_DELTAS(T&coll, U&output)
{
  bool first=true;
  typename T::reverse_iterator pos;
  typename T::reverse_iterator prev;

  for( pos=coll.rbegin() ; pos!=coll.rend() ; pos++ )
  {
    if(first==false)
    {
      output.push_front(prev->DataAddr-pos->DataAddr);
    }
    else
    {
      first=false;
    }
    prev=pos;
  }
}

template <class T, class U>
extern void PRINT_DELTAS(const T& coll, const U& a)
{
  bool first=true;
  typename T::const_iterator pos;
  typename T::const_iterator prev;
  for( pos=coll.begin() ; pos!=coll.end() ; pos++ )
  {
    if(first==false)
    {
      if(a==*pos)
        ;//cout<<(signed long long int)(*pos-*prev)<<"+ ";
      else
        ;//cout<<(signed long long int)(*pos-*prev)<<" ";
    }
    else
    {
      first=false;
    }
    prev=pos;
  }
}


// ahmad end







void InitPrefetchers() // DO NOT CHANGE THE PROTOTYPE
{
    // INSERT YOUR CHANGES IN HERE

    pref1 = new SamplePrefetcher();
    pref2 = new SamplePrefetcher();
    pref7=new NNLPrefetcher(24, 32, 128);
}


//
//  Function that is called every cycle to issue prefetches should the
//  prefetcher want to.  The arguments to the function are the current cycle,
//  the demand requests to L1 and L2 cache.  Again, DO NOT change the prototype of this
//  function.  You can change the body of the function by calling your necessary
//  routines to invoke your prefetcher.
//

// DO NOT CHANGE THE PROTOTYPE
void IssuePrefetches( COUNTER cycle, PrefetchData_t *L1Data, PrefetchData_t * L2Data )
{   

   pref7->IssuePrefetches(cycle, L1Data, L2Data);
 
#if 0

    // INSERT YOUR CHANGES IN HERE
    int i, j, k;
    MemLogEntry *entry;
    CacheAddr_t addr;

    // Issue L1 prefetches
    for(i = 0; i < 4; i++) {
        if((cycle == L1Data[i].LastRequestCycle) && (L1Data[i].hit == 0)) {
            entry = pref1->AccessEntry(0,L1Data[i].LastRequestAddr,L1Data[i].DataAddr);
            if(entry) {
                if(entry->count == 2) {

                    /*;//cout << "cycle: " << cycle;
                      ;//cout << "   seq: " << L1Data[i].SequenceNumber;
                      ;//cout << "   addr: " <<hex <<L1Data[i].LastRequestAddr <<dec;
                      ;//cout << "   count: " << entry->count;
                      ;//cout << "   pref: " << entry->last_mem_addr << endl; */

                    addr = L1Data[i].DataAddr;
                    for(j = 0; j < 2; j++) {
                        addr += entry->stride;
                        IssueL1Prefetch(cycle,addr);
                    }
                }
            }
        }
    }

    // Issue L2 prefetches
    if((cycle == L2Data->LastRequestCycle) && (L2Data->hit == 0)) {
        entry = pref2->AccessEntry( 0, L2Data->LastRequestAddr, L2Data->DataAddr);
        if(entry) {
            if(entry->trained) {

                /*;//cout << "cycle: " << cycle;
                  ;//cout << "   seq: " << L2Data->SequenceNumber;
                  ;//cout << "   addr: " <<hex <<L2Data->LastRequestAddr <<dec;
                  ;//cout << "   count: " << entry->count;
                  ;//cout << "   pref: " << entry->last_mem_addr << endl;*/

                addr = L2Data->DataAddr;
                for(k = 0; k < 8; k++) {
                    addr += entry->stride;
                    IssueL2Prefetch(cycle,addr);
                }
            }
        }
    }
#endif
}
