#include "./jsonio.h"

#include "./jsonio_types.h"

/*
  As used by Python's numpy, which we interoperate with.
*/
string ndarray_dtype(const double & /* x */)
{
  return "float64";
}
string ndarray_dtype(const float & /* x */)
{
  return "float32";
}
string ndarray_dtype(const U8 & /* x */)
{
  return "uint8";
}
string ndarray_dtype(const U16 & /* x */)
{
  return "uint16";
}
string ndarray_dtype(const U32 & /* x */)
{
  return "uint32";
}
string ndarray_dtype(const U64 & /* x */)
{
  return "uint64";
}
string ndarray_dtype(const S8 & /* x */)
{
  return "int8";
}
string ndarray_dtype(const S16 & /* x */)
{
  return "int16";
}
string ndarray_dtype(const S32 & /* x */)
{
  return "int32";
}
string ndarray_dtype(const S64 & /* x */)
{
  return "int64";
}
string ndarray_dtype(const std::complex<double> & /* x */)
{
  return "complex64";
}

/* ----------------------------------------------------------------------
   Basic C++ types
*/

// Json - bool

void wrJsonSize(WrJsonContext &ctx, bool const &value)
{
  ctx.size += 5;
}

void wrJson(WrJsonContext &ctx, bool const &value)
{
  if (value) {
    ctx.emit("true");
  }
  else {
    ctx.emit("false");
  }
}

bool rdJson(RdJsonContext &ctx, bool &value)
{
  u_char c;
  ctx.skipSpace();
  c = *ctx.s++;
  if (c == 't') {
    c = *ctx.s++;
    if (c == 'r') {
      c = *ctx.s++;
      if (c == 'u') {
        c = *ctx.s++;
        if (c == 'e') {
          value = true;
          return true;
        }
      }
    }
  }
  else if (c == 'f') {
    c = *ctx.s++;
    if (c == 'a') {
      c = *ctx.s++;
      if (c == 'l') {
        c = *ctx.s++;
        if (c == 's') {
          c = *ctx.s++;
          if (c == 'e') {
            value = false;
            return true;
          }
        }
      }
    }
  }
  ctx.s--;
  return ctx.fail(typeid(bool), "expected true or false");
}

/*
  Json - U8
*/

void wrJsonSize(WrJsonContext &ctx, U8 const &value)
{
  if (value == 0) {
    ctx.size += 1;
  }
  else {
    ctx.size += 5;
  }
}

void wrJson(WrJsonContext &ctx, U8 const &value)
{
  if (value == 0) {
    *ctx.s++ = '0';
  }
  else {
    ctx.s += snprintf(ctx.s, 5, "%u", (unsigned int)value);
  }
}

bool rdJson(RdJsonContext &ctx, U8 &value)
{
  char *end = nullptr;
  ctx.skipSpace();
  value = (U8)strtoul(ctx.s, &end, 10);
  ctx.s = end;
  return true;
}

/*
  Json - S16
*/

void wrJsonSize(WrJsonContext &ctx, S16 const &value)
{
  if (value == 0) {
    ctx.size += 1;
  }
  else {
    ctx.size += 8;
  }
}

void wrJson(WrJsonContext &ctx, S16 const &value)
{
  if (value == 0) {
    *ctx.s++ = '0';
  }
  else {
    ctx.s += snprintf(ctx.s, 8, "%d", (int)value);
  }
}

bool rdJson(RdJsonContext &ctx, S16 &value)
{
  char *end = nullptr;
  ctx.skipSpace();
  value = (S16)strtol(ctx.s, &end, 10);
  ctx.s = end;
  return true;
}

/*
  Json - S32
*/

void wrJsonSize(WrJsonContext &ctx, S32 const &value)
{
  if (value == 0) {
    ctx.size += 1;
  }
  else {
    ctx.size += 12;
  }
}

void wrJson(WrJsonContext &ctx, S32 const &value)
{
  if (value == 0) {
    *ctx.s++ = '0';
  }
  else {
    ctx.s += snprintf(ctx.s, 12, "%d", value);
  }
}

