// Copyright 2006 Google Inc. All Rights Reserved.

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

//      http://www.apache.org/licenses/LICENSE-2.0

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// pattern.cc : library of stressful data patterns

#include <sys/types.h>

// This file must work with autoconf on its public version,
// so these includes are correct.
#include "pattern.h"
#include "sattypes.h"

// Static data patterns.

static unsigned int walkingOnes_data[] =   {
  0x00000001, 0x00000002, 0x00000004, 0x00000008,
  0x00000010, 0x00000020, 0x00000040, 0x00000080,
  0x00000100, 0x00000200, 0x00000400, 0x00000800,
  0x00001000, 0x00002000, 0x00004000, 0x00008000,
  0x00010000, 0x00020000, 0x00040000, 0x00080000,
  0x00100000, 0x00200000, 0x00400000, 0x00800000,
  0x01000000, 0x02000000, 0x04000000, 0x08000000,
  0x10000000, 0x20000000, 0x40000000, 0x80000000,
  0x40000000, 0x20000000, 0x10000000, 0x08000000,
  0x04000000, 0x02000000, 0x01000000, 0x00800000,
  0x00400000, 0x00200000, 0x00100000, 0x00080000,
  0x00040000, 0x00020000, 0x00010000, 0x00008000,
  0x00004000, 0x00002000, 0x00001000, 0x00000800,
  0x00000400, 0x00000200, 0x00000100, 0x00000080,
  0x00000040, 0x00000020, 0x00000010, 0x00000008,
  0x00000004, 0x00000002, 0x00000001, 0x00000000
};
static const struct PatternData walkingOnes = {
  "walkingOnes",
  walkingOnes_data,
  (sizeof walkingOnes_data / sizeof walkingOnes_data[0]) - 1,
  {1, 1, 2, 1}  // Weight for choosing 32/64/128/256 bit wide of this pattern
};

static unsigned int walkingInvOnes_data[] =   {
  0x00000001, 0xfffffffe, 0x00000002, 0xfffffffd,
  0x00000004, 0xfffffffb, 0x00000008, 0xfffffff7,
  0x00000010, 0xffffffef, 0x00000020, 0xffffffdf,
  0x00000040, 0xffffffbf, 0x00000080, 0xffffff7f,
  0x00000100, 0xfffffeff, 0x00000200, 0xfffffdff,
  0x00000400, 0xfffffbff, 0x00000800, 0xfffff7ff,
  0x00001000, 0xffffefff, 0x00002000, 0xffffdfff,
  0x00004000, 0xffffbfff, 0x00008000, 0xffff7fff,
  0x00010000, 0xfffeffff, 0x00020000, 0xfffdffff,
  0x00040000, 0xfffbffff, 0x00080000, 0xfff7ffff,
  0x00100000, 0xffefffff, 0x00200000, 0xffdfffff,
  0x00400000, 0xffbfffff, 0x00800000, 0xff7fffff,
  0x01000000, 0xfeffffff, 0x02000000, 0xfdffffff,
  0x04000000, 0xfbffffff, 0x08000000, 0xf7ffffff,
  0x10000000, 0xefffffff, 0x20000000, 0xdfffffff,
  0x40000000, 0xbfffffff, 0x80000000, 0x7fffffff,
  0x40000000, 0xbfffffff, 0x20000000, 0xdfffffff,
  0x10000000, 0xefffffff, 0x08000000, 0xf7ffffff,
  0x04000000, 0xfbffffff, 0x02000000, 0xfdffffff,
  0x01000000, 0xfeffffff, 0x00800000, 0xff7fffff,
  0x00400000, 0xffbfffff, 0x00200000, 0xffdfffff,
  0x00100000, 0xffefffff, 0x00080000, 0xfff7ffff,
  0x00040000, 0xfffbffff, 0x00020000, 0xfffdffff,
  0x00010000, 0xfffeffff, 0x00008000, 0xffff7fff,
  0x00004000, 0xffffbfff, 0x00002000, 0xffffdfff,
  0x00001000, 0xffffefff, 0x00000800, 0xfffff7ff,
  0x00000400, 0xfffffbff, 0x00000200, 0xfffffdff,
  0x00000100, 0xfffffeff, 0x00000080, 0xffffff7f,
  0x00000040, 0xffffffbf, 0x00000020, 0xffffffdf,
  0x00000010, 0xffffffef, 0x00000008, 0xfffffff7,
  0x00000004, 0xfffffffb, 0x00000002, 0xfffffffd,
  0x00000001, 0xfffffffe, 0x00000000, 0xffffffff
};
static const struct PatternData walkingInvOnes = {
  "walkingInvOnes",
  walkingInvOnes_data,
  (sizeof walkingInvOnes_data / sizeof walkingInvOnes_data[0]) - 1,
  {2, 2, 5, 5}
};

