#include "utils.h"

inline ZYM::int2048 Any2Int(const std::any &value) {
  if (auto ptr = std::any_cast<ZYM::int2048>(&value))
    return *ptr;
  else if (auto ptr = std::any_cast<int>(&value))
    return std::move(ZYM::int2048(*ptr));
  else if (auto ptr = std::any_cast<double>(&value)) {
    std::stringstream buf;
    buf << std::fixed << std::setprecision(0) << *ptr;
    std::string tmp;
    buf >> tmp;
    return std::move(ZYM::int2048(tmp));
  } else if (auto ptr = std::any_cast<bool>(&value))
    return std::move(ZYM::int2048((long long)(*ptr)));
  else if (auto ptr = std::any_cast<std::string>(&value)) {
    std::string str = *ptr;
    size_t dot_position = str.find('.');
    if (dot_position != std::string::npos) {
      std::string integer_part = str.substr(0, dot_position);
      str = integer_part;
    }
    return std::move(ZYM::int2048(str));
  } else
    throw FatalError("Any2Int2048: unknown type");
}

double Any2Float(const std::any &value) {
  if (auto ptr = std::any_cast<double>(&value))
    return *ptr;
  else if (auto ptr = std::any_cast<ZYM::int2048>(&value)) {
    double res = 0;
    std::stringstream buf;
    buf << *ptr;
    std::string tmp;
    buf >> tmp;
    int p = 0;
    if (tmp[0] == '-') p++;
    for (; p < tmp.size(); p++) res = res * 10 + tmp[p] - '0';
    if (tmp[0] == '-') res = -res;
    return res;
  } else if (auto ptr = std::any_cast<bool>(&value))
    return (double)(*ptr);
  else if (auto ptr = std::any_cast<std::string>(&value))
    return std::stod(*ptr);
  else
    throw FatalError("Any2Float: unknown type");
}

std::string Any2String(const std::any &value) {
  std::stringstream buf;
  std::string res;
  if (auto ptr = std::any_cast<double>(&value)) {
    buf << std::setiosflags(std::ios::fixed) << std::setprecision(15) << *ptr;
  } else if (auto ptr = std::any_cast<bool>(&value)) {
    if (*ptr)
      buf << "True";
    else
      buf << "False";
  } else if (auto ptr = std::any_cast<ZYM::int2048>(&value))
    buf << *ptr;
  else if (auto ptr = std::any_cast<std::string>(&value))
    buf << *ptr;
  else if (auto ptr = std::any_cast<NoneType>(&value))
    buf << "None";
  else
    throw FatalError("Any2String: unknown type");
  std::getline(buf, res);
  return res;
}

bool Any2Bool(const std::any &value) {
  if (auto ptr = std::any_cast<ZYM::int2048>(&value)) {
    return (*ptr) != 0;
  } else if (auto ptr = std::any_cast<double>(&value)) {
    return (*ptr) != 0;
  } else if (auto ptr = std::any_cast<bool>(&value)) {
    return *ptr;
  } else if (auto ptr = std::any_cast<std::string>(&value)) {
    return (*ptr) != "";
  } else
    throw FatalError("Any2Bool: unknown type");
}

std::any DeQuate(std::any val, VariableContainer &Variables) {
  if (auto ptr = std::any_cast<std::vector<std::any>>(&val)) {
    for (int i = 0; i < ptr->size(); i++)
      (*ptr)[i] = DeQuate((*ptr)[i], Variables);
    return *ptr;
  } else if (auto ptr = std::any_cast<RawVarible>(&val)) {
    return Variables.ReadVariable(ptr->name);
  } else if (std::any_cast<double>(&val) || std::any_cast<ZYM::int2048>(&val) ||
             std::any_cast<bool>(&val) || std::any_cast<std::string>(&val) ||
             std::any_cast<NoneType>(&val))
    return val;
  else
    throw FatalError("DeQuate: unknown type");
}