bool rdJson(RdJsonContext &ctx, S32 &value)
{
  char *end = nullptr;
  ctx.skipSpace();
  value = strtol(ctx.s, &end, 10);
  ctx.s = end;
  return true;
}

/*
  Json - U32
*/

void wrJsonSize(WrJsonContext &ctx, U32 const &value)
{
  if (value == 0) {
    ctx.size += 1;
  }
  else {
    ctx.size += 12;
  }
}

void wrJson(WrJsonContext &ctx, U32 const &value)
{
  if (value == 0) {
    *ctx.s++ = '0';
  }
  else {
    ctx.s += snprintf(ctx.s, 12, "%u", value);
  }
}

bool rdJson(RdJsonContext &ctx, U32 &value)
{
  char *end = nullptr;
  ctx.skipSpace();
  value = strtoul(ctx.s, &end, 10);
  ctx.s = end;
  return true;
}

/*
  Json - S64
*/

void wrJsonSize(WrJsonContext &ctx, S64 const &value)
{
  if (value == 0) {
    ctx.size += 1;
  }
  else {
    ctx.size += 20;
  }
}

void wrJson(WrJsonContext &ctx, S64 const &value)
{
  if (value == 0) {
    *ctx.s++ = '0';
  }
  else {
    ctx.s += snprintf(ctx.s, 20, "%lld", value);
  }
}

bool rdJson(RdJsonContext &ctx, S64 &value)
{
  char *end = nullptr;
  ctx.skipSpace();
  value = strtol(ctx.s, &end, 10);
  ctx.s = end;
  return true;
}

/*
  json - U64
*/

void wrJsonSize(WrJsonContext &ctx, U64 const &value)
{
  if (value == 0) {
    ctx.size += 1;
  }
  else {
    ctx.size += 20;
  }
}

void wrJson(WrJsonContext &ctx, U64 const &value)
{
  if (value == 0) {
    *ctx.s++ = '0';
  }
  else {
    ctx.s += snprintf(ctx.s, 20, "%llu", value);
  }
}

bool rdJson(RdJsonContext &ctx, U64 &value)
{
  char *end = nullptr;
  ctx.skipSpace();
  value = strtoul(ctx.s, &end, 10);
  ctx.s = end;
  return true;
}

/*
  Json - float
*/

void wrJsonSize(WrJsonContext &ctx, float const &value)
{
  if (value == 0.0f) {
    ctx.size += 1;
  }
  else {
    ctx.size += 20;
  }
}

void wrJson(WrJsonContext &ctx, float const &value)
{
  if (value == 0.0f) {
    *ctx.s++ = '0';
  }
  else {
    ctx.s += snprintf(ctx.s, 20, "%.9g", value);
  }
}

bool rdJson(RdJsonContext &ctx, float &value)
{
  char *end = nullptr;
  ctx.skipSpace();
  value = strtof(ctx.s, &end);
  ctx.s = end;
  return true;
}

/*
  Json - double
*/

void wrJsonSize(WrJsonContext &ctx, double const &value)
{
  if (value == 0.0 || value == 1.0) {
    ctx.size += 1;
  }
  else {
    ctx.size += 25;
  }
}

void wrJson(WrJsonContext &ctx, double const &value)
{
  if (value == 0.0) {
    // Surprisingly powerful optimization, since zero is so common
    *ctx.s++ = '0';
  }
  else if (value == 1.0) {
    *ctx.s++ = '1';
  }
  else if (isinf(value)) {
    // Javascript JSON can't handle literal inf or nan.
    // This is the way JS does it:
    //   JSON.stringify(+inf) === 'null'
    //   JSON.stringify(-inf) === 'null'
    //   JSON.stringify(nan) === 'null'
    // But this loses information. (In rdJson(...double &) below, it reads in 'null' as NaN).
    // Here, we care about infs so we write them as 1e+999
    if (value > 0) {
      ctx.s += snprintf(ctx.s, 25, "1e+999");
    }
    else {
      ctx.s += snprintf(ctx.s, 25, "-1e+999");
    }
  }
  else if (isnan(value)) {
    ctx.s += snprintf(ctx.s, 25, "null");
  }
  else {
    // We spend most of our time while writing big structures right here.
    // For a flavor of what goes on, see
    // http://sourceware.org/git/?p=glibc.git;a=blob;f=stdio-common/printf_fp.c;h=f9ea379b042c871992d2f076a4185ab84b2ce7d9;hb=refs/heads/master
    // It'd be ever so splendid if we could use %a, to print in hex, if only the browser could read it.
    ctx.s += snprintf(ctx.s, 25, "%.17g", value);
  }
}