static unsigned int walkingZeros_data[] =   {
  0xfffffffe, 0xfffffffd, 0xfffffffb, 0xfffffff7,
  0xffffffef, 0xffffffdf, 0xffffffbf, 0xffffff7f,
  0xfffffeff, 0xfffffdff, 0xfffffbff, 0xfffff7ff,
  0xffffefff, 0xffffdfff, 0xffffbfff, 0xffff7fff,
  0xfffeffff, 0xfffdffff, 0xfffbffff, 0xfff7ffff,
  0xffefffff, 0xffdfffff, 0xffbfffff, 0xff7fffff,
  0xfeffffff, 0xfdffffff, 0xfbffffff, 0xf7ffffff,
  0xefffffff, 0xdfffffff, 0xbfffffff, 0x7fffffff,
  0xbfffffff, 0xdfffffff, 0xefffffff, 0xf7ffffff,
  0xfbffffff, 0xfdffffff, 0xfeffffff, 0xff7fffff,
  0xffbfffff, 0xffdfffff, 0xffefffff, 0xfff7ffff,
  0xfffbffff, 0xfffdffff, 0xfffeffff, 0xffff7fff,
  0xffffbfff, 0xffffdfff, 0xffffefff, 0xfffff7ff,
  0xfffffbff, 0xfffffdff, 0xfffffeff, 0xffffff7f,
  0xffffffbf, 0xffffffdf, 0xffffffef, 0xfffffff7,
  0xfffffffb, 0xfffffffd, 0xfffffffe, 0xffffffff
};
static const struct PatternData walkingZeros = {
  "walkingZeros",
  walkingZeros_data,
  (sizeof walkingZeros_data / sizeof walkingZeros_data[0]) - 1,
  {1, 1, 2, 1}
};

static unsigned int OneZero_data[] =   { 0x00000000, 0xffffffff};
static const struct PatternData OneZero = {
  "OneZero",
  OneZero_data,
  (sizeof OneZero_data / sizeof OneZero_data[0]) - 1,
  {5, 5, 15, 5}
};

static unsigned int JustZero_data[] =   { 0x00000000, 0x00000000};
static const struct PatternData JustZero = {
  "JustZero",
  JustZero_data,
  (sizeof JustZero_data / sizeof JustZero_data[0]) - 1,
  {2, 0, 0, 0}
};

static unsigned int JustOne_data[] =   { 0xffffffff, 0xffffffff};
static const struct PatternData JustOne = {
  "JustOne",
  JustOne_data,
  (sizeof JustOne_data / sizeof JustOne_data[0]) - 1,
  {2, 0, 0, 0}
};

static unsigned int JustFive_data[] =   { 0x55555555, 0x55555555};
static const struct PatternData JustFive = {
  "JustFive",
  JustFive_data,
  (sizeof JustFive_data / sizeof JustFive_data[0]) - 1,
  {2, 0, 0, 0}
};

static unsigned int JustA_data[] =   { 0xaaaaaaaa, 0xaaaaaaaa};
static const struct PatternData JustA = {
  "JustA",
  JustA_data,
  (sizeof JustA_data / sizeof JustA_data[0]) - 1,
  {2, 0, 0, 0}
};