int ConverToSameArithType(std::any &a, std::any &b,
                          bool allow_string_operation) {
  ZYM::int2048 *ptr_a_int2048 = std::any_cast<ZYM::int2048>(&a);
  ZYM::int2048 *ptr_b_int2048 = std::any_cast<ZYM::int2048>(&b);
  double *ptr_a_float = std::any_cast<double>(&a);
  double *ptr_b_float = std::any_cast<double>(&b);
  bool *ptr_a_bool = std::any_cast<bool>(&a);
  bool *ptr_b_bool = std::any_cast<bool>(&b);
  std::string *ptr_a_string = std::any_cast<std::string>(&a);
  std::string *ptr_b_string = std::any_cast<std::string>(&b);
  if ((!allow_string_operation) && (ptr_a_string || ptr_b_string))
    throw InterpretException(
        "ConverToSameArithType: string operation not allowed in this "
        "situation");
  if (allow_string_operation && (ptr_a_string || ptr_b_string)) {
    if (!(ptr_a_string || ptr_a_int2048))
      throw InterpretException(
          "ConverToSameArithType: string operation not allowed in this "
          "situation");
    if (!(ptr_b_string || ptr_b_int2048))
      throw InterpretException(
          "ConverToSameArithType: string operation not allowed in this "
          "situation");
    return 1;
  }
  int level_a = (ptr_a_bool != nullptr ? 1 : 0) +
                (ptr_a_int2048 != nullptr ? 2 : 0) +
                (ptr_a_float != nullptr ? 3 : 0);
  int level_b = (ptr_b_bool != nullptr ? 1 : 0) +
                (ptr_b_int2048 != nullptr ? 2 : 0) +
                (ptr_b_float != nullptr ? 3 : 0);
  int res_level = std::max(level_a, level_b);
  switch (res_level) {
    case 1:
      break;
    case 2:
      a = Any2Int(a);
      b = Any2Int(b);
      break;
    case 3:
      a = Any2Float(a);
      b = Any2Float(b);
      break;
    default:
      throw FatalError("ConverToSameArithType: unknown type");
  }
  return 0;
}