bool rdJson(RdJsonContext &ctx, double &value)
{
  char *end = nullptr;
  ctx.skipSpace();
  if (ctx.s[0] == '0' && (ctx.s[1] == ',' || ctx.s[1] == '}' || ctx.s[1] == ']')) {
    value = 0.0;
    ctx.s++;
    return true;
  }
  if (ctx.s[0] == 'n' && ctx.s[1] == 'u' && ctx.s[2] == 'l' && ctx.s[3] == 'l'
      && (ctx.s[4] == ',' || ctx.s[4] == '}' || ctx.s[4] == ']')) {
    value = numeric_limits<double>::quiet_NaN();
    ctx.s += 4;
    return true;
  }
  value = strtod(ctx.s, &end);
  ctx.s = end;
  return true;
}

/*
  Json - string
*/

void wrJsonSize(WrJsonContext &ctx, string const &value)
{
  ctx.size += 2;
  for (auto vi : value) {
    u_char c = vi;
    if (c == (u_char)0x22) {
      ctx.size += 2;
    }
    else if (c == (u_char)0x5c) {
      ctx.size += 2;
    }
    else if (c < 0x20 || c >= 0x80) {
      ctx.size += 6;
    }
    else {
      ctx.size += 1;
    }
  }
}

void wrJson(WrJsonContext &ctx, string const &value)
{
  *ctx.s++ = 0x22;
  for (auto vi : value) {
    u_char c = vi;
    if (c == (u_char)0x22) {
      *ctx.s++ = 0x5c;
      *ctx.s++ = 0x22;
    }
    else if (c == (u_char)0x5c) {
      *ctx.s++ = 0x5c;
      *ctx.s++ = 0x5c;
    }
    else if (c == (u_char)0x0a) {
      *ctx.s++ = 0x5c;
      *ctx.s++ = 'n';
    }
    else if (c < 0x20) {
      // Only ascii control characters are turned into \uxxxx escapes.
      // Multibyte characters just get passed through, which is legal.
      *ctx.s++ = 0x5c;
      *ctx.s++ = 'u';
      *ctx.s++ = '0';
      *ctx.s++ = '0';
      *ctx.s++ = toHexDigit((c >> 4) & 0x0f);
      *ctx.s++ = toHexDigit((c >> 0) & 0x0f);
    }
    else {
      *ctx.s++ = c;
    }
  }
  *ctx.s++ = 0x22;
}

