#ifndef __FAST_ARRAY_H__
#define __FAST_ARRAY_H__

#include "rings.h"
#include "options.h"

template < typename Ring >
struct Mapping {
  typedef typename Ring::T T;
  std::vector<T> data;

  Mapping() {}

  Mapping(const Mapping& x) : data(x.data) {}

  Mapping(size_t size = 0) : data(size, 0) {}
  inline size_t size() const { return data.size(); }

  inline T operator[] (size_t i) const { return data[i]; }
  inline void assign(size_t i, T val) { data[i] = val; }

  inline void combine(T beta, const Mapping& b) {
    size_t _size = size();
    for (size_t i = 0; i < _size; i++)
      data[i] = Ring::plus(data[i], Ring::multiply(beta, b.data[i]));
  }
};


#if OPTIMIZED_MAPPINGS
template < size_t i, size_t shift >
struct Consts {
  static const uint64_t clone = (Consts<i-1, shift>::clone << shift) | 1;
  static const uint64_t mask = (Consts<i-1, shift>::mask << shift) | 3;
};
template < size_t shift >
struct Consts<0, shift> {
  static const uint64_t clone = 0;
  static const uint64_t mask = 0;
};


template < bool isZ4 >
struct FastArray {
  typedef int T;
  enum { valuesPerWord = 16 };
  enum { words = (MAX_EDGES + valuesPerWord - 1) / valuesPerWord };
  enum { shift = 4 };

  static const uint64_t clone = Consts<valuesPerWord, shift>::clone;
  static const uint64_t mask = Consts<valuesPerWord, shift>::mask;

  size_t size_;
  uint64_t data[words];

  FastArray() {}

  FastArray(const FastArray& x) : size_(x.size_) {
    memcpy(data, x.data, sizeof(data));
  }

  FastArray(size_t size = 0) : size_(size) {
    assert(size <= MAX_EDGES);
    memset(data, 0, sizeof(data));
  }

  inline size_t size() const { return size_; }

  inline T operator[] (size_t i) const {
    return (data[i >> 4] >> ((i & 0xF) << 2)) & 3;
  }
  inline void assign(size_t i, T val) {
    size_t w = i >> 4;
    size_t off = i & 0xF;
    data[w] &= ~(((uint64_t)3) << (off << 2));
    data[w] |= (((uint64_t)val) << (off << 2));
  }

  inline void combine(T beta, const FastArray& b) {
    for (size_t i = 0; i < words; i++) {
      if (isZ4)
        data[i] = (data[i] + ((beta * b.data[i]) & mask)) & mask;
      else
        data[i] = data[i] ^ ((clone * beta) & b.data[i]);
    }
  }
};

template <> struct Mapping< Ring::Z4<int> > : public FastArray<true> {
  Mapping(const Mapping& x) : FastArray(x) {}
  Mapping(size_t s = 0) : FastArray(s) {}
};
template <> struct Mapping< Ring::Z2_2<int> > : public FastArray<false> {
  Mapping(const Mapping& x) : FastArray(x) {}
  Mapping(size_t s = 0) : FastArray(s) {}
};
#endif

#endif