std::any Add(std::any a, std::any b) {
  int status = ConverToSameArithType(a, b, true);
  bool *ptr_a_bool = std::any_cast<bool>(&a);
  bool *ptr_b_bool = std::any_cast<bool>(&b);
  ZYM::int2048 *ptr_a_int2048 = std::any_cast<ZYM::int2048>(&a);
  ZYM::int2048 *ptr_b_int2048 = std::any_cast<ZYM::int2048>(&b);
  double *ptr_a_float = std::any_cast<double>(&a);
  double *ptr_b_float = std::any_cast<double>(&b);
  std::string *ptr_a_string = std::any_cast<std::string>(&a);
  std::string *ptr_b_string = std::any_cast<std::string>(&b);
  if ((ptr_a_string != nullptr ? 1 : 0) + (ptr_b_string != nullptr ? 1 : 0) ==
      1) {
    throw InterpretException(
        "Add: string operation not allowed in this situation");
  }
  if ((ptr_a_string != nullptr ? 1 : 0) + (ptr_b_string != nullptr ? 1 : 0) ==
      2)
    return (*ptr_a_string) + (*ptr_b_string);
  if ((ptr_a_bool != nullptr) && (ptr_b_bool != nullptr))
    return (*ptr_a_bool) + (*ptr_b_bool);
  else if ((ptr_a_int2048 != nullptr) && (ptr_b_int2048 != nullptr))
    return (*ptr_a_int2048) + (*ptr_b_int2048);
  else if ((ptr_a_float != nullptr) && (ptr_b_float != nullptr))
    return (*ptr_a_float) + (*ptr_b_float);
  else
    throw FatalError("Add: Type Error");
}
std::any Sub(std::any a, std::any b) {
  ConverToSameArithType(a, b);
  bool *ptr_a_bool = std::any_cast<bool>(&a);
  bool *ptr_b_bool = std::any_cast<bool>(&b);
  ZYM::int2048 *ptr_a_int2048 = std::any_cast<ZYM::int2048>(&a);
  ZYM::int2048 *ptr_b_int2048 = std::any_cast<ZYM::int2048>(&b);
  double *ptr_a_float = std::any_cast<double>(&a);
  double *ptr_b_float = std::any_cast<double>(&b);
  if ((ptr_a_bool != nullptr) && (ptr_b_bool != nullptr))
    return (*ptr_a_bool) - (*ptr_b_bool);
  else if ((ptr_a_int2048 != nullptr) && (ptr_b_int2048 != nullptr))
    return (*ptr_a_int2048) - (*ptr_b_int2048);
  else if ((ptr_a_float != nullptr) && (ptr_b_float != nullptr))
    return (*ptr_a_float) - (*ptr_b_float);
  else
    throw FatalError("Sub: Type Error");
  return a;
}
std::any Mul(std::any a, std::any b) {
  int status = ConverToSameArithType(a, b, true);
  bool *ptr_a_bool = std::any_cast<bool>(&a);
  bool *ptr_b_bool = std::any_cast<bool>(&b);
  ZYM::int2048 *ptr_a_int2048 = std::any_cast<ZYM::int2048>(&a);
  ZYM::int2048 *ptr_b_int2048 = std::any_cast<ZYM::int2048>(&b);
  double *ptr_a_float = std::any_cast<double>(&a);
  double *ptr_b_float = std::any_cast<double>(&b);
  std::string *ptr_a_string = std::any_cast<std::string>(&a);
  std::string *ptr_b_string = std::any_cast<std::string>(&b);
  if ((ptr_a_string != nullptr ? 1 : 0) + (ptr_b_string != nullptr ? 1 : 0) ==
      2) {
    throw InterpretException(
        "Mul: string operation not allowed in this situation");
  } else if ((ptr_a_string != nullptr ? 1 : 0) +
                 (ptr_b_string != nullptr ? 1 : 0) ==
             1) {
    if (ptr_a_string == nullptr) {
      std::swap(ptr_a_string, ptr_b_string);
      std::swap(ptr_a_bool, ptr_b_bool);
      std::swap(ptr_a_int2048, ptr_b_int2048);
      std::swap(ptr_a_float, ptr_b_float);
    }
    if (ptr_b_float != nullptr)
      throw InterpretException("Mul: string*float not allowed");
    if (ptr_b_bool != nullptr) {
      if (*ptr_b_bool) {
        return *ptr_a_string;
      } else {
        return "";
      }
    }
    if (ptr_b_int2048 != nullptr) {
      std::string res;
      for (int i = 0; i < (*ptr_b_int2048); i++) res += *ptr_a_string;
      return res;
    }
  }
  if ((ptr_a_bool != nullptr) && (ptr_b_bool != nullptr))
    return (*ptr_a_bool) * (*ptr_b_bool);
  else if ((ptr_a_int2048 != nullptr) && (ptr_b_int2048 != nullptr))
    return (*ptr_a_int2048) * (*ptr_b_int2048);
  else if ((ptr_a_float != nullptr) && (ptr_b_float != nullptr))
    return (*ptr_a_float) * (*ptr_b_float);
  else
    throw FatalError("Mul: Type Error");
}
std::any Div(std::any a, std::any b) {
  bool *ptr_a_bool = std::any_cast<bool>(&a);
  bool *ptr_b_bool = std::any_cast<bool>(&b);
  ZYM::int2048 *ptr_a_int2048 = std::any_cast<ZYM::int2048>(&a);
  ZYM::int2048 *ptr_b_int2048 = std::any_cast<ZYM::int2048>(&b);
  double *ptr_a_float = std::any_cast<double>(&a);
  double *ptr_b_float = std::any_cast<double>(&b);
  std::string *ptr_a_string = std::any_cast<std::string>(&a);
  std::string *ptr_b_string = std::any_cast<std::string>(&b);
  if ((ptr_a_string != nullptr) || (ptr_b_string != nullptr))
    throw InterpretException(
        "Div: string operation not allowed in this situation");
  double t_a = Any2Float(a);
  double t_b = Any2Float(b);
  if (t_b == 0) throw InterpretException("Div: divided by zero");
  t_a = t_a / t_b;
  return a;
}
std::any Divv(std::any a, std::any b) {
  ConverToSameArithType(a, b);
  bool *ptr_a_bool = std::any_cast<bool>(&a);
  bool *ptr_b_bool = std::any_cast<bool>(&b);
  ZYM::int2048 *ptr_a_int2048 = std::any_cast<ZYM::int2048>(&a);
  ZYM::int2048 *ptr_b_int2048 = std::any_cast<ZYM::int2048>(&b);
  double *ptr_a_float = std::any_cast<double>(&a);
  double *ptr_b_float = std::any_cast<double>(&b);
  if ((ptr_a_float != nullptr) && (ptr_b_float != nullptr)) {
    if ((*ptr_b_float) == 0) throw InterpretException("Divv: divided by zero");
    return ZYM::int2048(std::floor((*ptr_a_float) / (*ptr_b_float)));
  } else if ((ptr_a_int2048 != nullptr) && (ptr_b_int2048 != nullptr)) {
    if ((*ptr_b_int2048) == 0)
      throw InterpretException("Divv: divided by zero");
    return (*ptr_a_int2048) / (*ptr_b_int2048);
  } else if ((ptr_a_bool != nullptr) && (ptr_b_bool != nullptr)) {
    if ((*ptr_b_bool) == 0) throw InterpretException("Divv: divided by zero");
    return ZYM::int2048((*ptr_a_bool) / (*ptr_b_bool));
  } else
    throw FatalError("Divv: Type Error");
}
std::any Mod(std::any a, std::any b) {
  ConverToSameArithType(a, b);
  bool *ptr_a_bool = std::any_cast<bool>(&a);
  bool *ptr_b_bool = std::any_cast<bool>(&b);
  ZYM::int2048 *ptr_a_int2048 = std::any_cast<ZYM::int2048>(&a);
  ZYM::int2048 *ptr_b_int2048 = std::any_cast<ZYM::int2048>(&b);
  double *ptr_a_float = std::any_cast<double>(&a);
  double *ptr_b_float = std::any_cast<double>(&b);
  if ((ptr_a_float != nullptr) && (ptr_b_float != nullptr)) {
    if ((*ptr_b_float) == 0) throw InterpretException("Mod: divided by zero");
    return ZYM::int2048((*ptr_a_float) -
                        (*ptr_b_float) *
                            std::floor((*ptr_a_float) / (*ptr_b_float)));
  } else if ((ptr_a_int2048 != nullptr) && (ptr_b_int2048 != nullptr)) {
    if ((*ptr_b_int2048) == 0) throw InterpretException("Mod: divided by zero");
    return (*ptr_a_int2048) % (*ptr_b_int2048);
  } else if ((ptr_a_bool != nullptr) && (ptr_b_bool != nullptr)) {
    if ((*ptr_b_bool) == 0) throw InterpretException("Mod: divided by zero");
    return ZYM::int2048((*ptr_a_bool) % (*ptr_b_bool));
  } else
    throw FatalError("Mod: Type Error");
}
std::any &SelfAdd(std::any &a, std::any b) {
  a = Add(a, b);
  return a;
}
std::any &SelfSub(std::any &a, std::any b) {
  a = Sub(a, b);
  return a;
}
std::any &SelfMul(std::any &a, std::any b) {
  a = Mul(a, b);
  return a;
}
std::any &SelfDiv(std::any &a, std::any b) {
  a = Div(a, b);
  return a;
}
std::any &SelfDivv(std::any &a, std::any b) {
  a = Divv(a, b);
  return a;
}
std::any &SelfMod(std::any &a, std::any b) {
  a = Mod(a, b);
  return a;
}
std::any Neg(std::any a) {
  bool *ptr_a_bool = std::any_cast<bool>(&a);
  ZYM::int2048 *ptr_a_int2048 = std::any_cast<ZYM::int2048>(&a);
  double *ptr_a_float = std::any_cast<double>(&a);
  if (ptr_a_bool != nullptr)
    return ZYM::int2048(-(*ptr_a_bool));
  else if (ptr_a_int2048 != nullptr)
    return -(*ptr_a_int2048);
  else if (ptr_a_float != nullptr)
    return -(*ptr_a_float);
  else
    throw FatalError("Neg: Type Error");
}