bool rdJson(RdJsonContext &ctx, string &value)
{
  u_char c;
  ctx.skipSpace();
  c = *ctx.s++;
  if (c == 0x22) {
    while (1) {
      c = *ctx.s++;
      if (c == 0x5c) {
        c = *ctx.s++;
        if (c == 0x5c) {
          value.push_back(0x5c);
        }
        else if (c == 0x22) {
          value.push_back(0x22);
        }
        else if (c == 'b') {
          value.push_back(0x08);
        }
        else if (c == 'f') {
          value.push_back(0x0c);
        }
        else if (c == 'n') {
          value.push_back(0x0a);
        }
        else if (c == 'r') {
          value.push_back(0x0d);
        }
        else if (c == 't') {
          value.push_back(0x09);
        }
        else if (c == 'u') {
          if (0) eprintf("Got unicode escape %s\n", ctx.s);
          uint32_t codept = 0;
          c = *ctx.s++;
          if (!isHexDigit(c)) return ctx.fail(typeid(value), "expected unicode escape hex");
          codept |= fromHexDigit(c) << 12;
          c = *ctx.s++;
          if (!isHexDigit(c)) return ctx.fail(typeid(value), "expected unicode escape hex");
          codept |= fromHexDigit(c) << 8;
          c = *ctx.s++;
          if (!isHexDigit(c)) return ctx.fail(typeid(value), "expected unicode escape hex");
          codept |= fromHexDigit(c) << 4;
          c = *ctx.s++;
          if (!isHexDigit(c)) return ctx.fail(typeid(value), "expected unicode escape hex");
          codept |= fromHexDigit(c) << 0;

          char mb[MB_LEN_MAX];
          int mblen = wctomb(mb, (wchar_t)codept);
          for (int mbi = 0; mbi < mblen; mbi++) {
            value.push_back(mb[mbi]);
          }
        }
        else {
          value.push_back(c);
        }
      }
      // WRITEME: handle other escapes
      else if (c == 0x22) {
        return true;
      }
      else if (c == 0) { // end of string
        ctx.s--;
        return ctx.fail(typeid(value), "end of string");
      }
      else if (c < 0x20) { // control character, error
        ctx.s--;
        return ctx.fail(typeid(value), "surprising control character");
      }
      else {
        value.push_back(c);
      }
    }
  }
  ctx.s--;
  return ctx.fail(typeid(value), "no closing quote");
}

/*
  Json -- jsonstr
  These are just passed verbatim to the stream
*/

void wrJsonSize(WrJsonContext &ctx, jsonstr const &value)
{
  if (value.it.empty()) {
    ctx.size += 4;
  }
  else {
    ctx.size += value.it.size();
  }
}

void wrJson(WrJsonContext &ctx, jsonstr const &value)
{
  if (value.it.empty()) {
    memcpy(ctx.s, "null", 4);
    ctx.s += 4;
  }
  else {
    memcpy(ctx.s, value.it.data(), value.it.size());
    ctx.s += value.it.size();
  }
}

bool rdJson(RdJsonContext &ctx, jsonstr &value)
{
  ctx.skipSpace();
  char const *begin = ctx.s;
  if (!ctx.skipValue()) {
    return ctx.fail(typeid(value), "skipping");
  }
  value.it = string(begin, ctx.s);
  value.blobs = ctx.blobs;
  if (0) eprintf("rdJson: read `%s'\n", value.it.c_str());
  return true;
}

/*
  Json - cx_double
*/

void wrJsonSize(WrJsonContext &ctx, std::complex<double> const &value)
{
  ctx.size += 8 + 8 + 1;
  wrJsonSize(ctx, value.real());
  wrJsonSize(ctx, value.imag());
}

void wrJson(WrJsonContext &ctx, std::complex<double> const &value)
{
  ctx.emit("{\"real\":");
  wrJson(ctx, value.real());
  ctx.emit(",\"imag\":");
  wrJson(ctx, value.imag());
  *ctx.s++ = '}';
}