static unsigned int FiveA_data[] =   { 0x55555555, 0xaaaaaaaa};
static const struct PatternData FiveA = {
  "FiveA",
  FiveA_data,
  (sizeof FiveA_data / sizeof FiveA_data[0]) - 1,
  {1, 1, 1, 1}
};

static unsigned int FiveA8_data[] =   {
  0x5aa5a55a, 0xa55a5aa5, 0xa55a5aa5, 0x5aa5a55a
};
static const struct PatternData FiveA8 = {
  "FiveA8",
  FiveA8_data,
  (sizeof FiveA8_data / sizeof FiveA8_data[0]) - 1,
  {1, 1, 1, 1}
};

static unsigned int Long8b10b_data[] =   { 0x16161616, 0x16161616 };
static const struct PatternData Long8b10b = {
  "Long8b10b",
  Long8b10b_data,
  (sizeof Long8b10b_data / sizeof Long8b10b_data[0]) - 1,
  {2, 0, 0, 0}
};

static unsigned int Short8b10b_data[] =   { 0xb5b5b5b5, 0xb5b5b5b5 };
static const struct PatternData Short8b10b = {
  "Short8b10b",
  Short8b10b_data,
  (sizeof Short8b10b_data / sizeof Short8b10b_data[0]) - 1,
  {2, 0, 0, 0}
};

static unsigned int Checker8b10b_data[] =   { 0xb5b5b5b5, 0x4a4a4a4a };
static const struct PatternData Checker8b10b = {
  "Checker8b10b",
  Checker8b10b_data,
  (sizeof Checker8b10b_data / sizeof Checker8b10b_data[0]) - 1,
  {1, 0, 0, 1}
};

static unsigned int Five7_data[] =   { 0x55555557, 0x55575555 };
static const struct PatternData Five7 = {
  "Five7",
  Five7_data,
  (sizeof Five7_data / sizeof Five7_data[0]) - 1,
  {0, 2, 0, 0}
};

static unsigned int Zero2fd_data[] =   { 0x00020002, 0xfffdfffd };
static const struct PatternData Zero2fd = {
  "Zero2fd",
  Zero2fd_data,
  (sizeof Zero2fd_data / sizeof Zero2fd_data[0]) - 1,
  {0, 2, 0, 0}
};

// Extern array of useable patterns.
static const struct PatternData pattern_array[] = {
  walkingOnes,
  walkingInvOnes,
  walkingZeros,
  OneZero,
  JustZero,
  JustOne,
  JustFive,
  JustA,
  FiveA,
  FiveA8,
  Long8b10b,
  Short8b10b,
  Checker8b10b,
  Five7,
  Zero2fd,
};
static const int pattern_array_size =
    sizeof pattern_array / sizeof pattern_array[0];

Pattern::Pattern() {
  crc_ = NULL;
}

Pattern::~Pattern() {
  if (crc_ != NULL) {
    delete crc_;
  }
}

// Calculate CRC for this pattern. This must match
// the CRC calculation in worker.cc.
int Pattern::CalculateCrc() {
  // TODO(johnhuang):
  // Consider refactoring to the form:
  // while (i < count) AdlerInc(uint64, uint64, AdlerChecksum*)
  uint64 a1 = 1;
  uint64 a2 = 1;
  uint64 b1 = 0;
  uint64 b2 = 0;

  // checksum is calculated using only the first 4096 bytes of data.
  int i = 0;
  int blocksize = 4096;
  int count = blocksize / sizeof i;
  while (i < count) {
    a1 += pattern(i);
    b1 += a1;
    i++;
    a1 += pattern(i);
    b1 += a1;
    i++;

    a2 += pattern(i);
    b2 += a2;
    i++;
    a2 += pattern(i);
    b2 += a2;
    i++;
  }
  if (crc_ != NULL) {
    delete crc_;
  }
  crc_ = new AdlerChecksum();
  crc_->Set(a1, a2, b1, b2);
  return 0;
}