bool Greater(std::any a, std::any b) {
  ConverToSameArithType(a, b, true);
  bool *ptr_a_bool = std::any_cast<bool>(&a);
  bool *ptr_b_bool = std::any_cast<bool>(&b);
  ZYM::int2048 *ptr_a_int2048 = std::any_cast<ZYM::int2048>(&a);
  ZYM::int2048 *ptr_b_int2048 = std::any_cast<ZYM::int2048>(&b);
  double *ptr_a_float = std::any_cast<double>(&a);
  double *ptr_b_float = std::any_cast<double>(&b);
  std::string *ptr_a_string = std::any_cast<std::string>(&a);
  std::string *ptr_b_string = std::any_cast<std::string>(&b);
  if ((ptr_a_bool != nullptr) && (ptr_b_bool != nullptr))
    return (*ptr_a_bool) > (*ptr_b_bool);
  else if ((ptr_a_int2048 != nullptr) && (ptr_b_int2048 != nullptr))
    return (*ptr_a_int2048) > (*ptr_b_int2048);
  else if ((ptr_a_float != nullptr) && (ptr_b_float != nullptr))
    return (*ptr_a_float) > (*ptr_b_float);
  else if ((ptr_a_string != nullptr) && (ptr_b_string != nullptr))
    return (*ptr_a_string) > (*ptr_b_string);
  else
    throw FatalError("Greater: Type Error");
}
bool Less(std::any a, std::any b) {
  ConverToSameArithType(a, b, true);
  bool *ptr_a_bool = std::any_cast<bool>(&a);
  bool *ptr_b_bool = std::any_cast<bool>(&b);
  ZYM::int2048 *ptr_a_int2048 = std::any_cast<ZYM::int2048>(&a);
  ZYM::int2048 *ptr_b_int2048 = std::any_cast<ZYM::int2048>(&b);
  double *ptr_a_float = std::any_cast<double>(&a);
  double *ptr_b_float = std::any_cast<double>(&b);
  std::string *ptr_a_string = std::any_cast<std::string>(&a);
  std::string *ptr_b_string = std::any_cast<std::string>(&b);
  if ((ptr_a_bool != nullptr) && (ptr_b_bool != nullptr))
    return (*ptr_a_bool) < (*ptr_b_bool);
  else if ((ptr_a_int2048 != nullptr) && (ptr_b_int2048 != nullptr))
    return (*ptr_a_int2048) < (*ptr_b_int2048);
  else if ((ptr_a_float != nullptr) && (ptr_b_float != nullptr))
    return (*ptr_a_float) < (*ptr_b_float);
  else if ((ptr_a_string != nullptr) && (ptr_b_string != nullptr))
    return (*ptr_a_string) < (*ptr_b_string);
  else
    throw FatalError("Less: Type Error");
}
bool Equal(std::any a, std::any b) {
  ConverToSameArithType(a, b, true);
  bool *ptr_a_bool = std::any_cast<bool>(&a);
  bool *ptr_b_bool = std::any_cast<bool>(&b);
  ZYM::int2048 *ptr_a_int2048 = std::any_cast<ZYM::int2048>(&a);
  ZYM::int2048 *ptr_b_int2048 = std::any_cast<ZYM::int2048>(&b);
  double *ptr_a_float = std::any_cast<double>(&a);
  double *ptr_b_float = std::any_cast<double>(&b);
  std::string *ptr_a_string = std::any_cast<std::string>(&a);
  std::string *ptr_b_string = std::any_cast<std::string>(&b);
  if ((ptr_a_bool != nullptr) && (ptr_b_bool != nullptr))
    return (*ptr_a_bool) == (*ptr_b_bool);
  else if ((ptr_a_int2048 != nullptr) && (ptr_b_int2048 != nullptr))
    return (*ptr_a_int2048) == (*ptr_b_int2048);
  else if ((ptr_a_float != nullptr) && (ptr_b_float != nullptr))
    return (*ptr_a_float) == (*ptr_b_float);
  else if ((ptr_a_string != nullptr) && (ptr_b_string != nullptr))
    return (*ptr_a_string) == (*ptr_b_string);
  else
    throw FatalError("Equal: Type Error");
}
bool NotEqual(std::any a, std::any b) {
  ConverToSameArithType(a, b, true);
  bool *ptr_a_bool = std::any_cast<bool>(&a);
  bool *ptr_b_bool = std::any_cast<bool>(&b);
  ZYM::int2048 *ptr_a_int2048 = std::any_cast<ZYM::int2048>(&a);
  ZYM::int2048 *ptr_b_int2048 = std::any_cast<ZYM::int2048>(&b);
  double *ptr_a_float = std::any_cast<double>(&a);
  double *ptr_b_float = std::any_cast<double>(&b);
  std::string *ptr_a_string = std::any_cast<std::string>(&a);
  std::string *ptr_b_string = std::any_cast<std::string>(&b);
  if ((ptr_a_bool != nullptr) && (ptr_b_bool != nullptr))
    return (*ptr_a_bool) != (*ptr_b_bool);
  else if ((ptr_a_int2048 != nullptr) && (ptr_b_int2048 != nullptr))
    return (*ptr_a_int2048) != (*ptr_b_int2048);
  else if ((ptr_a_float != nullptr) && (ptr_b_float != nullptr))
    return (*ptr_a_float) != (*ptr_b_float);
  else if ((ptr_a_string != nullptr) && (ptr_b_string != nullptr))
    return (*ptr_a_string) != (*ptr_b_string);
  else
    throw FatalError("NotEqual: Type Error");
}
bool GreaterEqual(std::any a, std::any b) {
  ConverToSameArithType(a, b, true);
  bool *ptr_a_bool = std::any_cast<bool>(&a);
  bool *ptr_b_bool = std::any_cast<bool>(&b);
  ZYM::int2048 *ptr_a_int2048 = std::any_cast<ZYM::int2048>(&a);
  ZYM::int2048 *ptr_b_int2048 = std::any_cast<ZYM::int2048>(&b);
  double *ptr_a_float = std::any_cast<double>(&a);
  double *ptr_b_float = std::any_cast<double>(&b);
  std::string *ptr_a_string = std::any_cast<std::string>(&a);
  std::string *ptr_b_string = std::any_cast<std::string>(&b);
  if ((ptr_a_bool != nullptr) && (ptr_b_bool != nullptr))
    return (*ptr_a_bool) >= (*ptr_b_bool);
  else if ((ptr_a_int2048 != nullptr) && (ptr_b_int2048 != nullptr))
    return (*ptr_a_int2048) >= (*ptr_b_int2048);
  else if ((ptr_a_float != nullptr) && (ptr_b_float != nullptr))
    return (*ptr_a_float) >= (*ptr_b_float);
  else if ((ptr_a_string != nullptr) && (ptr_b_string != nullptr))
    return (*ptr_a_string) >= (*ptr_b_string);
  else
    throw FatalError("GreaterEqual: Type Error");
}
bool LessEqual(std::any a, std::any b) {
  ConverToSameArithType(a, b, true);
  bool *ptr_a_bool = std::any_cast<bool>(&a);
  bool *ptr_b_bool = std::any_cast<bool>(&b);
  ZYM::int2048 *ptr_a_int2048 = std::any_cast<ZYM::int2048>(&a);
  ZYM::int2048 *ptr_b_int2048 = std::any_cast<ZYM::int2048>(&b);
  double *ptr_a_float = std::any_cast<double>(&a);
  double *ptr_b_float = std::any_cast<double>(&b);
  std::string *ptr_a_string = std::any_cast<std::string>(&a);
  std::string *ptr_b_string = std::any_cast<std::string>(&b);
  if ((ptr_a_bool != nullptr) && (ptr_b_bool != nullptr))
    return (*ptr_a_bool) <= (*ptr_b_bool);
  else if ((ptr_a_int2048 != nullptr) && (ptr_b_int2048 != nullptr))
    return (*ptr_a_int2048) <= (*ptr_b_int2048);
  else if ((ptr_a_float != nullptr) && (ptr_b_float != nullptr))
    return (*ptr_a_float) <= (*ptr_b_float);
  else if ((ptr_a_string != nullptr) && (ptr_b_string != nullptr))
    return (*ptr_a_string) <= (*ptr_b_string);
  else
    throw FatalError("LessEqual: Type Error");
}