/*
 *             Automatically Tuned Linear Algebra Software v3.2
 *                    (C) Copyright 1997 R. Clint Whaley                     
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *   1. Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *   2. Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions, and the following disclaimer in the
 *      documentation and/or other materials provided with the distribution.
 *   3. The name of the University of Tennessee, the ATLAS group,
 *      or the names of its contributers may not be used to endorse
 *      or promote products derived from this software without specific
 *      written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE. 
 *
 */

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

#define REPS 4096

#ifndef time00
   #define time00 ATL_cputime
#endif
double time00();

int L1CacheSize_read(int MAXL1CACHE, double tol)
{
   int ntests, h, i, j, k, reps=REPS*MAXL1CACHE, len, dlen, size=0;
   double *p, *d, *st;
   double t1, t2, *times;

   for (i=2, j=0; i <= MAXL1CACHE; i <<= 1, j++);
   times = malloc(j*sizeof(double));
   ntests = j;

   h=0;
   for (i=2; i <= MAXL1CACHE; i <<= 1)
   {
      len = i * 1024;
      dlen = len / sizeof(double);
      d = p = malloc(len);
      st = p + dlen;

      for (k=0; k != dlen; k++) d[k] = 5.0;
      t1 = time00();
      for (j=0; j != reps; j++)
      {
         do
         {
            if (*d == 27.5) exit(-99);
            d += 2;
         }
         while (d != st);
         d = p;
      }
      times[h] = time00() - t1;

      fprintf(stderr, "      L1CS=%d, time=%f\n",i, times[h]);
      reps >>= 1;
      free(p);
      h++;
   }
#ifdef RawSize
   t2 = 0.0;  j = 0;
   for (i=1; i < ntests; i++)
   {
      t1 = times[i] - times[i-1];
      if (t1 < 0) t1 = 0.0;
      if (t1 > t2) { t2 = t1;  j=i; }
   }
#else
   t2 = 1.0;  j = 0;
   for (i=1; i < ntests; i++)  /* find biggest % increase */
   {
      t1 = times[i] / times[i-1];
      if (t1 > t2) { t2 = t1;  j=i; }
   }
#endif
   fprintf(stderr, "\n");
   if (tol != 0.0)
   {
      if (tol*times[j-1] > times[j])
      {
         free(times);
         return(0);
      }
   }
   free(times);
   return(1 << j);
}

#define BIGBOY 256
#define L1CacheSize L1CacheSize_read

main(int nargs, char *args[])
{
   int L1Size, tmp, MaxSize=64, correct=1;
   FILE *L1f;

   if (nargs > 2)
   {
      fprintf(stderr, "USAGE: %s <max L1 cache size (in kilobytes)>\n",args[0]);
      exit(-1);
   }
   if (nargs > 1) MaxSize = atoi(args[1]);
   fprintf(stderr, "\n   Calculating L1 cache size:\n");
   L1Size = L1CacheSize(MaxSize, 1.08);
   if (L1Size == 0) tmp = -1.0;
   else
   {
      fprintf(stderr, "      Confirming result of %dkb:\n", L1Size);
      tmp = L1CacheSize(MaxSize, 0.0);
      if (tmp == L1Size) tmp = L1CacheSize(MaxSize, 0.0);
   }
   if (tmp != L1Size)
   {
      if (L1Size) fprintf(stderr, 
                     "   L1 size not confirmed; trying large maximum size:\n");
      else fprintf(stderr, "      Timing differences below tolerance, trying larger maximum cache size:\n");
      L1Size = L1CacheSize(BIGBOY, 0.0);
      fprintf(stderr, "      Confirming result of %dkb:\n", L1Size);
      tmp = L1CacheSize(BIGBOY, 0.0);
      if (tmp == L1Size) tmp = L1CacheSize(BIGBOY, 0.0);
      if (tmp != L1Size)
      {
         fprintf(stderr, "\nCan't detect the L1 cache; setting to 32KB.\n");
         L1Size = 32;
         correct = 0;
      }
   }
   fprintf(stderr, "Calculated L1 cache size = %dkb; Correct=%d\n",
           L1Size, correct);
   L1f = fopen("res/L1CacheSize", "w");
   fprintf(L1f, "%d\n",L1Size);
   fclose(L1f);
   exit(0);
}