bool rdJson(RdJsonContext &ctx, std::complex<double> &value)
{
  double value_real = 0.0, value_imag = 0.0;

  char c;
  ctx.skipSpace();
  c = *ctx.s++;
  if (c == '{') {
    while (1) {
      ctx.skipSpace();
      c = *ctx.s++;
      if (c == '}') {
        value = std::complex<double>(value_real, value_imag);
        return true;
      }
      else if (c == '\"') {
        c = *ctx.s++;
        if (c == 'r') {
          c = *ctx.s++;
          if (c == 'e') {
            c = *ctx.s++;
            if (c == 'a') {
              c = *ctx.s++;
              if (c == 'l') {
                c = *ctx.s++;
                if (c == '\"') {
                  c = *ctx.s++;
                  if (c == ':') {
                    if (rdJson(ctx, value_real)) {
                      ctx.skipSpace();
                      c = *ctx.s++;
                      if (c == ',') continue;
                      if (c == '}') {
                        value = std::complex<double>(value_real, value_imag);
                        return true;
                      }
                    }
                  }
                }
              }
            }
          }
        }
        if (c == 'i') {
          c = *ctx.s++;
          if (c == 'm') {
            c = *ctx.s++;
            if (c == 'a') {
              c = *ctx.s++;
              if (c == 'g') {
                c = *ctx.s++;
                if (c == '\"') {
                  c = *ctx.s++;
                  if (c == ':') {
                    if (rdJson(ctx, value_imag)) {
                      ctx.skipSpace();
                      c = *ctx.s++;
                      if (c == ',') continue;
                      if (c == '}') {
                        value = std::complex<double>(value_real, value_imag);
                        return true;
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
    return ctx.fail(typeid(value), "expected real or imag");
  }
  ctx.s--;
  return ctx.fail(typeid(value), "expected {");
}



/*
  packet/jsonstr
*/
namespace packetio {

void packet_wr_value(packet &p, jsonstr const &x)
{
  packet_wr_value(p, x.it);
}
string packet_get_typetag(jsonstr const & /* x */)
{
  return "json";
}
void packet_rd_value(packet &p, jsonstr &x)
{
  packet_rd_value(p, x.it);
}

std::function<void(packet &, jsonstr &)> packet_rd_value_compat(jsonstr const &x, string const &typetag)
{
  return static_cast<void (*)(packet &, jsonstr &)>(packet_rd_value);
}
}

/*
  MinMax from T
*/

#ifdef USE_EIGEN3
template <typename _Scalar, int _Rows, int _Cols>
MinMax mat_MinMax(Eigen::Matrix<_Scalar, _Rows, _Cols> const &arr)
{
  return MinMax((double)arr.minCoeff(), (double)arr.maxCoeff());
}

template <int _Rows, int _Cols>
MinMax mat_MinMax(Eigen::Matrix<std::complex<double>, _Rows, _Cols> const &arr)
{
  return MinMax(0.0, 0.0); // WRITEME
}
#endif

template <typename T>
void accum_range(MinMax &it, T value, bool &first)
{
  if (first) {
    it.min = (double)value;
    it.max = (double)value;
    first = false;
  }
  else {
    it.min = std::min(it.min, (double)value);
    it.max = std::max(it.max, (double)value);
  }
}

template <>
void accum_range(MinMax &it, std::complex<double> value, bool &first)
{
  if (first) {
    it.min = abs(value);
    it.max = abs(value);
    first = false;
  }
  else {
    it.min = std::min(it.min, abs(value));
    it.max = std::max(it.max, abs(value));
  }
}

template <>
void accum_range(MinMax &it, bool value, bool &first)
{
  if (first) {
    it.min = it.max = value ? 1.0 : 0.0;
    first = false;
  }
  else {
    if (value) {
      it.max = 1.0;
    }
    else {
      it.min = 0.0;
    }
  }
}

#ifdef USE_EIGEN3
/*
  Json - Eigen::Matrix
*/
template <typename _Scalar, int _Rows, int _Cols>
void wrJsonSize(WrJsonContext &ctx, Eigen::Matrix<_Scalar, _Rows, _Cols> const &obj)
{
  ctx.size += 102 + obj.size(); // brackets, commas
  for (long i = 0; i < obj.size(); i++) {
    wrJsonSize(ctx, obj.data()[i]);
  }
}

template <typename _Scalar, int _Rows, int _Cols>
void wrJson(WrJsonContext &ctx, Eigen::Matrix<_Scalar, _Rows, _Cols> const &obj)
{
  ctx.s += snprintf(
      ctx.s,
      100,
      "{\"eltype\":\"%s\",\"rows\":%ld,\"cols\":%ld,\"data\":[",
      ndarray_dtype(_Scalar()).c_str(),
      obj.rows(),
      obj.cols());
  bool sep = false;
  for (long i = 0; i < obj.size(); i++) {
    if (sep) *ctx.s++ = ',';
    sep = true;
    wrJson(ctx, obj.data()[i]);
  }
  *ctx.s++ = ']';
  *ctx.s++ = '}';
}

template <typename _Scalar, int _Rows, int _Cols>
bool rdJson(RdJsonContext &ctx, Eigen::Matrix<_Scalar, _Rows, _Cols> &obj)
{
  ctx.skipSpace();

  if (*ctx.s == '{') {
    ctx.s++;
    S64 rows = 0, cols = 0;
    string type;
    vector<_Scalar> data;
    bool typeOk = false, rowsOk = false, colsOk = false, dataOk = false;
    while (*ctx.s != 0 && *ctx.s != '}') {
      if (ctx.matchKey("__type")) {
        if (!rdJson(ctx, type)) return false;
        typeOk = true;
      }
      else if (ctx.matchKey("rows")) {
        if (!rdJson(ctx, rows)) return false;
        rowsOk = true;
      }
      else if (ctx.matchKey("cols")) {
        if (!rdJson(ctx, cols)) return false;
        colsOk = true;
      }
      else if (ctx.matchKey("data")) {
        if (!rdJson(ctx, data)) return false;
        dataOk = true;
      }
      else {
        if (!ctx.skipMember()) return false;
      }

      ctx.skipSpace();
      if (*ctx.s == ',') {
        ctx.s++;
      }
      else if (*ctx.s == '}') {
        ctx.s++;
        break;
      }
      else {
        return false;
      }
    }
    (void)typeOk;
    if (rowsOk && colsOk && dataOk) {
      obj = Eigen::Map<Eigen::Matrix<_Scalar, _Rows, _Cols>>(data.data(), rows, cols);
      return true;
    }
    else {
      return ctx.fail(typeid(obj), "Missing elements");
    }
  }
  else {
    return ctx.fail(typeid(obj), "Expected Eigen::Matrix representation");
  }
}
#endif

/*
  any
*/


void wrJsonSize(WrJsonContext &ctx, any const &value)
{
  if (auto v = any_cast<double>(&value)) {
    return wrJsonSize(ctx, *v);
  }
  if (auto v = any_cast<bool>(&value)) {
    return wrJsonSize(ctx, *v);
  }
  if (auto v = any_cast<string>(&value)) {
    return wrJsonSize(ctx, *v);
  }
  if (auto v = any_cast<map<string, any>>(&value)) {
    return wrJsonSize(ctx, *v);
  }
  if (auto v = any_cast<map<string, string>>(&value)) {
    return wrJsonSize(ctx, *v);
  }
  if (auto v = any_cast<map<string, double>>(&value)) {
    return wrJsonSize(ctx, *v);
  }
  if (auto v = any_cast<vector<any>>(&value)) {
    return wrJsonSize(ctx, *v);
  }
  if (auto v = any_cast<vector<string>>(&value)) {
    return wrJsonSize(ctx, *v);
  }
  if (auto v = any_cast<vector<double>>(&value)) {
    return wrJsonSize(ctx, *v);
  }
  throw runtime_error("Unknown any subtype");
}

void wrJson(WrJsonContext &ctx, any const &value)
{
  if (auto v = any_cast<double>(&value)) {
    return wrJson(ctx, *v);
  }
  if (auto v = any_cast<bool>(&value)) {
    return wrJson(ctx, *v);
  }
  if (auto v = any_cast<string>(&value)) {
    return wrJson(ctx, *v);
  }
  if (auto v = any_cast<map<string, any>>(&value)) {
    return wrJson(ctx, *v);
  }
  if (auto v = any_cast<map<string, string>>(&value)) {
    return wrJson(ctx, *v);
  }
  if (auto v = any_cast<map<string, double>>(&value)) {
    return wrJson(ctx, *v);
  }
  if (auto v = any_cast<vector<any>>(&value)) {
    return wrJson(ctx, *v);
  }
  if (auto v = any_cast<vector<string>>(&value)) {
    return wrJson(ctx, *v);
  }
  if (auto v = any_cast<vector<double>>(&value)) {
    return wrJson(ctx, *v);
  }
  throw runtime_error("Unknown any subtype");
}

bool rdJson(RdJsonContext &ctx, any &value)
{
  ctx.skipSpace();
  if (*ctx.s == '{') {
    value = map<string, any>();
    return rdJson(ctx, *any_cast<map<string, any>>(&value));
  }
  if (*ctx.s == '[') {
    value = vector<any>();
    return rdJson(ctx, *any_cast<vector<any>>(&value));
  }
  if (*ctx.s == '"') {
    value = ""s;
    return rdJson(ctx, *any_cast<string>(&value));
  }
  if (*ctx.s == 't' || *ctx.s == 'f') {
    value = false;
    return rdJson(ctx, *any_cast<bool>(&value));
  }

  value = 0.0;
  return rdJson(ctx, *any_cast<double>(&value));
}


/*
  Explicit template instantiation here, to save compilation time elsewhere
*/
#ifdef USE_EIGEN3

#define INSTANTIATE_EIGEN(_Scalar, _Rows, _Cols)                                                                     \
  template void wrJsonSize(WrJsonContext &ctx, Eigen::Matrix<_Scalar, _Rows, _Cols> const &arr);                   \
  template void wrJson(WrJsonContext &ctx, Eigen::Matrix<_Scalar, _Rows, _Cols> const &arr);                       \
  template bool rdJson(RdJsonContext &ctx, Eigen::Matrix<_Scalar, _Rows, _Cols> &arr);

INSTANTIATE_EIGEN(double, -1, -1)
INSTANTIATE_EIGEN(double, -1, 1)
INSTANTIATE_EIGEN(double, 1, -1)
INSTANTIATE_EIGEN(double, 1, 2)
INSTANTIATE_EIGEN(double, 1, 3)
INSTANTIATE_EIGEN(double, 1, 4)
INSTANTIATE_EIGEN(double, 1, 6)
INSTANTIATE_EIGEN(double, 2, 2)
INSTANTIATE_EIGEN(double, 3, 3)
INSTANTIATE_EIGEN(double, 4, 4)
INSTANTIATE_EIGEN(double, 6, 6)
INSTANTIATE_EIGEN(double, 2, 1)
INSTANTIATE_EIGEN(double, 3, 1)
INSTANTIATE_EIGEN(double, 4, 1)
INSTANTIATE_EIGEN(double, 6, 1)

INSTANTIATE_EIGEN(std::complex<double>, -1, -1)
INSTANTIATE_EIGEN(std::complex<double>, -1, 1)
INSTANTIATE_EIGEN(std::complex<double>, 1, -1)
INSTANTIATE_EIGEN(std::complex<double>, 1, 2)
INSTANTIATE_EIGEN(std::complex<double>, 1, 3)
INSTANTIATE_EIGEN(std::complex<double>, 1, 4)
INSTANTIATE_EIGEN(std::complex<double>, 1, 6)
INSTANTIATE_EIGEN(std::complex<double>, 2, 2)
INSTANTIATE_EIGEN(std::complex<double>, 3, 3)
INSTANTIATE_EIGEN(std::complex<double>, 4, 4)
INSTANTIATE_EIGEN(std::complex<double>, 6, 6)
INSTANTIATE_EIGEN(std::complex<double>, 2, 1)
INSTANTIATE_EIGEN(std::complex<double>, 3, 1)
INSTANTIATE_EIGEN(std::complex<double>, 4, 1)
INSTANTIATE_EIGEN(std::complex<double>, 6, 1)

INSTANTIATE_EIGEN(int, -1, -1)
INSTANTIATE_EIGEN(int, -1, 1)
INSTANTIATE_EIGEN(int, 1, -1)
INSTANTIATE_EIGEN(int, 1, 2)
INSTANTIATE_EIGEN(int, 1, 3)
INSTANTIATE_EIGEN(int, 1, 4)
INSTANTIATE_EIGEN(int, 1, 6)
INSTANTIATE_EIGEN(int, 2, 2)
INSTANTIATE_EIGEN(int, 3, 3)
INSTANTIATE_EIGEN(int, 4, 4)
INSTANTIATE_EIGEN(int, 6, 6)
INSTANTIATE_EIGEN(int, 2, 1)
INSTANTIATE_EIGEN(int, 3, 1)
INSTANTIATE_EIGEN(int, 4, 1)
INSTANTIATE_EIGEN(int, 6, 1)

#endif