// Initialize pattern's CRC.
int Pattern::Initialize(const struct PatternData &pattern_init,
                        int buswidth,
                        bool invert,
                        int weight) {
  int result = 1;

  pattern_ = &pattern_init;
  busshift_ = 2;
  inverse_ = invert;
  weight_ = weight;

  name_.clear();
  name_.append(pattern_->name);

  if (invert)
    name_.append("~");

  if (buswidth == 32) {
    name_.append("32");
    busshift_ = 0;
  } else if (buswidth == 64) {
    name_.append("64");
    busshift_ = 1;
  } else if (buswidth == 128) {
    name_.append("128");
    busshift_ = 2;
  } else if (buswidth == 256) {
    name_.append("256");
    busshift_ = 3;
  } else {
    logprintf(0, "Process Error: Confused by bus width %d\n",
              buswidth);
    name_.append("Broken");
    result = 0;
  }

  CalculateCrc();

  return result;
}


PatternList::PatternList() {
  size_= 0;
  initialized_ = 0;
}

PatternList::~PatternList() {
  if (initialized_) {
    Destroy();
  }
}

// Fill in the class with references to the static data patterns
int PatternList::Initialize() {
  int patterncount = 0;
  int weightcount = 0;

  patterns_.resize(pattern_array_size * 8);
  for (int i = 0; i < pattern_array_size; i++) {
    // Non inverted.
    weightcount += pattern_array[i].weight[0];
    patterns_[patterncount++].Initialize(pattern_array[i], 32, false,
                                         pattern_array[i].weight[0]);
    weightcount += pattern_array[i].weight[1];
    patterns_[patterncount++].Initialize(pattern_array[i], 64, false,
                                         pattern_array[i].weight[1]);
    weightcount += pattern_array[i].weight[2];
    patterns_[patterncount++].Initialize(pattern_array[i], 128, false,
                                         pattern_array[i].weight[2]);
    weightcount += pattern_array[i].weight[3];
    patterns_[patterncount++].Initialize(pattern_array[i], 256, false,
                                         pattern_array[i].weight[3]);

    // Inverted.
    weightcount += pattern_array[i].weight[0];
    patterns_[patterncount++].Initialize(pattern_array[i], 32, true,
                                         pattern_array[i].weight[0]);
    weightcount += pattern_array[i].weight[1];
    patterns_[patterncount++].Initialize(pattern_array[i], 64, true,
                                         pattern_array[i].weight[1]);
    weightcount += pattern_array[i].weight[2];
    patterns_[patterncount++].Initialize(pattern_array[i], 128, true,
                                         pattern_array[i].weight[2]);
    weightcount += pattern_array[i].weight[3];
    patterns_[patterncount++].Initialize(pattern_array[i], 256, true,
                                         pattern_array[i].weight[3]);
  }
  size_ = patterncount;
  weightcount_ = weightcount;
  initialized_ = 1;

  logprintf(12, "Log: initialized %d data patterns\n", size_);

  return 1;
}

// Free the stuff.
int PatternList::Destroy() {
  if (!initialized_)
    return 0;

  patterns_.clear();
  size_ = 0;
  initialized_ = 0;

  return 1;
}

// Return pattern numbered "i"
Pattern *PatternList::GetPattern(int i) {
  if (static_cast<unsigned int>(i) < size_) {
    return &patterns_[i];
  }

  logprintf(0, "Process Error: Out of bounds pattern access\n");
  return 0;
}

// Return a randomly selected pattern.
Pattern *PatternList::GetRandomPattern() {
  int target = random();
  unsigned int i = 0;
  target = (target % weightcount_) + 1;

  do {
    target -= patterns_[i].weight();
    if (target <= 0)
      break;
    i++;
  } while (i < size_);

  if (i < size_) {
    return &patterns_[i];
  }

  logprintf(0, "Process Error: Out of bounds pattern access\n");
  return 0;
}
